revisit function syntax, finish unfinished things

This commit is contained in:
Art 2023-01-13 21:07:12 +01:00
parent 4dcefea895
commit 1249bbd1b9
Signed by: prod2
GPG Key ID: F3BB5A97A70A8DDE
6 changed files with 124 additions and 53 deletions

View File

@ -12,7 +12,16 @@ let pcomment: Parser<unit, unit> = (skipString "(*" |> anyStringBetween <| skipS
//let pwhitespace: Parser<unit, unit> = (pcomment <|> spaces) |> many1 |>> ignore //let pwhitespace: Parser<unit, unit> = (pcomment <|> spaces) |> many1 |>> ignore
let pwhitespace = spaces let pwhitespace = spaces
let pnumber: Parser<double, unit> = pfloat .>> pwhitespace let numberFormat =
NumberLiteralOptions.AllowBinary |||
NumberLiteralOptions.AllowExponent |||
NumberLiteralOptions.AllowFraction |||
NumberLiteralOptions.AllowFractionWOIntegerPart |||
NumberLiteralOptions.AllowHexadecimal |||
NumberLiteralOptions.AllowOctal
let pdouble = numberLiteral numberFormat "number" |>> (fun nl -> double nl.String)
let pnumber: Parser<double, unit> = pdouble .>> pwhitespace
let pidentifier: Parser<string, unit> = let pidentifier: Parser<string, unit> =
let isIdentChar c = isLetter c || c = '_' let isIdentChar c = isLetter c || c = '_'
@ -30,6 +39,8 @@ type Symbol =
let pleftparen = skipString "(" .>> pwhitespace let pleftparen = skipString "(" .>> pwhitespace
let prightparen = skipString ")" .>> pwhitespace let prightparen = skipString ")" .>> pwhitespace
let pequal = skipString "=" .>> pwhitespace let pequal = skipString "=" .>> pwhitespace
let pcomma = skipString "," .>> pwhitespace
let pbang = skipString "!" .>> pwhitespace
let ppower = stringReturn "^" <| Caret .>> pwhitespace let ppower = stringReturn "^" <| Caret .>> pwhitespace
let pminus = stringReturn "-" <| Minus .>> pwhitespace let pminus = stringReturn "-" <| Minus .>> pwhitespace
let pplus = stringReturn "+" <| Plus .>> pwhitespace let pplus = stringReturn "+" <| Plus .>> pwhitespace
@ -51,11 +62,12 @@ let ppercent = stringReturn "%" <| Percent .>> pwhitespace
type Expression = type Expression =
| Number of double | Number of double
| VarGet of string | VarGet of string
| Grouping of Expression list | Grouping of Expression
| Power of Expression * List<Expression> | FuncCall of Expression * list<Expression>
| Power of Expression * list<Expression>
| Negate of Expression | Negate of Expression
| Factor of Expression * List<Symbol * Expression> | Factor of Expression * list<Symbol * Expression>
| Term of Expression * List<Symbol * Expression> | Term of Expression * list<Symbol * Expression>
let expr, exprref = createParserForwardedToRef() let expr, exprref = createParserForwardedToRef()
@ -66,9 +78,11 @@ let evarget = pidentifier |>> VarGet
// parentheses // parentheses
let egrouping = pleftparen >>. (many1 expr) .>> prightparen |>> Grouping let egrouping = pleftparen >>. (expr) .>> prightparen |>> Grouping
let primary = enumber <|> evarget <|> egrouping let efunccall = ((pidentifier |>> VarGet) .>>? pbang) .>>. (sepBy expr pcomma) |>> FuncCall
let primary = efunccall <|> enumber <|> evarget <|> egrouping
// power // power
@ -88,9 +102,10 @@ let enegate = choice [
let pfactorop = pstar <|> pslashslash <|> pslash <|> ppercent let pfactorop = pstar <|> pslashslash <|> pslash <|> ppercent
let pimplicitmul: Parser<Symbol, unit> = stringReturn "" <| Star
let efactor = choice [ let efactor = choice [
enegate .>>.? many1 (pfactorop .>>. enegate) |>> Factor enegate .>>.? many1 ((pfactorop .>>. enegate) <|> (pimplicitmul .>>. epower)) |>> Factor
enegate enegate
] ]
@ -113,7 +128,7 @@ type Statement =
let sexprstmt = expr |>> ExprStatement let sexprstmt = expr |>> ExprStatement
let sfuncdef = pidentifier .>>.? ((many1 pidentifier) .>>. (pequal >>. expr)) |>> (fun (a, (b, c)) -> FuncDef (a, b, c)) let sfuncdef = pidentifier .>>.? ((sepBy1 pidentifier pcomma) .>>. (pequal >>. expr)) |>> (fun (a, (b, c)) -> FuncDef (a, b, c))
let svarset = pidentifier .>>.? (pequal >>. expr) |>> VarSet let svarset = pidentifier .>>.? (pequal >>. expr) |>> VarSet
@ -124,34 +139,72 @@ let stmt = choice [
] ]
// REPL oriented => only 1 statement at once // REPL oriented => only 1 statement at once
let program = pwhitespace >>. stmt let program = pwhitespace >>. stmt .>> eof
type Value = type Value =
| Number of double | Number of double
| Native of string | Native of string
| Function of List<string> * Expression | Function of List<string> * Expression
| Nil
| Fail of string | Fail of string
// Store global variables // Store global variables
let mutable globals: Collections.Generic.Dictionary<string, Value> = new Collections.Generic.Dictionary<string, Value>() let mutable globals = new Collections.Generic.Dictionary<string, Value>()
// Native/builtin functions // natives
let mutable natives = new Collections.Generic.Dictionary<string, (List<Value> -> Value)>()
globals["sin"] <- Native "sin" // Natives
globals["cos"] <- Native "cos"
globals["tan"] <- Native "tan"
globals["arccos"] <- Native "arccos" let wrap1ArgNative name func =
globals["arcsin"] <- Native "arcsin" let native (args: List<Value>) =
globals["arctan"] <- Native "arctan" match args.Length with
| 1 ->
let arg = args[0]
match arg with
| Number x -> Number (func x)
| Fail _ -> arg
| _ -> Fail $"Invalid argument to '{name}'."
| _ -> Fail $"Invalid argument count to '{name}', expect 1, got {args.Length}."
globals[name] <- Native name
natives[name] <- native
globals["sqrt"] <- Native "sqrt" wrap1ArgNative "sin" Math.Sin
globals["log"] <- Native "log" wrap1ArgNative "cos" Math.Cos
globals["ln"] <- Native "ln" wrap1ArgNative "tan" Math.Tan
globals["exp"] <- Native "exp" wrap1ArgNative "arcsin" Math.Asin
wrap1ArgNative "arccos" Math.Acos
wrap1ArgNative "arctan" Math.Atan
wrap1ArgNative "ln" Math.Log
wrap1ArgNative "log" Math.Log10
wrap1ArgNative "exp" Math.Exp
wrap1ArgNative "sqrt" Math.Sqrt
let wrap2ArgNative name func =
let native (args: List<Value>) =
match args.Length with
| 2 ->
let arg = args[0]
let arg2 = args[1]
match arg, arg2 with
| Number x, Number y -> Number (func x y)
| Fail _, _ -> arg
| _, Fail _ -> arg2
| _ -> Fail $"Invalid argument to '{name}'."
| _ -> Fail $"Invalid argument count to '{name}', expect 2, got {args.Length}."
globals[name] <- Native name
natives[name] <- native
let logx logBase logArg =
Math.Log2(logArg) / Math.Log2(logBase)
let rtx rootBase rootArg =
rootArg ** (1.0/rootBase)
wrap2ArgNative "logx" logx
wrap2ArgNative "rtx" rtx
// native values // native values
@ -162,7 +215,6 @@ globals["inf"] <- Number infinity
globals["nan"] <- Number nan globals["nan"] <- Number nan
globals["ans"] <- Number 0 globals["ans"] <- Number 0
// exec // exec
let varGet key = let varGet key =
@ -180,11 +232,14 @@ let applyOp op left right =
| Percent, Number x, Number y -> Number (x % y) | Percent, Number x, Number y -> Number (x % y)
| SlashSlash, Number x, Number y -> Number (floor x / y) | SlashSlash, Number x, Number y -> Number (floor x / y)
| Caret, Number x, Number y -> Number (x ** y) | Caret, Number x, Number y -> Number (x ** y)
| _, Fail _, _ -> left
| _, _, Fail _ -> right
| _ -> Fail $"Invalid operation {op} on {left} and {right}." | _ -> Fail $"Invalid operation {op} on {left} and {right}."
let negate value = let negate value =
match value with match value with
| Number x -> Number -x | Number x -> Number -x
| Fail _ -> value
| _ -> Fail $"Attempt to negate invalid type {value}." | _ -> Fail $"Attempt to negate invalid type {value}."
let rec execExpr expr = let rec execExpr expr =
@ -204,32 +259,28 @@ let rec execExpr expr =
// parser only creates a power node if rights contains at least 1 element // parser only creates a power node if rights contains at least 1 element
let mutable res = execExpr rights[rights.Length - 1] let mutable res = execExpr rights[rights.Length - 1]
let mutable x = rights.Length - 2 let mutable x = rights.Length - 2
while x > 0 do while x >= 0 do
res <- applyOp Caret (execExpr rights[x]) res res <- applyOp Caret (execExpr rights[x]) res
x <- x - 1 x <- x - 1
applyOp Caret (execExpr left) res applyOp Caret (execExpr left) res
| Grouping exprs -> | Grouping expr -> execExpr expr
match exprs.Length with | FuncCall (expr, args) ->
| 1 -> execExpr exprs.Head let func = execExpr expr
| 0 -> Fail "() is an invalid expression" let args = args |> List.map execExpr
| _ ->
let func = exprs.Head |> execExpr
let args = exprs.Tail |> List.map execExpr
match func with match func with
| Function (parameters, f) -> | Function (parameters, f) ->
// function call let argLen = args.Length
let argLen = args.Length let paramLen = parameters.Length
let paramLen = parameters.Length if paramLen <> argLen then
if paramLen <> argLen then Fail $"Invalid number of arguments, expected {paramLen}, got {argLen}"
Fail $"Invalid number of arguments, expected {paramLen}, got {argLen}" else
else List.iter2 (fun k v -> globals[k] <- v) parameters args
List.iter2 (fun k v -> globals[k] <- v) parameters args execExpr f
execExpr f | Native nat ->
| Native n -> natives[nat] args
// native call TODO | Fail _ -> func
func | _ -> Fail $"Attempt to call an invalid type {func}"
| _ -> Fail $"Attempt to call an invalid type {func}"
let printValue value = let printValue value =
match value with match value with
@ -239,12 +290,16 @@ let printValue value =
let execStmt stmt = let execStmt stmt =
match stmt with match stmt with
| ExprStatement expr -> | ExprStatement expr ->
let res = execExpr expr let res = execExpr expr
globals["ans"] <- res match res with
| Fail _ -> ()
| _ -> globals["ans"] <- res
printValue res printValue res
| VarSet (name, expr) -> | VarSet (name, expr) ->
let res = execExpr expr let res = execExpr expr
globals[name] <- res match res with
| Fail _ -> ()
| _ -> globals[name] <- res
printValue res printValue res
| FuncDef (name, parameters, expr) -> globals[name] <- Function (parameters, expr) | FuncDef (name, parameters, expr) -> globals[name] <- Function (parameters, expr)

View File

@ -32,16 +32,23 @@ Example:
Parentheses increase precedence. Parentheses increase precedence.
### Parentheses for function calls ### Function calls
Parentheses can also represent function calls, if they contain more than one element (separated by a space). Functions are called with !.
Example: Example:
``` ```
(sin 50) sin! pi/2
``` ```
Multile arguments need to be separated with commas.
```
logx! 5, 3
```
### Arithmetic operators ### Arithmetic operators
The following operators are valid, in decreasing precedence: The following operators are valid, in decreasing precedence:
@ -53,6 +60,7 @@ The following operators are valid, in decreasing precedence:
- `/` division; left associative - `/` division; left associative
- `//` integer (flooring) division; left associative - `//` integer (flooring) division; left associative
- `%` modulo; left associative - `%` modulo; left associative
- if no binary operator is found, multiplication is assumed
- term precedence: - term precedence:
- `+` addition; left associative - `+` addition; left associative
- `-` subtraction; left associative - `-` subtraction; left associative
@ -85,6 +93,12 @@ Function declaration has the following syntax, demonstrated by an example below.
double x = x * 2 double x = x * 2
``` ```
Multiple parameters need to be declared with commas separating.
```
sum x, y = x + y
```
Warning! as there are no scopes in FSKalc, passing Warning! as there are no scopes in FSKalc, passing
arguments to functions will modify the global scope. arguments to functions will modify the global scope.
@ -102,10 +116,12 @@ arguments to functions will modify the global scope.
- sin, cos, tan - they take 1 argument, in radians - sin, cos, tan - they take 1 argument, in radians
- arcsin, arccos, arctan - they take 1 argument, they return in radians - arcsin, arccos, arctan - they take 1 argument, they return in radians
- sqrt - returns square root, optional second argument is the power, default of 2 - sqrt - returns square root
- log - logarithm, optional second argument is the base, default of 10 - log - logarithm base of 10
- ln - natural logarithm - ln - natural logarithm
- exp - returns e to the power of the argument - exp - returns e to the power of the argument
- rtx - returns the xth root (1st argument: base, 2nd argument: val)
- logx - returns the base x log (1st argument: base, 2nd argument: val)
## Comments ## Comments

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.