This commit is contained in:
Umur Ozkul 2022-08-19 17:13:31 +02:00
parent fdc7f06f08
commit f0ba68f64a
26 changed files with 621 additions and 433 deletions

View File

@ -1,6 +1,6 @@
@@warning("-44")
module ExternalExpressionValue = ReducerInterface_ExternalExpressionValue
module Project = ReducerProject
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Project = ForTS_ReducerProject
module Bindings = Reducer_Bindings
open Jest
@ -11,14 +11,14 @@ open Expect.Operators
let runFetchResult = (project, sourceId) => {
Project.run(project, sourceId)
Project.getExternalResult(project, sourceId)->ExternalExpressionValue.toStringOptionResult
Project.getResult(project, sourceId)->InternalExpressionValue.toStringOptionResult
}
let runFetchBindings = (project, sourceId) => {
Project.run(project, sourceId)
Project.getExternalBindings(project, sourceId)
->ExternalExpressionValue.EvModule
->ExternalExpressionValue.toString
Project.getBindings(project, sourceId)
->InternalExpressionValue.IEvBindings
->InternalExpressionValue.toString
}
test("setting continuation", () => {

View File

@ -1,6 +1,6 @@
@@warning("-44")
module ExternalExpressionValue = ReducerInterface_ExternalExpressionValue
module Project = ReducerProject
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module Project = ForTS_ReducerProject
module Bindings = Reducer_Bindings
open Jest
@ -45,13 +45,13 @@ Case "Running a single source".
Note that getResult returns None if the source has not been run.
Getting None means you have forgotten to run the source.
*/
let result = project->Project.getExternalResult("main")
let bindings = project->Project.getExternalBindings("main")
let result = project->Project.getResult("main")
let bindings = project->Project.getBindings("main")
/* Let's display the result and bindings */
(
result->ExternalExpressionValue.toStringOptionResult,
bindings->ExternalExpressionValue.EvModule->ExternalExpressionValue.toString,
result->InternalExpressionValue.toStringOptionResult,
bindings->InternalExpressionValue.IEvBindings->InternalExpressionValue.toString,
)->expect == ("Ok(3)", "@{}")
/* You've got 3 with empty bindings. */
})
@ -60,12 +60,12 @@ Case "Running a single source".
let project = Project.createProject()
Project.setSource(project, "main", "1 + 2")
Project.runAll(project)
let result = Project.getExternalResult(project, "main")
let bindings = Project.getExternalBindings(project, "main")
let result = Project.getResult(project, "main")
let bindings = Project.getBindings(project, "main")
/* Now you have external bindings and external result. */
(
result->ExternalExpressionValue.toStringOptionResult,
bindings->ExternalExpressionValue.EvModule->ExternalExpressionValue.toString,
result->InternalExpressionValue.toStringOptionResult,
bindings->InternalExpressionValue.IEvBindings->InternalExpressionValue.toString,
)->expect == ("Ok(3)", "@{}")
})
@ -74,13 +74,13 @@ Case "Running a single source".
let project = Project.createProject()
/* Optional. Set your custom environment anytime before running */
Project.setEnvironment(project, ExternalExpressionValue.defaultEnvironment)
Project.setEnvironment(project, InternalExpressionValue.defaultEnvironment)
Project.setSource(project, "main", "1 + 2")
Project.runAll(project)
let result = Project.getExternalResult(project, "main")
let _bindings = Project.getExternalBindings(project, "main")
result->ExternalExpressionValue.toStringOptionResult->expect == "Ok(3)"
let result = Project.getResult(project, "main")
let _bindings = Project.getBindings(project, "main")
result->InternalExpressionValue.toStringOptionResult->expect == "Ok(3)"
})
test("shortcut", () => {
@ -88,8 +88,8 @@ Case "Running a single source".
/* Examples above was to prepare you for the multi source tutorial. */
let (result, bindings) = Project.evaluate("1+2")
(
result->ExternalExpressionValue.toStringResult,
bindings->ExternalExpressionValue.EvModule->ExternalExpressionValue.toString,
result->InternalExpressionValue.toStringResult,
bindings->InternalExpressionValue.IEvBindings->InternalExpressionValue.toString,
)->expect == ("Ok(3)", "@{}")
})
})

View File

