Merge pull request #628 from quantified-uncertainty/reducer-type-grammar
Reducer type grammar - preview release
This commit is contained in:
commit
9b0def16ef
|
@ -1,26 +1,5 @@
|
||||||
module Parse = Reducer_Peggy_Parse
|
|
||||||
module Result = Belt.Result
|
|
||||||
|
|
||||||
open Jest
|
open Jest
|
||||||
open Expect
|
open Reducer_Peggy_TestHelpers
|
||||||
|
|
||||||
let expectParseToBe = (expr, answer) =>
|
|
||||||
Parse.parse(expr)->Parse.toStringResult->expect->toBe(answer)
|
|
||||||
|
|
||||||
let testParse = (expr, answer) => test(expr, () => expectParseToBe(expr, answer))
|
|
||||||
|
|
||||||
module MySkip = {
|
|
||||||
let testParse = (expr, answer) => Skip.test(expr, () => expectParseToBe(expr, answer))
|
|
||||||
|
|
||||||
let testDescriptionParse = (desc, expr, answer) =>
|
|
||||||
Skip.test(desc, () => expectParseToBe(expr, answer))
|
|
||||||
}
|
|
||||||
|
|
||||||
module MyOnly = {
|
|
||||||
let testParse = (expr, answer) => Only.test(expr, () => expectParseToBe(expr, answer))
|
|
||||||
let testDescriptionParse = (desc, expr, answer) =>
|
|
||||||
Only.test(desc, () => expectParseToBe(expr, answer))
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("Peggy parse", () => {
|
describe("Peggy parse", () => {
|
||||||
describe("float", () => {
|
describe("float", () => {
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
open Jest
|
||||||
|
open Reducer_Peggy_TestHelpers
|
||||||
|
|
||||||
|
describe("Peggy parse type", () => {
|
||||||
|
describe("type of", () => {
|
||||||
|
testParse("p: number", "{(::$_typeOf_$ :p #number)}")
|
||||||
|
})
|
||||||
|
describe("type alias", () => {
|
||||||
|
testParse("type index=number", "{(::$_typeAlias_$ #index #number)}")
|
||||||
|
})
|
||||||
|
describe("type or", () => {
|
||||||
|
testParse(
|
||||||
|
"answer: number|string",
|
||||||
|
"{(::$_typeOf_$ :answer (::$_typeOr_$ (::$_constructArray_$ (#number #string))))}",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
describe("type function", () => {
|
||||||
|
testParse(
|
||||||
|
"f: number=>number=>number",
|
||||||
|
"{(::$_typeOf_$ :f (::$_typeFunction_$ (::$_constructArray_$ (#number #number #number))))}",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
describe("high priority modifier", () => {
|
||||||
|
testParse(
|
||||||
|
"answer: number<-min<-max(100)|string",
|
||||||
|
"{(::$_typeOf_$ :answer (::$_typeOr_$ (::$_constructArray_$ ((::$_typeModifier_max_$ (::$_typeModifier_min_$ #number) 100) #string))))}",
|
||||||
|
)
|
||||||
|
testParse(
|
||||||
|
"answer: number<-memberOf([1,3,5])",
|
||||||
|
"{(::$_typeOf_$ :answer (::$_typeModifier_memberOf_$ #number (::$_constructArray_$ (1 3 5))))}",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
describe("low priority modifier", () => {
|
||||||
|
testParse(
|
||||||
|
"answer: number | string $ opaque",
|
||||||
|
"{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string)))))}",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
describe("type array", () => {
|
||||||
|
testParse("answer: [number]", "{(::$_typeOf_$ :answer (::$_typeArray_$ #number))}")
|
||||||
|
})
|
||||||
|
describe("type record", () => {
|
||||||
|
testParse(
|
||||||
|
"answer: {a: number, b: string}",
|
||||||
|
"{(::$_typeOf_$ :answer (::$_typeRecord_$ (::$_constructRecord_$ ('a': #number 'b': #string))))}",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
describe("type constructor", () => {
|
||||||
|
testParse(
|
||||||
|
"answer: Age(number)",
|
||||||
|
"{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Age (::$_constructArray_$ (#number))))}",
|
||||||
|
)
|
||||||
|
testParse(
|
||||||
|
"answer: Complex(number, number)",
|
||||||
|
"{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Complex (::$_constructArray_$ (#number #number))))}",
|
||||||
|
)
|
||||||
|
testParse(
|
||||||
|
"answer: Person({age: number, name: string})",
|
||||||
|
"{(::$_typeOf_$ :answer (::$_typeConstructor_$ #Person (::$_constructArray_$ ((::$_typeRecord_$ (::$_constructRecord_$ ('age': #number 'name': #string)))))))}",
|
||||||
|
)
|
||||||
|
testParse(
|
||||||
|
"weekend: Saturday | Sunday",
|
||||||
|
"{(::$_typeOf_$ :weekend (::$_typeOr_$ (::$_constructArray_$ ((::$_typeConstructor_$ #Saturday (::$_constructArray_$ ())) (::$_typeConstructor_$ #Sunday (::$_constructArray_$ ()))))))}",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
describe("type paranthesis", () => {
|
||||||
|
//$ is introduced to avoid paranthesis
|
||||||
|
testParse(
|
||||||
|
"answer: (number|string)<-opaque",
|
||||||
|
"{(::$_typeOf_$ :answer (::$_typeModifier_opaque_$ (::$_typeOr_$ (::$_constructArray_$ (#number #string)))))}",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
describe("squiggle expressions in type modifiers", () => {
|
||||||
|
testParse(
|
||||||
|
"odds1 = [1,3,5]; odds2 = [7, 9]; type odds = number<-memberOf(concat(odds1, odds2))",
|
||||||
|
"{:odds1 = {(::$_constructArray_$ (1 3 5))}; :odds2 = {(::$_constructArray_$ (7 9))}; (::$_typeAlias_$ #odds (::$_typeModifier_memberOf_$ #number (::concat :odds1 :odds2)))}",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,46 @@
|
||||||
|
module Expression = Reducer_Expression
|
||||||
|
module ExpressionT = Reducer_Expression_T
|
||||||
|
module ExpressionValue = ReducerInterface_ExpressionValue
|
||||||
|
module Parse = Reducer_Peggy_Parse
|
||||||
|
module Result = Belt.Result
|
||||||
|
module ToExpression = Reducer_Peggy_ToExpression
|
||||||
|
|
||||||
|
open Jest
|
||||||
|
open Expect
|
||||||
|
|
||||||
|
let expectParseToBe = (expr, answer) =>
|
||||||
|
Parse.parse(expr)->Parse.toStringResult->expect->toBe(answer)
|
||||||
|
|
||||||
|
let testParse = (expr, answer) => test(expr, () => expectParseToBe(expr, answer))
|
||||||
|
|
||||||
|
let expectToExpressionToBe = (expr, answer, ~v="_", ()) => {
|
||||||
|
let rExpr = Parse.parse(expr)->Result.map(ToExpression.fromNode)
|
||||||
|
let a1 = rExpr->ExpressionT.toStringResultOkless
|
||||||
|
|
||||||
|
if v == "_" {
|
||||||
|
a1->expect->toBe(answer)
|
||||||
|
} else {
|
||||||
|
let a2 =
|
||||||
|
rExpr
|
||||||
|
->Result.flatMap(expr =>
|
||||||
|
Expression.reduceExpression(expr, Belt.Map.String.empty, ExpressionValue.defaultEnvironment)
|
||||||
|
)
|
||||||
|
->ExpressionValue.toStringResultOkless
|
||||||
|
(a1, a2)->expect->toEqual((answer, v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let testToExpression = (expr, answer, ~v="_", ()) =>
|
||||||
|
test(expr, () => expectToExpressionToBe(expr, answer, ~v, ()))
|
||||||
|
|
||||||
|
module MyOnly = {
|
||||||
|
let testParse = (expr, answer) => Only.test(expr, () => expectParseToBe(expr, answer))
|
||||||
|
let testToExpression = (expr, answer, ~v="_", ()) =>
|
||||||
|
Only.test(expr, () => expectToExpressionToBe(expr, answer, ~v, ()))
|
||||||
|
}
|
||||||
|
|
||||||
|
module MySkip = {
|
||||||
|
let testParse = (expr, answer) => Skip.test(expr, () => expectParseToBe(expr, answer))
|
||||||
|
let testToExpression = (expr, answer, ~v="_", ()) =>
|
||||||
|
Skip.test(expr, () => expectToExpressionToBe(expr, answer, ~v, ()))
|
||||||
|
}
|
|
@ -1,41 +1,5 @@
|
||||||
module Expression = Reducer_Expression
|
|
||||||
module ExpressionT = Reducer_Expression_T
|
|
||||||
module ExpressionValue = ReducerInterface_ExpressionValue
|
|
||||||
module Parse = Reducer_Peggy_Parse
|
|
||||||
module ToExpression = Reducer_Peggy_ToExpression
|
|
||||||
module Result = Belt.Result
|
|
||||||
|
|
||||||
open Jest
|
open Jest
|
||||||
open Expect
|
open Reducer_Peggy_TestHelpers
|
||||||
|
|
||||||
let expectToExpressionToBe = (expr, answer, ~v="_", ()) => {
|
|
||||||
let rExpr = Parse.parse(expr)->Result.map(ToExpression.fromNode)
|
|
||||||
let a1 = rExpr->ExpressionT.toStringResultOkless
|
|
||||||
if v == "_" {
|
|
||||||
a1->expect->toBe(answer)
|
|
||||||
} else {
|
|
||||||
let a2 =
|
|
||||||
rExpr
|
|
||||||
->Result.flatMap(expr =>
|
|
||||||
Expression.reduceExpression(expr, Belt.Map.String.empty, ExpressionValue.defaultEnvironment)
|
|
||||||
)
|
|
||||||
->ExpressionValue.toStringResultOkless
|
|
||||||
(a1, a2)->expect->toEqual((answer, v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let testToExpression = (expr, answer, ~v="_", ()) =>
|
|
||||||
test(expr, () => expectToExpressionToBe(expr, answer, ~v, ()))
|
|
||||||
|
|
||||||
module MySkip = {
|
|
||||||
let testToExpression = (expr, answer, ~v="_", ()) =>
|
|
||||||
Skip.test(expr, () => expectToExpressionToBe(expr, answer, ~v, ()))
|
|
||||||
}
|
|
||||||
|
|
||||||
module MyOnly = {
|
|
||||||
let testToExpression = (expr, answer, ~v="_", ()) =>
|
|
||||||
Only.test(expr, () => expectToExpressionToBe(expr, answer, ~v, ()))
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("Peggy to Expression", () => {
|
describe("Peggy to Expression", () => {
|
||||||
describe("literals operators parenthesis", () => {
|
describe("literals operators parenthesis", () => {
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
open Jest
|
||||||
|
open Reducer_Peggy_TestHelpers
|
||||||
|
|
||||||
|
describe("Peggy Types to Expression", () => {
|
||||||
|
describe("type of", () => {
|
||||||
|
testToExpression(
|
||||||
|
"p: number",
|
||||||
|
"{(:$_typeOf_$ :p #number)}",
|
||||||
|
~v="{_typeReferences_: {p: #number}}",
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
describe("type alias", () => {
|
||||||
|
testToExpression(
|
||||||
|
"type index=number",
|
||||||
|
"{(:$_typeAlias_$ #index #number)}",
|
||||||
|
~v="{_typeAliases_: {index: #number}}",
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
describe("type or", () => {
|
||||||
|
testToExpression(
|
||||||
|
"answer: number|string|distribution",
|
||||||
|
"{(:$_typeOf_$ :answer (:$_typeOr_$ (:$_constructArray_$ (#number #string #distribution))))}",
|
||||||
|
~v="{_typeReferences_: {answer: {typeTag: 'typeOr',typeOr: [#number,#string,#distribution]}}}",
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
describe("type function", () => {
|
||||||
|
testToExpression(
|
||||||
|
"f: number=>number=>number",
|
||||||
|
"{(:$_typeOf_$ :f (:$_typeFunction_$ (:$_constructArray_$ (#number #number #number))))}",
|
||||||
|
~v="{_typeReferences_: {f: {typeTag: 'typeFunction',inputs: [#number,#number],output: #number}}}",
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
testToExpression(
|
||||||
|
"f: number=>number",
|
||||||
|
"{(:$_typeOf_$ :f (:$_typeFunction_$ (:$_constructArray_$ (#number #number))))}",
|
||||||
|
~v="{_typeReferences_: {f: {typeTag: 'typeFunction',inputs: [#number],output: #number}}}",
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
describe("high priority modifier", () => {
|
||||||
|
testToExpression(
|
||||||
|
"answer: number<-min(1)<-max(100)|string",
|
||||||
|
"{(:$_typeOf_$ :answer (:$_typeOr_$ (:$_constructArray_$ ((:$_typeModifier_max_$ (:$_typeModifier_min_$ #number 1) 100) #string))))}",
|
||||||
|
~v="{_typeReferences_: {answer: {typeTag: 'typeOr',typeOr: [{typeTag: 'typeIdentifier',typeIdentifier: #number,min: 1,max: 100},#string]}}}",
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
testToExpression(
|
||||||
|
"answer: number<-memberOf([1,3,5])",
|
||||||
|
"{(:$_typeOf_$ :answer (:$_typeModifier_memberOf_$ #number (:$_constructArray_$ (1 3 5))))}",
|
||||||
|
~v="{_typeReferences_: {answer: {typeTag: 'typeIdentifier',typeIdentifier: #number,memberOf: [1,3,5]}}}",
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
testToExpression(
|
||||||
|
"answer: number<-min(1)",
|
||||||
|
"{(:$_typeOf_$ :answer (:$_typeModifier_min_$ #number 1))}",
|
||||||
|
~v="{_typeReferences_: {answer: {typeTag: 'typeIdentifier',typeIdentifier: #number,min: 1}}}",
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
testToExpression(
|
||||||
|
"answer: number<-max(10)",
|
||||||
|
"{(:$_typeOf_$ :answer (:$_typeModifier_max_$ #number 10))}",
|
||||||
|
~v="{_typeReferences_: {answer: {typeTag: 'typeIdentifier',typeIdentifier: #number,max: 10}}}",
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
testToExpression(
|
||||||
|
"answer: number<-min(1)<-max(10)",
|
||||||
|
"{(:$_typeOf_$ :answer (:$_typeModifier_max_$ (:$_typeModifier_min_$ #number 1) 10))}",
|
||||||
|
~v="{_typeReferences_: {answer: {typeTag: 'typeIdentifier',typeIdentifier: #number,min: 1,max: 10}}}",
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
testToExpression(
|
||||||
|
"answer: number<-max(10)<-min(1)",
|
||||||
|
"{(:$_typeOf_$ :answer (:$_typeModifier_min_$ (:$_typeModifier_max_$ #number 10) 1))}",
|
||||||
|
~v="{_typeReferences_: {answer: {typeTag: 'typeIdentifier',typeIdentifier: #number,max: 10,min: 1}}}",
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
describe("low priority modifier", () => {
|
||||||
|
testToExpression(
|
||||||
|
"answer: number | string $ opaque",
|
||||||
|
"{(:$_typeOf_$ :answer (:$_typeModifier_opaque_$ (:$_typeOr_$ (:$_constructArray_$ (#number #string)))))}",
|
||||||
|
~v="{_typeReferences_: {answer: {typeTag: 'typeOr',typeOr: [#number,#string],opaque: true}}}",
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
describe("squiggle expressions in type modifiers", () => {
|
||||||
|
testToExpression(
|
||||||
|
"odds1 = [1,3,5]; odds2 = [7, 9]; type odds = number<-memberOf(odds1 + odds2)",
|
||||||
|
"{(:$_let_$ :odds1 {(:$_constructArray_$ (1 3 5))}); (:$_let_$ :odds2 {(:$_constructArray_$ (7 9))}); (:$_typeAlias_$ #odds (:$_typeModifier_memberOf_$ #number (:add :odds1 :odds2)))}",
|
||||||
|
~v="{_typeAliases_: {odds: {typeTag: 'typeIdentifier',typeIdentifier: #number,memberOf: [1,3,5,7,9]}},odds1: [1,3,5],odds2: [7,9]}",
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
// TODO: type bindings. Type bindings are not yet supported.
|
||||||
|
// TODO: type constructor
|
||||||
|
})
|
|
@ -185,5 +185,7 @@ function createTsExport(
|
||||||
return tag("timeDuration", x.value);
|
return tag("timeDuration", x.value);
|
||||||
case "EvDeclaration":
|
case "EvDeclaration":
|
||||||
return tag("lambdaDeclaration", x.value);
|
return tag("lambdaDeclaration", x.value);
|
||||||
|
case "EvTypeIdentifier":
|
||||||
|
return tag("typeIdentifier", x.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,6 +69,10 @@ export type rescriptExport =
|
||||||
| {
|
| {
|
||||||
TAG: 12; // EvDeclaration
|
TAG: 12; // EvDeclaration
|
||||||
_0: rescriptLambdaDeclaration;
|
_0: rescriptLambdaDeclaration;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
TAG: 13; // EvTypeIdentifier
|
||||||
|
_0: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type rescriptDist =
|
type rescriptDist =
|
||||||
|
@ -120,7 +124,8 @@ export type squiggleExpression =
|
||||||
| tagged<"date", Date>
|
| tagged<"date", Date>
|
||||||
| tagged<"timeDuration", number>
|
| tagged<"timeDuration", number>
|
||||||
| tagged<"lambdaDeclaration", lambdaDeclaration>
|
| tagged<"lambdaDeclaration", lambdaDeclaration>
|
||||||
| tagged<"record", { [key: string]: squiggleExpression }>;
|
| tagged<"record", { [key: string]: squiggleExpression }>
|
||||||
|
| tagged<"typeIdentifier", string>;
|
||||||
|
|
||||||
export { lambdaValue };
|
export { lambdaValue };
|
||||||
|
|
||||||
|
@ -170,6 +175,8 @@ export function convertRawToTypescript(
|
||||||
fn: result._0.fn,
|
fn: result._0.fn,
|
||||||
args: result._0.args.map(convertDeclaration),
|
args: result._0.args.map(convertDeclaration),
|
||||||
});
|
});
|
||||||
|
case 13: // EvSymbol
|
||||||
|
return tag("typeIdentifier", result._0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,16 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
|
||||||
| None => RERecordPropertyNotFound("Record property not found", sIndex)->Error
|
| None => RERecordPropertyNotFound("Record property not found", sIndex)->Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let doAddArray = (originalA, b) => {
|
||||||
|
let a = originalA->Js.Array2.copy
|
||||||
|
let _ = Js.Array2.pushMany(a, b)
|
||||||
|
a->EvArray->Ok
|
||||||
|
}
|
||||||
|
let doAddString = (a, b) => {
|
||||||
|
let answer = Js.String2.concat(a, b)
|
||||||
|
answer->EvString->Ok
|
||||||
|
}
|
||||||
|
|
||||||
let inspect = (value: expressionValue) => {
|
let inspect = (value: expressionValue) => {
|
||||||
Js.log(value->toString)
|
Js.log(value->toString)
|
||||||
value->Ok
|
value->Ok
|
||||||
|
@ -74,6 +84,40 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
|
||||||
->Ok
|
->Ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let doSetBindingsInNamespace = (
|
||||||
|
externalBindings: externalBindings,
|
||||||
|
symbol: string,
|
||||||
|
value: expressionValue,
|
||||||
|
namespace: string,
|
||||||
|
) => {
|
||||||
|
let bindings = Bindings.fromExternalBindings(externalBindings)
|
||||||
|
let evAliases = bindings->Belt.Map.String.getWithDefault(namespace, EvRecord(Js.Dict.empty()))
|
||||||
|
let newEvAliases = switch evAliases {
|
||||||
|
| EvRecord(dict) => {
|
||||||
|
Js.Dict.set(dict, symbol, value)
|
||||||
|
dict->EvRecord
|
||||||
|
}
|
||||||
|
| _ => Js.Dict.empty()->EvRecord
|
||||||
|
}
|
||||||
|
bindings
|
||||||
|
->Belt.Map.String.set(namespace, newEvAliases)
|
||||||
|
->Bindings.toExternalBindings
|
||||||
|
->EvRecord
|
||||||
|
->Ok
|
||||||
|
}
|
||||||
|
|
||||||
|
let doSetTypeAliasBindings = (
|
||||||
|
externalBindings: externalBindings,
|
||||||
|
symbol: string,
|
||||||
|
value: expressionValue,
|
||||||
|
) => doSetBindingsInNamespace(externalBindings, symbol, value, Bindings.typeAliasesKey)
|
||||||
|
|
||||||
|
let doSetTypeOfBindings = (
|
||||||
|
externalBindings: externalBindings,
|
||||||
|
symbol: string,
|
||||||
|
value: expressionValue,
|
||||||
|
) => doSetBindingsInNamespace(externalBindings, symbol, value, Bindings.typeReferencesKey)
|
||||||
|
|
||||||
let doExportBindings = (externalBindings: externalBindings) => EvRecord(externalBindings)->Ok
|
let doExportBindings = (externalBindings: externalBindings) => EvRecord(externalBindings)->Ok
|
||||||
|
|
||||||
let doKeepArray = (aValueArray, aLambdaValue) => {
|
let doKeepArray = (aValueArray, aLambdaValue) => {
|
||||||
|
@ -147,6 +191,69 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let typeModifier_memberOf = (aType, anArray) => {
|
||||||
|
let newRecord = Js.Dict.fromArray([
|
||||||
|
("typeTag", EvString("typeIdentifier")),
|
||||||
|
("typeIdentifier", aType),
|
||||||
|
])
|
||||||
|
newRecord->Js.Dict.set("memberOf", anArray)
|
||||||
|
newRecord->EvRecord->Ok
|
||||||
|
}
|
||||||
|
let typeModifier_memberOf_update = (aRecord, anArray) => {
|
||||||
|
let newRecord = aRecord->Js.Dict.entries->Js.Dict.fromArray
|
||||||
|
newRecord->Js.Dict.set("memberOf", anArray)
|
||||||
|
newRecord->EvRecord->Ok
|
||||||
|
}
|
||||||
|
|
||||||
|
let typeModifier_min = (aType, value) => {
|
||||||
|
let newRecord = Js.Dict.fromArray([
|
||||||
|
("typeTag", EvString("typeIdentifier")),
|
||||||
|
("typeIdentifier", aType),
|
||||||
|
])
|
||||||
|
newRecord->Js.Dict.set("min", value)
|
||||||
|
newRecord->EvRecord->Ok
|
||||||
|
}
|
||||||
|
let typeModifier_min_update = (aRecord, value) => {
|
||||||
|
let newRecord = aRecord->Js.Dict.entries->Js.Dict.fromArray
|
||||||
|
newRecord->Js.Dict.set("min", value)
|
||||||
|
newRecord->EvRecord->Ok
|
||||||
|
}
|
||||||
|
|
||||||
|
let typeModifier_max = (aType, value) => {
|
||||||
|
let newRecord = Js.Dict.fromArray([
|
||||||
|
("typeTag", EvString("typeIdentifier")),
|
||||||
|
("typeIdentifier", aType),
|
||||||
|
])
|
||||||
|
newRecord->Js.Dict.set("max", value)
|
||||||
|
newRecord->EvRecord->Ok
|
||||||
|
}
|
||||||
|
let typeModifier_max_update = (aRecord, value) => {
|
||||||
|
let newRecord = aRecord->Js.Dict.entries->Js.Dict.fromArray
|
||||||
|
newRecord->Js.Dict.set("max", value)
|
||||||
|
newRecord->EvRecord->Ok
|
||||||
|
}
|
||||||
|
|
||||||
|
let typeModifier_opaque_update = aRecord => {
|
||||||
|
let newRecord = aRecord->Js.Dict.entries->Js.Dict.fromArray
|
||||||
|
newRecord->Js.Dict.set("opaque", EvBool(true))
|
||||||
|
newRecord->EvRecord->Ok
|
||||||
|
}
|
||||||
|
|
||||||
|
let typeOr = evArray => {
|
||||||
|
let newRecord = Js.Dict.fromArray([("typeTag", EvString("typeOr")), ("typeOr", evArray)])
|
||||||
|
newRecord->EvRecord->Ok
|
||||||
|
}
|
||||||
|
let typeFunction = anArray => {
|
||||||
|
let output = Belt.Array.getUnsafe(anArray, Js.Array2.length(anArray) - 1)
|
||||||
|
let inputs = Js.Array2.slice(anArray, ~start=0, ~end_=-1)
|
||||||
|
let newRecord = Js.Dict.fromArray([
|
||||||
|
("typeTag", EvString("typeFunction")),
|
||||||
|
("inputs", EvArray(inputs)),
|
||||||
|
("output", output),
|
||||||
|
])
|
||||||
|
newRecord->EvRecord->Ok
|
||||||
|
}
|
||||||
|
|
||||||
switch call {
|
switch call {
|
||||||
| ("$_atIndex_$", [EvArray(aValueArray), EvNumber(fIndex)]) => arrayAtIndex(aValueArray, fIndex)
|
| ("$_atIndex_$", [EvArray(aValueArray), EvNumber(fIndex)]) => arrayAtIndex(aValueArray, fIndex)
|
||||||
| ("$_atIndex_$", [EvRecord(dict), EvString(sIndex)]) => recordAtIndex(dict, sIndex)
|
| ("$_atIndex_$", [EvRecord(dict), EvString(sIndex)]) => recordAtIndex(dict, sIndex)
|
||||||
|
@ -155,6 +262,28 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
|
||||||
| ("$_exportBindings_$", [EvRecord(externalBindings)]) => doExportBindings(externalBindings)
|
| ("$_exportBindings_$", [EvRecord(externalBindings)]) => doExportBindings(externalBindings)
|
||||||
| ("$_setBindings_$", [EvRecord(externalBindings), EvSymbol(symbol), value]) =>
|
| ("$_setBindings_$", [EvRecord(externalBindings), EvSymbol(symbol), value]) =>
|
||||||
doSetBindings(externalBindings, symbol, value)
|
doSetBindings(externalBindings, symbol, value)
|
||||||
|
| ("$_setTypeAliasBindings_$", [EvRecord(externalBindings), EvTypeIdentifier(symbol), value]) =>
|
||||||
|
doSetTypeAliasBindings(externalBindings, symbol, value)
|
||||||
|
| ("$_setTypeOfBindings_$", [EvRecord(externalBindings), EvSymbol(symbol), value]) =>
|
||||||
|
doSetTypeOfBindings(externalBindings, symbol, value)
|
||||||
|
| ("$_typeModifier_memberOf_$", [EvTypeIdentifier(typeIdentifier), EvArray(arr)]) =>
|
||||||
|
typeModifier_memberOf(EvTypeIdentifier(typeIdentifier), EvArray(arr))
|
||||||
|
| ("$_typeModifier_memberOf_$", [EvRecord(typeRecord), EvArray(arr)]) =>
|
||||||
|
typeModifier_memberOf_update(typeRecord, EvArray(arr))
|
||||||
|
| ("$_typeModifier_min_$", [EvTypeIdentifier(typeIdentifier), value]) =>
|
||||||
|
typeModifier_min(EvTypeIdentifier(typeIdentifier), value)
|
||||||
|
| ("$_typeModifier_min_$", [EvRecord(typeRecord), value]) =>
|
||||||
|
typeModifier_min_update(typeRecord, value)
|
||||||
|
| ("$_typeModifier_max_$", [EvTypeIdentifier(typeIdentifier), value]) =>
|
||||||
|
typeModifier_max(EvTypeIdentifier(typeIdentifier), value)
|
||||||
|
| ("$_typeModifier_max_$", [EvRecord(typeRecord), value]) =>
|
||||||
|
typeModifier_max_update(typeRecord, value)
|
||||||
|
| ("$_typeModifier_opaque_$", [EvRecord(typeRecord)]) => typeModifier_opaque_update(typeRecord)
|
||||||
|
| ("$_typeOr_$", [EvArray(arr)]) => typeOr(EvArray(arr))
|
||||||
|
| ("$_typeFunction_$", [EvArray(arr)]) => typeFunction(arr)
|
||||||
|
| ("add", [EvArray(aValueArray), EvArray(bValueArray)]) => doAddArray(aValueArray, bValueArray)
|
||||||
|
| ("add", [EvString(aValueString), EvString(bValueString)]) =>
|
||||||
|
doAddString(aValueString, bValueString)
|
||||||
| ("inspect", [value, EvString(label)]) => inspectLabel(value, label)
|
| ("inspect", [value, EvString(label)]) => inspectLabel(value, label)
|
||||||
| ("inspect", [value]) => inspect(value)
|
| ("inspect", [value]) => inspect(value)
|
||||||
| ("keep", [EvArray(aValueArray), EvLambda(aLambdaValue)]) =>
|
| ("keep", [EvArray(aValueArray), EvLambda(aLambdaValue)]) =>
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
Macros are used to define language building blocks. They are like Lisp macros.
|
Macros are used to define language building blocks. They are like Lisp macros.
|
||||||
*/
|
*/
|
||||||
module Bindings = Reducer_Expression_Bindings
|
module Bindings = Reducer_Expression_Bindings
|
||||||
|
module ErrorValue = Reducer_ErrorValue
|
||||||
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
|
module ExpressionBuilder = Reducer_Expression_ExpressionBuilder
|
||||||
module ExpressionT = Reducer_Expression_T
|
module ExpressionT = Reducer_Expression_T
|
||||||
module ExpressionValue = ReducerInterface.ExpressionValue
|
module ExpressionValue = ReducerInterface.ExpressionValue
|
||||||
|
@ -12,7 +13,7 @@ module Result = Belt.Result
|
||||||
open Reducer_Expression_ExpressionBuilder
|
open Reducer_Expression_ExpressionBuilder
|
||||||
|
|
||||||
type environment = ExpressionValue.environment
|
type environment = ExpressionValue.environment
|
||||||
type errorValue = Reducer_ErrorValue.errorValue
|
type errorValue = ErrorValue.errorValue
|
||||||
type expression = ExpressionT.expression
|
type expression = ExpressionT.expression
|
||||||
type expressionValue = ExpressionValue.expressionValue
|
type expressionValue = ExpressionValue.expressionValue
|
||||||
type expressionWithContext = ExpressionWithContext.expressionWithContext
|
type expressionWithContext = ExpressionWithContext.expressionWithContext
|
||||||
|
@ -23,84 +24,78 @@ let dispatchMacroCall = (
|
||||||
environment,
|
environment,
|
||||||
reduceExpression: ExpressionT.reducerFn,
|
reduceExpression: ExpressionT.reducerFn,
|
||||||
): result<expressionWithContext, errorValue> => {
|
): result<expressionWithContext, errorValue> => {
|
||||||
let doBindStatement = (bindingExpr: expression, statement: expression, environment) =>
|
let useExpressionToSetBindings = (bindingExpr: expression, environment, statement, newCode) => {
|
||||||
switch statement {
|
let rExternalBindingsValue = reduceExpression(bindingExpr, bindings, environment)
|
||||||
| ExpressionT.EList(list{ExpressionT.EValue(EvCall("$_let_$")), symbolExpr, statement}) => {
|
|
||||||
let rExternalBindingsValue = reduceExpression(bindingExpr, bindings, environment)
|
|
||||||
|
|
||||||
rExternalBindingsValue->Result.flatMap(externalBindingsValue => {
|
rExternalBindingsValue->Result.flatMap(externalBindingsValue => {
|
||||||
let newBindings = Bindings.fromValue(externalBindingsValue)
|
let newBindings = Bindings.fromValue(externalBindingsValue)
|
||||||
|
|
||||||
// Js.log(
|
let rNewStatement = Bindings.replaceSymbols(newBindings, statement)
|
||||||
// `bindStatement ${Bindings.toString(newBindings)}<==${ExpressionT.toString(
|
rNewStatement->Result.map(boundStatement =>
|
||||||
// bindingExpr,
|
ExpressionWithContext.withContext(
|
||||||
// )} statement: $_let_$ ${ExpressionT.toString(symbolExpr)}=${ExpressionT.toString(
|
newCode(newBindings->Bindings.toExternalBindings->eRecord, boundStatement),
|
||||||
// statement,
|
newBindings,
|
||||||
// )}`,
|
)
|
||||||
// )
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
let rNewStatement = Bindings.replaceSymbols(newBindings, statement)
|
let correspondingSetBindingsFn = (fnName: string): string =>
|
||||||
rNewStatement->Result.map(newStatement =>
|
switch fnName {
|
||||||
ExpressionWithContext.withContext(
|
| "$_let_$" => "$_setBindings_$"
|
||||||
eFunction(
|
| "$_typeOf_$" => "$_setTypeOfBindings_$"
|
||||||
"$_setBindings_$",
|
| "$_typeAlias_$" => "$_setTypeAliasBindings_$"
|
||||||
list{newBindings->Bindings.toExternalBindings->eRecord, symbolExpr, newStatement},
|
| _ => ""
|
||||||
),
|
|
||||||
newBindings,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
| _ => REAssignmentExpected->Error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let doBindStatement = (bindingExpr: expression, statement: expression, environment) => {
|
||||||
|
let defaultStatement = ErrorValue.REAssignmentExpected->Error
|
||||||
|
switch statement {
|
||||||
|
| ExpressionT.EList(list{ExpressionT.EValue(EvCall(callName)), symbolExpr, statement}) => {
|
||||||
|
let setBindingsFn = correspondingSetBindingsFn(callName)
|
||||||
|
if setBindingsFn !== "" {
|
||||||
|
useExpressionToSetBindings(bindingExpr, environment, statement, (
|
||||||
|
newBindingsExpr,
|
||||||
|
boundStatement,
|
||||||
|
) => eFunction(setBindingsFn, list{newBindingsExpr, symbolExpr, boundStatement}))
|
||||||
|
} else {
|
||||||
|
defaultStatement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
| _ => defaultStatement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let doBindExpression = (bindingExpr: expression, statement: expression, environment): result<
|
let doBindExpression = (bindingExpr: expression, statement: expression, environment): result<
|
||||||
expressionWithContext,
|
expressionWithContext,
|
||||||
errorValue,
|
errorValue,
|
||||||
> =>
|
> => {
|
||||||
switch statement {
|
let defaultStatement = () =>
|
||||||
| ExpressionT.EList(list{ExpressionT.EValue(EvCall("$_let_$")), symbolExpr, statement}) => {
|
useExpressionToSetBindings(bindingExpr, environment, statement, (
|
||||||
let rExternalBindingsValue = reduceExpression(bindingExpr, bindings, environment)
|
_newBindingsExpr,
|
||||||
|
boundStatement,
|
||||||
|
) => boundStatement)
|
||||||
|
|
||||||
rExternalBindingsValue->Result.flatMap(externalBindingsValue => {
|
switch statement {
|
||||||
let newBindings = Bindings.fromValue(externalBindingsValue)
|
| ExpressionT.EList(list{ExpressionT.EValue(EvCall(callName)), symbolExpr, statement}) => {
|
||||||
let rNewStatement = Bindings.replaceSymbols(newBindings, statement)
|
let setBindingsFn = correspondingSetBindingsFn(callName)
|
||||||
rNewStatement->Result.map(newStatement =>
|
if setBindingsFn !== "" {
|
||||||
ExpressionWithContext.withContext(
|
useExpressionToSetBindings(bindingExpr, environment, statement, (
|
||||||
eFunction(
|
newBindingsExpr,
|
||||||
"$_exportBindings_$",
|
boundStatement,
|
||||||
list{
|
) =>
|
||||||
eFunction(
|
eFunction(
|
||||||
"$_setBindings_$",
|
"$_exportBindings_$",
|
||||||
list{
|
list{eFunction(setBindingsFn, list{newBindingsExpr, symbolExpr, boundStatement})},
|
||||||
newBindings->Bindings.toExternalBindings->eRecord,
|
|
||||||
symbolExpr,
|
|
||||||
newStatement,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
newBindings,
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
})
|
} else {
|
||||||
}
|
defaultStatement()
|
||||||
| _ => {
|
}
|
||||||
let rExternalBindingsValue: result<expressionValue, errorValue> = reduceExpression(
|
|
||||||
bindingExpr,
|
|
||||||
bindings,
|
|
||||||
environment,
|
|
||||||
)
|
|
||||||
|
|
||||||
rExternalBindingsValue->Result.flatMap(externalBindingsValue => {
|
|
||||||
let newBindings = Bindings.fromValue(externalBindingsValue)
|
|
||||||
let rNewStatement = Bindings.replaceSymbols(newBindings, statement)
|
|
||||||
rNewStatement->Result.map(newStatement =>
|
|
||||||
ExpressionWithContext.withContext(newStatement, newBindings)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
| _ => defaultStatement()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let doBlock = (exprs: list<expression>, _bindings: ExpressionT.bindings, _environment): result<
|
let doBlock = (exprs: list<expression>, _bindings: ExpressionT.bindings, _environment): result<
|
||||||
expressionWithContext,
|
expressionWithContext,
|
||||||
|
|
|
@ -75,9 +75,9 @@ and reduceValueList = (valueList: list<expressionValue>, environment): result<
|
||||||
> =>
|
> =>
|
||||||
switch valueList {
|
switch valueList {
|
||||||
| list{EvCall(fName), ...args} => {
|
| list{EvCall(fName), ...args} => {
|
||||||
let rCheckedArgs = switch fName == "$_setBindings_$" {
|
let rCheckedArgs = switch fName {
|
||||||
| false => args->Lambda.checkIfReduced
|
| "$_setBindings_$" | "$_setTypeOfBindings_$" | "$_setTypeAliasBindings_$" => args->Ok
|
||||||
| true => args->Ok
|
| _ => args->Lambda.checkIfReduced
|
||||||
}
|
}
|
||||||
|
|
||||||
rCheckedArgs->Result.flatMap(checkedArgs =>
|
rCheckedArgs->Result.flatMap(checkedArgs =>
|
||||||
|
|
|
@ -10,13 +10,8 @@ type externalBindings = ReducerInterface_ExpressionValue.externalBindings
|
||||||
|
|
||||||
let defaultBindings: ExpressionT.bindings = Belt.Map.String.empty
|
let defaultBindings: ExpressionT.bindings = Belt.Map.String.empty
|
||||||
|
|
||||||
let fromExternalBindings = (externalBindings: externalBindings): ExpressionT.bindings => {
|
let typeAliasesKey = "_typeAliases_"
|
||||||
let keys = Js.Dict.keys(externalBindings)
|
let typeReferencesKey = "_typeReferences_"
|
||||||
keys->Belt.Array.reduce(defaultBindings, (acc, key) => {
|
|
||||||
let value = Js.Dict.unsafeGet(externalBindings, key)
|
|
||||||
acc->Belt.Map.String.set(key, value)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let toExternalBindings = (bindings: ExpressionT.bindings): externalBindings => {
|
let toExternalBindings = (bindings: ExpressionT.bindings): externalBindings => {
|
||||||
let keys = Belt.Map.String.keysToArray(bindings)
|
let keys = Belt.Map.String.keysToArray(bindings)
|
||||||
|
@ -27,6 +22,50 @@ let toExternalBindings = (bindings: ExpressionT.bindings): externalBindings => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let fromExternalBindings_ = (externalBindings: externalBindings): ExpressionT.bindings => {
|
||||||
|
let keys = Js.Dict.keys(externalBindings)
|
||||||
|
keys->Belt.Array.reduce(defaultBindings, (acc, key) => {
|
||||||
|
let value = Js.Dict.unsafeGet(externalBindings, key)
|
||||||
|
acc->Belt.Map.String.set(key, value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let fromExternalBindings = (externalBindings: externalBindings): ExpressionT.bindings => {
|
||||||
|
// TODO: This code will be removed in the future when maps are used instead of records. Please don't mind this function for now.
|
||||||
|
|
||||||
|
let internalBindings0 = fromExternalBindings_(externalBindings)
|
||||||
|
|
||||||
|
let oExistingTypeAliases = Belt.Map.String.get(internalBindings0, typeAliasesKey)
|
||||||
|
let internalBindings1 = Belt.Option.mapWithDefault(
|
||||||
|
oExistingTypeAliases,
|
||||||
|
internalBindings0,
|
||||||
|
existingTypeAliases => {
|
||||||
|
let newTypeAliases = switch existingTypeAliases {
|
||||||
|
| EvRecord(actualTypeAliases) =>
|
||||||
|
actualTypeAliases->fromExternalBindings_->toExternalBindings->ExpressionValue.EvRecord
|
||||||
|
| _ => existingTypeAliases
|
||||||
|
}
|
||||||
|
Belt.Map.String.set(internalBindings0, typeAliasesKey, newTypeAliases)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
let oExistingTypeReferences = Belt.Map.String.get(internalBindings1, typeReferencesKey)
|
||||||
|
let internalBindings2 = Belt.Option.mapWithDefault(
|
||||||
|
oExistingTypeReferences,
|
||||||
|
internalBindings1,
|
||||||
|
existingTypeReferences => {
|
||||||
|
let newTypeReferences = switch existingTypeReferences {
|
||||||
|
| EvRecord(actualTypeReferences) =>
|
||||||
|
actualTypeReferences->fromExternalBindings_->toExternalBindings->ExpressionValue.EvRecord
|
||||||
|
| _ => existingTypeReferences
|
||||||
|
}
|
||||||
|
Belt.Map.String.set(internalBindings0, typeReferencesKey, newTypeReferences)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
internalBindings2
|
||||||
|
}
|
||||||
|
|
||||||
let fromValue = (aValue: expressionValue) =>
|
let fromValue = (aValue: expressionValue) =>
|
||||||
switch aValue {
|
switch aValue {
|
||||||
| EvRecord(externalBindings) => fromExternalBindings(externalBindings)
|
| EvRecord(externalBindings) => fromExternalBindings(externalBindings)
|
||||||
|
|
|
@ -64,3 +64,6 @@ let eBindExpression = (bindingExpr: expression, expression: expression): express
|
||||||
|
|
||||||
let eBindExpressionDefault = (expression: expression): expression =>
|
let eBindExpressionDefault = (expression: expression): expression =>
|
||||||
eFunction("$$_bindExpression_$$", list{expression})
|
eFunction("$$_bindExpression_$$", list{expression})
|
||||||
|
|
||||||
|
let eTypeIdentifier = (name: string): expression =>
|
||||||
|
name->BExpressionValue.EvTypeIdentifier->BExpressionT.EValue
|
||||||
|
|
|
@ -36,11 +36,6 @@
|
||||||
'[]': '$_atIndex_$',
|
'[]': '$_atIndex_$',
|
||||||
}
|
}
|
||||||
|
|
||||||
function nodeBlock(statements) {return{type: 'Block', statements: statements}}
|
|
||||||
function nodeBoolean(value) {return {type: 'Boolean', value: value}}
|
|
||||||
function nodeCallIndentifier(value) {return {type: 'CallIdentifier', value: value}}
|
|
||||||
function nodeExpression(args) {return {type: 'Expression', nodes: args}}
|
|
||||||
function nodeFloat(value) {return {type: 'Float', value: value}}
|
|
||||||
function makeFunctionCall(fn, args) {
|
function makeFunctionCall(fn, args) {
|
||||||
if (fn === '$$_applyAll_$$') {
|
if (fn === '$$_applyAll_$$') {
|
||||||
// Any list of values is applied from left to right anyway.
|
// Any list of values is applied from left to right anyway.
|
||||||
|
@ -52,6 +47,16 @@
|
||||||
return nodeExpression([nodeCallIndentifier(fn), ...args])
|
return nodeExpression([nodeCallIndentifier(fn), ...args])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function apply(fn, arg) { return makeFunctionCall(fn, [arg]); }
|
||||||
|
function constructArray(elems) { return apply('$_constructArray_$', nodeExpression(elems)); }
|
||||||
|
function constructRecord(elems) { return apply('$_constructRecord_$', nodeExpression(elems)); }
|
||||||
|
|
||||||
|
function nodeBlock(statements) {return{type: 'Block', statements: statements}}
|
||||||
|
function nodeBoolean(value) {return {type: 'Boolean', value: value}}
|
||||||
|
function nodeCallIndentifier(value) {return {type: 'CallIdentifier', value: value}}
|
||||||
|
function nodeExpression(args) {return {type: 'Expression', nodes: args}}
|
||||||
|
function nodeFloat(value) {return {type: 'Float', value: value}}
|
||||||
function nodeIdentifier(value) {return {type: 'Identifier', value: value}}
|
function nodeIdentifier(value) {return {type: 'Identifier', value: value}}
|
||||||
function nodeInteger(value) {return {type: 'Integer', value: value}}
|
function nodeInteger(value) {return {type: 'Integer', value: value}}
|
||||||
function nodeKeyValue(key, value) {
|
function nodeKeyValue(key, value) {
|
||||||
|
@ -61,9 +66,12 @@
|
||||||
function nodeLetStatment(variable, value) {return {type: 'LetStatement', variable: variable, value: value}}
|
function nodeLetStatment(variable, value) {return {type: 'LetStatement', variable: variable, value: value}}
|
||||||
function nodeString(value) {return {type: 'String', value: value}}
|
function nodeString(value) {return {type: 'String', value: value}}
|
||||||
function nodeTernary(condition, trueExpression, falseExpression) {return {type: 'Ternary', condition: condition, trueExpression: trueExpression, falseExpression: falseExpression}}
|
function nodeTernary(condition, trueExpression, falseExpression) {return {type: 'Ternary', condition: condition, trueExpression: trueExpression, falseExpression: falseExpression}}
|
||||||
|
|
||||||
|
function nodeTypeIdentifier(typeValue) {return {type: 'TypeIdentifier', value: typeValue}}
|
||||||
}}
|
}}
|
||||||
|
|
||||||
start
|
start
|
||||||
|
// = _nl start:typeExpression _nl finalComment? {return start}
|
||||||
= _nl start:outerBlock _nl finalComment? {return start}
|
= _nl start:outerBlock _nl finalComment? {return start}
|
||||||
|
|
||||||
zeroOMoreArgumentsBlockOrExpression = innerBlockOrExpression / lambda
|
zeroOMoreArgumentsBlockOrExpression = innerBlockOrExpression / lambda
|
||||||
|
@ -96,6 +104,7 @@ array_statements
|
||||||
statement
|
statement
|
||||||
= letStatement
|
= letStatement
|
||||||
/ defunStatement
|
/ defunStatement
|
||||||
|
/ typeStatement
|
||||||
|
|
||||||
letStatement
|
letStatement
|
||||||
= variable:identifier _ assignmentOp _nl value:zeroOMoreArgumentsBlockOrExpression
|
= variable:identifier _ assignmentOp _nl value:zeroOMoreArgumentsBlockOrExpression
|
||||||
|
@ -205,7 +214,7 @@ chainFunctionCall
|
||||||
|
|
||||||
unary
|
unary
|
||||||
= unaryOperator:unaryOperator _nl right:(unary/postOperator)
|
= unaryOperator:unaryOperator _nl right:(unary/postOperator)
|
||||||
{ return makeFunctionCall(unaryToFunction[unaryOperator], [right])}
|
{ return apply(unaryToFunction[unaryOperator], right)}
|
||||||
/ postOperator
|
/ postOperator
|
||||||
|
|
||||||
unaryOperator "unary operator"
|
unaryOperator "unary operator"
|
||||||
|
@ -267,7 +276,7 @@ number = number:(float / integer) unit:identifier?
|
||||||
if (unit === null)
|
if (unit === null)
|
||||||
{ return number }
|
{ return number }
|
||||||
else
|
else
|
||||||
{ return makeFunctionCall('fromUnit_'+unit.value, [number])
|
{ return apply('fromUnit_'+unit.value, number)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,9 +310,9 @@ lambda
|
||||||
|
|
||||||
arrayConstructor 'array'
|
arrayConstructor 'array'
|
||||||
= '[' _nl ']'
|
= '[' _nl ']'
|
||||||
{ return makeFunctionCall('$_constructArray_$', [nodeExpression([])])}
|
{ return constructArray([]); }
|
||||||
/ '[' _nl args:array_elements _nl ']'
|
/ '[' _nl args:array_elements _nl ']'
|
||||||
{ return makeFunctionCall('$_constructArray_$', [nodeExpression(args)])}
|
{ return constructArray(args); }
|
||||||
|
|
||||||
array_elements
|
array_elements
|
||||||
= head:expression tail:(_ ',' _nl @expression)*
|
= head:expression tail:(_ ',' _nl @expression)*
|
||||||
|
@ -311,7 +320,7 @@ arrayConstructor 'array'
|
||||||
|
|
||||||
recordConstructor 'record'
|
recordConstructor 'record'
|
||||||
= '{' _nl args:array_recordArguments _nl '}'
|
= '{' _nl args:array_recordArguments _nl '}'
|
||||||
{ return makeFunctionCall('$_constructRecord_$', [nodeExpression(args)])}
|
{ return constructRecord(args); }
|
||||||
|
|
||||||
array_recordArguments
|
array_recordArguments
|
||||||
= head:keyValuePair tail:(_ ',' _nl @keyValuePair)*
|
= head:keyValuePair tail:(_ ',' _nl @keyValuePair)*
|
||||||
|
@ -320,7 +329,9 @@ recordConstructor 'record'
|
||||||
keyValuePair
|
keyValuePair
|
||||||
= key:expression _ ':' _nl value:expression
|
= key:expression _ ':' _nl value:expression
|
||||||
{ return nodeKeyValue(key, value)}
|
{ return nodeKeyValue(key, value)}
|
||||||
|
|
||||||
|
// Separators
|
||||||
|
|
||||||
_ 'whitespace'
|
_ 'whitespace'
|
||||||
= whiteSpaceCharactersOrComment*
|
= whiteSpaceCharactersOrComment*
|
||||||
|
|
||||||
|
@ -351,4 +362,79 @@ statementSeparator 'statement separator'
|
||||||
newLine "newline"
|
newLine "newline"
|
||||||
= [\n\r]
|
= [\n\r]
|
||||||
|
|
||||||
|
// Types
|
||||||
|
|
||||||
|
noArguments = ('(' _nl ')' )?
|
||||||
|
|
||||||
|
typeIdentifier 'type identifier'
|
||||||
|
= ([a-z]+[_a-z0-9]i*) {return nodeTypeIdentifier(text())}
|
||||||
|
|
||||||
|
typeConstructorIdentifier 'type constructor identifier'
|
||||||
|
= ([A-Z]+[_a-z0-9]i*) {return nodeTypeIdentifier(text())}
|
||||||
|
|
||||||
|
typeExpression = typePostModifierExpression
|
||||||
|
|
||||||
|
typePostModifierExpression = head:typeOr tail:(_ '$' _nl @typeModifier)*
|
||||||
|
{
|
||||||
|
return tail.reduce((result, element) => {
|
||||||
|
return makeFunctionCall('$_typeModifier_'+element.modifier.value+'_$', [result, ...element.args])
|
||||||
|
}, head)
|
||||||
|
}
|
||||||
|
|
||||||
|
typeOr = head:typeFunction tail:(_ '|' _nl @typeFunction)*
|
||||||
|
{ return tail.length === 0 ? head : apply('$_typeOr_$', constructArray([head, ...tail])); }
|
||||||
|
|
||||||
|
typeFunction = head:typeModifierExpression tail:(_ '=>' _nl @typeModifierExpression)*
|
||||||
|
{ return tail.length === 0 ? head : apply( '$_typeFunction_$', constructArray([head, ...tail])); }
|
||||||
|
|
||||||
|
typeModifierExpression = head:basicType tail:(_ '<-' _nl @typeModifier)*
|
||||||
|
{
|
||||||
|
return tail.reduce((result, element) => {
|
||||||
|
return makeFunctionCall('$_typeModifier_'+element.modifier.value+'_$', [result, ...element.args])
|
||||||
|
}, head)
|
||||||
|
}
|
||||||
|
|
||||||
|
typeModifier
|
||||||
|
= modifier:identifier _ '(' _nl args:array_elements _nl ')'
|
||||||
|
{ return {modifier: modifier, args: args}; }
|
||||||
|
/ modifier:identifier _ noArguments
|
||||||
|
{ return {modifier: modifier, args: []}; }
|
||||||
|
|
||||||
|
basicType = typeConstructor / typeArray / typeRecord / typeInParanthesis / typeIdentifier
|
||||||
|
|
||||||
|
typeArray = '[' _nl elem:typeExpression _nl ']'
|
||||||
|
{return apply('$_typeArray_$', elem)}
|
||||||
|
|
||||||
|
typeRecord = '{' _nl elems:array_typeRecordArguments _nl '}'
|
||||||
|
{ return apply('$_typeRecord_$', constructRecord(elems)); }
|
||||||
|
|
||||||
|
array_typeRecordArguments
|
||||||
|
= head:typeKeyValuePair tail:(_ ',' _nl @typeKeyValuePair)*
|
||||||
|
{ return [head, ...tail]; }
|
||||||
|
|
||||||
|
typeKeyValuePair
|
||||||
|
= key:identifier _ ':' _nl value:typeExpression
|
||||||
|
{ return nodeKeyValue(key, value)}
|
||||||
|
|
||||||
|
typeConstructor
|
||||||
|
= constructor:typeConstructorIdentifier _ '(' _nl args:array_types _nl ')'
|
||||||
|
{ return makeFunctionCall('$_typeConstructor_$', [constructor, constructArray(args)]); }
|
||||||
|
/ constructor:typeConstructorIdentifier _ noArguments
|
||||||
|
{ return makeFunctionCall('$_typeConstructor_$', [constructor, constructArray([])]); }
|
||||||
|
|
||||||
|
array_types = head:typeExpression tail:(_ ',' _nl @typeExpression)*
|
||||||
|
{ return [head, ...tail]; }
|
||||||
|
|
||||||
|
typeStatement = typeAliasStatement / typeOfStatement
|
||||||
|
typeAliasStatement = 'type' __nl typeIdentifier:typeIdentifier _nl '=' _nl typeExpression:typeExpression
|
||||||
|
{ return makeFunctionCall('$_typeAlias_$', [typeIdentifier, typeExpression])}
|
||||||
|
typeOfStatement = identifier:identifier _ ':' _nl typeExpression:typeExpression
|
||||||
|
{ return makeFunctionCall('$_typeOf_$', [identifier, typeExpression])}
|
||||||
|
|
||||||
|
typeInParanthesis = '(' _nl typeExpression:typeExpression _nl ')' {return typeExpression}
|
||||||
|
|
||||||
|
// TODO: min max example
|
||||||
|
// TODO: Example of foo = {a: 2, b: 5}; type fooKeys = string $ memberOf(foo->keys)
|
||||||
|
// TODO: Example of memberOf( [1,2,3] )
|
||||||
|
// TODO: Example of $
|
||||||
|
// TODO: Cons(a, list) | EmptyList
|
|
@ -24,6 +24,7 @@ type nodeLambda = {...node, "args": array<nodeIdentifier>, "body": nodeBlock}
|
||||||
type nodeLetStatement = {...node, "variable": nodeIdentifier, "value": node}
|
type nodeLetStatement = {...node, "variable": nodeIdentifier, "value": node}
|
||||||
type nodeString = {...node, "value": string}
|
type nodeString = {...node, "value": string}
|
||||||
type nodeTernary = {...node, "condition": node, "trueExpression": node, "falseExpression": node}
|
type nodeTernary = {...node, "condition": node, "trueExpression": node, "falseExpression": node}
|
||||||
|
type nodeTypeIdentifier = {...node, "value": string}
|
||||||
|
|
||||||
type peggyNode =
|
type peggyNode =
|
||||||
| PgNodeBlock(nodeBlock)
|
| PgNodeBlock(nodeBlock)
|
||||||
|
@ -38,6 +39,7 @@ type peggyNode =
|
||||||
| PgNodeLetStatement(nodeLetStatement)
|
| PgNodeLetStatement(nodeLetStatement)
|
||||||
| PgNodeString(nodeString)
|
| PgNodeString(nodeString)
|
||||||
| PgNodeTernary(nodeTernary)
|
| PgNodeTernary(nodeTernary)
|
||||||
|
| PgNodeTypeIdentifier(nodeTypeIdentifier)
|
||||||
|
|
||||||
external castNodeBlock: node => nodeBlock = "%identity"
|
external castNodeBlock: node => nodeBlock = "%identity"
|
||||||
external castNodeBoolean: node => nodeBoolean = "%identity"
|
external castNodeBoolean: node => nodeBoolean = "%identity"
|
||||||
|
@ -51,6 +53,7 @@ external castNodeLambda: node => nodeLambda = "%identity"
|
||||||
external castNodeLetStatement: node => nodeLetStatement = "%identity"
|
external castNodeLetStatement: node => nodeLetStatement = "%identity"
|
||||||
external castNodeString: node => nodeString = "%identity"
|
external castNodeString: node => nodeString = "%identity"
|
||||||
external castNodeTernary: node => nodeTernary = "%identity"
|
external castNodeTernary: node => nodeTernary = "%identity"
|
||||||
|
external castNodeTypeIdentifier: node => nodeTypeIdentifier = "%identity"
|
||||||
|
|
||||||
exception UnsupportedPeggyNodeType(string) // This should never happen; programming error
|
exception UnsupportedPeggyNodeType(string) // This should never happen; programming error
|
||||||
let castNodeType = (node: node) =>
|
let castNodeType = (node: node) =>
|
||||||
|
@ -67,6 +70,7 @@ let castNodeType = (node: node) =>
|
||||||
| "LetStatement" => node->castNodeLetStatement->PgNodeLetStatement
|
| "LetStatement" => node->castNodeLetStatement->PgNodeLetStatement
|
||||||
| "String" => node->castNodeString->PgNodeString
|
| "String" => node->castNodeString->PgNodeString
|
||||||
| "Ternary" => node->castNodeTernary->PgNodeTernary
|
| "Ternary" => node->castNodeTernary->PgNodeTernary
|
||||||
|
| "TypeIdentifier" => node->castNodeTypeIdentifier->PgNodeTypeIdentifier
|
||||||
| _ => raise(UnsupportedPeggyNodeType(node["type"]))
|
| _ => raise(UnsupportedPeggyNodeType(node["type"]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,6 +102,7 @@ let rec pgToString = (peggyNode: peggyNode): string => {
|
||||||
toString(node["trueExpression"]) ++
|
toString(node["trueExpression"]) ++
|
||||||
" " ++
|
" " ++
|
||||||
toString(node["falseExpression"]) ++ ")"
|
toString(node["falseExpression"]) ++ ")"
|
||||||
|
| PgNodeTypeIdentifier(node) => `#${node["value"]}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
and toString = (node: node): string => node->castNodeType->pgToString
|
and toString = (node: node): string => node->castNodeType->pgToString
|
||||||
|
|
|
@ -44,5 +44,7 @@ let rec fromNode = (node: Parse.node): expression => {
|
||||||
fromNode(nodeTernary["falseExpression"]),
|
fromNode(nodeTernary["falseExpression"]),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
| PgNodeTypeIdentifier(nodeTypeIdentifier) =>
|
||||||
|
ExpressionBuilder.eTypeIdentifier(nodeTypeIdentifier["value"])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ type rec expressionValue =
|
||||||
| EvDate(Js.Date.t)
|
| EvDate(Js.Date.t)
|
||||||
| EvTimeDuration(float)
|
| EvTimeDuration(float)
|
||||||
| EvDeclaration(lambdaDeclaration)
|
| EvDeclaration(lambdaDeclaration)
|
||||||
|
| EvTypeIdentifier(string)
|
||||||
and record = Js.Dict.t<expressionValue>
|
and record = Js.Dict.t<expressionValue>
|
||||||
and externalBindings = record
|
and externalBindings = record
|
||||||
and lambdaValue = {
|
and lambdaValue = {
|
||||||
|
@ -58,6 +59,7 @@ let rec toString = aValue =>
|
||||||
| EvDate(date) => DateTime.Date.toString(date)
|
| EvDate(date) => DateTime.Date.toString(date)
|
||||||
| EvTimeDuration(t) => DateTime.Duration.toString(t)
|
| EvTimeDuration(t) => DateTime.Duration.toString(t)
|
||||||
| EvDeclaration(d) => Declaration.toString(d, r => toString(EvLambda(r)))
|
| EvDeclaration(d) => Declaration.toString(d, r => toString(EvLambda(r)))
|
||||||
|
| EvTypeIdentifier(id) => `#${id}`
|
||||||
}
|
}
|
||||||
and toStringRecord = aRecord => {
|
and toStringRecord = aRecord => {
|
||||||
let pairs =
|
let pairs =
|
||||||
|
@ -83,6 +85,7 @@ let toStringWithType = aValue =>
|
||||||
| EvDate(_) => `Date::${toString(aValue)}`
|
| EvDate(_) => `Date::${toString(aValue)}`
|
||||||
| EvTimeDuration(_) => `Date::${toString(aValue)}`
|
| EvTimeDuration(_) => `Date::${toString(aValue)}`
|
||||||
| EvDeclaration(_) => `Declaration::${toString(aValue)}`
|
| EvDeclaration(_) => `Declaration::${toString(aValue)}`
|
||||||
|
| EvTypeIdentifier(_) => `TypeIdentifier::${toString(aValue)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
let argsToString = (args: array<expressionValue>): string => {
|
let argsToString = (args: array<expressionValue>): string => {
|
||||||
|
@ -129,6 +132,7 @@ type expressionValueType =
|
||||||
| EvtDate
|
| EvtDate
|
||||||
| EvtTimeDuration
|
| EvtTimeDuration
|
||||||
| EvtDeclaration
|
| EvtDeclaration
|
||||||
|
| EvtTypeIdentifier
|
||||||
|
|
||||||
type functionCallSignature = CallSignature(string, array<expressionValueType>)
|
type functionCallSignature = CallSignature(string, array<expressionValueType>)
|
||||||
type functionDefinitionSignature =
|
type functionDefinitionSignature =
|
||||||
|
@ -149,6 +153,7 @@ let valueToValueType = value =>
|
||||||
| EvDate(_) => EvtDate
|
| EvDate(_) => EvtDate
|
||||||
| EvTimeDuration(_) => EvtTimeDuration
|
| EvTimeDuration(_) => EvtTimeDuration
|
||||||
| EvDeclaration(_) => EvtDeclaration
|
| EvDeclaration(_) => EvtDeclaration
|
||||||
|
| EvTypeIdentifier(_) => EvtTypeIdentifier
|
||||||
}
|
}
|
||||||
|
|
||||||
let functionCallToCallSignature = (functionCall: functionCall): functionCallSignature => {
|
let functionCallToCallSignature = (functionCall: functionCall): functionCallSignature => {
|
||||||
|
@ -171,6 +176,7 @@ let valueTypeToString = (valueType: expressionValueType): string =>
|
||||||
| EvtDate => `Date`
|
| EvtDate => `Date`
|
||||||
| EvtTimeDuration => `Duration`
|
| EvtTimeDuration => `Duration`
|
||||||
| EvtDeclaration => `Declaration`
|
| EvtDeclaration => `Declaration`
|
||||||
|
| EvtTypeIdentifier => `TypeIdentifier`
|
||||||
}
|
}
|
||||||
|
|
||||||
let functionCallSignatureToString = (functionCallSignature: functionCallSignature): string => {
|
let functionCallSignatureToString = (functionCallSignature: functionCallSignature): string => {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user