squiggle/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res
Umur Ozkul db050668d1 parser
toFunction

additive multiplicative

compact

whitespace

pow

relational equality

boolean

whitespace separator

left associative operators

expression

not

identifier

function call

array constructor

string

indexed values

ident

priority

block

outerBlock

optional final expression

statement separator

outerBlock

innerBlock

better errors

note xor

white space and record

unary minus

inner/outer block

statement

lambda

sort

lambda is a value constructor

lambdaCall

ternary

ternary

basicValue

cleanup

quotes

chained Functions

dot operators

unify unary operators

unify unary operatos

notes

notes

notes

notes

parser

priorities set

white space or newline defined

allow newlines

notes

function call has become a post operator

recordElement

recursive index

postOperatorToFunction

better integer

comments

notes

record priority

comment

atom

finalComment

generated parser

type cast

format

initiate test file

test initiated; todo nodeCall; nodeExpression

callIdentifier

recover extra

initiate testing

initial tests pass

tests pass

remove function node

ternary

test parse passed

to

tests pass

notes

sort

toExpression

format

notes

remove unused modules

remove unnecessary nodeLambdaCall

notes

note

fix construct array

comment test

todo

elixir pipe

fix toString

notes

initial to expression test

value test

parsing records

records

comments

ternary

ifthenelse

inner block passed

inner block

lambda

lambda

new parser tested

now test tricks

ternary in expression

to test lambda as argument

to test lambda in structures

Use peggy Parser

expectEvalError

macros tested

remove mathjs parse

reducer test

comparison operator
2022-05-19 01:23:43 +02:00

145 lines
4.7 KiB
Plaintext

module Bindings = Reducer_Expression_Bindings
module BuiltIn = Reducer_Dispatch_BuiltIn
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
module ExpressionValue = ReducerInterface.ExpressionValue
module Extra = Reducer_Extra
module Lambda = Reducer_Expression_Lambda
module Macro = Reducer_Expression_Macro
module MathJs = Reducer_MathJs
module Result = Belt.Result
module T = Reducer_Expression_T
type environment = ReducerInterface_ExpressionValue.environment
type errorValue = Reducer_ErrorValue.errorValue
type expression = T.expression
type expressionValue = ReducerInterface_ExpressionValue.expressionValue
type externalBindings = ReducerInterface_ExpressionValue.externalBindings
type internalCode = ReducerInterface_ExpressionValue.internalCode
type t = expression
/*
Converts a Squigle code to expression
*/
let parse = (peggyCode: string): result<t, errorValue> =>
peggyCode->Reducer_Peggy_Parse.parse->Result.map(node => Reducer_Peggy_ToExpression.fromNode(node))
/*
Recursively evaluate/reduce the expression (Lisp AST)
*/
let rec reduceExpression = (expression: t, bindings: T.bindings, environment: environment): result<
expressionValue,
'e,
> => {
// Js.log(`reduce: ${T.toString(expression)} bindings: ${bindings->Bindings.toString}`)
switch expression {
| T.EValue(value) => value->Ok
| T.EList(list) =>
switch list {
| list{EValue(EvCall(fName)), ..._args} =>
switch Macro.isMacroName(fName) {
// A macro expands then reduces itself
| true => Macro.doMacroCall(expression, bindings, environment, reduceExpression)
| false => reduceExpressionList(list, bindings, environment)
}
| _ => reduceExpressionList(list, bindings, environment)
}
}
}
and reduceExpressionList = (
expressions: list<t>,
bindings: T.bindings,
environment: environment,
): result<expressionValue, 'e> => {
let racc: result<list<expressionValue>, 'e> = expressions->Belt.List.reduceReverse(Ok(list{}), (
racc,
each: expression,
) =>
racc->Result.flatMap(acc => {
each
->reduceExpression(bindings, environment)
->Result.map(newNode => {
acc->Belt.List.add(newNode)
})
})
)
racc->Result.flatMap(acc => acc->reduceValueList(environment))
}
/*
After reducing each level of expression(Lisp AST), we have a value list to evaluate
*/
and reduceValueList = (valueList: list<expressionValue>, environment): result<
expressionValue,
'e,
> =>
switch valueList {
| list{EvCall(fName), ...args} =>
(fName, args->Belt.List.toArray)->BuiltIn.dispatch(environment, reduceExpression)
| list{EvLambda(_lamdaCall)} =>
valueList
->Lambda.checkIfReduced
->Result.flatMap(reducedValueList =>
reducedValueList->Belt.List.toArray->ExpressionValue.EvArray->Ok
)
| list{EvLambda(lamdaCall), ...args} =>
Lambda.doLambdaCall(lamdaCall, args, environment, reduceExpression)
| _ =>
valueList
->Lambda.checkIfReduced
->Result.flatMap(reducedValueList =>
reducedValueList->Belt.List.toArray->ExpressionValue.EvArray->Ok
)
}
let evalUsingBindingsExpression_ = (aExpression, bindings, environment): result<
expressionValue,
'e,
> => reduceExpression(aExpression, bindings, environment)
let evaluateUsingOptions = (
~environment: option<ReducerInterface_ExpressionValue.environment>,
~externalBindings: option<ReducerInterface_ExpressionValue.externalBindings>,
code: string,
): result<expressionValue, errorValue> => {
let anEnvironment = switch environment {
| Some(env) => env
| None => ReducerInterface_ExpressionValue.defaultEnvironment
}
let anExternalBindings = switch externalBindings {
| Some(bindings) => bindings
| None => ReducerInterface_ExpressionValue.defaultExternalBindings
}
let bindings = anExternalBindings->Bindings.fromExternalBindings
parse(code)->Result.flatMap(expr => evalUsingBindingsExpression_(expr, bindings, anEnvironment))
}
/*
Evaluates MathJs code and bindings via Reducer and answers the result
*/
let evaluate = (code: string): result<expressionValue, errorValue> => {
evaluateUsingOptions(~environment=None, ~externalBindings=None, code)
}
let eval = evaluate
let evaluatePartialUsingExternalBindings = (
code: string,
externalBindings: ReducerInterface_ExpressionValue.externalBindings,
environment: ReducerInterface_ExpressionValue.environment,
): result<externalBindings, errorValue> => {
let rAnswer = evaluateUsingOptions(
~environment=Some(environment),
~externalBindings=Some(externalBindings),
code,
)
switch rAnswer {
| Ok(EvRecord(externalBindings)) => Ok(externalBindings)
| Ok(_) =>
Error(Reducer_ErrorValue.RESyntaxError(`Partials must end with an assignment or record`))
| Error(err) => err->Error
}
}