@ -1,19 +1,14 @@
import * as _ from "lodash";
import type {
environment,
expressionValue,
externalBindings,
errorValue,
} from "../rescript/TypescriptInterface.gen";
import {
defaultEnvironment,
evaluatePartialUsingExternalBindings,
evaluateUsingOptions,
foreignFunctionInterface,
} from "../rescript/TypescriptInterface.gen";
export {
makeSampleSetDist,
errorValueToString,
distributionErrorToString,
} from "../rescript/TypescriptInterface.gen";
export type {
@ -23,12 +18,7 @@ export type {
} from "../rescript/TypescriptInterface.gen";
export type { errorValue, externalBindings as bindings, jsImports };
import {
jsValueToBinding,
jsValueToExpressionValue,
jsValue,
rescriptExport,
squiggleExpression,
convertRawToTypescript,
lambdaValue,
} from "./rescript_interop";
import { result, resultMap, tag, tagged } from "./types";
@ -44,162 +34,167 @@ export let defaultSamplingInputs: environment = {
xyPointLength: 10000,
};
export function run(
squiggleString: string,
bindings?: externalBindings,
environment?: environment,
imports?: jsImports
): result<squiggleExpression, errorValue> {
let b = bindings ? bindings : defaultBindings;
let i = imports ? imports : defaultImports;
let e = environment ? environment : defaultEnvironment;
let res: result<expressionValue, errorValue> = evaluateUsingOptions(
{ externalBindings: mergeImportsWithBindings(b, i), environment: e },
squiggleString
);
return resultMap(res, (x) => createTsExport(x, e));
}
// Run Partial. A partial is a block of code that doesn't return a value
export function runPartial(
squiggleString: string,
bindings?: externalBindings,
environment?: environment,
imports?: jsImports
): result<externalBindings, errorValue> {
let b = bindings ? bindings : defaultBindings;
let i = imports ? imports : defaultImports;
let e = environment ? environment : defaultEnvironment;
/*
All those functions below are invalid since the introduction of ReducerProject
*/
// export function run(
// squiggleString: string,
// bindings?: externalBindings,
// environment?: environment,
// imports?: jsImports
// ): result<squiggleExpression, errorValue> {
// let b = bindings ? bindings : defaultBindings;
// let i = imports ? imports : defaultImports;
// let e = environment ? environment : defaultEnvironment;
// let res: result<expressionValue, errorValue> = evaluateUsingOptions(
// { externalBindings: mergeImportsWithBindings(b, i), environment: e },
// squiggleString
// );
// return resultMap(res, (x) => createTsExport(x, e));
// }
return evaluatePartialUsingExternalBindings(
squiggleString,
mergeImportsWithBindings(b, i),
e
);
}
// // Run Partial. A partial is a block of code that doesn't return a value
// export function runPartial(
// squiggleString: string,
// bindings?: externalBindings,
// environment?: environment,
// imports?: jsImports
// ): result<externalBindings, errorValue> {
// let b = bindings ? bindings : defaultBindings;
// let i = imports ? imports : defaultImports;
// let e = environment ? environment : defaultEnvironment;
export function runForeign(
fn: lambdaValue,
args: jsValue[],
environment?: environment
): result<squiggleExpression, errorValue> {
let e = environment ? environment : defaultEnvironment;
let res: result<expressionValue, errorValue> = foreignFunctionInterface(
fn,
args.map(jsValueToExpressionValue),
e
);
return resultMap(res, (x) => createTsExport(x, e));
}
// return evaluatePartialUsingExternalBindings(
// squiggleString,
// mergeImportsWithBindings(b, i),
// e
// );
// }
function mergeImportsWithBindings(
bindings: externalBindings,
imports: jsImports
): externalBindings {
let transformedImports = Object.fromEntries(
Object.entries(imports).map(([key, value]) => [
"$" + key,
jsValueToBinding(value),
])
);
return _.merge(bindings, transformedImports);
}
// jsValueToExpressionValue is invalid
// export function runForeign(
// fn: lambdaValue,
// args: jsValue[],
// environment?: environment
// ): result<squiggleExpression, errorValue> {
// let e = environment ? environment : defaultEnvironment;
// let res: result<expressionValue, errorValue> = foreignFunctionInterface(
// fn,
// args.map(jsValueToExpressionValue),
// e
// );
// return resultMap(res, (x) => createTsExport(x, e));
// }
type jsImports = { [key: string]: jsValue };
// function mergeImportsWithBindings(
// bindings: externalBindings,
// imports: jsImports
// ): externalBindings {
// let transformedImports = Object.fromEntries(
// Object.entries(imports).map(([key, value]) => [
// "$" + key,
// jsValueToBinding(value),
// ])
// );
// return _.merge(bindings, transformedImports);
// }
export let defaultImports: jsImports = {};
export let defaultBindings: externalBindings = {};
// type jsImports = { [key: string]: jsValue };
export function mergeBindings(
allBindings: externalBindings[]
): externalBindings {
return allBindings.reduce((acc, x) => ({ ...acc, ...x }));
}
// export let defaultImports: jsImports = {};
// export let defaultBindings: externalBindings = {};
function createTsExport(
x: expressionValue,
environment: environment
): squiggleExpression {
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;
}
}
}
}
// export function mergeBindings(
// allBindings: externalBindings[]
// ): externalBindings {
// return allBindings.reduce((acc, x) => ({ ...acc, ...x }));
// }
// function createTsExport(
// x: expressionValue,
// environment: environment
// ): squiggleExpression {
// 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;
// }
// }
// }
// }

