revisit function syntax, finish unfinished things
This commit is contained in:
parent
4dcefea895
commit
1249bbd1b9
151
Program.fs
151
Program.fs
|
@ -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)
|
||||
|
||||
|
|
26
Readme.md
26
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
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue