From 4d801c0b10ca4399d58848a4959271f96be116a4 Mon Sep 17 00:00:00 2001 From: Umur Ozkul Date: Mon, 25 Apr 2022 15:05:31 +0200 Subject: [PATCH 1/6] ternary operator mathjs parse (tested) --- .../Reducer/Reducer_MathJs/Reducer_MathJsParse_test.res | 5 +++++ .../Reducer/Reducer_MathJs/Reducer_MathJs_Parse.res | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsParse_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsParse_test.res index 988d5a88..ff9fd12c 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsParse_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_MathJs/Reducer_MathJsParse_test.res @@ -66,4 +66,9 @@ describe("MathJs parse", () => { describe("comments", () => { testDescriptionParse("define", "1 # This is a comment", "1") }) + + describe("ternary operator", () => { + testParse("1 ? 2 : 3", "ternary(1, 2, 3)") + testParse("1 ? 2 : 3 ? 4 : 5", "ternary(1, 2, ternary(3, 4, 5))") + }) }) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_Parse.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_Parse.res index 1f5df97d..704d1d38 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_Parse.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_Parse.res @@ -9,7 +9,7 @@ type node = {"type": string, "isNode": bool, "comment": string} type arrayNode = {...node, "items": array} type block = {"node": node} type blockNode = {...node, "blocks": array} -//conditionalNode +type conditionalNode = {...node, "condition": node, "trueExpr": node, "falseExpr": node} type constantNode = {...node, "value": unit} type functionAssignmentNode = {...node, "name": string, "params": array, "expr": node} type indexNode = {...node, "dimensions": array} @@ -31,6 +31,7 @@ external castAssignmentNode: node => assignmentNode = "%identity" external castAssignmentNodeWAccessor: node => assignmentNodeWAccessor = "%identity" external castAssignmentNodeWIndex: node => assignmentNodeWIndex = "%identity" external castBlockNode: node => blockNode = "%identity" +external castConditionalNode: node => conditionalNode = "%identity" external castConstantNode: node => constantNode = "%identity" external castFunctionAssignmentNode: node => functionAssignmentNode = "%identity" external castFunctionNode: node => functionNode = "%identity" @@ -58,6 +59,7 @@ type mathJsNode = | MjArrayNode(arrayNode) | MjAssignmentNode(assignmentNode) | MjBlockNode(blockNode) + | MjConditionalNode(conditionalNode) | MjConstantNode(constantNode) | MjFunctionAssignmentNode(functionAssignmentNode) | MjFunctionNode(functionNode) @@ -82,6 +84,7 @@ let castNodeType = (node: node) => { | "ArrayNode" => node->castArrayNode->MjArrayNode->Ok | "AssignmentNode" => node->decideAssignmentNode | "BlockNode" => node->castBlockNode->MjBlockNode->Ok + | "ConditionalNode" => node->castConditionalNode->MjConditionalNode->Ok | "ConstantNode" => node->castConstantNode->MjConstantNode->Ok | "FunctionAssignmentNode" => node->castFunctionAssignmentNode->MjFunctionAssignmentNode->Ok | "FunctionNode" => node->castFunctionNode->MjFunctionNode->Ok @@ -157,6 +160,10 @@ let rec toString = (mathJsNode: mathJsNode): string => { | MjAssignmentNode(aNode) => `${aNode["object"]->toStringSymbolNode} = ${aNode["value"]->toStringMathJsNode}` | MjBlockNode(bNode) => `{${bNode["blocks"]->toStringBlocks}}` + | MjConditionalNode(cNode) => + `ternary(${toStringMathJsNode(cNode["condition"])}, ${toStringMathJsNode( + cNode["trueExpr"], + )}, ${toStringMathJsNode(cNode["falseExpr"])})` | MjConstantNode(cNode) => cNode["value"]->toStringValue | MjFunctionAssignmentNode(faNode) => faNode->toStringFunctionAssignmentNode | MjFunctionNode(fNode) => fNode->toStringFunctionNode From c810c4576d4a67039d4829e69c20c99756d494df Mon Sep 17 00:00:00 2001 From: Umur Ozkul Date: Mon, 25 Apr 2022 15:30:27 +0200 Subject: [PATCH 2/6] reducer semantics ternary (tested) --- .../Reducer/Reducer_ternaryOperator_test.res | 6 +++--- .../Reducer_MathJs/Reducer_MathJs_ToExpression.res | 13 +++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res index 61bfc8c7..b04944b9 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res @@ -1,9 +1,9 @@ open Jest open Reducer_TestHelpers -Skip.describe("Parse ternary operator", () => { - testParseToBe("true ? 'YES' : 'NO'", "Ok('YES')") - testParseToBe("false ? 'YES' : 'NO'", "Ok('NO')") +describe("Parse ternary operator", () => { + testParseToBe("true ? 'YES' : 'NO'", "Ok((:$$ternary true 'YES' 'NO'))") + testParseToBe("2>1 ? 'YES' : 'NO'", "Ok((:$$ternary (:larger 2 1) 'YES' 'NO'))") }) Skip.describe("Evaluate ternary operator", () => { diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_ToExpression.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_ToExpression.res index 5dc53413..e600a156 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_ToExpression.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_MathJs/Reducer_MathJs_ToExpression.res @@ -116,6 +116,19 @@ let rec fromNode = (mathJsNode: Parse.node): result => rExpr } | MjBlockNode(bNode) => bNode["blocks"]->Belt.Array.map(toTagOrNode)->caseTagOrNodes + | MjConditionalNode(cndNode) => { + let rCondition = fromNode(cndNode["condition"]) + let rTrueExpr = fromNode(cndNode["trueExpr"]) + let rFalse = fromNode(cndNode["falseExpr"]) + + rCondition->Result.flatMap(condition => + rTrueExpr->Result.flatMap(trueExpr => + rFalse->Result.flatMap(falseExpr => + Builder.passToFunction("$$ternary", list{condition, trueExpr, falseExpr})->Ok + ) + ) + ) + } | MjConstantNode(cNode) => cNode["value"]->JavaScript.Gate.jsToEv->Result.flatMap(v => v->ExpressionT.EValue->Ok) | MjFunctionAssignmentNode(faNode) => caseFunctionAssignmentNode(faNode) From cbc2b73b205dc8eeb3be4e705f0ae3439843c68a Mon Sep 17 00:00:00 2001 From: Umur Ozkul Date: Mon, 25 Apr 2022 16:01:02 +0200 Subject: [PATCH 3/6] Evaluate ternary operator (tested) --- .../Reducer/Reducer_ternaryOperator_test.res | 2 +- .../Reducer_Dispatch_BuiltInMacros.res | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res index b04944b9..940ff3c7 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res @@ -6,7 +6,7 @@ describe("Parse ternary operator", () => { testParseToBe("2>1 ? 'YES' : 'NO'", "Ok((:$$ternary (:larger 2 1) 'YES' 'NO'))") }) -Skip.describe("Evaluate ternary operator", () => { +describe("Evaluate ternary operator", () => { testEvalToBe("true ? 'YES' : 'NO'", "Ok('YES')") testEvalToBe("false ? 'YES' : 'NO'", "Ok('NO')") }) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res index 5facc9e7..7174a5f7 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res @@ -158,6 +158,21 @@ let dispatchMacroCall = ( | _ => replaceSymbols(expression, bindings) } + let doTernary = ( + conditionExpr: expression, + trueExpression: expression, + falseExpression: expression, + bindings: ExpressionT.bindings, + ) => { + let rCondition = conditionExpr->reduceExpression(bindings) + rCondition->Result.flatMap(condition => + switch condition { + | EvBool(true) => trueExpression->Ok + | EvBool(false) => falseExpression->Ok + | _ => RESyntaxError("Boolean value expected for conditional")->Error + } + ) + } switch list { | list{ExpressionT.EValue(EvCall("$$bindings"))} => bindings->ExpressionT.EBindings->Ok @@ -173,6 +188,8 @@ let dispatchMacroCall = ( expression, } => doBindExpression(expression, bindings) + | list{ExpressionT.EValue(EvCall("$$ternary")), conditionExpr, trueExpr, falseExpr} => + doTernary(conditionExpr, trueExpr, falseExpr, bindings) | _ => list->ExpressionT.EList->Ok } } From ac8827da287ab1d3c88776800f20174eb93145b3 Mon Sep 17 00:00:00 2001 From: Umur Ozkul Date: Mon, 25 Apr 2022 17:15:28 +0200 Subject: [PATCH 4/6] ternary with bindings (failed) --- .../Reducer/Reducer_ternaryOperator_test.res | 9 +++++++++ .../Reducer_Dispatch_BuiltInMacros.res | 12 ++++++++++++ .../Reducer_Expression/Reducer_Expression.res | 6 +----- .../Reducer_Expression_Builder.res | 3 +++ 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res index 940ff3c7..79bd870a 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res @@ -4,9 +4,18 @@ open Reducer_TestHelpers describe("Parse ternary operator", () => { testParseToBe("true ? 'YES' : 'NO'", "Ok((:$$ternary true 'YES' 'NO'))") testParseToBe("2>1 ? 'YES' : 'NO'", "Ok((:$$ternary (:larger 2 1) 'YES' 'NO'))") + MyOnly.testParseToBe( + "x = true; x ? 'YES' : 'NO' in x", + "Ok((:$$bindExpression (:$$bindStatement (:$$bindings) (:$let :x true)) (:$$ternary :x 'YES' (:multiply (:multiply 'NO' :in) :x))))", + ) }) describe("Evaluate ternary operator", () => { testEvalToBe("true ? 'YES' : 'NO'", "Ok('YES')") + MyOnly.testEvalToBe("x = true; x ? 'YES' : 'NO' in x", "Ok('YES')") testEvalToBe("false ? 'YES' : 'NO'", "Ok('NO')") }) + +describe("Evaluate ternary operator in subscope", () => { + testEvalToBe("x = true ? 'YES' : 'NO' in x", "Ok('YES')") +}) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res index 7174a5f7..b174eb88 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res @@ -3,6 +3,7 @@ they take expressions as parameters and return a new expression. Macros are used to define language building blocks. They are like Lisp macros. */ +module Builder = Reducer_Expression_Builder module ExpressionT = Reducer_Expression_T module ExpressionValue = ReducerInterface.ExpressionValue module Result = Belt.Result @@ -164,6 +165,17 @@ let dispatchMacroCall = ( falseExpression: expression, bindings: ExpressionT.bindings, ) => { + // let rCondition = conditionExpr->Builder.asBindableExpression->reduceExpression(bindings) + let xString = switch Belt.Map.String.get(bindings, "x") { + | Some(EValue(value)) => value->ExpressionValue.toString + | None => "x Not Found" + } + + let bindingsString= Belt.Map.String.toArray(bindings)->Js.Array2.map( keyValue => { + let (key, ExpressionT.EValue(value)) = keyValue + `${key} = ${value->ExpressionValue.toString}` + })->Js.Array2.toString + Js.log(`ternary; bindings: {${bindingsString}}`) let rCondition = conditionExpr->reduceExpression(bindings) rCondition->Result.flatMap(condition => switch condition { diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res index d27ece78..8829b4af 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression.res @@ -77,11 +77,7 @@ let rec reduceExpression = (expression: t, bindings: T.bindings): result a->Belt.Map.String.set(p, e->T.EValue) ) - let newExpression = Builder.passToFunction( - "$$bindExpression", - list{Builder.passToFunction("$$bindings", list{}), expr}, - ) - reduceExpression(newExpression, bindings) + reduceExpression(Builder.asBindableExpression(expr), bindings) } /* diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Builder.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Builder.res index 4e5a988e..eab60bd2 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Builder.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_Builder.res @@ -14,3 +14,6 @@ let passToFunction = (fName: string, lispArgs: list): expression => let toEvSymbolValue = (name: string): expression => name->ExpressionValue.EvSymbol->ExpressionT.EValue + +let asBindableExpression = (expr: expression): expression => + passToFunction("$$bindExpression", list{passToFunction("$$bindings", list{}), expr}) From 8aa4248996f69fcffdf628dcd3c3ca10cf6457f3 Mon Sep 17 00:00:00 2001 From: Umur Ozkul Date: Wed, 4 May 2022 21:23:40 +0200 Subject: [PATCH 5/6] spelling --- .../__tests__/Reducer/Reducer_functionTricks_test.res | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res index 284364e6..d9f7e7c8 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_functionTricks_test.res @@ -62,7 +62,7 @@ describe("call and bindings", () => { testEvalToBe("f(x)=x+1; g(x)=f(x)+1; g(2)", "Ok(4)") }) -describe("function trics", () => { +describe("function tricks", () => { testParseToBe( "f(x)=f(y)=2; f(2)", "Ok((:$$block (:$$block (:$let :f (:$$lambda [x] (:$$block (:$let :f (:$$lambda [y] (:$$block 2)))))) (:f 2))))", From 99f54f596c2062ccebdec332eb0f5959d48e037c Mon Sep 17 00:00:00 2001 From: Umur Ozkul Date: Wed, 4 May 2022 22:36:34 +0200 Subject: [PATCH 6/6] ternary operator (tested) --- .../Reducer/Reducer_ternaryOperator_test.res | 10 ++++++---- .../src/rescript/Reducer/Reducer.res | 7 +------ .../src/rescript/Reducer/Reducer.resi | 1 - .../Reducer_Dispatch_BuiltInMacros.res | 19 +++++++++++++++++++ .../rescript/Reducer/Reducer_ErrorValue.res | 2 ++ 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res index 61bfc8c7..c0311450 100644 --- a/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_ternaryOperator_test.res @@ -1,12 +1,14 @@ open Jest open Reducer_TestHelpers -Skip.describe("Parse ternary operator", () => { - testParseToBe("true ? 'YES' : 'NO'", "Ok('YES')") - testParseToBe("false ? 'YES' : 'NO'", "Ok('NO')") +describe("Parse ternary operator", () => { + testParseToBe("true ? 'YES' : 'NO'", "Ok((:$$block (:$$ternary true 'YES' 'NO')))") }) -Skip.describe("Evaluate ternary operator", () => { +describe("Evaluate ternary operator", () => { testEvalToBe("true ? 'YES' : 'NO'", "Ok('YES')") testEvalToBe("false ? 'YES' : 'NO'", "Ok('NO')") + testEvalToBe("2 > 1 ? 'YES' : 'NO'", "Ok('YES')") + testEvalToBe("2 <= 1 ? 'YES' : 'NO'", "Ok('NO')") + testEvalToBe("1+1 ? 'YES' : 'NO'", "Error(Expected type: Boolean)") }) diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer.res index 0ea1a733..3b3450c3 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer.res @@ -19,12 +19,7 @@ let foreignFunctionInterface = ( argArray: array, environment: ExpressionValue.environment, ) => { - Lambda.foreignFunctionInterface( - lambdaValue, - argArray, - environment, - Expression.reduceExpression, - ) + Lambda.foreignFunctionInterface(lambdaValue, argArray, environment, Expression.reduceExpression) } let defaultEnvironment = ExpressionValue.defaultEnvironment diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer.resi b/packages/squiggle-lang/src/rescript/Reducer/Reducer.resi index 2cd4155b..d4e40a63 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer.resi +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer.resi @@ -12,7 +12,6 @@ type externalBindings = ReducerInterface_ExpressionValue.externalBindings @genType type lambdaValue = ReducerInterface_ExpressionValue.lambdaValue - @genType let evaluateUsingOptions: ( ~environment: option, diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res index c106c0ff..a771dd32 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Dispatch/Reducer_Dispatch_BuiltInMacros.res @@ -132,6 +132,23 @@ let dispatchMacroCall = ( eLambda(parameters, bindings->Bindings.toExternalBindings, lambdaDefinition), )->Ok + let doTernary = ( + condition: expression, + ifTrue: expression, + ifFalse: expression, + bindings: ExpressionT.bindings, + environment, + ): result => { + let rCondition = reduceExpression(condition, bindings, environment) + rCondition->Result.flatMap(conditionValue => + switch conditionValue { + | ExpressionValue.EvBool(false) => ExpressionWithContext.noContext(ifFalse)->Ok + | ExpressionValue.EvBool(true) => ExpressionWithContext.noContext(ifTrue)->Ok + | _ => REExpectedType("Boolean")->Error + } + ) + } + let expandExpressionList = (aList, bindings: ExpressionT.bindings, environment): result< expressionWithContext, errorValue, @@ -162,6 +179,8 @@ let dispatchMacroCall = ( lambdaDefinition, } => doLambdaDefinition(bindings, parameters, lambdaDefinition) + | list{ExpressionT.EValue(EvCall("$$ternary")), condition, ifTrue, ifFalse} => + doTernary(condition, ifTrue, ifFalse, bindings, environment) | _ => ExpressionWithContext.noContext(ExpressionT.EList(aList))->Ok } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res index 7964c3a4..4d859a79 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_ErrorValue.res @@ -13,6 +13,7 @@ type errorValue = | RESymbolNotFound(string) | RESyntaxError(string) | RETodo(string) // To do + | REExpectedType(string) type t = errorValue @@ -46,4 +47,5 @@ let errorToString = err => | RESymbolNotFound(symbolName) => `${symbolName} is not defined` | RESyntaxError(desc) => `Syntax Error: ${desc}` | RETodo(msg) => `TODO: ${msg}` + | REExpectedType(typeName) => `Expected type: ${typeName}` }