View File

@ -0,0 +1,192 @@
open ForTS__Types
// If a module is built for TypeScript then it can only refer to ForTS__Types for other types and modules
// The exception is its implementation and private modules
module T = ReducerProject_T
module Private = ReducerProject.Private
/*
PUBLIC FUNCTIONS
*/
/*
Creates a new project to hold the sources, executables, bindings, and other data.
The new project runs the sources according to their topological sorting because of the includes and continues.
Any source can include or continue other sources. "Therefore, the project is a graph data structure."
The difference between including and continuing is that includes are stated inside the source code while continues are stated in the project.
To run a group of source codes and get results/bindings, the necessary methods are
- setSource
- setContinues
- parseIncludes
- run or runAll
- getExternalBindings
- getExternalResult
A project has a public field tag with a constant value "reducerProject"
project = {tag: "reducerProject"}
*/
let createProject = (): reducerProject => Private.createProject()->T.Private.castFromInternalProject
/*
Answer all the source ids of all the sources in the project.
*/
let getSourceIds = (project: reducerProject): array<string> =>
project->T.Private.castToInternalProject->Private.getSourceIds
/*
Sets the source for a given source Id.
*/
let setSource = (project: reducerProject, sourceId: string, value: string): unit =>
project->T.Private.castToInternalProject->Private.setSource(sourceId, value)
/*
Gets the source for a given source id.
*/
let getSource = (project: reducerProject, sourceId: string): option<string> =>
project->T.Private.castToInternalProject->Private.getSource(sourceId)
/*
Touches the source for a given source id. This and dependent, sources are set to be re-evaluated.
*/
let touchSource = (project: reducerProject, sourceId: string): unit =>
project->T.Private.castToInternalProject->Private.touchSource(sourceId)
/*
Cleans the compilation artifacts for a given source ID. The results stay untouched, so compilation won't be run again.
Normally, you would never need the compilation artifacts again as the results with the same sources would never change. However, they are needed in case of any debugging reruns
*/
let clean = (project: reducerProject, sourceId: string): unit =>
project->T.Private.castToInternalProject->Private.clean(sourceId)
/*
Cleans all the compilation artifacts in all of the project
*/
let cleanAll = (project: reducerProject): unit =>
project->T.Private.castToInternalProject->Private.cleanAll
/*
Cleans results. Compilation stays untouched to be able to re-run the source.
You would not do this if you were not trying to debug the source code.
*/
let cleanResults = (project: reducerProject, sourceId: string): unit =>
project->T.Private.castToInternalProject->Private.cleanResults(sourceId)
/*
Cleans all results. Compilations remains untouched to rerun the source.
*/
let cleanAllResults = (project: reducerProject): unit =>
project->T.Private.castToInternalProject->Private.cleanAllResults
/*
To set the includes one first has to call "parseIncludes". The parsed includes or the parser error is returned.
*/
let getIncludes = (project: reducerProject, sourceId: string): result<
array<string>,
Reducer_ErrorValue.errorValue,
> => project->T.Private.castToInternalProject->Private.getIncludes(sourceId)
/*
Answers the source codes after which this source code is continuing
*/
let getContinues = (project: reducerProject, sourceId: string): array<string> =>
project->T.Private.castToInternalProject->Private.getContinues(sourceId)
/*
"continues" acts like hidden includes in the source.
It is used to define a continuation that is not visible in the source code.
You can chain source codes on the web interface for example
*/
let setContinues = (project: reducerProject, sourceId: string, continues: array<string>): unit =>
project->T.Private.castToInternalProject->Private.setContinues(sourceId, continues)
/*
This source depends on the array of sources returned.
*/
let getDependencies = (project: reducerProject, sourceId: string): array<string> =>
project->T.Private.castToInternalProject->Private.getDependencies(sourceId)
/*
The sources returned are dependent on this
*/
let getDependents = (project: reducerProject, sourceId: string): array<string> =>
project->T.Private.castToInternalProject->Private.getDependents(sourceId)
/*
Get the run order for the sources in the project.
*/
let getRunOrder = (project: reducerProject): array<string> =>
project->T.Private.castToInternalProject->Private.getRunOrder
/*
Get the run order to get the results of this specific source
*/
let getRunOrderFor = (project: reducerProject, sourceId: string) =>
project->T.Private.castToInternalProject->Private.getRunOrderFor(sourceId)
/*
Parse includes so that you can load them before running.
Load includes by calling getIncludes which returns the includes that have been parsed.
It is your responsibility to load the includes before running.
*/module Topology = ReducerProject_Topology
let parseIncludes = (project: reducerProject, sourceId: string): unit =>
project->T.Private.castToInternalProject->Private.parseIncludes(sourceId)
/*
Parse the source code if it is not done already.
Use getRawParse to get the parse tree.
You would need this function if you want to see the parse tree without running the source code.
*/
let rawParse = (project: reducerProject, sourceId: string): unit =>
project->T.Private.castToInternalProject->Private.rawParse(sourceId)
/*
Runs a specific source code if it is not done already. The code is parsed if it is not already done. It runs the dependencies if it is not already done.
*/
let run = (project: reducerProject, sourceId: string): unit =>
project->T.Private.castToInternalProject->Private.run(sourceId)
/*
Runs all of the sources in a project. Their results and bindings will be available
*/
let runAll = (project: reducerProject): unit =>
project->T.Private.castToInternalProject->Private.runAll
/*
Get the bindings after running this source file or the project
*/
let getBindings = (project: reducerProject, sourceId: string): squiggleValue_Module =>
project->T.Private.castToInternalProject->Private.getBindings(sourceId)
/*
Get the result after running this source file or the project
*/
let getResult = (project: reducerProject, sourceId: string): option<
result_<squiggleValue, reducerErrorValue>,
> => project->T.Private.castToInternalProject->Private.getResult(sourceId)
/*
This is a convenience function to get the result of a single source without creating a project.
However, without a project, you cannot handle include directives.
The source has to be include free
*/
let evaluate = (sourceCode: string): ('r, 'b) => Private.evaluate(sourceCode)
let setEnvironment = (project: reducerProject, environment: environment): unit =>
project->T.Private.castToInternalProject->Private.setEnvironment(environment)
let foreignFunctionInterface = (
lambdaValue: squiggleValue_Lambda,
argArray: array<squiggleValue>,
environment: environment,
): result_<squiggleValue, reducerErrorValue> => {
let accessors = ReducerProject_ProjectAccessors_T.identityAccessorsWithEnvironment(environment)
Reducer_Expression_Lambda.foreignFunctionInterface(
lambdaValue,
argArray,
accessors,
Reducer_Expression.reduceExpressionInProject,
)
}

