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 = 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 isIdentChar c = isLetter c || c = '_'
@ -30,6 +39,8 @@ type Symbol =
let pleftparen = skipString "(" .>> pwhitespace
let prightparen = skipString ")" .>> pwhitespace
let pequal = skipString "=" .>> pwhitespace
let pcomma = skipString "," .>> pwhitespace
let pbang = skipString "!" .>> pwhitespace
let ppower = stringReturn "^" <| Caret .>> pwhitespace
let pminus = stringReturn "-" <| Minus .>> pwhitespace
let pplus = stringReturn "+" <| Plus .>> pwhitespace
@ -51,11 +62,12 @@ let ppercent = stringReturn "%" <| Percent .>> pwhitespace
type Expression =
| Number of double
| VarGet of string
| Grouping of Expression list
| Power of Expression * List<Expression>
| Grouping of Expression
| FuncCall of Expression * list<Expression>
| Power of Expression * list<Expression>
| Negate of Expression
| Factor of Expression * List<Symbol * Expression>
| Term of Expression * List<Symbol * Expression>
| Factor of Expression * list<Symbol * Expression>
| Term of Expression * list<Symbol * Expression>
let expr, exprref = createParserForwardedToRef()
@ -66,9 +78,11 @@ let evarget = pidentifier |>> VarGet
// 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
@ -88,9 +102,10 @@ let enegate = choice [
let pfactorop = pstar <|> pslashslash <|> pslash <|> ppercent
let pimplicitmul: Parser<Symbol, unit> = stringReturn "" <| Star
let efactor = choice [
enegate .>>.? many1 (pfactorop .>>. enegate) |>> Factor
enegate .>>.? many1 ((pfactorop .>>. enegate) <|> (pimplicitmul .>>. epower)) |>> Factor
enegate
]
@ -113,7 +128,7 @@ type Statement =
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
@ -124,34 +139,72 @@ let stmt = choice [
]
// REPL oriented => only 1 statement at once
let program = pwhitespace >>. stmt
let program = pwhitespace >>. stmt .>> eof
type Value =
| Number of double
| Native of string
| Function of List<string> * Expression
| Nil
| Fail of string
// 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"
globals["cos"] <- Native "cos"
globals["tan"] <- Native "tan"
// Natives
globals["arccos"] <- Native "arccos"
globals["arcsin"] <- Native "arcsin"
globals["arctan"] <- Native "arctan"
let wrap1ArgNative name func =
let native (args: List<Value>) =
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"
globals["log"] <- Native "log"
globals["ln"] <- Native "ln"
globals["exp"] <- Native "exp"
wrap1ArgNative "sin" Math.Sin
wrap1ArgNative "cos" Math.Cos
wrap1ArgNative "tan" Math.Tan
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
@ -162,7 +215,6 @@ globals["inf"] <- Number infinity
globals["nan"] <- Number nan
globals["ans"] <- Number 0
// exec
let varGet key =
@ -180,11 +232,14 @@ let applyOp op left right =
| Percent, Number x, Number y -> Number (x % y)
| SlashSlash, Number x, Number y -> Number (floor x / y)
| Caret, Number x, Number y -> Number (x ** y)
| _, Fail _, _ -> left
| _, _, Fail _ -> right
| _ -> Fail $"Invalid operation {op} on {left} and {right}."
let negate value =
match value with
| Number x -> Number -x
| Fail _ -> value
| _ -> Fail $"Attempt to negate invalid type {value}."
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
let mutable res = execExpr rights[rights.Length - 1]
let mutable x = rights.Length - 2
while x > 0 do
while x >= 0 do
res <- applyOp Caret (execExpr rights[x]) res
x <- x - 1
applyOp Caret (execExpr left) res
| Grouping exprs ->
match exprs.Length with
| 1 -> execExpr exprs.Head
| 0 -> Fail "() is an invalid expression"
| _ ->
let func = exprs.Head |> execExpr
let args = exprs.Tail |> List.map execExpr
| Grouping expr -> execExpr expr
| FuncCall (expr, args) ->
let func = execExpr expr
let args = args |> List.map execExpr
match func with
| Function (parameters, f) ->
// function call
let argLen = args.Length
let paramLen = parameters.Length
if paramLen <> argLen then
Fail $"Invalid number of arguments, expected {paramLen}, got {argLen}"
else
List.iter2 (fun k v -> globals[k] <- v) parameters args
execExpr f
| Native n ->
// native call TODO
func
| _ -> Fail $"Attempt to call an invalid type {func}"
match func with
| Function (parameters, f) ->
let argLen = args.Length
let paramLen = parameters.Length
if paramLen <> argLen then
Fail $"Invalid number of arguments, expected {paramLen}, got {argLen}"
else
List.iter2 (fun k v -> globals[k] <- v) parameters args
execExpr f
| Native nat ->
natives[nat] args
| Fail _ -> func
| _ -> Fail $"Attempt to call an invalid type {func}"
let printValue value =
match value with
@ -239,12 +290,16 @@ let printValue value =
let execStmt stmt =
match stmt with
| ExprStatement expr ->
let res = execExpr expr
globals["ans"] <- res
let res = execExpr expr
match res with
| Fail _ -> ()
| _ -> globals["ans"] <- res
printValue res
| VarSet (name, expr) ->
let res = execExpr expr
globals[name] <- res
match res with
| Fail _ -> ()
| _ -> globals[name] <- res
printValue res
| FuncDef (name, parameters, expr) -> globals[name] <- Function (parameters, expr)

View File

@ -32,16 +32,23 @@ Example:
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:
```
(sin 50)
sin! pi/2
```
Multile arguments need to be separated with commas.
```
logx! 5, 3
```
### Arithmetic operators
The following operators are valid, in decreasing precedence:
@ -53,6 +60,7 @@ The following operators are valid, in decreasing precedence:
- `/` division; left associative
- `//` integer (flooring) division; left associative
- `%` modulo; left associative
- if no binary operator is found, multiplication is assumed
- term precedence:
- `+` addition; left associative
- `-` subtraction; left associative
@ -85,6 +93,12 @@ Function declaration has the following syntax, demonstrated by an example below.
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
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
- arcsin, arccos, arctan - they take 1 argument, they return in radians
- sqrt - returns square root, optional second argument is the power, default of 2
- log - logarithm, optional second argument is the base, default of 10
- sqrt - returns square root
- log - logarithm base of 10
- ln - natural logarithm
- 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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.