diff --git a/Program.fs b/Program.fs index caad791..23a8b17 100644 --- a/Program.fs +++ b/Program.fs @@ -12,7 +12,16 @@ let pcomment: Parser = (skipString "(*" |> anyStringBetween <| skipS //let pwhitespace: Parser = (pcomment <|> spaces) |> many1 |>> ignore let pwhitespace = spaces -let pnumber: Parser = 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 = pdouble .>> pwhitespace let pidentifier: Parser = 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 + | Grouping of Expression + | FuncCall of Expression * list + | Power of Expression * list | Negate of Expression - | Factor of Expression * List - | Term of Expression * List + | Factor of Expression * list + | Term of Expression * list 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 = 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 * Expression + | Nil | Fail of string // Store global variables -let mutable globals: Collections.Generic.Dictionary = new Collections.Generic.Dictionary() +let mutable globals = new Collections.Generic.Dictionary() -// Native/builtin functions +// natives +let mutable natives = new Collections.Generic.Dictionary -> 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) = + 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) = + 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) diff --git a/Readme.md b/Readme.md index 5cc8a2d..faf05a4 100644 --- a/Readme.md +++ b/Readme.md @@ -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 diff --git a/bin/Debug/net6.0/fskalc.dll b/bin/Debug/net6.0/fskalc.dll index b9fbf54..ecb9b2c 100644 Binary files a/bin/Debug/net6.0/fskalc.dll and b/bin/Debug/net6.0/fskalc.dll differ diff --git a/bin/Debug/net6.0/fskalc.pdb b/bin/Debug/net6.0/fskalc.pdb index a59ce24..f6695fe 100644 Binary files a/bin/Debug/net6.0/fskalc.pdb and b/bin/Debug/net6.0/fskalc.pdb differ diff --git a/obj/Debug/net6.0/fskalc.dll b/obj/Debug/net6.0/fskalc.dll index b9fbf54..ecb9b2c 100644 Binary files a/obj/Debug/net6.0/fskalc.dll and b/obj/Debug/net6.0/fskalc.dll differ diff --git a/obj/Debug/net6.0/fskalc.pdb b/obj/Debug/net6.0/fskalc.pdb index a59ce24..f6695fe 100644 Binary files a/obj/Debug/net6.0/fskalc.pdb and b/obj/Debug/net6.0/fskalc.pdb differ