View File

@ -1 +0,0 @@
open ForTS__Types

View File

@ -1 +1,193 @@
open ForTS__Types
// Return values as they are if they are JavaScript types.
@module("ForTS_SquiggleValue_tag") @scope("SquiggleValueTag")
external svtArray_: int = "SvtArray"
@module("ForTS_SquiggleValue_tag") @scope("SquiggleValueTag")
external svtArrayString_: int = "SvtArrayString"
@module("ForTS_SquiggleValue_tag") @scope("SquiggleValueTag")
external svtBool_: int = "SvtBool"
@module("ForTS_SquiggleValue_tag") @scope("SquiggleValueTag")
external svtCall_: int = "SvtCall"
@module("ForTS_SquiggleValue_tag") @scope("SquiggleValueTag")
external svtDate_: int = "SvtDate"
@module("ForTS_SquiggleValue_tag") @scope("SquiggleValueTag")
external svtDeclaration_: int = "SvtDeclaration"
@module("ForTS_SquiggleValue_tag") @scope("SquiggleValueTag")
external svtDistribution_: int = "SvtDistribution"
@module("ForTS_SquiggleValue_tag") @scope("SquiggleValueTag")
external svtLambda_: int = "SvtLambda"
@module("ForTS_SquiggleValue_tag") @scope("SquiggleValueTag")
external svtModule_: int = "SvtModule"
@module("ForTS_SquiggleValue_tag") @scope("SquiggleValueTag")
external svtNumber_: int = "SvtNumber"
@module("ForTS_SquiggleValue_tag") @scope("SquiggleValueTag")
external svtRecord_: int = "SvtRecord"
@module("ForTS_SquiggleValue_tag") @scope("SquiggleValueTag")
external svtString_: int = "SvtString"
@module("ForTS_SquiggleValue_tag") @scope("SquiggleValueTag")
external svtSymbol_: int = "SvtSymbol"
@module("ForTS_SquiggleValue_tag") @scope("SquiggleValueTag")
external svtTimeDuration_: int = "SvtTimeDuration"
@module("ForTS_SquiggleValue_tag") @scope("SquiggleValueTag")
external svtType_: int = "SvtType"
@module("ForTS_SquiggleValue_tag") @scope("SquiggleValueTag")
external svtTypeIdentifier_: int = "SvtUndefined"
@module("ForTS_SquiggleValue_tag") @scope("SquiggleValueTag")
external svtVoid_: int = "SvtVoid"
@genType
let getTag = (variant: squiggleValue) =>
switch variant {
| IEvArray(_) => svtArray_
| IEvArrayString(_) => svtArrayString_
| IEvBool(_) => svtBool_
| IEvCall(_) => svtCall_ //Impossible
| IEvDate(_) => svtDate_
| IEvDeclaration(_) => svtDeclaration_
| IEvDistribution(_) => svtDistribution_
| IEvLambda(_) => svtLambda_
| IEvBindings(_) => svtModule_ //Impossible
| IEvNumber(_) => svtNumber_
| IEvRecord(_) => svtRecord_
| IEvString(_) => svtString_
| IEvSymbol(_) => svtSymbol_
| IEvTimeDuration(_) => svtTimeDuration_
| IEvType(_) => svtType_
| IEvTypeIdentifier(_) => svtTypeIdentifier_
| IEvVoid => svtVoid_
}
@genType
let toString = (variant: squiggleValue) =>
ReducerInterface_InternalExpressionValue.toString(variant)
@genType
let getArray = (variant: squiggleValue): option<squiggleValue_Array> =>
//FIXME: Convert
switch variant {
| IEvArray(arrayLike) => arrayLike->Some
| _ => None
}
@genType
let getArrayString = (variant: squiggleValue): option<array<string>> =>
switch variant {
| IEvArrayString(value) => value->Some
| _ => None
}
@genType
let getBool = (variant: squiggleValue): option<bool> =>
switch variant {
| IEvBool(value) => value->Some
| _ => None
}
@genType
let getCall = (variant: squiggleValue): option<string> =>
switch variant {
| IEvCall(value) => value->Some
| _ => None
}
@genType
let getDate = (variant: squiggleValue): option<Js.Date.t> =>
switch variant {
| IEvDate(value) => value->Some
| _ => None
}
@genType
let getDeclaration = (variant: squiggleValue): option<squiggleValue_Declaration> =>
switch variant {
| IEvDeclaration(value) => value->Some
| _ => None
}
@genType
let getDistribution = (variant: squiggleValue): option<squiggleValue_Distribution> =>
switch variant {
| IEvDistribution(value) => value->Some
| _ => None
}
@genType
let getLambda = (variant: squiggleValue): option<squiggleValue_Lambda> =>
switch variant {
| IEvLambda(value) => value->Some
| _ => None
}
@genType
let getModule = (variant: squiggleValue): option<squiggleValue_Module> =>
switch variant {
| IEvBindings(value) => value->Some
| _ => None
}
@genType
let getNumber = (variant: squiggleValue): option<float> =>
switch variant {
| IEvNumber(value) => value->Some
| _ => None
}
@genType
let getRecord = (variant: squiggleValue): option<squiggleValue_Record> =>
switch variant {
| IEvRecord(value) => value->Some
| _ => None
}
@genType
let getString = (variant: squiggleValue): option<string> =>
switch variant {
| IEvString(value) => value->Some
| _ => None
}
@genType
let getSymbol = (variant: squiggleValue): option<string> =>
switch variant {
| IEvSymbol(value) => value->Some
| _ => None
}
@genType
let getTimeDuration = (variant: squiggleValue): option<float> =>
switch variant {
| IEvTimeDuration(value) => value->Some
| _ => None
}
@genType
let getType = (variant: squiggleValue): option<squiggleValue_Type> =>
switch variant {
| IEvType(value) => value->Some
| _ => None
}
@genType
let getTypeIdentifier = (variant: squiggleValue): option<string> =>
switch variant {
| IEvTypeIdentifier(value) => value->Some
| _ => None
}

