diff --git a/packages/components/src/components/SquiggleItem.tsx b/packages/components/src/components/SquiggleItem.tsx index 48a8a0fb..52330e8b 100644 --- a/packages/components/src/components/SquiggleItem.tsx +++ b/packages/components/src/components/SquiggleItem.tsx @@ -200,6 +200,12 @@ export const SquiggleItem: React.FC = ({ {expression.value.toDateString()} ); + case "void": + return ( + + {"Void"} + + ); case "timeDuration": { return ( diff --git a/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_void_test.res b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_void_test.res new file mode 100644 index 00000000..a106a188 --- /dev/null +++ b/packages/squiggle-lang/__tests__/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression_void_test.res @@ -0,0 +1,20 @@ +open Jest +open Reducer_Peggy_TestHelpers + +describe("Peggy void", () => { + //literal + testToExpression("()", "{()}", ~v="()", ()) + testToExpression( + "fn()=1", + "{(:$_let_$ :fn (:$$_lambda_$$ [_] {1}))}", + ~v="@{fn: lambda(_=>internal code)}", + (), + ) + testToExpression("fn()=1; fn()", "{(:$_let_$ :fn (:$$_lambda_$$ [_] {1})); (:fn ())}", ~v="1", ()) + testToExpression( + "fn(a)=(); call fn(1)", + "{(:$_let_$ :fn (:$$_lambda_$$ [a] {()})); (:$_let_$ :_ {(:fn 1)})}", + ~v="@{_: (),fn: lambda(a=>internal code)}", + (), + ) +}) diff --git a/packages/squiggle-lang/package.json b/packages/squiggle-lang/package.json index 75531a28..1c4ba3ac 100644 --- a/packages/squiggle-lang/package.json +++ b/packages/squiggle-lang/package.json @@ -31,6 +31,7 @@ "format:prettier": "prettier --write .", "format": "yarn format:rescript && yarn format:prettier", "prepack": "yarn build && yarn test && yarn bundle", + "all:rescript": "yarn build:rescript && yarn test:rescript && yarn format:rescript", "all": "yarn build && yarn bundle && yarn test" }, "keywords": [ diff --git a/packages/squiggle-lang/src/js/index.ts b/packages/squiggle-lang/src/js/index.ts index 5e4cf2c1..acee005c 100644 --- a/packages/squiggle-lang/src/js/index.ts +++ b/packages/squiggle-lang/src/js/index.ts @@ -120,77 +120,86 @@ function createTsExport( x: expressionValue, environment: environment ): squiggleExpression { - switch (x.tag) { - case "EvArray": - // genType doesn't convert anything more than 2 layers down into {tag: x, value: x} - // format, leaving it as the raw values. This converts the raw values - // directly into typescript values. - // - // The casting here is because genType is about the types of the returned - // values, claiming they are fully recursive when that's not actually the - // case - return tag( - "array", - x.value.map( - (arrayItem): squiggleExpression => - convertRawToTypescript( - arrayItem as unknown as rescriptExport, - environment + switch (x) { + case "EvVoid": + return tag("void", ""); + default: { + switch (x.tag) { + case "EvArray": + // genType doesn't convert anything more than 2 layers down into {tag: x, value: x} + // format, leaving it as the raw values. This converts the raw values + // directly into typescript values. + // + // The casting here is because genType is about the types of the returned + // values, claiming they are fully recursive when that's not actually the + // case + return tag( + "array", + x.value.map( + (arrayItem): squiggleExpression => + convertRawToTypescript( + arrayItem as unknown as rescriptExport, + environment + ) ) - ) - ); - case "EvArrayString": - return tag("arraystring", x.value); - case "EvBool": - return tag("boolean", x.value); - case "EvCall": - return tag("call", x.value); - case "EvLambda": - return tag("lambda", x.value); - case "EvDistribution": - return tag("distribution", new Distribution(x.value, environment)); - case "EvNumber": - return tag("number", x.value); - case "EvRecord": - // genType doesn't support records, so we have to do the raw conversion ourself - let result: tagged<"record", { [key: string]: squiggleExpression }> = tag( - "record", - _.mapValues(x.value, (x: unknown) => - convertRawToTypescript(x as rescriptExport, environment) - ) - ); - return result; - case "EvString": - return tag("string", x.value); - case "EvSymbol": - return tag("symbol", x.value); - case "EvDate": - return tag("date", x.value); - case "EvTimeDuration": - return tag("timeDuration", x.value); - case "EvDeclaration": - return tag("lambdaDeclaration", x.value); - case "EvTypeIdentifier": - return tag("typeIdentifier", x.value); - case "EvType": - let typeResult: tagged<"type", { [key: string]: squiggleExpression }> = - tag( - "type", - _.mapValues(x.value, (x: unknown) => - convertRawToTypescript(x as rescriptExport, environment) - ) - ); - return typeResult; - case "EvModule": - let moduleResult: tagged< - "module", - { [key: string]: squiggleExpression } - > = tag( - "module", - _.mapValues(x.value, (x: unknown) => - convertRawToTypescript(x as rescriptExport, environment) - ) - ); - return moduleResult; + ); + case "EvArrayString": + return tag("arraystring", x.value); + case "EvBool": + return tag("boolean", x.value); + case "EvCall": + return tag("call", x.value); + case "EvLambda": + return tag("lambda", x.value); + case "EvDistribution": + return tag("distribution", new Distribution(x.value, environment)); + case "EvNumber": + return tag("number", x.value); + case "EvRecord": + // genType doesn't support records, so we have to do the raw conversion ourself + let result: tagged<"record", { [key: string]: squiggleExpression }> = + tag( + "record", + _.mapValues(x.value, (x: unknown) => + convertRawToTypescript(x as rescriptExport, environment) + ) + ); + return result; + case "EvString": + return tag("string", x.value); + case "EvSymbol": + return tag("symbol", x.value); + case "EvDate": + return tag("date", x.value); + case "EvTimeDuration": + return tag("timeDuration", x.value); + case "EvDeclaration": + return tag("lambdaDeclaration", x.value); + case "EvTypeIdentifier": + return tag("typeIdentifier", x.value); + case "EvType": + let typeResult: tagged< + "type", + { [key: string]: squiggleExpression } + > = tag( + "type", + _.mapValues(x.value, (x: unknown) => + convertRawToTypescript(x as rescriptExport, environment) + ) + ); + return typeResult; + case "EvModule": + let moduleResult: tagged< + "module", + { [key: string]: squiggleExpression } + > = tag( + "module", + _.mapValues(x.value, (x: unknown) => + convertRawToTypescript(x as rescriptExport, environment) + ) + ); + return moduleResult; + } + } } } diff --git a/packages/squiggle-lang/src/js/rescript_interop.ts b/packages/squiggle-lang/src/js/rescript_interop.ts index dcba24e6..80548a7f 100644 --- a/packages/squiggle-lang/src/js/rescript_interop.ts +++ b/packages/squiggle-lang/src/js/rescript_interop.ts @@ -131,7 +131,8 @@ export type squiggleExpression = | tagged<"record", { [key: string]: squiggleExpression }> | tagged<"type", { [key: string]: squiggleExpression }> | tagged<"typeIdentifier", string> - | tagged<"module", { [key: string]: squiggleExpression }>; + | tagged<"module", { [key: string]: squiggleExpression }> + | tagged<"void", string>; export { lambdaValue }; diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res index 14ffe685..9cc25725 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Expression/Reducer_Expression_ExpressionBuilder.res @@ -82,3 +82,5 @@ let eIdentifier = (name: string): expression => let eTypeIdentifier = (name: string): expression => name->BInternalExpressionValue.IEvTypeIdentifier->BExpressionT.EValue + +let eVoid: expression = BInternalExpressionValue.IEvVoid->BExpressionT.EValue diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy index f4acd4a3..befda109 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_GeneratedParser.peggy @@ -5,13 +5,12 @@ }} start - // = _nl start:typeExpression _nl finalComment? {return start} = _nl start:outerBlock _nl finalComment? {return start} zeroOMoreArgumentsBlockOrExpression = innerBlockOrExpression / lambda outerBlock - = statements:array_statements finalExpression: (statementSeparator @expression)? + = statements:array_statements finalExpression: (statementSeparator @expression)? { if (finalExpression != null) { statements.push(finalExpression) } return h.nodeBlock(statements) } / finalExpression: expression @@ -24,25 +23,31 @@ innerBlockOrExpression quotedInnerBlock = '{' _nl statements:array_statements finalExpression: (statementSeparator @expression) _nl '}' - { statements.push(finalExpression) - return h.nodeBlock(statements) } + { statements.push(finalExpression) + return h.nodeBlock(statements) } / '{' _nl finalExpression: expression _nl '}' - { return h.nodeBlock([finalExpression]) } + { return h.nodeBlock([finalExpression]) } array_statements = head:statement tail:(statementSeparator @array_statements ) { return [head, ...tail] } / head:statement - { return [head] } + { return [head] } statement = letStatement / defunStatement / typeStatement + / voidStatement + +voidStatement + = "call" _nl value:zeroOMoreArgumentsBlockOrExpression + { var variable = h.nodeIdentifier("_", location()); + return h.nodeLetStatement(variable, value); } letStatement = variable:identifier _ assignmentOp _nl value:zeroOMoreArgumentsBlockOrExpression - { return h.nodeLetStatement(variable, value) } + { return h.nodeLetStatement(variable, value) } defunStatement = variable:identifier '(' _nl args:array_parameters _nl ')' _ assignmentOp _nl body:innerBlockOrExpression @@ -53,13 +58,15 @@ defunStatement array_parameters = head:dollarIdentifier tail:(_ ',' _nl @dollarIdentifier)* - { return [head, ...tail]; } + { return [head, ...tail]; } + / "" + { return [h.nodeIdentifier("_", location())]; } expression = ifthenelse / ternary / logicalAdditive ifthenelse = 'if' __nl condition:logicalAdditive - __nl 'then' __nl trueExpression:innerBlockOrExpression + __nl 'then' __nl trueExpression:innerBlockOrExpression __nl 'else' __nl falseExpression:(ifthenelse/innerBlockOrExpression) { return h.nodeTernary(condition, trueExpression, falseExpression) } @@ -88,8 +95,8 @@ equality = left:relational _ operator:equalityOp _nl right:relational { return h.makeFunctionCall(h.toFunction[operator], [left, right])} / relational - - equalityOp "operator" = '=='/'!=' + + equalityOp "operator" = '=='/'!=' relational = left:additive _ operator:relationalOp _nl right:additive @@ -172,19 +179,25 @@ collectionElement array_functionArguments = head:expression tail:(_ ',' _nl @expression)* { return [head, ...tail]; } + / "" + {return [h.nodeVoid()];} atom = '(' _nl expression:expression _nl ')' {return expression} / basicValue basicValue = valueConstructor / basicLiteral - + basicLiteral = string / number / boolean / dollarIdentifierWithModule / dollarIdentifier + / voidLiteral + +voidLiteral 'void' + = "()" {return h.nodeVoid();} dollarIdentifierWithModule 'identifier' = head:$moduleIdentifier @@ -195,7 +208,7 @@ dollarIdentifierWithModule 'identifier' modifiers.unshift(head) modifiers.push(final) let modifiedIdentifier = modifiers.join('.') - return h.nodeIdentifier(modifiedIdentifier) + return h.nodeIdentifier(modifiedIdentifier, location()) } identifier 'identifier' @@ -232,8 +245,8 @@ float 'float' = $(((d+ "\." d*) / ("\." d+)) floatExponent? / d+ floatExponent) { return h.nodeFloat(parseFloat(text()))} - floatExponent = [e]i '-'? d+ - d = [0-9] + floatExponent = [e]i '-'? d+ + d = [0-9] boolean 'boolean' = ('true'/'false') @@ -247,10 +260,10 @@ valueConstructor lambda = '{' _nl '|' _nl args:array_parameters _nl '|' _nl statements:array_statements finalExpression: (statementSeparator @expression) _nl '}' - { statements.push(finalExpression) - return h.nodeLambda(args, h.nodeBlock(statements)) } + { statements.push(finalExpression) + return h.nodeLambda(args, h.nodeBlock(statements)) } / '{' _nl '|' _nl args:array_parameters _nl '|' _nl finalExpression: expression _nl '}' - { return h.nodeLambda(args, h.nodeBlock([finalExpression])) } + { return h.nodeLambda(args, h.nodeBlock([finalExpression])) } arrayConstructor 'array' = '[' _nl ']' @@ -289,7 +302,7 @@ __nl 'whitespace or newline' = (whiteSpaceCharactersOrComment / commentOrNewLine )+ statementSeparator 'statement separator' - = _ (';'/ commentOrNewLine)+ _nl + = _ (';'/ commentOrNewLine)+ _nl commentOrNewLine = finalComment? newLine diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.res index a38c66e9..193cb893 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_Parse.res @@ -34,6 +34,7 @@ type nodeModuleIdentifier = {...node, "value": string} type nodeString = {...node, "value": string} type nodeTernary = {...node, "condition": node, "trueExpression": node, "falseExpression": node} type nodeTypeIdentifier = {...node, "value": string} +type nodeVoid = node type peggyNode = | PgNodeBlock(nodeBlock) @@ -50,6 +51,7 @@ type peggyNode = | PgNodeString(nodeString) | PgNodeTernary(nodeTernary) | PgNodeTypeIdentifier(nodeTypeIdentifier) + | PgNodeVoid(nodeVoid) external castNodeBlock: node => nodeBlock = "%identity" external castNodeBoolean: node => nodeBoolean = "%identity" @@ -65,6 +67,7 @@ external castNodeModuleIdentifier: node => nodeModuleIdentifier = "%identity" external castNodeString: node => nodeString = "%identity" external castNodeTernary: node => nodeTernary = "%identity" external castNodeTypeIdentifier: node => nodeTypeIdentifier = "%identity" +external castNodeVoid: node => nodeVoid = "%identity" exception UnsupportedPeggyNodeType(string) // This should never happen; programming error let castNodeType = (node: node) => @@ -83,6 +86,7 @@ let castNodeType = (node: node) => | "String" => node->castNodeString->PgNodeString | "Ternary" => node->castNodeTernary->PgNodeTernary | "TypeIdentifier" => node->castNodeTypeIdentifier->PgNodeTypeIdentifier + | "Void" => node->castNodeVoid->PgNodeVoid | _ => raise(UnsupportedPeggyNodeType(node["type"])) } @@ -116,6 +120,7 @@ let rec pgToString = (peggyNode: peggyNode): string => { " " ++ toString(node["falseExpression"]) ++ ")" | PgNodeTypeIdentifier(node) => `#${node["value"]}` + | PgNodeVoid(_node) => "()" } } and toString = (node: node): string => node->castNodeType->pgToString diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression.res b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression.res index 6e04a55d..73247a8e 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression.res +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/Reducer_Peggy_ToExpression.res @@ -48,5 +48,6 @@ let rec fromNode = (node: Parse.node): expression => { ) | PgNodeTypeIdentifier(nodeTypeIdentifier) => ExpressionBuilder.eTypeIdentifier(nodeTypeIdentifier["value"]) + | PgNodeVoid(_) => ExpressionBuilder.eVoid } } diff --git a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/helpers.ts b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/helpers.ts index 57b85f9e..94975233 100644 --- a/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/helpers.ts +++ b/packages/squiggle-lang/src/rescript/Reducer/Reducer_Peggy/helpers.ts @@ -213,3 +213,7 @@ export function nodeTernary( export function nodeTypeIdentifier(typeValue: string) { return { type: "TypeIdentifier", value: typeValue }; } + +export function nodeVoid() { + return { type: "Void" }; +} diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalExpressionValue.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalExpressionValue.res index b21ba3c6..a4d6e713 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalExpressionValue.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_ExternalExpressionValue.res @@ -25,6 +25,7 @@ type rec externalExpressionValue = | EvTypeIdentifier(string) | EvModule(record) | EvType(record) + | EvVoid and record = Js.Dict.t and externalBindings = record and lambdaValue = { @@ -63,6 +64,7 @@ let rec toString = aValue => | EvTimeDuration(t) => DateTime.Duration.toString(t) | EvType(t) => `type${t->toStringRecord}` | EvTypeIdentifier(id) => `#${id}` + | EvVoid => `()` } and toStringRecord = aRecord => { let pairs = diff --git a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_InternalExpressionValue.res b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_InternalExpressionValue.res index 914729a5..af951d83 100644 --- a/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_InternalExpressionValue.res +++ b/packages/squiggle-lang/src/rescript/ReducerInterface/ReducerInterface_InternalExpressionValue.res @@ -23,6 +23,7 @@ type rec t = | IEvTimeDuration(float) | IEvType(map) | IEvTypeIdentifier(string) + | IEvVoid and map = Belt.Map.String.t and nameSpace = NameSpace(Belt.Map.String.t) and lambdaValue = { @@ -60,6 +61,7 @@ let rec toString = aValue => | IEvType(aMap) => aMap->toStringMap | IEvTimeDuration(t) => DateTime.Duration.toString(t) | IEvTypeIdentifier(id) => `#${id}` + | IEvVoid => `()` } and toStringMap = aMap => { let pairs = @@ -92,6 +94,7 @@ let toStringWithType = aValue => | IEvTimeDuration(_) => `Date::${toString(aValue)}` | IEvType(_) => `Type::${toString(aValue)}` | IEvTypeIdentifier(_) => `TypeIdentifier::${toString(aValue)}` + | IEvVoid => `Void` } let argsToString = (args: array): string => { @@ -135,6 +138,7 @@ type internalExpressionValueType = | EvtTimeDuration | EvtType | EvtTypeIdentifier + | EvtVoid type functionCallSignature = CallSignature(string, array) type functionDefinitionSignature = @@ -158,6 +162,7 @@ let valueToValueType = value => | IEvTimeDuration(_) => EvtTimeDuration | IEvType(_) => EvtType | IEvTypeIdentifier(_) => EvtTypeIdentifier + | IEvVoid => EvtVoid } let externalValueToValueType = (value: ExternalExpressionValue.t) => @@ -203,6 +208,7 @@ let valueTypeToString = (valueType: internalExpressionValueType): string => | EvtTimeDuration => `Duration` | EvtType => `Type` | EvtTypeIdentifier => `TypeIdentifier` + | EvtVoid => `Void` } let functionCallSignatureToString = (functionCallSignature: functionCallSignature): string => { @@ -232,6 +238,7 @@ let rec toExternal = (iev: t): ExternalExpressionValue.t => { | IEvType(v) => v->mapToExternal->EvType | IEvTypeIdentifier(v) => EvTypeIdentifier(v) | IEvBindings(v) => v->nameSpaceToTypeScriptBindings->EvModule + | IEvVoid => EvVoid } } and mapToExternal = v => @@ -271,6 +278,7 @@ let rec toInternal = (ev: ExternalExpressionValue.t): t => { | EvTimeDuration(v) => IEvTimeDuration(v) | EvType(v) => v->recordToInternal->IEvType | EvTypeIdentifier(v) => IEvTypeIdentifier(v) + | EvVoid => IEvVoid } } and recordToInternal = v =>