View File

@ -1 +1,5 @@
open ForTS__Types
// Note: Internal representation will not be an array in the future.
// Thus we still to have a conversion
let getValues = (v: squiggleValue_Array): array<squiggleValue> =>
ReducerInterface_InternalExpressionValue.arrayToValueArray(v)

View File

@ -0,0 +1,4 @@
open ForTS__Types
let getKeyValuePairs = (v: squiggleValue_Module): array<(string, squiggleValue)> =>
ReducerInterface_InternalExpressionValue.nameSpaceToKeyValueArray(v)

View File

@ -1 +1,4 @@
open ForTS__Types
let getKeyValuePairs = (value: squiggleValue_Record): array<(string, squiggleValue)> =>
ReducerInterface_InternalExpressionValue.recordToKeyValuePairs(value)

View File

@ -1 +1,4 @@
open ForTS__Types
let getKeyValuePairs = (value: squiggleValue_Type): array<(string, squiggleValue)> =>
ReducerInterface_InternalExpressionValue.recordToKeyValuePairs(value)

View File

@ -5,7 +5,7 @@ enum SquiggleValueTag {
SvtCall,
SvtDate,
SvtDeclaration,
SvtDistribution
SvtDistribution,
SvtLambda,
SvtModule,
SvtNumber,

View File

@ -5,16 +5,21 @@ So we rename it right away not cause a compatibility problem
*/
@genType.opaque type result_<'a, 'e> = result<'a, 'e>
@genType.opaque type squiggleValue
@genType.opaque type squiggleValue_Array
@genType.opaque type squiggleValue_ArrayString
@genType.opaque type squiggleValue_Date
@genType.opaque type squiggleValue_Declaration
@genType.opaque type squiggleValue_Distribution
@genType.opaque type squiggleValue_Lambda
@genType.opaque type squiggleValue_Record
@genType.opaque type squiggleValue_TimeDuration
@genType.opaque type squiggleValue_Type
@genType.opaque type squiggleValue_Void
@genType.opaque type reducer_errorValue
@genType.opaque type squiggleValue = ReducerInterface_InternalExpressionValue.t
@genType.opaque type squiggleValue_Array = ReducerInterface_InternalExpressionValue.squiggleArray
@genType.opaque
type squiggleValue_Declaration = ReducerInterface_InternalExpressionValue.lambdaDeclaration
@genType.opaque type squiggleValue_Module = ReducerInterface_InternalExpressionValue.nameSpace
@genType.opaque type squiggleValue_Lambda = ReducerInterface_InternalExpressionValue.lambdaValue
@genType.opaque type squiggleValue_Record = ReducerInterface_InternalExpressionValue.map
@genType.opaque type squiggleValue_Type = ReducerInterface_InternalExpressionValue.map
@genType.opaque type reducerErrorValue = Reducer_ErrorValue.errorValue
@genType.opaque type reducerProject = ReducerProject_T.t
// From now on one should introduce any new types as opaque types
// Already existing open types we cannot dive in now
@genType type environment = GenericDist.env
@genType type squiggleValue_Distribution = DistributionTypes.genericDist
//TODO: index.ts should use types from here or vice versa

View File

@ -16,18 +16,18 @@ type rec externalExpressionValue =
| EvArrayString(array<string>)
| EvBool(bool)
| EvCall(string) // External function call
| EvDate(Js.Date.t)
| EvDeclaration(lambdaDeclaration)
| EvDistribution(DistributionTypes.genericDist)
| EvLambda(lambdaValue)
| EvModule(record)
| EvNumber(float)
| EvRecord(record)
| EvString(string)
| EvSymbol(string)
| EvDate(Js.Date.t)
| EvTimeDuration(float)
| EvDeclaration(lambdaDeclaration)
| EvTypeIdentifier(string)
| EvModule(record)
| EvType(record)
| EvTypeIdentifier(string)
| EvVoid
and record = Js.Dict.t<externalExpressionValue>
and lambdaValue = {

View File

@ -23,6 +23,7 @@ type rec t =
| IEvType(map)
| IEvTypeIdentifier(string)
| IEvVoid
and squiggleArray = array<t>
and map = Belt.Map.String.t<t>
and nameSpace = NameSpace(Belt.Map.String.t<t>)
and lambdaValue = {
@ -32,6 +33,7 @@ and lambdaValue = {
}
and lambdaDeclaration = Declaration.declaration<lambdaValue>
type squiggleMap = map
type internalExpressionValue = t
type functionCall = (string, array<t>)
@ -312,3 +314,12 @@ and nameSpaceFromTypeScriptBindings = (
r: ReducerInterface_ExternalExpressionValue.externalBindings,
): nameSpace =>
r->Js.Dict.entries->Belt.Map.String.fromArray->Belt.Map.String.map(e => toInternal(e))->NameSpace
let nameSpaceToKeyValueArray = (nameSpace: nameSpace): array<(string, t)> => {
let NameSpace(container) = nameSpace
container->Belt.Map.String.toArray
}
let arrayToValueArray = (arr: array<t>): array<t> => arr
let recordToKeyValuePairs = (record: map): array<(string, t)> => record->Belt.Map.String.toArray

View File

@ -3,14 +3,12 @@
module Bindings = Reducer_Bindings
module Continuation = ReducerInterface_Value_Continuation
module ErrorValue = Reducer_ErrorValue
module ExternalExpressionValue = ReducerInterface_ExternalExpressionValue
module InternalExpressionValue = ReducerInterface_InternalExpressionValue
module ProjectAccessorsT = ReducerProject_ProjectAccessors_T
module ProjectItem = ReducerProject_ProjectItem
module T = ReducerProject_T
module Topology = ReducerProject_Topology
type reducerProject = T.t
type t = T.t
module Private = {
@ -106,9 +104,6 @@ module Private = {
Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _)
}
let getExternalResult = (project: t, sourceId: string): ProjectItem.T.externalResultType =>
project->getItem(sourceId)->ProjectItem.getExternalResult
let parseIncludes = (project: t, sourceId: string): unit => {
let newItem = project->getItem(sourceId)->ProjectItem.parseIncludes
Belt.Map.String.set(project["items"], sourceId, newItem)->T.Private.setFieldItems(project, _)
@ -127,14 +122,11 @@ module Private = {
let setEnvironment = (project: t, value: InternalExpressionValue.environment): unit =>
T.Private.setFieldEnvironment(project, value)
let getExternalBindings = (
project: t,
sourceId: string,
): ProjectItem.T.externalBindingsArgumentType => {
let getBindings = (project: t, sourceId: string): ProjectItem.T.bindingsArgumentType => {
let those = project->getContinuation(sourceId)
let these = project->getStdLib
let ofUser = Continuation.minus(those, these)
ofUser->InternalExpressionValue.nameSpaceToTypeScriptBindings
ofUser
}
let buildProjectAccessors = (project: t): ProjectAccessorsT.t => {
@ -208,202 +200,3 @@ module Private = {
(getResult(project, "main")->Belt.Option.getWithDefault(IEvVoid->Ok), ofUser)
}
}
/*
PUBLIC FUNCTIONS
*/
/*
Creates a new project to hold the sources, executables, bindings, and other data.
The new project runs the sources according to their topological sorting because of the includes and continues.
Any source can include or continue other sources. "Therefore, the project is a graph data structure."
The difference between including and continuing is that includes are stated inside the source code while continues are stated in the project.
To run a group of source codes and get results/bindings, the necessary methods are
- setSource
- setContinues
- parseIncludes
- run or runAll
- getExternalBindings
- getExternalResult
A project has a public field tag with a constant value "reducerProject"
project = {tag: "reducerProject"}
*/
let createProject = (): reducerProject => Private.createProject()->T.Private.castFromInternalProject
/*
Answer all the source ids of all the sources in the project.
*/
let getSourceIds = (project: reducerProject): array<string> =>
project->T.Private.castToInternalProject->Private.getSourceIds
/*
Sets the source for a given source Id.
*/
let setSource = (project: reducerProject, sourceId: string, value: string): unit =>
project->T.Private.castToInternalProject->Private.setSource(sourceId, value)
/*
Gets the source for a given source id.
*/
let getSource = (project: reducerProject, sourceId: string): option<string> =>
project->T.Private.castToInternalProject->Private.getSource(sourceId)
/*
Touches the source for a given source id. This and dependent, sources are set to be re-evaluated.
*/
let touchSource = (project: reducerProject, sourceId: string): unit =>
project->T.Private.castToInternalProject->Private.touchSource(sourceId)
/*
Cleans the compilation artifacts for a given source ID. The results stay untouched, so compilation won't be run again.
Normally, you would never need the compilation artifacts again as the results with the same sources would never change. However, they are needed in case of any debugging reruns
*/
let clean = (project: reducerProject, sourceId: string): unit =>
project->T.Private.castToInternalProject->Private.clean(sourceId)
/*
Cleans all the compilation artifacts in all of the project
*/
let cleanAll = (project: reducerProject): unit =>
project->T.Private.castToInternalProject->Private.cleanAll
/*
Cleans results. Compilation stays untouched to be able to re-run the source.
You would not do this if you were not trying to debug the source code.
*/
let cleanResults = (project: reducerProject, sourceId: string): unit =>
project->T.Private.castToInternalProject->Private.cleanResults(sourceId)
/*
Cleans all results. Compilations remains untouched to rerun the source.
*/
let cleanAllResults = (project: reducerProject): unit =>
project->T.Private.castToInternalProject->Private.cleanAllResults
/*
To set the includes one first has to call "parseIncludes". The parsed includes or the parser error is returned.
*/
let getIncludes = (project: reducerProject, sourceId: string): result<
array<string>,
Reducer_ErrorValue.errorValue,
> => project->T.Private.castToInternalProject->Private.getIncludes(sourceId)
/*
Answers the source codes after which this source code is continuing
*/
let getContinues = (project: reducerProject, sourceId: string): array<string> =>
project->T.Private.castToInternalProject->Private.getContinues(sourceId)
/*
"continues" acts like hidden includes in the source.
It is used to define a continuation that is not visible in the source code.
You can chain source codes on the web interface for example
*/
let setContinues = (project: reducerProject, sourceId: string, continues: array<string>): unit =>
project->T.Private.castToInternalProject->Private.setContinues(sourceId, continues)
/*
This source depends on the array of sources returned.
*/
let getDependencies = (project: reducerProject, sourceId: string): array<string> =>
project->T.Private.castToInternalProject->Private.getDependencies(sourceId)
/*
The sources returned are dependent on this
*/
let getDependents = (project: reducerProject, sourceId: string): array<string> =>
project->T.Private.castToInternalProject->Private.getDependents(sourceId)
/*
Get the run order for the sources in the project.
*/
let getRunOrder = (project: reducerProject): array<string> =>
project->T.Private.castToInternalProject->Private.getRunOrder
/*
Get the run order to get the results of this specific source
*/
let getRunOrderFor = (project: reducerProject, sourceId: string) =>
project->T.Private.castToInternalProject->Private.getRunOrderFor(sourceId)
/*
Parse includes so that you can load them before running.
Load includes by calling getIncludes which returns the includes that have been parsed.
It is your responsibility to load the includes before running.
*/
let parseIncludes = (project: reducerProject, sourceId: string): unit =>
project->T.Private.castToInternalProject->Private.parseIncludes(sourceId)
/*
Parse the source code if it is not done already.
Use getRawParse to get the parse tree.
You would need this function if you want to see the parse tree without running the source code.
*/
let rawParse = (project: reducerProject, sourceId: string): unit =>
project->T.Private.castToInternalProject->Private.rawParse(sourceId)
/*
Runs a specific source code if it is not done already. The code is parsed if it is not already done. It runs the dependencies if it is not already done.
*/
let run = (project: reducerProject, sourceId: string): unit =>
project->T.Private.castToInternalProject->Private.run(sourceId)
/*
Runs all of the sources in a project. Their results and bindings will be available
*/
let runAll = (project: reducerProject): unit =>
project->T.Private.castToInternalProject->Private.runAll
/*
Get the bindings after running this source file or the project
*/
let getExternalBindings = (
project: reducerProject,
sourceId: string,
): ExternalExpressionValue.record =>
project->T.Private.castToInternalProject->Private.getExternalBindings(sourceId)
/*
Get the result after running this source file or the project
*/
let getExternalResult = (project: reducerProject, sourceId: string): option<
result<ExternalExpressionValue.externalExpressionValue, Reducer_ErrorValue.errorValue>,
> => project->T.Private.castToInternalProject->Private.getExternalResult(sourceId)
/*
This is a convenience function to get the result of a single source without creating a project.
However, without a project, you cannot handle include directives.
The source has to be include free
*/
let evaluate = (sourceCode: string): ('r, 'b) => {
let (result, continuation) = Private.evaluate(sourceCode)
(
result->Belt.Result.map(InternalExpressionValue.toExternal),
continuation->InternalExpressionValue.nameSpaceToTypeScriptBindings,
)
}
let setEnvironment = (
project: reducerProject,
environment: ExternalExpressionValue.environment,
): unit => project->T.Private.castToInternalProject->Private.setEnvironment(environment)
let foreignFunctionInterface = (
lambdaValue: ExternalExpressionValue.lambdaValue,
argArray: array<ExternalExpressionValue.externalExpressionValue>,
environment: ExternalExpressionValue.environment,
) => {
let internallambdaValue = InternalExpressionValue.lambdaValueToInternal(lambdaValue)
let internalArgArray = argArray->Js.Array2.map(InternalExpressionValue.toInternal)
let accessors = ProjectAccessorsT.identityAccessorsWithEnvironment(environment)
Reducer_Expression_Lambda.foreignFunctionInterface(
internallambdaValue,
internalArgArray,
accessors,
Reducer_Expression.reduceExpressionInProject,
)->Belt.Result.map(InternalExpressionValue.toExternal)
}

View File

@ -20,19 +20,13 @@ let emptyItem = T.ProjectItem({
continues: [],
includes: []->Ok,
})
// source -> rawParse -> includes -> expression -> continuation -> externalBindings -> result -> externalResult
// source -> rawParse -> includes -> expression -> continuation -> result
let getSource = (T.ProjectItem(r)): T.sourceType => r.source
let getRawParse = (T.ProjectItem(r)): T.rawParseType => r.rawParse
let getExpression = (T.ProjectItem(r)): T.expressionType => r.expression
let getContinuation = (T.ProjectItem(r)): T.continuationArgumentType => r.continuation
let toExternalBindings = continuation =>
continuation->InternalExpressionValue.nameSpaceToTypeScriptBindings
let getResult = (T.ProjectItem(r)): T.resultType => r.result
let getExternalResult = (T.ProjectItem(r)): T.externalResultType =>
r.result->Belt.Option.map(opt =>
opt->Belt.Result.map(value => value->InternalExpressionValue.toExternal)
)
let getContinues = (T.ProjectItem(r)): T.continuesType => r.continues
let getIncludes = (T.ProjectItem(r)): T.includesType => r.includes

View File

@ -14,12 +14,10 @@ type continuation = InternalExpressionValue.nameSpace
type continuationArgumentType = InternalExpressionValue.nameSpace
type continuationType = option<continuationArgumentType>
type continuationResultType = option<result<continuationArgumentType, errorValue>>
type externalBindingsArgumentType = ExternalExpressionValue.record
type externalBindingsType = option<externalBindingsArgumentType>
type bindingsArgumentType = InternalExpressionValue.nameSpace
type bindingsType = option<bindingsArgumentType>
type resultArgumentType = result<InternalExpressionValue.t, errorValue>
type resultType = option<resultArgumentType>
type externalResultArgumentType = result<ExternalExpressionValue.t, errorValue>
type externalResultType = option<externalResultArgumentType>
type continuesArgumentType = array<string>
type continuesType = array<string>
type includesArgumentType = string