├── AUTHORS ├── website.json ├── src ├── fetcher │ ├── index.ts │ └── Fetcher.ts ├── lang │ ├── sign │ │ ├── Sign.ts │ │ └── index.ts │ ├── apply │ │ ├── index.ts │ │ ├── apply.ts │ │ ├── applyTypeCtor.ts │ │ └── applyFunction.ts │ ├── execute │ │ └── index.ts │ ├── checking │ │ ├── index.ts │ │ ├── Checking.ts │ │ └── createChecking.ts │ ├── interact │ │ ├── index.ts │ │ └── interact.ts │ ├── definition │ │ ├── index.ts │ │ ├── definitionMaybeSpan.ts │ │ └── Definition.ts │ ├── exp │ │ ├── index.ts │ │ ├── formatBlockStmt.ts │ │ ├── BlockStmt.ts │ │ ├── Exp.ts │ │ └── formatExp.ts │ ├── stmt │ │ ├── index.ts │ │ └── Stmt.ts │ ├── run │ │ ├── index.ts │ │ ├── runNet.ts │ │ └── runHalfEdge.ts │ ├── span │ │ └── index.ts │ ├── value │ │ ├── index.ts │ │ ├── formatValues.ts │ │ ├── Value.ts │ │ └── formatValue.ts │ ├── half-edge │ │ ├── globalHalfEdgeInfo.ts │ │ ├── HalfEdge.ts │ │ ├── index.ts │ │ ├── halfEdgeEqual.ts │ │ ├── createHalfEdgeId.ts │ │ ├── halfEdgeFromPort.ts │ │ ├── formatHalfEdgeOtherPort.ts │ │ └── formatHalfEdge.ts │ ├── node │ │ ├── globalNodeCounters.ts │ │ ├── nodeKey.ts │ │ ├── nodeEqual.ts │ │ ├── index.ts │ │ ├── nodeKeyWithoutId.ts │ │ ├── Node.ts │ │ ├── formatNode.ts │ │ ├── createNodeId.ts │ │ └── addNodeFromDefinition.ts │ ├── import │ │ ├── ImportBinding.ts │ │ ├── formatImportBinding.ts │ │ ├── importMany.ts │ │ ├── importNodeRules.ts │ │ ├── importAll.ts │ │ └── importOne.ts │ ├── edge │ │ ├── index.ts │ │ ├── Edge.ts │ │ ├── edgeEqual.ts │ │ └── formatEdge.ts │ ├── env │ │ ├── index.ts │ │ ├── Env.ts │ │ ├── createEnv.ts │ │ ├── formatEnv.ts │ │ └── defineLocals.ts │ ├── port │ │ ├── index.ts │ │ ├── portEqual.ts │ │ ├── Port.ts │ │ ├── createInputPort.ts │ │ ├── formatPort.ts │ │ └── createOutputPort.ts │ ├── errors │ │ ├── index.ts │ │ ├── createReport.ts │ │ ├── createReportEntry.ts │ │ ├── appendReport.ts │ │ └── Report.ts │ ├── present │ │ ├── index.ts │ │ ├── presentNode.ts │ │ ├── presentNode.test.ts │ │ ├── presentRule.test.ts │ │ ├── presentFunction.ts │ │ └── presentFunction.test.ts │ ├── rule │ │ ├── index.ts │ │ ├── formatRuleTarget.ts │ │ ├── Rule.ts │ │ ├── exposeRuleTargets.ts │ │ └── disconnectNodeAndMatchParameters.ts │ ├── evaluate │ │ ├── index.ts │ │ ├── evaluateParameters.ts │ │ ├── evaluateOne.ts │ │ ├── evaluateBlock.ts │ │ ├── evaluateDefinition.ts │ │ └── evaluateBlockStmt.ts │ ├── net │ │ ├── netIsEmpty.ts │ │ ├── createNet.ts │ │ ├── deleteHalfEdgeEntry.ts │ │ ├── cloneNodeEntry.ts │ │ ├── deleteNodeEntry.ts │ │ ├── hasNode.ts │ │ ├── index.ts │ │ ├── findNodeEntry.ts │ │ ├── clonePortEntry.ts │ │ ├── formatNet.ts │ │ ├── findHalfEdgeEntry.ts │ │ ├── findPortRecord.ts │ │ ├── clonePortRecord.ts │ │ ├── createNodeFromNodeEntry.ts │ │ ├── findConnectedComponent.ts │ │ ├── findPortEntry.ts │ │ ├── createPortFromPortEntry.ts │ │ ├── findHalfEdgePort.ts │ │ ├── findPortEntryOrFail.ts │ │ ├── disconnectPort.ts │ │ ├── findHalfEdgeEntryOrFail.ts │ │ ├── findInputPorts.ts │ │ ├── findOutputPorts.ts │ │ ├── disconnectPortEntry.ts │ │ ├── disconnectHalfEdge.ts │ │ ├── findHalfEdgePortOrFail.ts │ │ ├── findNodeEntryOrFail.ts │ │ ├── findPortRecordOrFail.ts │ │ ├── findPrincipalPort.ts │ │ ├── disconnectNode.ts │ │ ├── addEdge.ts │ │ ├── addNode.ts │ │ ├── Net.ts │ │ └── allEdges.ts │ ├── cap │ │ ├── index.ts │ │ ├── capNodeAllPorts.ts │ │ ├── capType.ts │ │ ├── capNodeNonPrinciplePorts.ts │ │ ├── capOutputPort.ts │ │ └── capInputPort.ts │ ├── parameter │ │ ├── index.ts │ │ ├── formatParameterWithoutType.ts │ │ ├── formatParameterExp.ts │ │ ├── formatParameters.ts │ │ ├── Parameter.ts │ │ └── formatParameter.ts │ ├── mod │ │ ├── findDefinition.ts │ │ ├── define.ts │ │ ├── RuleEntry.ts │ │ ├── index.ts │ │ ├── findRuleByPorts.ts │ │ ├── findDefinitionOrFail.ts │ │ ├── findRuleByNodes.ts │ │ ├── hasNodeDefinition.ts │ │ ├── Mod.ts │ │ ├── findRuleByName.ts │ │ ├── createMod.ts │ │ ├── findNodeRuleEntries.ts │ │ └── defineRule.ts │ ├── syntax │ │ ├── grammars │ │ │ ├── rule_target.ts │ │ │ ├── arg.ts │ │ │ ├── index.ts │ │ │ ├── block_stmt.ts │ │ │ ├── name.ts │ │ │ ├── exp.ts │ │ │ ├── import_binding.ts │ │ │ └── parameter.ts │ │ ├── matchers │ │ │ ├── index.ts │ │ │ ├── name_matcher.ts │ │ │ ├── rule_target_matcher.ts │ │ │ ├── arg_matcher.ts │ │ │ ├── block_stmt_matcher.ts │ │ │ ├── import_binding_matcher.ts │ │ │ ├── exp_matcher.ts │ │ │ └── parameter_matcher.ts │ │ └── index.ts │ ├── builtins │ │ ├── defineBuiltinValue.ts │ │ ├── defineBuiltinPrimitiveFunction.ts │ │ ├── defineBuiltins.ts │ │ ├── run.ts │ │ ├── connect.ts │ │ └── inspect.ts │ ├── unify │ │ ├── walkType.ts │ │ ├── occurInType.ts │ │ └── deepWalkType.ts │ ├── check │ │ ├── checkAllLocalsAreUsed.ts │ │ ├── checkTypeTermArgs.ts │ │ ├── checkTypeParameters.ts │ │ ├── checkRuleIsAboutOwnNode.ts │ │ ├── checkPortSigns.ts │ │ ├── checkHalfEdges.ts │ │ ├── checkNodeParameters.ts │ │ ├── checkRule.ts │ │ └── checkRuleNodeOrder.ts │ ├── index.ts │ ├── freshen │ │ ├── refreshNode.ts │ │ └── freshenType.ts │ └── connect │ │ ├── connectValues.ts │ │ ├── connectPorts.ts │ │ ├── connectHalfEdgeWithPort.ts │ │ └── connectHalfEdges.ts ├── loader │ ├── index.ts │ └── Loader.ts ├── version.ts ├── app │ ├── index.ts │ ├── App.ts │ └── AppHome.ts ├── utils │ ├── colors.ts │ ├── arrayPickOut.ts │ ├── indent.ts │ ├── manyTimes.ts │ ├── arrayPopMany.ts │ ├── createURL.ts │ ├── stringToSubscript.ts │ └── countStringOccurrences.ts ├── index.ts └── command-line │ ├── commands │ ├── index.ts │ ├── Repl.ts │ └── Default.ts │ └── index.ts ├── examples ├── tests │ ├── builtin │ │ ├── inspect-args.error.i │ │ ├── connect-args.error.i │ │ ├── inspect-args.error.i.err │ │ ├── connect-args.error.i.err │ │ ├── connect-type.error.i │ │ └── connect-type.error.i.err │ ├── value │ │ ├── type-term.i.out │ │ ├── symbol.i.out │ │ ├── node.i.out │ │ ├── symbol.i │ │ ├── type-term.i │ │ ├── port-cap.i.out │ │ ├── node.i │ │ └── port-cap.i │ ├── module │ │ ├── circular-require-2.error.i │ │ ├── circular-require-3.error.i │ │ ├── self-require.error.i │ │ ├── circular-import-2.error.i │ │ ├── circular-import-3.error.i │ │ ├── require-already-defined.error.i │ │ ├── circular-require-1.error.i │ │ ├── self-import.error.i │ │ ├── circular-import-1.error.i │ │ ├── require.i │ │ ├── import.i │ │ ├── reuqire-more-rules-1.i │ │ ├── import.i.out │ │ ├── require.i.out │ │ ├── require-already-defined.error.i.err │ │ ├── reuqire-more-rules-3.i │ │ ├── reuqire-more-rules-3.i.out │ │ ├── self-require.error.i.err │ │ ├── self-import.error.i.err │ │ ├── reuqire-more-rules-2.i │ │ ├── circular-require-1.error.i.err │ │ ├── circular-require-2.error.i.err │ │ ├── circular-require-3.error.i.err │ │ ├── circular-import-1.error.i.err │ │ ├── circular-import-2.error.i.err │ │ └── circular-import-3.error.i.err │ ├── statement │ │ ├── type-not-enough-args.error.i │ │ ├── eval-sign.error.i │ │ ├── node-principal-ports-two.error.i │ │ ├── node-principal-ports-zero.error.i │ │ ├── type-parameters-not-type.error.i │ │ ├── type-args-not-type.error.i │ │ ├── eval-type.error.i │ │ ├── function-redefine.error.i │ │ ├── function-input-type.error.i │ │ ├── function-return-type.error.i │ │ ├── rule-for-non-own-node.error.i │ │ ├── type-not-enough-args.error.i.err │ │ ├── function-redefine.error.i.err │ │ ├── node-principal-ports-zero.error.i.err │ │ ├── eval-type-nested.error.i │ │ ├── node-principal-ports-two.error.i.err │ │ ├── function-return-type.error.i.err │ │ ├── type-parameters-not-type.error.i.err │ │ ├── rule-before-node.error.i.err │ │ ├── rule-node-order.error.i.err │ │ ├── eval-type-nested.error.i.err │ │ ├── rule-before-node.error.i │ │ ├── type-args-not-type.error.i.err │ │ ├── eval-type.error.i.err │ │ ├── rule-node-order.error.i │ │ ├── rule-type.error.i │ │ ├── rule-sign.error.i │ │ ├── eval-sign.error.i.err │ │ ├── rule-for-non-own-node.error.i.err │ │ ├── rule-type-occur-check.error.i │ │ ├── rule-type.error.i.err │ │ ├── function-input-type.error.i.err │ │ ├── rule-sign.error.i.err │ │ └── rule-type-occur-check.error.i.err │ ├── currying │ │ ├── function-extra-one-arg.i.out │ │ ├── node-extra-one-arg.i.out │ │ ├── node-not-enough-args.i.out │ │ ├── node-extra-one-arg.i │ │ ├── node-not-enough-args.i │ │ ├── function-not-enough-args.i.out │ │ ├── function-extra-one-arg.i │ │ └── function-not-enough-args.i │ └── linearity │ │ ├── function-variable-not-used.error.i │ │ ├── function-variable-not-used.error.i.err │ │ ├── rule-variable-not-used.error.i.err │ │ ├── function-variable-double-use.error.i │ │ ├── rule-variable-not-used.error.i │ │ └── function-variable-double-use.error.i.err └── datatypes │ ├── Trivial.i │ ├── Pair.i.out │ ├── List.test.i │ ├── Bin.test.i.out │ ├── Bin.test.i │ ├── List.i │ ├── DiffList.test.i │ ├── Nat.test.i │ ├── List.test.i.out │ ├── Pair.i │ ├── DiffList.i │ └── DiffList.test.i.out ├── .prettierrc.json ├── STYLE-GUIDE.md ├── docs └── references │ ├── 1990-interaction-nets.pdf │ ├── 1997-interaction-combinators.pdf │ └── 2009-models-of-computation--maribel-fernández.pdf ├── bin └── inet-js ├── .gitignore ├── TODO.md ├── tsconfig.json ├── .github └── workflows │ └── node.js.yml └── package.json /AUTHORS: -------------------------------------------------------------------------------- 1 | Xie Yuheng 2 | -------------------------------------------------------------------------------- /website.json: -------------------------------------------------------------------------------- 1 | { 2 | "cors": true 3 | } 4 | -------------------------------------------------------------------------------- /src/fetcher/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Fetcher.ts" 2 | -------------------------------------------------------------------------------- /src/lang/sign/Sign.ts: -------------------------------------------------------------------------------- 1 | export type Sign = 1 | -1 2 | -------------------------------------------------------------------------------- /src/lang/sign/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Sign.ts" 2 | -------------------------------------------------------------------------------- /src/loader/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Loader.ts" 2 | -------------------------------------------------------------------------------- /src/version.ts: -------------------------------------------------------------------------------- 1 | export const version = "0.3.0" 2 | -------------------------------------------------------------------------------- /src/lang/apply/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./apply.ts" 2 | -------------------------------------------------------------------------------- /src/lang/execute/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./execute.ts" 2 | -------------------------------------------------------------------------------- /examples/tests/builtin/inspect-args.error.i: -------------------------------------------------------------------------------- 1 | eval @inspect() 2 | -------------------------------------------------------------------------------- /examples/tests/value/type-term.i.out: -------------------------------------------------------------------------------- 1 | 'A List 2 | Nat List 3 | -------------------------------------------------------------------------------- /src/lang/checking/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Checking.ts" 2 | -------------------------------------------------------------------------------- /src/lang/interact/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./interact.ts" 2 | -------------------------------------------------------------------------------- /src/lang/definition/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Definition.ts" 2 | -------------------------------------------------------------------------------- /examples/tests/value/symbol.i.out: -------------------------------------------------------------------------------- 1 | 'A 2 | 'B 3 | 'B 4 | 'C 5 | 'C 6 | 'C 7 | -------------------------------------------------------------------------------- /examples/tests/value/node.i.out: -------------------------------------------------------------------------------- 1 | (zero₀) 2 | (add1₀) 3 | (zero₁) 4 | (add1₁) 5 | -------------------------------------------------------------------------------- /examples/datatypes/Trivial.i: -------------------------------------------------------------------------------- 1 | type Trivial 2 | 3 | node sole(-- value!: Trivial) 4 | -------------------------------------------------------------------------------- /src/lang/exp/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Exp.ts" 2 | export * from "./formatExp.ts" 3 | -------------------------------------------------------------------------------- /src/lang/stmt/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./formatStmt.ts" 2 | export * from "./Stmt.ts" 3 | -------------------------------------------------------------------------------- /examples/tests/module/circular-require-2.error.i: -------------------------------------------------------------------------------- 1 | require "./circular-require-3.error.i" 2 | -------------------------------------------------------------------------------- /examples/tests/module/circular-require-3.error.i: -------------------------------------------------------------------------------- 1 | require "./circular-require-1.error.i" 2 | -------------------------------------------------------------------------------- /src/app/index.ts: -------------------------------------------------------------------------------- 1 | import { App } from "./App.ts" 2 | 3 | export const app = new App() 4 | -------------------------------------------------------------------------------- /src/lang/run/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./runHalfEdge.ts" 2 | export * from "./runNet.ts" 3 | -------------------------------------------------------------------------------- /src/lang/span/index.ts: -------------------------------------------------------------------------------- 1 | export { type Span } from "@xieyuheng/partech/lib/span/index.js" 2 | -------------------------------------------------------------------------------- /src/lang/value/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./formatValue.ts" 2 | export * from "./Value.ts" 3 | -------------------------------------------------------------------------------- /examples/tests/module/self-require.error.i: -------------------------------------------------------------------------------- 1 | type Trivial 2 | 3 | require "./self-require.error.i" 4 | -------------------------------------------------------------------------------- /src/utils/colors.ts: -------------------------------------------------------------------------------- 1 | import picocolors from "picocolors" 2 | 3 | export const colors = picocolors 4 | -------------------------------------------------------------------------------- /examples/tests/module/circular-import-2.error.i: -------------------------------------------------------------------------------- 1 | import { Trivial } from "./circular-import-3.error.i" 2 | -------------------------------------------------------------------------------- /examples/tests/module/circular-import-3.error.i: -------------------------------------------------------------------------------- 1 | import { Trivial } from "./circular-import-1.error.i" 2 | -------------------------------------------------------------------------------- /examples/tests/module/require-already-defined.error.i: -------------------------------------------------------------------------------- 1 | type Nat 2 | 3 | require "../../datatypes/Nat.i" 4 | -------------------------------------------------------------------------------- /src/lang/half-edge/globalHalfEdgeInfo.ts: -------------------------------------------------------------------------------- 1 | export const globalHalfEdgeInfo = { 2 | counter: 0, 3 | } 4 | -------------------------------------------------------------------------------- /src/lang/node/globalNodeCounters.ts: -------------------------------------------------------------------------------- 1 | export const globalNodeCounters: Map = new Map() 2 | -------------------------------------------------------------------------------- /examples/tests/module/circular-require-1.error.i: -------------------------------------------------------------------------------- 1 | type Trivial 2 | 3 | require "./circular-require-2.error.i" 4 | -------------------------------------------------------------------------------- /src/lang/import/ImportBinding.ts: -------------------------------------------------------------------------------- 1 | export type ImportBinding = { 2 | name: string 3 | alias?: string 4 | } 5 | -------------------------------------------------------------------------------- /examples/tests/module/self-import.error.i: -------------------------------------------------------------------------------- 1 | type Trivial 2 | 3 | import { Trivial } from "./self-import.error.i" 4 | -------------------------------------------------------------------------------- /examples/tests/statement/type-not-enough-args.error.i: -------------------------------------------------------------------------------- 1 | type List(Element: @Type) 2 | node null(-- value!: List) 3 | -------------------------------------------------------------------------------- /src/lang/edge/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Edge.ts" 2 | export * from "./edgeEqual.ts" 3 | export * from "./formatEdge.ts" 4 | -------------------------------------------------------------------------------- /src/lang/env/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./createEnv.ts" 2 | export * from "./Env.ts" 3 | export * from "./formatEnv.ts" 4 | -------------------------------------------------------------------------------- /src/lang/port/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./formatPort.ts" 2 | export * from "./Port.ts" 3 | export * from "./portEqual.ts" 4 | -------------------------------------------------------------------------------- /examples/tests/module/circular-import-1.error.i: -------------------------------------------------------------------------------- 1 | type Trivial 2 | 3 | import { Trivial } from "./circular-import-2.error.i" 4 | -------------------------------------------------------------------------------- /examples/tests/module/require.i: -------------------------------------------------------------------------------- 1 | require "../../datatypes/Nat.i" 2 | 3 | eval @inspect(@run(@inspect(add(one(), zero())))) 4 | -------------------------------------------------------------------------------- /examples/tests/value/symbol.i: -------------------------------------------------------------------------------- 1 | eval @inspect('A) 2 | eval @inspect(@inspect('B)) 3 | eval @inspect(@inspect(@inspect('C))) 4 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./fetcher/index.ts" 2 | export * from "./lang/index.ts" 3 | export * from "./loader/index.ts" 4 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "trailingComma": "all", 4 | "plugins": ["prettier-plugin-organize-imports"] 5 | } 6 | -------------------------------------------------------------------------------- /STYLE-GUIDE.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Style Guide 3 | --- 4 | 5 | **In general, observe the style of existing code and respect it.** 6 | -------------------------------------------------------------------------------- /examples/tests/statement/eval-sign.error.i: -------------------------------------------------------------------------------- 1 | type Trivial 2 | node sole(-- value!: Trivial) 3 | 4 | eval @connect(sole(), sole()) 5 | -------------------------------------------------------------------------------- /src/lang/errors/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./appendReport.ts" 2 | export * from "./createReport.ts" 3 | export * from "./Report.ts" 4 | -------------------------------------------------------------------------------- /src/lang/half-edge/HalfEdge.ts: -------------------------------------------------------------------------------- 1 | export type HalfEdge = { 2 | "@type": "Value" 3 | "@kind": "HalfEdge" 4 | id: string 5 | } 6 | -------------------------------------------------------------------------------- /docs/references/1990-interaction-nets.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieyuheng/inet-js/HEAD/docs/references/1990-interaction-nets.pdf -------------------------------------------------------------------------------- /examples/tests/value/type-term.i: -------------------------------------------------------------------------------- 1 | type Nat 2 | type List(Element: @Type) 3 | 4 | eval @inspect(List('A)) 5 | eval @inspect(List(Nat)) 6 | -------------------------------------------------------------------------------- /examples/tests/statement/node-principal-ports-two.error.i: -------------------------------------------------------------------------------- 1 | type Nat 2 | node zero(-- value!: Nat) 3 | node add1(prev!: Nat -- value!: Nat) 4 | -------------------------------------------------------------------------------- /examples/tests/statement/node-principal-ports-zero.error.i: -------------------------------------------------------------------------------- 1 | type Nat 2 | node zero(-- value: Nat) 3 | node add1(prev: Nat -- value: Nat) 4 | -------------------------------------------------------------------------------- /src/lang/present/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./presentFunction.ts" 2 | export * from "./presentNode.ts" 3 | export * from "./presentRule.ts" 4 | -------------------------------------------------------------------------------- /bin/inet-js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { createCommandRunner } from "../lib/command-line/index.js" 4 | 5 | createCommandRunner().run() 6 | -------------------------------------------------------------------------------- /examples/tests/module/import.i: -------------------------------------------------------------------------------- 1 | import { zero, one, add } from "../../datatypes/Nat.i" 2 | 3 | eval @inspect(@run(@inspect(add(one(), zero())))) 4 | -------------------------------------------------------------------------------- /examples/tests/statement/type-parameters-not-type.error.i: -------------------------------------------------------------------------------- 1 | type Trivial 2 | node sole(-- value!: Trivial) 3 | 4 | type List(Element: Trivial) 5 | -------------------------------------------------------------------------------- /docs/references/1997-interaction-combinators.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieyuheng/inet-js/HEAD/docs/references/1997-interaction-combinators.pdf -------------------------------------------------------------------------------- /src/lang/half-edge/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./formatHalfEdge.ts" 2 | export * from "./formatHalfEdgeOtherPort.ts" 3 | export * from "./HalfEdge.ts" 4 | -------------------------------------------------------------------------------- /src/lang/rule/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./disconnectNodeAndMatchParameters.ts" 2 | export * from "./formatRuleTarget.ts" 3 | export * from "./Rule.ts" 4 | -------------------------------------------------------------------------------- /examples/tests/builtin/connect-args.error.i: -------------------------------------------------------------------------------- 1 | type Trivial 2 | 3 | node sole( 4 | -------- 5 | value!: Trivial 6 | ) 7 | 8 | eval @connect(sole()) 9 | -------------------------------------------------------------------------------- /examples/tests/statement/type-args-not-type.error.i: -------------------------------------------------------------------------------- 1 | type Trivial 2 | node sole(-- value!: Trivial) 3 | 4 | type List(Element: @Type) 5 | 6 | eval List(sole) 7 | -------------------------------------------------------------------------------- /src/lang/edge/Edge.ts: -------------------------------------------------------------------------------- 1 | import { type HalfEdge } from "../half-edge/index.ts" 2 | 3 | export type Edge = { 4 | first: HalfEdge 5 | second: HalfEdge 6 | } 7 | -------------------------------------------------------------------------------- /src/utils/arrayPickOut.ts: -------------------------------------------------------------------------------- 1 | export function arrayPickOut(array: Array, i: number): A { 2 | const x = array[i] 3 | array.splice(i, 1) 4 | return x 5 | } 6 | -------------------------------------------------------------------------------- /examples/datatypes/Pair.i.out: -------------------------------------------------------------------------------- 1 | net handle-(pair₄) { 2 | (pair₄)-left first-(pairDup₄) 3 | (pair₄)-right second-(pairDup₄) 4 | (pair₄)-value!target-(pairDup₄) 5 | } 6 | -------------------------------------------------------------------------------- /docs/references/2009-models-of-computation--maribel-fernández.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xieyuheng/inet-js/HEAD/docs/references/2009-models-of-computation--maribel-fernández.pdf -------------------------------------------------------------------------------- /src/lang/node/nodeKey.ts: -------------------------------------------------------------------------------- 1 | import { type Node } from "./Node.ts" 2 | 3 | export function nodeKey(node: Node): string { 4 | return `${node.modId}/${node.name}#${node.id}` 5 | } 6 | -------------------------------------------------------------------------------- /src/lang/evaluate/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./evaluate.ts" 2 | export * from "./evaluateBlockStmt.ts" 3 | export * from "./evaluateOne.ts" 4 | export * from "./evaluateParameters.ts" 5 | -------------------------------------------------------------------------------- /src/lang/node/nodeEqual.ts: -------------------------------------------------------------------------------- 1 | import { type Node } from "./Node.ts" 2 | 3 | export function nodeEqual(x: Node, y: Node): boolean { 4 | return x.name === y.name && x.id === y.id 5 | } 6 | -------------------------------------------------------------------------------- /src/lang/errors/createReport.ts: -------------------------------------------------------------------------------- 1 | import { Report, type ReportEntry } from "./Report.ts" 2 | 3 | export function createReport(entry: ReportEntry): Report { 4 | return new Report([entry]) 5 | } 6 | -------------------------------------------------------------------------------- /src/lang/half-edge/halfEdgeEqual.ts: -------------------------------------------------------------------------------- 1 | import { type HalfEdge } from "./HalfEdge.ts" 2 | 3 | export function halfEdgeEqual(x: HalfEdge, y: HalfEdge): boolean { 4 | return x.id === y.id 5 | } 6 | -------------------------------------------------------------------------------- /src/lang/node/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./formatNode.ts" 2 | export * from "./Node.ts" 3 | export * from "./nodeEqual.ts" 4 | export * from "./nodeKey.ts" 5 | export * from "./nodeKeyWithoutId.ts" 6 | -------------------------------------------------------------------------------- /examples/tests/value/port-cap.i.out: -------------------------------------------------------------------------------- 1 | net result-(add₃) { 2 | (add₃)-target!value-(add1₂) 3 | (add₃)-addend value-(add1₃) 4 | (add1₂)-prev value-(zero₁) 5 | (add1₃)-prev value-(zero₂) 6 | } 7 | -------------------------------------------------------------------------------- /src/lang/checking/Checking.ts: -------------------------------------------------------------------------------- 1 | import { type Value } from "../value/index.ts" 2 | 3 | export type Checking = { 4 | substitution: Map 5 | typeVarCounters: Map 6 | } 7 | -------------------------------------------------------------------------------- /src/lang/net/netIsEmpty.ts: -------------------------------------------------------------------------------- 1 | import { type Net } from "./Net.ts" 2 | 3 | export function netIsEmpty(net: Net): boolean { 4 | return net.activeEdges.length === 0 && net.nodeEntries.size === 0 5 | } 6 | -------------------------------------------------------------------------------- /examples/tests/module/reuqire-more-rules-1.i: -------------------------------------------------------------------------------- 1 | type Nat 2 | 3 | node zero( 4 | ------ 5 | value!: Nat 6 | ) 7 | 8 | node add1( 9 | prev: Nat 10 | ---------- 11 | value!: Nat 12 | ) 13 | -------------------------------------------------------------------------------- /examples/tests/statement/eval-type.error.i: -------------------------------------------------------------------------------- 1 | type Trivial 2 | node sole(-- value!: Trivial) 3 | 4 | type Nat 5 | node zero(-- value!: Nat) 6 | node add1(prev: Nat -- value!: Nat) 7 | 8 | eval add1(sole()) 9 | -------------------------------------------------------------------------------- /src/lang/node/nodeKeyWithoutId.ts: -------------------------------------------------------------------------------- 1 | import { type NodeWithoutId } from "./Node.ts" 2 | 3 | export function nodeKeyWithoutId(node: NodeWithoutId): string { 4 | return `${node.modId}/${node.name}` 5 | } 6 | -------------------------------------------------------------------------------- /src/utils/indent.ts: -------------------------------------------------------------------------------- 1 | export function indent(text: string, indentation: string = " "): string { 2 | return text 3 | .split("\n") 4 | .map((line) => indentation + line) 5 | .join("\n") 6 | } 7 | -------------------------------------------------------------------------------- /examples/tests/currying/function-extra-one-arg.i.out: -------------------------------------------------------------------------------- 1 | net !value-(add1₃) { 2 | (add1₃)-prev value-(add1₈) 3 | (add1₈)-prev value-(add1₉) 4 | (add1₉)-prev value-(add1₆) 5 | (add1₆)-prev value-(zero₄) 6 | } 7 | -------------------------------------------------------------------------------- /examples/tests/linearity/function-variable-not-used.error.i: -------------------------------------------------------------------------------- 1 | type Nat 2 | node zero(-- value!: Nat) 3 | node add1(prev: Nat -- value!: Nat) 4 | 5 | function add2(n: Nat): Nat { 6 | return add1(add1(zero())) 7 | } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # typescript 2 | lib 3 | 4 | # pkg 5 | dist 6 | 7 | # npm 8 | node_modules 9 | 10 | # 0x 11 | *.0x/ 12 | 13 | # emacs 14 | *~ 15 | *#* 16 | .#* 17 | 18 | .vercel 19 | 20 | # idea 21 | .idea 22 | -------------------------------------------------------------------------------- /src/lang/cap/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./capInputPort.ts" 2 | export * from "./capNodeAllPorts.ts" 3 | export * from "./capNodeNonPrinciplePorts.ts" 4 | export * from "./capOutputPort.ts" 5 | export * from "./capType.ts" 6 | -------------------------------------------------------------------------------- /src/app/App.ts: -------------------------------------------------------------------------------- 1 | import { AppHome } from "./AppHome.ts" 2 | import { AppReplEventHandler } from "./AppReplEventHandler.ts" 3 | 4 | export class App { 5 | home = new AppHome() 6 | replEventHandler = new AppReplEventHandler() 7 | } 8 | -------------------------------------------------------------------------------- /src/lang/checking/createChecking.ts: -------------------------------------------------------------------------------- 1 | import { type Checking } from "./Checking.ts" 2 | 3 | export function createChecking(): Checking { 4 | return { 5 | substitution: new Map(), 6 | typeVarCounters: new Map(), 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/lang/half-edge/createHalfEdgeId.ts: -------------------------------------------------------------------------------- 1 | import { globalHalfEdgeInfo } from "./globalHalfEdgeInfo.ts" 2 | 3 | export function createHalfEdgeId(): string { 4 | const n = globalHalfEdgeInfo.counter++ 5 | return n.toString() 6 | } 7 | -------------------------------------------------------------------------------- /src/utils/manyTimes.ts: -------------------------------------------------------------------------------- 1 | export function manyTimes(n: number, f: () => A): Array { 2 | const results: Array = [] 3 | while (n > 0) { 4 | n-- 5 | results.push(f()) 6 | } 7 | 8 | return results 9 | } 10 | -------------------------------------------------------------------------------- /examples/tests/module/import.i.out: -------------------------------------------------------------------------------- 1 | net result-(add₁₀) { 2 | (add₁₀)-target!value-(add1₂₁) 3 | (add₁₀)-addend value-(zero₂₀) 4 | (add1₂₁)-prev value-(zero₁₉) 5 | } 6 | net !value-(add1₂₂) { 7 | (add1₂₂)-prev value-(zero₂₀) 8 | } 9 | -------------------------------------------------------------------------------- /examples/tests/module/require.i.out: -------------------------------------------------------------------------------- 1 | net result-(add₁₀) { 2 | (add₁₀)-target!value-(add1₂₁) 3 | (add₁₀)-addend value-(zero₂₀) 4 | (add1₂₁)-prev value-(zero₁₉) 5 | } 6 | net !value-(add1₂₂) { 7 | (add1₂₂)-prev value-(zero₂₀) 8 | } 9 | -------------------------------------------------------------------------------- /src/lang/net/createNet.ts: -------------------------------------------------------------------------------- 1 | import { type Net } from "./Net.ts" 2 | 3 | export function createNet(): Net { 4 | return { 5 | activeEdges: [], 6 | nodeEntries: new Map(), 7 | halfEdgeEntries: new Map(), 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lang/parameter/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./formatParameter.ts" 2 | export * from "./formatParameterExp.ts" 3 | export * from "./formatParameters.ts" 4 | export * from "./formatParameterWithoutType.ts" 5 | export * from "./Parameter.ts" 6 | -------------------------------------------------------------------------------- /src/lang/node/Node.ts: -------------------------------------------------------------------------------- 1 | export type Node = { 2 | "@type": "Value" 3 | "@kind": "Node" 4 | modId: string 5 | id: string 6 | name: string 7 | } 8 | 9 | export type NodeWithoutId = { 10 | modId: string 11 | name: string 12 | } 13 | -------------------------------------------------------------------------------- /src/lang/port/portEqual.ts: -------------------------------------------------------------------------------- 1 | import { nodeEqual } from "../node/nodeEqual.ts" 2 | import { type Port } from "./Port.ts" 3 | 4 | export function portEqual(x: Port, y: Port): boolean { 5 | return nodeEqual(x.node, y.node) && x.name === y.name 6 | } 7 | -------------------------------------------------------------------------------- /examples/tests/statement/function-redefine.error.i: -------------------------------------------------------------------------------- 1 | type Trivial 2 | node sole(-- value!: Trivial) 3 | 4 | function trivialId(x: Trivial): Trivial { 5 | return x 6 | } 7 | 8 | function trivialId(x: Trivial): Trivial { 9 | return x 10 | } 11 | -------------------------------------------------------------------------------- /src/command-line/commands/index.ts: -------------------------------------------------------------------------------- 1 | export * from "@xieyuheng/command-line/lib/commands/index.js" 2 | export * from "./Default.ts" 3 | export * from "./Format.ts" 4 | export * from "./Parse.ts" 5 | export * from "./Repl.ts" 6 | export * from "./Run.ts" 7 | -------------------------------------------------------------------------------- /src/lang/mod/findDefinition.ts: -------------------------------------------------------------------------------- 1 | import { type Definition } from "../definition/index.ts" 2 | import { type Mod } from "./Mod.ts" 3 | 4 | export function findDefinition(mod: Mod, name: string): Definition | undefined { 5 | return mod.definitions.get(name) 6 | } 7 | -------------------------------------------------------------------------------- /src/lang/net/deleteHalfEdgeEntry.ts: -------------------------------------------------------------------------------- 1 | import { type HalfEdge } from "../half-edge/index.ts" 2 | import { type Net } from "./Net.ts" 3 | 4 | export function deleteHalfEdgeEntry(net: Net, halfEdge: HalfEdge): void { 5 | net.halfEdgeEntries.delete(halfEdge.id) 6 | } 7 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | `NodeId` vs `Node` -- instead of `Node` vs `NodeEntry` -- the same for `HalfEdge` 2 | static import should handled by an extra pass -- instead of injecting a `Loader` to `Mod` 3 | Type as Value -- 用 HalfEdge 来编码 Type -- 删除 TypeCtor | Type | Symbol | TypeTerm 4 | -------------------------------------------------------------------------------- /examples/tests/module/require-already-defined.error.i.err: -------------------------------------------------------------------------------- 1 | [importAll] I can not import already defined name. 2 | 3 | name: Nat 4 | 5 | [execute] I fail to execute a statement. 6 | 7 | 1 |type Nat 8 | 2 | 9 | 3 |require "../../datatypes/Nat.i" 10 | 4 | 11 | -------------------------------------------------------------------------------- /examples/tests/statement/function-input-type.error.i: -------------------------------------------------------------------------------- 1 | type Nat 2 | node zero(-- value!: Nat) 3 | node add1(prev: Nat -- value!: Nat) 4 | 5 | type Trivial 6 | node sole(-- value!: Trivial) 7 | 8 | function add2(n: Trivial): Nat { 9 | return add1(add1(n)) 10 | } 11 | -------------------------------------------------------------------------------- /examples/tests/statement/function-return-type.error.i: -------------------------------------------------------------------------------- 1 | type Nat 2 | node zero(-- value!: Nat) 3 | node add1(prev: Nat -- value!: Nat) 4 | 5 | type Trivial 6 | node sole(-- value!: Trivial) 7 | 8 | function add2(n: Nat): Trivial { 9 | return add1(add1(n)) 10 | } 11 | -------------------------------------------------------------------------------- /src/lang/net/cloneNodeEntry.ts: -------------------------------------------------------------------------------- 1 | import { type NodeEntry } from "./Net.ts" 2 | import { clonePortRecord } from "./clonePortRecord.ts" 3 | 4 | export function cloneNodeEntry(entry: NodeEntry): NodeEntry { 5 | return { ...entry, ports: clonePortRecord(entry.ports) } 6 | } 7 | -------------------------------------------------------------------------------- /examples/tests/module/reuqire-more-rules-3.i: -------------------------------------------------------------------------------- 1 | import { zero, add1, add } from "./reuqire-more-rules-2.i" 2 | 3 | eval @inspect(@run(add(zero(), zero()))) 4 | eval @inspect(@run(add(add1(zero()), add1(zero())))) 5 | eval @inspect(@run(add(add1(add1(zero())), add1(add1(zero()))))) 6 | -------------------------------------------------------------------------------- /src/lang/syntax/grammars/rule_target.ts: -------------------------------------------------------------------------------- 1 | export const rule_target = { 2 | $grammar: { 3 | "rule_target:rule_target": [ 4 | { name: "variable_name" }, 5 | '"("', 6 | { parameters: "parameters_without_type" }, 7 | '")"', 8 | ], 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /src/lang/net/deleteNodeEntry.ts: -------------------------------------------------------------------------------- 1 | import { type Node } from "../node/index.ts" 2 | import { nodeKey } from "../node/nodeKey.ts" 3 | import { type Net } from "./Net.ts" 4 | 5 | export function deleteNodeEntry(net: Net, node: Node): void { 6 | net.nodeEntries.delete(nodeKey(node)) 7 | } 8 | -------------------------------------------------------------------------------- /src/lang/net/hasNode.ts: -------------------------------------------------------------------------------- 1 | import { type Node } from "../node/index.ts" 2 | import { type Net } from "./Net.ts" 3 | import { findPortRecord } from "./findPortRecord.ts" 4 | 5 | export function hasNode(net: Net, node: Node): boolean { 6 | return Boolean(findPortRecord(net, node)) 7 | } 8 | -------------------------------------------------------------------------------- /examples/tests/builtin/inspect-args.error.i.err: -------------------------------------------------------------------------------- 1 | [@inspect] I expect one argument. 2 | 3 | [evaluate] I fail to evaluate an exp. 4 | 5 | exp: @inspect() 6 | 7 | 1 |eval @inspect() 8 | 2 | 9 | 10 | [execute] I fail to execute a statement. 11 | 12 | 1 |eval @inspect() 13 | 2 | 14 | -------------------------------------------------------------------------------- /src/lang/env/Env.ts: -------------------------------------------------------------------------------- 1 | import { type Mod } from "../mod/index.ts" 2 | import { type Net } from "../net/index.ts" 3 | import { type Value } from "../value/index.ts" 4 | 5 | export type Env = { 6 | mod: Mod 7 | net: Net 8 | stack: Array 9 | locals: Map 10 | } 11 | -------------------------------------------------------------------------------- /src/lang/net/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./allEdges.ts" 2 | export * from "./copyConnectedComponent.ts" 3 | export * from "./createNet.ts" 4 | export * from "./findHalfEdgeEntryOrFail.ts" 5 | export * from "./findHalfEdgePort.ts" 6 | export * from "./findHalfEdgePortOrFail.ts" 7 | export * from "./Net.ts" 8 | -------------------------------------------------------------------------------- /src/lang/port/Port.ts: -------------------------------------------------------------------------------- 1 | import { type Node } from "../node/index.ts" 2 | import { type Sign } from "../sign/index.ts" 3 | import { type Value } from "../value/index.ts" 4 | 5 | export type Port = { 6 | node: Node 7 | name: string 8 | sign: Sign 9 | t: Value 10 | isPrincipal: boolean 11 | } 12 | -------------------------------------------------------------------------------- /examples/tests/currying/node-extra-one-arg.i.out: -------------------------------------------------------------------------------- 1 | net result-(add₃) { 2 | (add₃)-target!value-(add1₂) 3 | (add₃)-addend value-(add1₃) 4 | (add1₂)-prev value-(zero₁) 5 | (add1₃)-prev value-(zero₂) 6 | } 7 | net !value-(add1₄) { 8 | (add1₄)-prev value-(add1₃) 9 | (add1₃)-prev value-(zero₂) 10 | } 11 | -------------------------------------------------------------------------------- /examples/tests/currying/node-not-enough-args.i.out: -------------------------------------------------------------------------------- 1 | net result-(add₃) { 2 | (add₃)-target!value-(add1₂) 3 | (add₃)-addend value-(add1₃) 4 | (add1₂)-prev value-(zero₁) 5 | (add1₃)-prev value-(zero₂) 6 | } 7 | net !value-(add1₄) { 8 | (add1₄)-prev value-(add1₃) 9 | (add1₃)-prev value-(zero₂) 10 | } 11 | -------------------------------------------------------------------------------- /src/lang/net/findNodeEntry.ts: -------------------------------------------------------------------------------- 1 | import { type Node } from "../node/index.ts" 2 | import { nodeKey } from "../node/nodeKey.ts" 3 | import { type Net, type NodeEntry } from "./Net.ts" 4 | 5 | export function findNodeEntry(net: Net, node: Node): NodeEntry | undefined { 6 | return net.nodeEntries.get(nodeKey(node)) 7 | } 8 | -------------------------------------------------------------------------------- /examples/tests/statement/rule-for-non-own-node.error.i: -------------------------------------------------------------------------------- 1 | import { Nat, zero, add1, add } from "../../datatypes/Nat.i" 2 | 3 | rule add(target!, addend, result) zero(value!) { 4 | @connect(addend, result) 5 | } 6 | 7 | rule add(target!, addend, result) add1(prev, value!) { 8 | add1(add(prev, addend), result) 9 | } 10 | -------------------------------------------------------------------------------- /examples/tests/value/node.i: -------------------------------------------------------------------------------- 1 | type Nat 2 | 3 | node zero( 4 | ------ 5 | value!: Nat 6 | ) 7 | 8 | node add1( 9 | prev: Nat 10 | ---------- 11 | value!: Nat 12 | ) 13 | 14 | eval @inspect(zero) 15 | eval @inspect(add1) 16 | 17 | // New node new id: 18 | 19 | eval @inspect(zero) 20 | eval @inspect(add1) 21 | -------------------------------------------------------------------------------- /src/lang/import/formatImportBinding.ts: -------------------------------------------------------------------------------- 1 | import { type ImportBinding } from "./ImportBinding.ts" 2 | 3 | export function formatImportBinding(binding: ImportBinding): string { 4 | if (binding.alias) { 5 | return `${binding.name} as ${binding.alias}` 6 | } else { 7 | return `${binding.name}` 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/lang/net/clonePortEntry.ts: -------------------------------------------------------------------------------- 1 | import { type PortEntry } from "./Net.ts" 2 | 3 | export function clonePortEntry(entry: PortEntry): PortEntry { 4 | return { 5 | ...entry, 6 | connection: entry.connection 7 | ? { 8 | ...entry.connection, 9 | } 10 | : undefined, 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/lang/net/formatNet.ts: -------------------------------------------------------------------------------- 1 | import { formatEdge } from "../edge/formatEdge.ts" 2 | import { type Net } from "./Net.ts" 3 | import { allEdges } from "./allEdges.ts" 4 | 5 | export function formatNet(net: Net): string { 6 | return allEdges(net) 7 | .map((edge) => formatEdge(net, edge)) 8 | .join("\n") 9 | } 10 | -------------------------------------------------------------------------------- /src/lang/value/formatValues.ts: -------------------------------------------------------------------------------- 1 | import { type Env } from "../env/index.ts" 2 | import { type Value } from "./Value.ts" 3 | import { formatValue } from "./formatValue.ts" 4 | 5 | export function formatValues(env: Env, values: Array): string { 6 | return values.map((value) => formatValue(env, value)).join(", ") 7 | } 8 | -------------------------------------------------------------------------------- /examples/datatypes/List.test.i: -------------------------------------------------------------------------------- 1 | require "List.i" 2 | require "Trivial.i" 3 | 4 | function sixSoles(): List(Trivial) { 5 | return append( 6 | cons(sole(), cons(sole(), cons(sole(), null()))), 7 | cons(sole(), cons(sole(), cons(sole(), null()))), 8 | ) 9 | } 10 | 11 | eval @inspect(@run(@inspect(sixSoles()))) 12 | -------------------------------------------------------------------------------- /examples/tests/statement/type-not-enough-args.error.i.err: -------------------------------------------------------------------------------- 1 | [checkNodeParameters] I expect all parameters of a node to be Symbol or TypeTerm. 2 | 3 | parameters: [value!: #TypeCtor(List)] 4 | 5 | [execute] I fail to execute a statement. 6 | 7 | 1 |type List(Element: @Type) 8 | 2 |node null(-- value!: List) 9 | 3 | 10 | -------------------------------------------------------------------------------- /src/lang/net/findHalfEdgeEntry.ts: -------------------------------------------------------------------------------- 1 | import { type HalfEdge } from "../half-edge/index.ts" 2 | import { type HalfEdgeEntry, type Net } from "./Net.ts" 3 | 4 | export function findHalfEdgeEntry( 5 | net: Net, 6 | halfEdge: HalfEdge, 7 | ): HalfEdgeEntry | undefined { 8 | return net.halfEdgeEntries.get(halfEdge.id) 9 | } 10 | -------------------------------------------------------------------------------- /examples/tests/module/reuqire-more-rules-3.i.out: -------------------------------------------------------------------------------- 1 | net !value-(zero₂) {} 2 | net !value-(add1₄) { 3 | (add1₄)-prev value-(add1₃) 4 | (add1₃)-prev value-(zero₄) 5 | } 6 | net !value-(add1₉) { 7 | (add1₉)-prev value-(add1₁₀) 8 | (add1₁₀)-prev value-(add1₇) 9 | (add1₇)-prev value-(add1₈) 10 | (add1₈)-prev value-(zero₆) 11 | } 12 | -------------------------------------------------------------------------------- /examples/tests/statement/function-redefine.error.i.err: -------------------------------------------------------------------------------- 1 | [define] Can not redefine name: trivialId 2 | 3 | [execute] I fail to execute a statement. 4 | 5 | 4 |function trivialId(x: Trivial): Trivial { 6 | 5 | return x 7 | 6 |} 8 | 7 | 9 | 8 |function trivialId(x: Trivial): Trivial { 10 | 9 | return x 11 | 10 |} 12 | 11 | 13 | -------------------------------------------------------------------------------- /examples/tests/statement/node-principal-ports-zero.error.i.err: -------------------------------------------------------------------------------- 1 | [checkNodeParameters] I expect a node to have one and only one principal port. 2 | 3 | declared principal ports: [] 4 | 5 | [execute] I fail to execute a statement. 6 | 7 | 1 |type Nat 8 | 2 |node zero(-- value: Nat) 9 | 3 |node add1(prev: Nat -- value: Nat) 10 | 4 | 11 | -------------------------------------------------------------------------------- /examples/tests/statement/eval-type-nested.error.i: -------------------------------------------------------------------------------- 1 | type Trivial 2 | node sole(-- value!: Trivial) 3 | 4 | type List(Element: @Type) 5 | 6 | node null( 7 | -------- 8 | value!: List('A) 9 | ) 10 | 11 | node cons( 12 | head: 'A, 13 | tail: List('A) 14 | -------- 15 | value!: List('A) 16 | ) 17 | 18 | eval cons(sole(), sole()) 19 | -------------------------------------------------------------------------------- /src/lang/errors/createReportEntry.ts: -------------------------------------------------------------------------------- 1 | import { type ReportEntry } from "./Report.ts" 2 | 3 | export function createReportEntry(error: unknown): ReportEntry { 4 | if (error instanceof Error) { 5 | return { 6 | message: error.message, 7 | } 8 | } else { 9 | return { 10 | message: String(error), 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/lang/syntax/matchers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./arg_matcher.ts" 2 | export * from "./block_stmt_matcher.ts" 3 | export * from "./exp_matcher.ts" 4 | export * from "./import_binding_matcher.ts" 5 | export * from "./name_matcher.ts" 6 | export * from "./parameter_matcher.ts" 7 | export * from "./rule_target_matcher.ts" 8 | export * from "./stmt_matcher.ts" 9 | -------------------------------------------------------------------------------- /src/lang/net/findPortRecord.ts: -------------------------------------------------------------------------------- 1 | import { type Node } from "../node/index.ts" 2 | import { type Net, type PortRecord } from "./Net.ts" 3 | import { findNodeEntry } from "./findNodeEntry.ts" 4 | 5 | export function findPortRecord(net: Net, node: Node): PortRecord | undefined { 6 | const nodeEntry = findNodeEntry(net, node) 7 | return nodeEntry?.ports 8 | } 9 | -------------------------------------------------------------------------------- /src/lang/parameter/formatParameterWithoutType.ts: -------------------------------------------------------------------------------- 1 | import { type ParameterWithoutType } from "./Parameter.ts" 2 | 3 | export function formatParameterWithoutType( 4 | parameter: ParameterWithoutType, 5 | ): string { 6 | if (parameter.isPrincipal) { 7 | return `${parameter.name}!` 8 | } else { 9 | return `${parameter.name}` 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/lang/node/formatNode.ts: -------------------------------------------------------------------------------- 1 | import { stringToSubscript } from "../../utils/stringToSubscript.ts" 2 | import { type Net } from "../net/index.ts" 3 | import { type Node } from "../node/index.ts" 4 | 5 | export function formatNode(net: Net, node: Node): string { 6 | const subscript = stringToSubscript(node.id.toString()) 7 | return `${node.name}${subscript}` 8 | } 9 | -------------------------------------------------------------------------------- /src/utils/arrayPopMany.ts: -------------------------------------------------------------------------------- 1 | export function arrayPopMany(array: Array, n: number): Array { 2 | const results: Array = [] 3 | while (n > 0) { 4 | const value = array.pop() 5 | if (value === undefined) { 6 | break 7 | } else { 8 | results.push(value) 9 | } 10 | 11 | n-- 12 | } 13 | 14 | return results 15 | } 16 | -------------------------------------------------------------------------------- /examples/tests/statement/node-principal-ports-two.error.i.err: -------------------------------------------------------------------------------- 1 | [checkNodeParameters] I expect a node to have one and only one principal port. 2 | 3 | declared principal ports: [prev!: Nat, value!: Nat] 4 | 5 | [execute] I fail to execute a statement. 6 | 7 | 1 |type Nat 8 | 2 |node zero(-- value!: Nat) 9 | 3 |node add1(prev!: Nat -- value!: Nat) 10 | 4 | 11 | -------------------------------------------------------------------------------- /src/lang/syntax/grammars/arg.ts: -------------------------------------------------------------------------------- 1 | export const arg = { 2 | $grammar: { 3 | "arg:plain": [{ arg: "exp" }], 4 | }, 5 | } 6 | 7 | export const args = { 8 | $grammar: { 9 | "args:args": [ 10 | { entries: { $ap: ["zero_or_more", "arg", '","'] } }, 11 | { last_entry: "arg" }, 12 | { $ap: ["optional", '","'] }, 13 | ], 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /examples/tests/statement/function-return-type.error.i.err: -------------------------------------------------------------------------------- 1 | [unifyTypes] I fail to unify types. 2 | 3 | left: Nat 4 | right: Trivial 5 | 6 | [execute] I fail to execute a statement. 7 | 8 | 4 | 9 | 5 |type Trivial 10 | 6 |node sole(-- value!: Trivial) 11 | 7 | 12 | 8 |function add2(n: Nat): Trivial { 13 | 9 | return add1(add1(n)) 14 | 10 |} 15 | 11 | 16 | -------------------------------------------------------------------------------- /examples/tests/module/self-require.error.i.err: -------------------------------------------------------------------------------- 1 | [execute / Require] I can not do circular require. 2 | 3 | loading module url: examples/tests/module/self-require.error.i 4 | requiring module url: examples/tests/module/self-require.error.i 5 | 6 | [execute] I fail to execute a statement. 7 | 8 | 1 |type Trivial 9 | 2 | 10 | 3 |require "./self-require.error.i" 11 | 4 | 12 | -------------------------------------------------------------------------------- /examples/tests/statement/type-parameters-not-type.error.i.err: -------------------------------------------------------------------------------- 1 | [checkTypeParameters] I expect the claimed input parameters 2 | of a type definition to be Type. 3 | 4 | input parameters: [Element: Trivial] 5 | 6 | [execute] I fail to execute a statement. 7 | 8 | 1 |type Trivial 9 | 2 |node sole(-- value!: Trivial) 10 | 3 | 11 | 4 |type List(Element: Trivial) 12 | 5 | 13 | -------------------------------------------------------------------------------- /src/lang/builtins/defineBuiltinValue.ts: -------------------------------------------------------------------------------- 1 | import { type Mod } from "../mod/index.ts" 2 | import { type Value } from "../value/index.ts" 3 | 4 | export function defineBuiltinValue(mod: Mod, name: string, value: Value): void { 5 | mod.builtins.set(name, { 6 | "@type": "Definition", 7 | "@kind": "ValueDefinition", 8 | mod, 9 | name, 10 | value, 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /src/lang/edge/edgeEqual.ts: -------------------------------------------------------------------------------- 1 | import { halfEdgeEqual } from "../half-edge/halfEdgeEqual.ts" 2 | import { type Edge } from "./Edge.ts" 3 | 4 | export function edgeEqual(x: Edge, y: Edge): boolean { 5 | return ( 6 | (halfEdgeEqual(x.first, y.first) && halfEdgeEqual(x.second, y.second)) || 7 | (halfEdgeEqual(x.first, y.second) && halfEdgeEqual(x.second, y.first)) 8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /src/lang/net/clonePortRecord.ts: -------------------------------------------------------------------------------- 1 | import { type PortRecord } from "./Net.ts" 2 | import { clonePortEntry } from "./clonePortEntry.ts" 3 | 4 | export function clonePortRecord(record: PortRecord): PortRecord { 5 | return Object.fromEntries( 6 | Object.entries(record).map(([name, portEntry]) => [ 7 | name, 8 | clonePortEntry(portEntry), 9 | ]), 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /src/lang/net/createNodeFromNodeEntry.ts: -------------------------------------------------------------------------------- 1 | import { type Node } from "../node/index.ts" 2 | import { type NodeEntry } from "./Net.ts" 3 | 4 | export function createNodeFromNodeEntry(nodeEntry: NodeEntry): Node { 5 | return { 6 | "@type": "Value", 7 | "@kind": "Node", 8 | id: nodeEntry.id, 9 | name: nodeEntry.name, 10 | modId: nodeEntry.modId, 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/lang/rule/formatRuleTarget.ts: -------------------------------------------------------------------------------- 1 | import { formatParameterWithoutType } from "../parameter/index.ts" 2 | import { type RuleTarget } from "./Rule.ts" 3 | 4 | export function formatRuleTarget(target: RuleTarget): string { 5 | const parameters = target.parameters 6 | .map(formatParameterWithoutType) 7 | .join(", ") 8 | 9 | return `${target.name}(${parameters})` 10 | } 11 | -------------------------------------------------------------------------------- /examples/tests/module/self-import.error.i.err: -------------------------------------------------------------------------------- 1 | [execute / Import] I can not do circular import. 2 | 3 | loading module url: examples/tests/module/self-import.error.i 4 | importing module url: examples/tests/module/self-import.error.i 5 | 6 | [execute] I fail to execute a statement. 7 | 8 | 1 |type Trivial 9 | 2 | 10 | 3 |import { Trivial } from "./self-import.error.i" 11 | 4 | 12 | -------------------------------------------------------------------------------- /src/lang/unify/walkType.ts: -------------------------------------------------------------------------------- 1 | import { type Value } from "../value/index.ts" 2 | 3 | export function walkType(substitution: Map, t: Value): Value { 4 | while (t["@kind"] === "Symbol") { 5 | const found = substitution.get(t.name) 6 | if (found === undefined) { 7 | return t 8 | } else { 9 | t = found 10 | } 11 | } 12 | 13 | return t 14 | } 15 | -------------------------------------------------------------------------------- /src/utils/createURL.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from "node:path" 2 | 3 | export function createURL(path: string): URL { 4 | if ( 5 | path.startsWith("http://") || 6 | path.startsWith("https://") || 7 | path.startsWith("https://") 8 | ) { 9 | return new URL(path) 10 | } 11 | 12 | const absolutePath = resolve(path) 13 | return new URL(`file://${absolutePath}`) 14 | } 15 | -------------------------------------------------------------------------------- /src/app/AppHome.ts: -------------------------------------------------------------------------------- 1 | import { LocalFileStore } from "@xieyuheng/framework/lib/file-stores/LocalFileStore.js" 2 | import os from "os" 3 | import Path from "path" 4 | import process from "process" 5 | 6 | export class AppHome extends LocalFileStore { 7 | constructor() { 8 | super({ 9 | dir: process.env["INET_HOME"] || Path.resolve(os.homedir(), ".inet"), 10 | }) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/stringToSubscript.ts: -------------------------------------------------------------------------------- 1 | export function stringToSubscript(s: string): string { 2 | return [...s].map((c) => numberSubscripts[c] || c).join("") 3 | } 4 | 5 | const numberSubscripts: Record = { 6 | "0": "₀", 7 | "1": "₁", 8 | "2": "₂", 9 | "3": "₃", 10 | "4": "₄", 11 | "5": "₅", 12 | "6": "₆", 13 | "7": "₇", 14 | "8": "₈", 15 | "9": "₉", 16 | } 17 | -------------------------------------------------------------------------------- /src/lang/syntax/matchers/name_matcher.ts: -------------------------------------------------------------------------------- 1 | import * as pt from "@xieyuheng/partech" 2 | 3 | export function variable_names_matcher(tree: pt.Tree): Array { 4 | return pt.matcher({ 5 | "variable_names:variable_names": ({ variable_names, last_name }) => [ 6 | ...pt.matchers.zero_or_more_matcher(variable_names).map(pt.str), 7 | pt.str(last_name), 8 | ], 9 | })(tree) 10 | } 11 | -------------------------------------------------------------------------------- /examples/tests/linearity/function-variable-not-used.error.i.err: -------------------------------------------------------------------------------- 1 | [checkAllLocalsAreUsed] I expect all locals are used. 2 | 3 | unused local names: n 4 | 5 | [execute] I fail to execute a statement. 6 | 7 | 1 |type Nat 8 | 2 |node zero(-- value!: Nat) 9 | 3 |node add1(prev: Nat -- value!: Nat) 10 | 4 | 11 | 5 |function add2(n: Nat): Nat { 12 | 6 | return add1(add1(zero())) 13 | 7 |} 14 | 8 | 15 | -------------------------------------------------------------------------------- /examples/tests/module/reuqire-more-rules-2.i: -------------------------------------------------------------------------------- 1 | import { Nat, zero, add1 } from "./reuqire-more-rules-1.i" 2 | 3 | node add( 4 | target!: Nat, 5 | addend: Nat 6 | -------- 7 | result: Nat 8 | ) 9 | 10 | rule add(target!, addend, result) zero(value!) { 11 | @connect(addend, result) 12 | } 13 | 14 | rule add(target!, addend, result) add1(prev, value!) { 15 | add1(add(prev, addend), result) 16 | } 17 | -------------------------------------------------------------------------------- /src/lang/import/importMany.ts: -------------------------------------------------------------------------------- 1 | import { type Mod } from "../mod/index.ts" 2 | import { type ImportBinding } from "./ImportBinding.ts" 3 | import { importOne } from "./importOne.ts" 4 | 5 | export function importMany( 6 | mod: Mod, 7 | targetMod: Mod, 8 | bindings: Array, 9 | ): void { 10 | for (const binding of bindings) { 11 | importOne(mod, targetMod, binding) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/lang/parameter/formatParameterExp.ts: -------------------------------------------------------------------------------- 1 | import { formatExp } from "../exp/index.ts" 2 | import { type ParameterExp } from "./Parameter.ts" 3 | 4 | export function formatParameterExp(parameter: ParameterExp): string { 5 | const t = formatExp(parameter.t) 6 | if (parameter.isPrincipal) { 7 | return `${parameter.name}!: ${t}` 8 | } else { 9 | return `${parameter.name}: ${t}` 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/lang/mod/define.ts: -------------------------------------------------------------------------------- 1 | import { type Definition } from "../definition/index.ts" 2 | import { type Mod } from "./Mod.ts" 3 | 4 | export function define(mod: Mod, name: string, definition: Definition): void { 5 | const found = mod.definitions.get(name) 6 | if (found !== undefined) { 7 | throw new Error(`[define] Can not redefine name: ${name}`) 8 | } 9 | 10 | mod.definitions.set(name, definition) 11 | } 12 | -------------------------------------------------------------------------------- /src/lang/parameter/formatParameters.ts: -------------------------------------------------------------------------------- 1 | import { type Env } from "../env/index.ts" 2 | import { type Parameter } from "./Parameter.ts" 3 | import { formatParameter } from "./formatParameter.ts" 4 | 5 | export function formatParameters( 6 | env: Env, 7 | parameters: Array, 8 | ): string { 9 | return parameters 10 | .map((parameter) => formatParameter(env, parameter)) 11 | .join(", ") 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/countStringOccurrences.ts: -------------------------------------------------------------------------------- 1 | export function countStringOccurrences( 2 | words: Array, 3 | ): Map { 4 | const counts = new Map() 5 | 6 | for (const word of words) { 7 | const count = counts.get(word) 8 | if (count === undefined) { 9 | counts.set(word, 1) 10 | } else { 11 | counts.set(word, count + 1) 12 | } 13 | } 14 | 15 | return counts 16 | } 17 | -------------------------------------------------------------------------------- /src/lang/port/createInputPort.ts: -------------------------------------------------------------------------------- 1 | import { type Node } from "../node/index.ts" 2 | import { type Parameter } from "../parameter/index.ts" 3 | import { type Port } from "./Port.ts" 4 | 5 | export function createInputPort(node: Node, parameter: Parameter): Port { 6 | return { 7 | sign: -1, 8 | node, 9 | name: parameter.name, 10 | t: parameter.t, 11 | isPrincipal: parameter.isPrincipal, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/lang/port/formatPort.ts: -------------------------------------------------------------------------------- 1 | import { type Net } from "../net/index.ts" 2 | import { formatNode } from "../node/formatNode.ts" 3 | import { type Port } from "./Port.ts" 4 | 5 | export function formatPort(net: Net, port: Port): string { 6 | if (port.isPrincipal) { 7 | return `!${port.name}-(${formatNode(net, port.node)})` 8 | } else { 9 | return `${port.name}-(${formatNode(net, port.node)})` 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/tests/statement/rule-before-node.error.i.err: -------------------------------------------------------------------------------- 1 | [findDefinitionOrFail] I meet undefined name. 2 | 3 | name: add 4 | 5 | [execute] I fail to execute a statement. 6 | 7 | 10 | ---------- 8 | 11 | value!: Nat 9 | 12 |) 10 | 13 | 11 | 14 |rule add(target!, addend, result) zero(value!) { 12 | 15 | @connect(addend, result) 13 | 16 |} 14 | 17 | 15 | 18 |rule add(target!, addend, result) add1(prev, value!) { 16 | -------------------------------------------------------------------------------- /src/lang/port/createOutputPort.ts: -------------------------------------------------------------------------------- 1 | import { type Node } from "../node/index.ts" 2 | import { type Parameter } from "../parameter/index.ts" 3 | import { type Port } from "./Port.ts" 4 | 5 | export function createOutputPort(node: Node, parameter: Parameter): Port { 6 | return { 7 | sign: 1, 8 | node, 9 | name: parameter.name, 10 | t: parameter.t, 11 | isPrincipal: parameter.isPrincipal, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /examples/tests/builtin/connect-args.error.i.err: -------------------------------------------------------------------------------- 1 | [@connect] I expect two arguments. 2 | 3 | [evaluate] I fail to evaluate an exp. 4 | 5 | exp: @connect(sole()) 6 | 7 | 5 | value!: Trivial 8 | 6 |) 9 | 7 | 10 | 8 |eval @connect(sole()) 11 | 9 | 12 | 13 | [execute] I fail to execute a statement. 14 | 15 | 4 | -------- 16 | 5 | value!: Trivial 17 | 6 |) 18 | 7 | 19 | 8 |eval @connect(sole()) 20 | 9 | 21 | -------------------------------------------------------------------------------- /src/lang/mod/RuleEntry.ts: -------------------------------------------------------------------------------- 1 | import { type BlockStmt } from "../exp/BlockStmt.ts" 2 | import { type RuleTarget } from "../rule/index.ts" 3 | import { type Mod } from "./Mod.ts" 4 | 5 | export type RuleTargetWithModId = RuleTarget & { 6 | modId: string 7 | } 8 | 9 | export type RuleEntry = { 10 | name: string 11 | mod: Mod 12 | first: RuleTargetWithModId 13 | second: RuleTargetWithModId 14 | body: Array 15 | } 16 | -------------------------------------------------------------------------------- /src/lang/rule/Rule.ts: -------------------------------------------------------------------------------- 1 | import { type BlockStmt } from "../exp/BlockStmt.ts" 2 | import { type Mod } from "../mod/index.ts" 3 | import { type ParameterWithoutType } from "../parameter/index.ts" 4 | 5 | export type RuleTarget = { 6 | name: string 7 | parameters: Array 8 | } 9 | 10 | export type Rule = { 11 | mod: Mod 12 | first: RuleTarget 13 | second: RuleTarget 14 | body: Array 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src"], 3 | "compilerOptions": { 4 | "lib": ["esnext", "dom"], 5 | "module": "node16", 6 | "target": "es6", 7 | "strict": true, 8 | "rewriteRelativeImportExtensions": true, 9 | "verbatimModuleSyntax": true, 10 | "declaration": true, 11 | "sourceMap": true, 12 | "declarationMap": true, 13 | "resolveJsonModule": true, 14 | "outDir": "lib" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/lang/definition/definitionMaybeSpan.ts: -------------------------------------------------------------------------------- 1 | import { type Span } from "../span/index.ts" 2 | import { type Definition } from "./Definition.ts" 3 | 4 | export function definitionMaybeSpan(definition: Definition): Span | undefined { 5 | switch (definition["@kind"]) { 6 | case "PrimitiveFunctionDefinition": 7 | case "ValueDefinition": 8 | return undefined 9 | default: 10 | return definition.span 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/lang/mod/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./createMod.ts" 2 | export * from "./define.ts" 3 | export * from "./defineRule.ts" 4 | export * from "./findDefinition.ts" 5 | export * from "./findDefinitionOrFail.ts" 6 | export * from "./findNodeRuleEntries.ts" 7 | export * from "./findRuleByName.ts" 8 | export * from "./findRuleByNodes.ts" 9 | export * from "./findRuleByPorts.ts" 10 | export * from "./Mod.ts" 11 | export * from "./RuleEntry.ts" 12 | -------------------------------------------------------------------------------- /src/lang/net/findConnectedComponent.ts: -------------------------------------------------------------------------------- 1 | import { type Node } from "../node/index.ts" 2 | import { type Net } from "./Net.ts" 3 | import { copyConnectedComponent } from "./copyConnectedComponent.ts" 4 | import { createNet } from "./createNet.ts" 5 | 6 | export function findConnectedComponent(net: Net, node: Node): Net { 7 | const component = createNet() 8 | copyConnectedComponent(net, component, node) 9 | return component 10 | } 11 | -------------------------------------------------------------------------------- /src/lang/parameter/Parameter.ts: -------------------------------------------------------------------------------- 1 | import { type Exp } from "../exp/index.ts" 2 | import { type Value } from "../value/index.ts" 3 | 4 | export type ParameterExp = { 5 | name: string 6 | t: Exp 7 | isPrincipal: boolean 8 | } 9 | 10 | export type Parameter = { 11 | name: string 12 | t: Value 13 | isPrincipal: boolean 14 | } 15 | 16 | export type ParameterWithoutType = { 17 | name: string 18 | isPrincipal: boolean 19 | } 20 | -------------------------------------------------------------------------------- /src/lang/node/createNodeId.ts: -------------------------------------------------------------------------------- 1 | import { globalNodeCounters } from "./globalNodeCounters.ts" 2 | 3 | export function createNodeId(name: string): string { 4 | const foundCounter = globalNodeCounters.get(name) 5 | if (foundCounter === undefined) { 6 | globalNodeCounters.set(name, 0) 7 | return String(0) 8 | } else { 9 | globalNodeCounters.set(name, foundCounter + 1) 10 | return String(foundCounter + 1) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/lang/check/checkAllLocalsAreUsed.ts: -------------------------------------------------------------------------------- 1 | import { type Value } from "../value/index.ts" 2 | 3 | export function checkAllLocalsAreUsed(locals: Map): void { 4 | if (locals.size > 0) { 5 | throw new Error( 6 | [ 7 | `[checkAllLocalsAreUsed] I expect all locals are used.`, 8 | ``, 9 | ` unused local names: ${Array.from(locals.keys()).join(", ")}`, 10 | ].join("\n"), 11 | ) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/lang/net/findPortEntry.ts: -------------------------------------------------------------------------------- 1 | import { type Port } from "../port/index.ts" 2 | import { type Net, type PortEntry } from "./Net.ts" 3 | import { findNodeEntry } from "./findNodeEntry.ts" 4 | 5 | export function findPortEntry(net: Net, port: Port): PortEntry | undefined { 6 | const nodeEntry = findNodeEntry(net, port.node) 7 | if (nodeEntry === undefined) { 8 | return undefined 9 | } 10 | 11 | return nodeEntry.ports[port.name] 12 | } 13 | -------------------------------------------------------------------------------- /examples/tests/builtin/connect-type.error.i: -------------------------------------------------------------------------------- 1 | type Trivial 2 | node sole(-- value!: Trivial) 3 | 4 | type Nat 5 | node zero(-- value!: Nat) 6 | node add1(prev: Nat -- value!: Nat) 7 | 8 | node add( 9 | target!: Nat, 10 | addend: Nat 11 | -------- 12 | result: Nat 13 | ) 14 | 15 | rule add(target!, addend, result) zero(value!) { 16 | @connect(sole(), result) 17 | 18 | // The correct definition is: 19 | // @connect(addend, result) 20 | } 21 | -------------------------------------------------------------------------------- /examples/tests/currying/node-extra-one-arg.i: -------------------------------------------------------------------------------- 1 | type Nat 2 | node zero(-- value!: Nat) 3 | node add1(prev: Nat -- value!: Nat) 4 | 5 | node add(target!: Nat, addend: Nat -- result: Nat) 6 | 7 | rule add(target!, addend, result) zero(value!) { 8 | @connect(addend, result) 9 | } 10 | 11 | rule add(target!, addend, result) add1(prev, value!) { 12 | add1(add(prev, addend), result) 13 | } 14 | 15 | eval @inspect(@run(@inspect(add(add1(zero()), add1(zero()))))) 16 | -------------------------------------------------------------------------------- /src/lang/env/createEnv.ts: -------------------------------------------------------------------------------- 1 | import { type Env } from "../env/index.ts" 2 | import { type Mod } from "../mod/index.ts" 3 | import { createNet } from "../net/createNet.ts" 4 | import { type Net } from "../net/index.ts" 5 | 6 | export function createEnv( 7 | mod: Mod, 8 | options?: { 9 | net?: Net 10 | }, 11 | ): Env { 12 | return { 13 | mod, 14 | net: options?.net || createNet(), 15 | stack: [], 16 | locals: new Map(), 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/lang/parameter/formatParameter.ts: -------------------------------------------------------------------------------- 1 | import { type Env } from "../env/index.ts" 2 | import { formatValue } from "../value/index.ts" 3 | import { type Parameter } from "./Parameter.ts" 4 | 5 | export function formatParameter(env: Env, parameter: Parameter): string { 6 | const t = formatValue(env, parameter.t) 7 | if (parameter.isPrincipal) { 8 | return `${parameter.name}!: ${t}` 9 | } else { 10 | return `${parameter.name}: ${t}` 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/datatypes/Bin.test.i.out: -------------------------------------------------------------------------------- 1 | net !value-(b1₂₂) { 2 | (b1₂₂)-higherbits value-(b0₂₂) 3 | (b0₂₂)-higherbits value-(b1₂₈) 4 | (b1₂₈)-higherbits value-(b1₂₉) 5 | (b1₂₉)-higherbits value-(bend₂₃) 6 | } 7 | net !value-(b0₂₅) { 8 | (b0₂₅)-higherbits value-(b1₄₂) 9 | (b1₄₂)-higherbits value-(b0₃₁) 10 | (b0₃₁)-higherbits value-(b1₅₅) 11 | (b1₅₅)-higherbits value-(b0₃₃) 12 | (b0₃₃)-higherbits value-(b1₆₀) 13 | (b1₆₀)-higherbits value-(bend₄₂) 14 | } 15 | -------------------------------------------------------------------------------- /src/lang/net/createPortFromPortEntry.ts: -------------------------------------------------------------------------------- 1 | import { type Node } from "../node/index.ts" 2 | import { type Port } from "../port/index.ts" 3 | import { type PortEntry } from "./Net.ts" 4 | 5 | export function createPortFromPortEntry( 6 | node: Node, 7 | portEntry: PortEntry, 8 | ): Port { 9 | return { 10 | node, 11 | name: portEntry.name, 12 | sign: portEntry.sign, 13 | t: portEntry.t, 14 | isPrincipal: portEntry.isPrincipal, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/lang/net/findHalfEdgePort.ts: -------------------------------------------------------------------------------- 1 | import { type HalfEdge } from "../half-edge/index.ts" 2 | import { type Port } from "../port/index.ts" 3 | import { type Net } from "./Net.ts" 4 | import { findHalfEdgeEntryOrFail } from "./findHalfEdgeEntryOrFail.ts" 5 | 6 | export function findHalfEdgePort( 7 | net: Net, 8 | halfEdge: HalfEdge, 9 | ): Port | undefined { 10 | const halfEdgeEntry = findHalfEdgeEntryOrFail(net, halfEdge) 11 | return halfEdgeEntry.port 12 | } 13 | -------------------------------------------------------------------------------- /src/lang/net/findPortEntryOrFail.ts: -------------------------------------------------------------------------------- 1 | import { type Port } from "../port/index.ts" 2 | import { type Net, type PortEntry } from "./Net.ts" 3 | import { findPortEntry } from "./findPortEntry.ts" 4 | 5 | export function findPortEntryOrFail(net: Net, port: Port): PortEntry { 6 | const portEntry = findPortEntry(net, port) 7 | if (portEntry === undefined) { 8 | throw new Error(`[findPortEntryOrFail] Undefined port`) 9 | } 10 | 11 | return portEntry 12 | } 13 | -------------------------------------------------------------------------------- /examples/tests/statement/rule-node-order.error.i.err: -------------------------------------------------------------------------------- 1 | [checkRuleNodeOrder] The first node of a rule must have its principal port in the input. 2 | 3 | first node: zero 4 | second node: add 5 | 6 | [execute] I fail to execute a statement. 7 | 8 | 17 | -------- 9 | 18 | result: Nat 10 | 19 |) 11 | 20 | 12 | 21 |rule zero(value!) add(target!, addend, result) { 13 | 22 | @connect(addend, result) 14 | 23 |} 15 | 24 | 16 | 25 |// The correct definition is: 17 | -------------------------------------------------------------------------------- /examples/tests/linearity/rule-variable-not-used.error.i.err: -------------------------------------------------------------------------------- 1 | [checkAllLocalsAreUsed] I expect all locals are used. 2 | 3 | unused local names: abc 4 | 5 | [execute] I fail to execute a statement. 6 | 7 | 21 |rule add(target!, addend, result) zero(value!) { 8 | 22 | @connect(addend, result) 9 | 23 |} 10 | 24 | 11 | 25 |rule add(target!, addend, result) add1(prev, value!) { 12 | 26 | let abc = 'abc 13 | 27 | add1(add(prev, addend), result) 14 | 28 |} 15 | 29 | 16 | -------------------------------------------------------------------------------- /src/lang/mod/findRuleByPorts.ts: -------------------------------------------------------------------------------- 1 | import { type Port } from "../port/index.ts" 2 | import { type Rule } from "../rule/index.ts" 3 | import { type Mod } from "./Mod.ts" 4 | import { findRuleByNodes } from "./findRuleByNodes.ts" 5 | 6 | export function findRuleByPorts( 7 | mod: Mod, 8 | first: Port, 9 | second: Port, 10 | ): Rule | undefined { 11 | if (first.isPrincipal && second.isPrincipal) { 12 | return findRuleByNodes(mod, first.node, second.node) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/tests/statement/eval-type-nested.error.i.err: -------------------------------------------------------------------------------- 1 | [unifyTypes] I fail to unify types. 2 | 3 | left: Trivial List 4 | right: Trivial 5 | 6 | [evaluate] I fail to evaluate an exp. 7 | 8 | exp: cons(sole(), sole()) 9 | 10 | 15 | value!: List('A) 11 | 16 |) 12 | 17 | 13 | 18 |eval cons(sole(), sole()) 14 | 19 | 15 | 16 | [execute] I fail to execute a statement. 17 | 18 | 14 | -------- 19 | 15 | value!: List('A) 20 | 16 |) 21 | 17 | 22 | 18 |eval cons(sole(), sole()) 23 | 19 | 24 | -------------------------------------------------------------------------------- /examples/tests/statement/rule-before-node.error.i: -------------------------------------------------------------------------------- 1 | type Nat 2 | 3 | node zero( 4 | ------ 5 | value!: Nat 6 | ) 7 | 8 | node add1( 9 | prev: Nat 10 | ---------- 11 | value!: Nat 12 | ) 13 | 14 | rule add(target!, addend, result) zero(value!) { 15 | @connect(addend, result) 16 | } 17 | 18 | rule add(target!, addend, result) add1(prev, value!) { 19 | add1(add(prev, addend), result) 20 | } 21 | 22 | node add( 23 | target!: Nat, 24 | addend: Nat 25 | -------- 26 | result: Nat 27 | ) 28 | -------------------------------------------------------------------------------- /src/lang/half-edge/halfEdgeFromPort.ts: -------------------------------------------------------------------------------- 1 | import { connectHalfEdgeWithPort } from "../connect/connectHalfEdgeWithPort.ts" 2 | import { addEdge } from "../net/addEdge.ts" 3 | import { type Net } from "../net/index.ts" 4 | import { type Port } from "../port/index.ts" 5 | import { type HalfEdge } from "./HalfEdge.ts" 6 | 7 | export function halfEdgeFromPort(net: Net, port: Port): HalfEdge { 8 | const edge = addEdge(net) 9 | connectHalfEdgeWithPort(net, edge.first, port) 10 | return edge.second 11 | } 12 | -------------------------------------------------------------------------------- /src/lang/builtins/defineBuiltinPrimitiveFunction.ts: -------------------------------------------------------------------------------- 1 | import { type PrimitiveApply } from "../definition/index.ts" 2 | import { type Mod } from "../mod/index.ts" 3 | 4 | export function defineBuiltinPrimitiveFunction( 5 | mod: Mod, 6 | name: string, 7 | options: { 8 | apply: PrimitiveApply 9 | }, 10 | ): void { 11 | mod.builtins.set(name, { 12 | "@type": "Definition", 13 | "@kind": "PrimitiveFunctionDefinition", 14 | mod, 15 | name, 16 | apply: options.apply, 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /examples/tests/currying/node-not-enough-args.i: -------------------------------------------------------------------------------- 1 | type Nat 2 | node zero(-- value!: Nat) 3 | node add1(prev: Nat -- value!: Nat) 4 | 5 | node add(target!: Nat, addend: Nat -- result: Nat) 6 | 7 | rule add(target!, addend, result) zero(value!) { 8 | @connect(addend, result) 9 | } 10 | 11 | rule add(target!, addend, result) add1(prev, value!) { 12 | let x, y = add1() 13 | @connect(x, add(prev, addend)) 14 | @connect(y, result) 15 | } 16 | 17 | eval @inspect(@run(@inspect(add(add1(zero()), add1(zero()))))) 18 | -------------------------------------------------------------------------------- /examples/tests/statement/type-args-not-type.error.i.err: -------------------------------------------------------------------------------- 1 | [checkTypeTermArgs] I expect all args of a TypeTerm to be Symbol or TypeTerm. 2 | 3 | args: [(sole₀)] 4 | 5 | [evaluate] I fail to evaluate an exp. 6 | 7 | exp: List(sole) 8 | 9 | 3 | 10 | 4 |type List(Element: @Type) 11 | 5 | 12 | 6 |eval List(sole) 13 | 7 | 14 | 15 | [execute] I fail to execute a statement. 16 | 17 | 2 |node sole(-- value!: Trivial) 18 | 3 | 19 | 4 |type List(Element: @Type) 20 | 5 | 21 | 6 |eval List(sole) 22 | 7 | 23 | -------------------------------------------------------------------------------- /src/lang/run/runNet.ts: -------------------------------------------------------------------------------- 1 | import { createEnv } from "../env/createEnv.ts" 2 | import { interact } from "../interact/index.ts" 3 | import { type Mod } from "../mod/index.ts" 4 | import { type Net } from "../net/index.ts" 5 | 6 | export function runNet(mod: Mod, net: Net): void { 7 | const env = createEnv(mod, { net }) 8 | while (net.activeEdges.length > 0) { 9 | const activeEdge = net.activeEdges.pop() 10 | if (activeEdge !== undefined) { 11 | interact(env, activeEdge, {}) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/tests/linearity/function-variable-double-use.error.i: -------------------------------------------------------------------------------- 1 | type Nat 2 | node zero(-- value!: Nat) 3 | node add1(prev: Nat -- value!: Nat) 4 | 5 | node add( 6 | target!: Nat, 7 | addend: Nat 8 | -------- 9 | result: Nat 10 | ) 11 | 12 | rule add(target!, addend, result) zero(value!) { 13 | @connect(addend, result) 14 | } 15 | 16 | rule add(target!, addend, result) add1(prev, value!) { 17 | add1(add(prev, addend), result) 18 | } 19 | 20 | function double(n: Nat): Nat { 21 | return add(n, n) 22 | } 23 | -------------------------------------------------------------------------------- /src/lang/exp/formatBlockStmt.ts: -------------------------------------------------------------------------------- 1 | import { type BlockStmt } from "./BlockStmt.ts" 2 | import { formatExp } from "./formatExp.ts" 3 | 4 | export function formatBlockStmt(stmt: BlockStmt): string { 5 | switch (stmt["@kind"]) { 6 | case "Let": { 7 | return `let ${stmt.names.join(", ")} = ${formatExp(stmt.exp)}` 8 | } 9 | 10 | case "Evaluate": { 11 | return `${formatExp(stmt.exp)}` 12 | } 13 | 14 | case "Return": { 15 | return `return ${formatExp(stmt.exp)}` 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/lang/syntax/matchers/rule_target_matcher.ts: -------------------------------------------------------------------------------- 1 | import * as pt from "@xieyuheng/partech" 2 | import { type RuleTarget } from "../../rule/index.ts" 3 | import * as matchers from "../matchers/index.ts" 4 | 5 | export function rule_target_matcher(tree: pt.Tree): RuleTarget { 6 | return pt.matcher({ 7 | "rule_target:rule_target": ({ name, parameters }, { span }) => ({ 8 | name: pt.str(name), 9 | parameters: matchers.parameters_without_type_matcher(parameters), 10 | }), 11 | })(tree) 12 | } 13 | -------------------------------------------------------------------------------- /src/command-line/index.ts: -------------------------------------------------------------------------------- 1 | import { CommandRunner, CommandRunners } from "@xieyuheng/command-line" 2 | import * as Commands from "./commands/index.ts" 3 | 4 | export function createCommandRunner(): CommandRunner { 5 | return new CommandRunners.CommonCommandRunner({ 6 | defaultCommand: new Commands.Default(), 7 | commands: [ 8 | new Commands.Repl(), 9 | new Commands.Run(), 10 | new Commands.Parse(), 11 | new Commands.Format(), 12 | new Commands.CommonHelp(), 13 | ], 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /examples/tests/linearity/rule-variable-not-used.error.i: -------------------------------------------------------------------------------- 1 | type Nat 2 | 3 | node zero( 4 | ------ 5 | value!: Nat 6 | ) 7 | 8 | node add1( 9 | prev: Nat 10 | ---------- 11 | value!: Nat 12 | ) 13 | 14 | node add( 15 | target!: Nat, 16 | addend: Nat 17 | -------- 18 | result: Nat 19 | ) 20 | 21 | rule add(target!, addend, result) zero(value!) { 22 | @connect(addend, result) 23 | } 24 | 25 | rule add(target!, addend, result) add1(prev, value!) { 26 | let abc = 'abc 27 | add1(add(prev, addend), result) 28 | } 29 | -------------------------------------------------------------------------------- /src/lang/errors/appendReport.ts: -------------------------------------------------------------------------------- 1 | import { Report, type ReportEntry } from "./Report.ts" 2 | import { createReportEntry } from "./createReportEntry.ts" 3 | 4 | export function appendReport(error: unknown, entry: ReportEntry): Report { 5 | // NOTE We put the most recent report entry at the end, 6 | // because the end is closer to user's terminal output. 7 | 8 | if (error instanceof Report) { 9 | error.entries.push(entry) 10 | return error 11 | } 12 | 13 | return new Report([createReportEntry(error), entry]) 14 | } 15 | -------------------------------------------------------------------------------- /src/lang/mod/findDefinitionOrFail.ts: -------------------------------------------------------------------------------- 1 | import { type Definition } from "../definition/index.ts" 2 | import { type Mod } from "./Mod.ts" 3 | 4 | export function findDefinitionOrFail(mod: Mod, name: string): Definition { 5 | const definition = mod.definitions.get(name) 6 | if (definition === undefined) { 7 | throw new Error( 8 | [ 9 | `[findDefinitionOrFail] I meet undefined name.`, 10 | ``, 11 | ` name: ${name}`, 12 | ].join("\n"), 13 | ) 14 | } 15 | 16 | return definition 17 | } 18 | -------------------------------------------------------------------------------- /examples/datatypes/Bin.test.i: -------------------------------------------------------------------------------- 1 | require "./Bin.i" 2 | 3 | function bsix(): Bin { 4 | // = 0b110 = 4+2 = 6 5 | return b0(b1(b1(bend()))) 6 | } 7 | 8 | function bseven(): Bin { 9 | // return b1(b1(b1(bend()))) 10 | return ntob(add1(add1(add1(add1(add1(add1(add1(zero())))))))) 11 | } 12 | 13 | // = 7 + 6 = 13 -- 0b1101 14 | eval @inspect(@run(badd(bseven(), bsix()))) 15 | 16 | // = 7 * 6 = 42 -- 0b101010 17 | eval @inspect(@run(bmul(bseven(), bsix()))) 18 | 19 | // -> takes 61 steps; doing the same with Nat mul takes 125 steps 20 | -------------------------------------------------------------------------------- /src/lang/net/disconnectPort.ts: -------------------------------------------------------------------------------- 1 | import { type Port } from "../port/index.ts" 2 | import { type Net } from "./Net.ts" 3 | import { disconnectPortEntry } from "./disconnectPortEntry.ts" 4 | import { findNodeEntry } from "./findNodeEntry.ts" 5 | 6 | export function disconnectPort(net: Net, port: Port): void { 7 | const nodeEntry = findNodeEntry(net, port.node) 8 | if (nodeEntry === undefined) { 9 | return undefined 10 | } 11 | 12 | const portEntry = nodeEntry.ports[port.name] 13 | disconnectPortEntry(net, portEntry) 14 | } 15 | -------------------------------------------------------------------------------- /examples/tests/statement/eval-type.error.i.err: -------------------------------------------------------------------------------- 1 | [unifyTypes] I fail to unify types. 2 | 3 | left: Nat 4 | right: Trivial 5 | 6 | [evaluate] I fail to evaluate an exp. 7 | 8 | exp: add1(sole()) 9 | 10 | 5 |node zero(-- value!: Nat) 11 | 6 |node add1(prev: Nat -- value!: Nat) 12 | 7 | 13 | 8 |eval add1(sole()) 14 | 9 | 15 | 16 | [execute] I fail to execute a statement. 17 | 18 | 4 |type Nat 19 | 5 |node zero(-- value!: Nat) 20 | 6 |node add1(prev: Nat -- value!: Nat) 21 | 7 | 22 | 8 |eval add1(sole()) 23 | 9 | 24 | -------------------------------------------------------------------------------- /examples/tests/statement/rule-node-order.error.i: -------------------------------------------------------------------------------- 1 | type Nat 2 | 3 | node zero( 4 | ------ 5 | value!: Nat 6 | ) 7 | 8 | node add1( 9 | prev: Nat 10 | ---------- 11 | value!: Nat 12 | ) 13 | 14 | node add( 15 | target!: Nat, 16 | addend: Nat 17 | -------- 18 | result: Nat 19 | ) 20 | 21 | rule zero(value!) add(target!, addend, result) { 22 | @connect(addend, result) 23 | } 24 | 25 | // The correct definition is: 26 | 27 | // rule add(target!, addend, result) zero(value!) { 28 | // @connect(addend, result) 29 | // } 30 | -------------------------------------------------------------------------------- /examples/tests/statement/rule-type.error.i: -------------------------------------------------------------------------------- 1 | type Trivial 2 | node sole(-- value!: Trivial) 3 | 4 | type Nat 5 | node zero(-- value!: Nat) 6 | node add1(prev: Nat -- value!: Nat) 7 | 8 | node add( 9 | target!: Nat, 10 | addend: Nat 11 | -------- 12 | result: Nat 13 | ) 14 | 15 | rule add(target!, addend, result) zero(value!) { 16 | @connect(addend, result) 17 | } 18 | 19 | rule add(target!, addend, result) add1(prev, value!) { 20 | add1(sole()) // Apply node to value of wrong type. 21 | add1(add(prev, addend), result) 22 | } 23 | -------------------------------------------------------------------------------- /examples/tests/value/port-cap.i: -------------------------------------------------------------------------------- 1 | type Nat 2 | 3 | node zero( 4 | ------ 5 | value!: Nat 6 | ) 7 | 8 | node add1( 9 | prev: Nat 10 | ---------- 11 | value!: Nat 12 | ) 13 | 14 | node add( 15 | target!: Nat, 16 | addend: Nat 17 | -------- 18 | result: Nat 19 | ) 20 | 21 | rule add(target!, addend, result) zero(value!) { 22 | @connect(addend, result) 23 | } 24 | 25 | rule add(target!, addend, result) add1(prev, value!) { 26 | add1(add(prev, addend), result) 27 | } 28 | 29 | eval @inspect(add(add1(zero()), add1(zero()))) 30 | -------------------------------------------------------------------------------- /examples/tests/statement/rule-sign.error.i: -------------------------------------------------------------------------------- 1 | type Trivial 2 | node sole(-- value!: Trivial) 3 | 4 | type Nat 5 | node zero(-- value!: Nat) 6 | node add1(prev: Nat -- value!: Nat) 7 | 8 | node add( 9 | target!: Nat, 10 | addend: Nat 11 | -------- 12 | result: Nat 13 | ) 14 | 15 | rule add(target!, addend, result) zero(value!) { 16 | @connect(addend, result) 17 | } 18 | 19 | rule add(target!, addend, result) add1(prev, value!) { 20 | @connect(prev, addend) // Connecting ports of the same sign. 21 | add1(add(prev, addend), result) 22 | } 23 | -------------------------------------------------------------------------------- /examples/datatypes/List.i: -------------------------------------------------------------------------------- 1 | type List(Element: @Type) 2 | 3 | node null( 4 | -------- 5 | value!: List('A) 6 | ) 7 | 8 | node cons( 9 | head: 'A, 10 | tail: List('A) 11 | -------- 12 | value!: List('A) 13 | ) 14 | 15 | node append( 16 | target!: List('A), 17 | rest: List('A) 18 | -------- 19 | result: List('A) 20 | ) 21 | 22 | rule append(target!, rest, result) null(value!) { 23 | @connect(rest, result) 24 | } 25 | 26 | rule append(target!, rest, result) cons(head, tail, value!) { 27 | cons(head, append(tail, rest), result) 28 | } 29 | -------------------------------------------------------------------------------- /src/lang/syntax/grammars/index.ts: -------------------------------------------------------------------------------- 1 | import * as pt from "@xieyuheng/partech" 2 | 3 | export const zero_or_more = pt.grammars.zero_or_more 4 | export const one_or_more = pt.grammars.one_or_more 5 | export const optional = pt.grammars.optional 6 | export const dashline = pt.grammars.dashline 7 | 8 | export * from "./arg.ts" 9 | export * from "./block_stmt.ts" 10 | export * from "./exp.ts" 11 | export * from "./import_binding.ts" 12 | export * from "./name.ts" 13 | export * from "./parameter.ts" 14 | export * from "./rule_target.ts" 15 | export * from "./stmt.ts" 16 | -------------------------------------------------------------------------------- /src/lang/exp/BlockStmt.ts: -------------------------------------------------------------------------------- 1 | import { type Span } from "../span/index.ts" 2 | import { type Exp } from "./Exp.ts" 3 | 4 | export type BlockStmt = Let | Evaluate | Return 5 | 6 | export type Let = { 7 | "@type": "BlockStmt" 8 | "@kind": "Let" 9 | names: Array 10 | exp: Exp 11 | span: Span 12 | } 13 | 14 | export type Evaluate = { 15 | "@type": "BlockStmt" 16 | "@kind": "Evaluate" 17 | exp: Exp 18 | span: Span 19 | } 20 | 21 | export type Return = { 22 | "@type": "BlockStmt" 23 | "@kind": "Return" 24 | exp: Exp 25 | span: Span 26 | } 27 | -------------------------------------------------------------------------------- /src/lang/import/importNodeRules.ts: -------------------------------------------------------------------------------- 1 | import { type Mod } from "../mod/index.ts" 2 | import { nodeKeyWithoutId, type NodeWithoutId } from "../node/index.ts" 3 | 4 | export function importNodeRules( 5 | mod: Mod, 6 | targetMod: Mod, 7 | node: NodeWithoutId, 8 | ): void { 9 | const nodeKey = nodeKeyWithoutId(node) 10 | for (const [key, ruleEntry] of targetMod.ruleEntries) { 11 | const [firstKey, secondKey] = key.split(" ") 12 | 13 | if (firstKey === nodeKey || secondKey === nodeKey) { 14 | mod.ruleEntries.set(key, ruleEntry) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/lang/present/presentNode.ts: -------------------------------------------------------------------------------- 1 | import { capNodeAllPorts } from "../cap/index.ts" 2 | import { findDefinitionOrFail, type Mod } from "../mod/index.ts" 3 | import { createNet, type Net } from "../net/index.ts" 4 | import { addNodeFromDefinition } from "../node/addNodeFromDefinition.ts" 5 | 6 | export function presentNode(mod: Mod, nodeName: string): Net { 7 | const net = createNet() 8 | 9 | const definition = findDefinitionOrFail(mod, nodeName) 10 | const node = addNodeFromDefinition(net, definition) 11 | capNodeAllPorts(mod, net, node) 12 | 13 | return net 14 | } 15 | -------------------------------------------------------------------------------- /src/lang/net/findHalfEdgeEntryOrFail.ts: -------------------------------------------------------------------------------- 1 | import { type HalfEdge } from "../half-edge/index.ts" 2 | import { type HalfEdgeEntry, type Net } from "./Net.ts" 3 | import { findHalfEdgeEntry } from "./findHalfEdgeEntry.ts" 4 | 5 | export function findHalfEdgeEntryOrFail( 6 | net: Net, 7 | halfEdge: HalfEdge, 8 | ): HalfEdgeEntry { 9 | const halfEdgeEntry = findHalfEdgeEntry(net, halfEdge) 10 | if (halfEdgeEntry === undefined) { 11 | console.trace() 12 | throw new Error(`[findHalfEdgeEntryOrFail] Undefined halfEdgeEntry`) 13 | } 14 | 15 | return halfEdgeEntry 16 | } 17 | -------------------------------------------------------------------------------- /src/lang/syntax/grammars/block_stmt.ts: -------------------------------------------------------------------------------- 1 | import { variable_names } from "./name.ts" 2 | 3 | export const block_stmt = { 4 | $grammar: { 5 | "block_stmt:let": [ 6 | '"let"', 7 | { names: variable_names }, 8 | '"="', 9 | { exp: "exp" }, 10 | ], 11 | "block_stmt:evaluate": [{ exp: "exp" }], 12 | "block_stmt:return": ['"return"', { exp: "exp" }], 13 | }, 14 | } 15 | 16 | export const block_stmts = { 17 | $grammar: { 18 | "block_stmts:block_stmts": [ 19 | { entries: { $ap: ["zero_or_more", "block_stmt"] } }, 20 | ], 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /examples/tests/currying/function-not-enough-args.i.out: -------------------------------------------------------------------------------- 1 | net !value-(add1₇) { 2 | (add1₇)-prev value-(add1₈) 3 | (add1₈)-prev value-(add1₅) 4 | (add1₅)-prev value-(zero₄) 5 | } 6 | net !value-(add1₁₃) { 7 | (add1₁₃)-prev value-(add1₁₄) 8 | (add1₁₄)-prev value-(add1₁₁) 9 | (add1₁₁)-prev value-(zero₇) 10 | } 11 | net !value-(add1₁₉) { 12 | (add1₁₉)-prev value-(add1₂₀) 13 | (add1₂₀)-prev value-(add1₁₇) 14 | (add1₁₇)-prev value-(zero₁₀) 15 | } 16 | net !value-(add1₂₅) { 17 | (add1₂₅)-prev value-(add1₂₆) 18 | (add1₂₆)-prev value-(add1₂₃) 19 | (add1₂₃)-prev value-(zero₁₃) 20 | } 21 | -------------------------------------------------------------------------------- /src/lang/mod/findRuleByNodes.ts: -------------------------------------------------------------------------------- 1 | import { nodeKeyWithoutId, type NodeWithoutId } from "../node/index.ts" 2 | import { type Rule } from "../rule/index.ts" 3 | import { type Mod } from "./Mod.ts" 4 | 5 | export function findRuleByNodes( 6 | mod: Mod, 7 | firstNode: NodeWithoutId, 8 | secondNode: NodeWithoutId, 9 | ): Rule | undefined { 10 | const firstKey = nodeKeyWithoutId(firstNode) 11 | const secondKey = nodeKeyWithoutId(secondNode) 12 | 13 | return ( 14 | mod.ruleEntries.get(`${firstKey} ${secondKey}`) || 15 | mod.ruleEntries.get(`${secondKey} ${firstKey}`) 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /src/lang/net/findInputPorts.ts: -------------------------------------------------------------------------------- 1 | import { type Node } from "../node/index.ts" 2 | import { type Port } from "../port/index.ts" 3 | import { type Net } from "./Net.ts" 4 | import { createPortFromPortEntry } from "./createPortFromPortEntry.ts" 5 | import { findPortRecordOrFail } from "./findPortRecordOrFail.ts" 6 | 7 | export function findInputPorts(net: Net, node: Node): Array { 8 | const portRecord = findPortRecordOrFail(net, node) 9 | return Object.values(portRecord) 10 | .filter(({ sign }) => sign === -1) 11 | .map((portEntry) => createPortFromPortEntry(node, portEntry)) 12 | } 13 | -------------------------------------------------------------------------------- /src/lang/net/findOutputPorts.ts: -------------------------------------------------------------------------------- 1 | import { type Node } from "../node/index.ts" 2 | import { type Port } from "../port/index.ts" 3 | import { type Net } from "./Net.ts" 4 | import { createPortFromPortEntry } from "./createPortFromPortEntry.ts" 5 | import { findPortRecordOrFail } from "./findPortRecordOrFail.ts" 6 | 7 | export function findOutputPorts(net: Net, node: Node): Array { 8 | const portRecord = findPortRecordOrFail(net, node) 9 | return Object.values(portRecord) 10 | .filter(({ sign }) => sign === 1) 11 | .map((portEntry) => createPortFromPortEntry(node, portEntry)) 12 | } 13 | -------------------------------------------------------------------------------- /src/lang/mod/hasNodeDefinition.ts: -------------------------------------------------------------------------------- 1 | import { type NodeWithoutId } from "../node/index.ts" 2 | import { type Mod } from "./Mod.ts" 3 | import { findDefinition } from "./findDefinition.ts" 4 | 5 | export function hasNodeDefinition(mod: Mod, node: NodeWithoutId): boolean { 6 | const definition = findDefinition(mod, node.name) 7 | if (definition === undefined) { 8 | return false 9 | } 10 | 11 | if (definition["@kind"] !== "NodeDefinition") { 12 | return false 13 | } 14 | 15 | if (definition.mod.url.href !== node.modId) { 16 | return false 17 | } 18 | 19 | return true 20 | } 21 | -------------------------------------------------------------------------------- /examples/tests/statement/eval-sign.error.i.err: -------------------------------------------------------------------------------- 1 | [checkPortSigns] I expect the two ports to have opposite signs, 2 | but they all have positive sign. 3 | 4 | first port: !value-(sole₀) 5 | second port: !value-(sole₁) 6 | 7 | [evaluate] I fail to evaluate an exp. 8 | 9 | exp: @connect(sole(), sole()) 10 | 11 | 1 |type Trivial 12 | 2 |node sole(-- value!: Trivial) 13 | 3 | 14 | 4 |eval @connect(sole(), sole()) 15 | 5 | 16 | 17 | [execute] I fail to execute a statement. 18 | 19 | 1 |type Trivial 20 | 2 |node sole(-- value!: Trivial) 21 | 3 | 22 | 4 |eval @connect(sole(), sole()) 23 | 5 | 24 | -------------------------------------------------------------------------------- /src/lang/net/disconnectPortEntry.ts: -------------------------------------------------------------------------------- 1 | import { type Net, type PortEntry } from "./Net.ts" 2 | import { findHalfEdgeEntry } from "./findHalfEdgeEntry.ts" 3 | 4 | export function disconnectPortEntry(net: Net, portEntry: PortEntry): void { 5 | const halfEdge = portEntry.connection?.halfEdge 6 | delete portEntry.connection 7 | 8 | if (halfEdge !== undefined) { 9 | const halfEdgeEntry = findHalfEdgeEntry(net, halfEdge) 10 | if (halfEdgeEntry === undefined) { 11 | throw new Error(`[disconnectPort] Fail to find halfEdgeEntry`) 12 | } 13 | 14 | delete halfEdgeEntry.port 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/lang/syntax/index.ts: -------------------------------------------------------------------------------- 1 | import * as pt from "@xieyuheng/partech" 2 | import * as grammars from "./grammars/index.ts" 3 | import * as matchers from "./matchers/index.ts" 4 | 5 | /** 6 | 7 | TODO We should use `camelCase` naming convention, 8 | but "@xieyuheng/partech" is using `snake_case`, 9 | we follow the library for now (will change eventually). 10 | 11 | **/ 12 | 13 | export const parseStmts = pt.gen_parse({ 14 | preprocess: pt.preprocess.erase_comment, 15 | lexer: pt.lexers.common, 16 | grammar: pt.grammar_start(grammars, "stmts"), 17 | matcher: matchers.stmts_matcher, 18 | }) 19 | -------------------------------------------------------------------------------- /src/lang/unify/occurInType.ts: -------------------------------------------------------------------------------- 1 | import { type Value } from "../value/index.ts" 2 | import { walkType } from "./walkType.ts" 3 | 4 | export function occurInType( 5 | substitution: Map, 6 | name: string, 7 | t: Value, 8 | ): boolean { 9 | t = walkType(substitution, t) 10 | 11 | switch (t["@kind"]) { 12 | case "Symbol": { 13 | return t.name === name 14 | } 15 | 16 | case "TypeTerm": { 17 | return t.args.some((arg) => occurInType(substitution, name, arg)) 18 | } 19 | 20 | default: { 21 | // TODO 22 | return false 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/tests/module/circular-require-1.error.i.err: -------------------------------------------------------------------------------- 1 | [execute / Require] I can not do circular require. 2 | 3 | loading module url: examples/tests/module/circular-require-3.error.i 4 | requiring module url: examples/tests/module/circular-require-1.error.i 5 | 6 | [execute] I fail to execute a statement. 7 | 8 | 1 |require "./circular-require-1.error.i" 9 | 2 | 10 | 11 | [execute] I fail to execute a statement. 12 | 13 | 1 |require "./circular-require-3.error.i" 14 | 2 | 15 | 16 | [execute] I fail to execute a statement. 17 | 18 | 1 |type Trivial 19 | 2 | 20 | 3 |require "./circular-require-2.error.i" 21 | 4 | 22 | -------------------------------------------------------------------------------- /examples/tests/module/circular-require-2.error.i.err: -------------------------------------------------------------------------------- 1 | [execute / Require] I can not do circular require. 2 | 3 | loading module url: examples/tests/module/circular-require-1.error.i 4 | requiring module url: examples/tests/module/circular-require-2.error.i 5 | 6 | [execute] I fail to execute a statement. 7 | 8 | 1 |type Trivial 9 | 2 | 10 | 3 |require "./circular-require-2.error.i" 11 | 4 | 12 | 13 | [execute] I fail to execute a statement. 14 | 15 | 1 |require "./circular-require-1.error.i" 16 | 2 | 17 | 18 | [execute] I fail to execute a statement. 19 | 20 | 1 |require "./circular-require-3.error.i" 21 | 2 | 22 | -------------------------------------------------------------------------------- /examples/tests/module/circular-require-3.error.i.err: -------------------------------------------------------------------------------- 1 | [execute / Require] I can not do circular require. 2 | 3 | loading module url: examples/tests/module/circular-require-2.error.i 4 | requiring module url: examples/tests/module/circular-require-3.error.i 5 | 6 | [execute] I fail to execute a statement. 7 | 8 | 1 |require "./circular-require-3.error.i" 9 | 2 | 10 | 11 | [execute] I fail to execute a statement. 12 | 13 | 1 |type Trivial 14 | 2 | 15 | 3 |require "./circular-require-2.error.i" 16 | 4 | 17 | 18 | [execute] I fail to execute a statement. 19 | 20 | 1 |require "./circular-require-1.error.i" 21 | 2 | 22 | -------------------------------------------------------------------------------- /src/lang/net/disconnectHalfEdge.ts: -------------------------------------------------------------------------------- 1 | import { type HalfEdge } from "../half-edge/index.ts" 2 | import { type Net } from "./Net.ts" 3 | import { findHalfEdgeEntryOrFail } from "./findHalfEdgeEntryOrFail.ts" 4 | import { findPortEntryOrFail } from "./findPortEntryOrFail.ts" 5 | 6 | export function disconnectHalfEdge(net: Net, halfEdge: HalfEdge): void { 7 | const halfEdgeEntry = findHalfEdgeEntryOrFail(net, halfEdge) 8 | const port = halfEdgeEntry.port 9 | delete halfEdgeEntry.port 10 | 11 | if (port !== undefined) { 12 | const portEntry = findPortEntryOrFail(net, port) 13 | delete portEntry.connection 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/lang/net/findHalfEdgePortOrFail.ts: -------------------------------------------------------------------------------- 1 | import { type HalfEdge } from "../half-edge/index.ts" 2 | import { type Port } from "../port/index.ts" 3 | import { type Net } from "./Net.ts" 4 | import { findHalfEdgeEntryOrFail } from "./findHalfEdgeEntryOrFail.ts" 5 | 6 | export function findHalfEdgePortOrFail(net: Net, halfEdge: HalfEdge): Port { 7 | const halfEdgeEntry = findHalfEdgeEntryOrFail(net, halfEdge) 8 | if (halfEdgeEntry.port === undefined) { 9 | console.trace() 10 | throw new Error( 11 | `[findHalfEdgePortOrFail] I expect halfEdgeEntry to have port.`, 12 | ) 13 | } 14 | return halfEdgeEntry.port 15 | } 16 | -------------------------------------------------------------------------------- /src/lang/syntax/grammars/name.ts: -------------------------------------------------------------------------------- 1 | // NOTE Preserve keywords for JSON. 2 | 3 | const preserved: Array = ["return"] 4 | 5 | export const variable_name = { 6 | $pattern: [ 7 | "identifier", 8 | `(^(?!(${preserved.join("|")})$)([_A-Za-z][_\\p{Letter}0-9]*))`, 9 | ], 10 | } 11 | 12 | export const variable_names = { 13 | $grammar: { 14 | "variable_names:variable_names": [ 15 | { 16 | variable_names: { 17 | $ap: ["zero_or_more", "variable_name", '","'], 18 | }, 19 | }, 20 | { last_name: "variable_name" }, 21 | { $ap: ["optional", '","'] }, 22 | ], 23 | }, 24 | } 25 | -------------------------------------------------------------------------------- /src/lang/syntax/matchers/arg_matcher.ts: -------------------------------------------------------------------------------- 1 | import * as pt from "@xieyuheng/partech" 2 | import { type Exp } from "../../exp/index.ts" 3 | import * as matchers from "../matchers/index.ts" 4 | 5 | export function arg_matcher(tree: pt.Tree): Exp { 6 | return pt.matcher({ 7 | "arg:plain": ({ arg }) => matchers.exp_matcher(arg), 8 | })(tree) 9 | } 10 | 11 | export function args_matcher(tree: pt.Tree): Array { 12 | return pt.matcher({ 13 | "args:args": ({ entries, last_entry }) => [ 14 | ...pt.matchers.zero_or_more_matcher(entries).map(arg_matcher), 15 | arg_matcher(last_entry), 16 | ], 17 | })(tree) 18 | } 19 | -------------------------------------------------------------------------------- /src/lang/check/checkTypeTermArgs.ts: -------------------------------------------------------------------------------- 1 | import { type Env } from "../env/index.ts" 2 | import { formatValues } from "../value/formatValues.ts" 3 | import { type Value } from "../value/index.ts" 4 | 5 | export function checkTypeTermArgs(env: Env, args: Array): void { 6 | for (const arg of args) { 7 | if (arg["@kind"] !== "Symbol" && arg["@kind"] !== "TypeTerm") { 8 | throw new Error( 9 | [ 10 | `[checkTypeTermArgs] I expect all args of a TypeTerm to be Symbol or TypeTerm.`, 11 | ``, 12 | ` args: [${formatValues(env, args)}]`, 13 | ].join("\n"), 14 | ) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/lang/mod/Mod.ts: -------------------------------------------------------------------------------- 1 | import { Loader } from "../../loader/index.ts" 2 | import { type Checking } from "../checking/index.ts" 3 | import { type Definition } from "../definition/index.ts" 4 | import { type Env } from "../env/index.ts" 5 | import { type Stmt } from "../stmt/index.ts" 6 | import { type RuleEntry } from "./RuleEntry.ts" 7 | 8 | export type Mod = { 9 | loader: Loader 10 | env: Env 11 | checking: Checking 12 | url: URL 13 | text: string 14 | stmts: Array 15 | definitions: Map 16 | builtins: Map 17 | ruleEntries: Map 18 | requiredMods: Map 19 | } 20 | -------------------------------------------------------------------------------- /examples/tests/statement/rule-for-non-own-node.error.i.err: -------------------------------------------------------------------------------- 1 | [checkRuleIsAboutOwnNode] To define a rule, one of the node must be owned by this module. 2 | 3 | loading module url: examples/tests/statement/rule-for-non-own-node.error.i 4 | first node module url: examples/datatypes/Nat.i 5 | second node module url: examples/datatypes/Nat.i 6 | 7 | [execute] I fail to execute a statement. 8 | 9 | 1 |import { Nat, zero, add1, add } from "../../datatypes/Nat.i" 10 | 2 | 11 | 3 |rule add(target!, addend, result) zero(value!) { 12 | 4 | @connect(addend, result) 13 | 5 |} 14 | 6 | 15 | 7 |rule add(target!, addend, result) add1(prev, value!) { 16 | -------------------------------------------------------------------------------- /src/lang/check/checkTypeParameters.ts: -------------------------------------------------------------------------------- 1 | import { type Mod } from "../mod/index.ts" 2 | import { formatParameters, type Parameter } from "../parameter/index.ts" 3 | 4 | export function checkTypeParameters(mod: Mod, input: Array): void { 5 | for (const parameter of input) { 6 | if (parameter.t["@kind"] !== "Type") { 7 | throw new Error( 8 | [ 9 | `[checkTypeParameters] I expect the claimed input parameters`, 10 | ` of a type definition to be Type.`, 11 | ``, 12 | ` input parameters: [${formatParameters(mod.env, input)}]`, 13 | ].join("\n"), 14 | ) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/lang/evaluate/evaluateParameters.ts: -------------------------------------------------------------------------------- 1 | import { type Env } from "../env/index.ts" 2 | import { type Mod } from "../mod/index.ts" 3 | import { type Parameter, type ParameterExp } from "../parameter/index.ts" 4 | import { type EvaluateOptions } from "./evaluate.ts" 5 | import { evaluateOne } from "./evaluateOne.ts" 6 | 7 | export function evaluateParameters( 8 | mod: Mod, 9 | env: Env, 10 | parameterExps: Array, 11 | options: EvaluateOptions, 12 | ): Array { 13 | return parameterExps.map(({ name, t, isPrincipal }) => ({ 14 | name, 15 | t: evaluateOne(mod, env, t, options), 16 | isPrincipal, 17 | })) 18 | } 19 | -------------------------------------------------------------------------------- /examples/tests/module/circular-import-1.error.i.err: -------------------------------------------------------------------------------- 1 | [execute / Import] I can not do circular import. 2 | 3 | loading module url: examples/tests/module/circular-import-3.error.i 4 | importing module url: examples/tests/module/circular-import-1.error.i 5 | 6 | [execute] I fail to execute a statement. 7 | 8 | 1 |import { Trivial } from "./circular-import-1.error.i" 9 | 2 | 10 | 11 | [execute] I fail to execute a statement. 12 | 13 | 1 |import { Trivial } from "./circular-import-3.error.i" 14 | 2 | 15 | 16 | [execute] I fail to execute a statement. 17 | 18 | 1 |type Trivial 19 | 2 | 20 | 3 |import { Trivial } from "./circular-import-2.error.i" 21 | 4 | 22 | -------------------------------------------------------------------------------- /examples/tests/module/circular-import-2.error.i.err: -------------------------------------------------------------------------------- 1 | [execute / Import] I can not do circular import. 2 | 3 | loading module url: examples/tests/module/circular-import-1.error.i 4 | importing module url: examples/tests/module/circular-import-2.error.i 5 | 6 | [execute] I fail to execute a statement. 7 | 8 | 1 |type Trivial 9 | 2 | 10 | 3 |import { Trivial } from "./circular-import-2.error.i" 11 | 4 | 12 | 13 | [execute] I fail to execute a statement. 14 | 15 | 1 |import { Trivial } from "./circular-import-1.error.i" 16 | 2 | 17 | 18 | [execute] I fail to execute a statement. 19 | 20 | 1 |import { Trivial } from "./circular-import-3.error.i" 21 | 2 | 22 | -------------------------------------------------------------------------------- /examples/tests/module/circular-import-3.error.i.err: -------------------------------------------------------------------------------- 1 | [execute / Import] I can not do circular import. 2 | 3 | loading module url: examples/tests/module/circular-import-2.error.i 4 | importing module url: examples/tests/module/circular-import-3.error.i 5 | 6 | [execute] I fail to execute a statement. 7 | 8 | 1 |import { Trivial } from "./circular-import-3.error.i" 9 | 2 | 10 | 11 | [execute] I fail to execute a statement. 12 | 13 | 1 |type Trivial 14 | 2 | 15 | 3 |import { Trivial } from "./circular-import-2.error.i" 16 | 4 | 17 | 18 | [execute] I fail to execute a statement. 19 | 20 | 1 |import { Trivial } from "./circular-import-1.error.i" 21 | 2 | 22 | -------------------------------------------------------------------------------- /src/lang/net/findNodeEntryOrFail.ts: -------------------------------------------------------------------------------- 1 | import { formatNode } from "../node/formatNode.ts" 2 | import { type Node } from "../node/index.ts" 3 | import { nodeKey } from "../node/nodeKey.ts" 4 | import { type Net, type NodeEntry } from "./Net.ts" 5 | 6 | export function findNodeEntryOrFail(net: Net, node: Node): NodeEntry { 7 | const nodeEntry = net.nodeEntries.get(nodeKey(node)) 8 | if (nodeEntry === undefined) { 9 | throw new Error( 10 | [ 11 | `[findNodeEntryOrFail] I can not find nodeEntry for node.`, 12 | ``, 13 | ` node: ${formatNode(net, node)}`, 14 | ].join("\n"), 15 | ) 16 | } 17 | 18 | return nodeEntry 19 | } 20 | -------------------------------------------------------------------------------- /examples/tests/statement/rule-type-occur-check.error.i: -------------------------------------------------------------------------------- 1 | type List(Element: @Type) 2 | 3 | node null( 4 | -------- 5 | value!: List('A) 6 | ) 7 | 8 | node cons( 9 | head: 'A, 10 | tail: List('A) 11 | -------- 12 | value!: List('A) 13 | ) 14 | 15 | node append( 16 | target!: List('A), 17 | rest: List('A) 18 | -------- 19 | result: List('A) 20 | ) 21 | 22 | rule append(target!, rest, result) null(value!) { 23 | @connect(rest, result) 24 | } 25 | 26 | rule append(target!, rest, result) cons(head, tail, value!) { 27 | cons(append(tail, rest), head, result) 28 | 29 | // The correct definition is: 30 | // cons(head, append(tail, rest), result) 31 | } 32 | -------------------------------------------------------------------------------- /src/lang/net/findPortRecordOrFail.ts: -------------------------------------------------------------------------------- 1 | import { formatNode } from "../node/formatNode.ts" 2 | import { type Node } from "../node/index.ts" 3 | import { type Net, type PortRecord } from "./Net.ts" 4 | import { findNodeEntry } from "./findNodeEntry.ts" 5 | 6 | export function findPortRecordOrFail(net: Net, node: Node): PortRecord { 7 | const nodeEntry = findNodeEntry(net, node) 8 | if (nodeEntry === undefined) { 9 | throw new Error( 10 | [ 11 | `[findPortRecordOrFail] I can not find node entry for node.`, 12 | ``, 13 | ` node: ${formatNode(net, node)}`, 14 | ].join("\n"), 15 | ) 16 | } 17 | 18 | return nodeEntry.ports 19 | } 20 | -------------------------------------------------------------------------------- /src/lang/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./cap/index.ts" 2 | export * from "./definition/index.ts" 3 | export * from "./edge/index.ts" 4 | export * from "./env/index.ts" 5 | export * from "./errors/index.ts" 6 | export * from "./exp/index.ts" 7 | export * from "./half-edge/index.ts" 8 | export * from "./interact/index.ts" 9 | export * from "./mod/index.ts" 10 | export * from "./net/index.ts" 11 | export * from "./node/index.ts" 12 | export * from "./port/index.ts" 13 | export * from "./present/index.ts" 14 | export * from "./rule/index.ts" 15 | export * from "./run/index.ts" 16 | export * from "./stmt/index.ts" 17 | export * from "./syntax/index.ts" 18 | export * from "./value/index.ts" 19 | -------------------------------------------------------------------------------- /examples/tests/currying/function-extra-one-arg.i: -------------------------------------------------------------------------------- 1 | type Nat 2 | node zero(-- value!: Nat) 3 | node add1(prev: Nat -- value!: Nat) 4 | 5 | node add(target!: Nat, addend: Nat -- result: Nat) 6 | 7 | rule add(target!, addend, result) zero(value!) { 8 | @connect(addend, result) 9 | } 10 | 11 | rule add(target!, addend, result) add1(prev, value!) { 12 | add1(add(prev, addend), result) 13 | } 14 | 15 | function one(): Nat { 16 | return add1(zero()) 17 | } 18 | 19 | function addadd(x: Nat, y: Nat, z: Nat): Nat { 20 | return add(add(x, y), z) 21 | } 22 | 23 | eval { 24 | let prev, result = add1() 25 | addadd(one(), one(), one(), prev) 26 | return @inspect(@run(result)) 27 | } 28 | -------------------------------------------------------------------------------- /src/lang/import/importAll.ts: -------------------------------------------------------------------------------- 1 | import { type Mod } from "../mod/index.ts" 2 | 3 | export function importAll(mod: Mod, targetMod: Mod): void { 4 | for (const [name, definition] of targetMod.definitions) { 5 | const found = mod.definitions.get(name) 6 | if (found !== undefined) { 7 | throw new Error( 8 | [ 9 | `[importAll] I can not import already defined name.`, 10 | ``, 11 | ` name: ${name}`, 12 | ].join("\n"), 13 | ) 14 | } 15 | 16 | mod.definitions.set(name, definition) 17 | } 18 | 19 | for (const [key, ruleEntry] of targetMod.ruleEntries) { 20 | mod.ruleEntries.set(key, ruleEntry) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/lang/cap/capNodeAllPorts.ts: -------------------------------------------------------------------------------- 1 | import { type Mod } from "../mod/index.ts" 2 | import { findInputPorts } from "../net/findInputPorts.ts" 3 | import { findOutputPorts } from "../net/findOutputPorts.ts" 4 | import { type Net } from "../net/index.ts" 5 | import { type Node } from "../node/index.ts" 6 | import { capInputPort } from "./capInputPort.ts" 7 | import { capOutputPort } from "./capOutputPort.ts" 8 | 9 | export function capNodeAllPorts(mod: Mod, net: Net, node: Node): void { 10 | for (const port of findInputPorts(net, node)) { 11 | capInputPort(mod, net, port) 12 | } 13 | 14 | for (const port of findOutputPorts(net, node)) { 15 | capOutputPort(mod, net, port) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/lang/builtins/defineBuiltins.ts: -------------------------------------------------------------------------------- 1 | import { type Mod } from "../mod/index.ts" 2 | import * as connect from "./connect.ts" 3 | import { defineBuiltinPrimitiveFunction } from "./defineBuiltinPrimitiveFunction.ts" 4 | import { defineBuiltinValue } from "./defineBuiltinValue.ts" 5 | import * as inspect from "./inspect.ts" 6 | import * as run from "./run.ts" 7 | 8 | export function defineBuiltins(mod: Mod): void { 9 | defineBuiltinPrimitiveFunction(mod, "connect", connect) 10 | defineBuiltinPrimitiveFunction(mod, "inspect", inspect) 11 | defineBuiltinPrimitiveFunction(mod, "run", run) 12 | 13 | defineBuiltinValue(mod, "Type", { 14 | "@type": "Value", 15 | "@kind": "Type", 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /src/lang/syntax/grammars/exp.ts: -------------------------------------------------------------------------------- 1 | export const exp = { 2 | $grammar: { 3 | "exp:operator": [{ operator: "operator" }], 4 | "exp:operand": [{ operand: "operand" }], 5 | }, 6 | } 7 | 8 | export const operator = { 9 | $grammar: { 10 | "operator:var": [{ name: "variable_name" }], 11 | "operator:builtin": ['"@"', { name: "variable_name" }], 12 | }, 13 | } 14 | 15 | export const operand = { 16 | $grammar: { 17 | "operand:ap": [{ target: "operator" }, '"("', { args: "args" }, '")"'], 18 | "operand:ap_nullary": [{ target: "operator" }, '"("', '")"'], 19 | "operand:quote_symbol": ['"\'"', { name: "variable_name" }], 20 | "operand:block": ['"{"', { body: "block_stmts" }, '"}"'], 21 | }, 22 | } 23 | -------------------------------------------------------------------------------- /src/lang/mod/findRuleByName.ts: -------------------------------------------------------------------------------- 1 | import { type Rule } from "../rule/index.ts" 2 | import { type Mod } from "./Mod.ts" 3 | import { findDefinitionOrFail } from "./findDefinitionOrFail.ts" 4 | import { findRuleByNodes } from "./findRuleByNodes.ts" 5 | 6 | export function findRuleByName(mod: Mod, ruleName: string): Rule | undefined { 7 | const [firstName, secondName] = ruleName.split(" ") 8 | 9 | const firstDefinition = findDefinitionOrFail(mod, firstName) 10 | const secondDefinition = findDefinitionOrFail(mod, secondName) 11 | 12 | return findRuleByNodes( 13 | mod, 14 | { modId: firstDefinition.mod.url.href, name: firstDefinition.name }, 15 | { modId: secondDefinition.mod.url.href, name: secondDefinition.name }, 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /examples/datatypes/DiffList.test.i: -------------------------------------------------------------------------------- 1 | require "DiffList.i" 2 | require "Trivial.i" 3 | 4 | function oneTwoSoles(): DiffList(Trivial) { 5 | let front, back, value1 = diff() 6 | @connect(front, cons(sole(), back)) 7 | let front, back, value2 = diff() 8 | @connect(front, cons(sole(), cons(sole(), back))) 9 | return diffAppend(value1, value2) 10 | } 11 | 12 | eval @inspect(@run(@inspect(oneTwoSoles()))) 13 | 14 | function twoTwoSoles(): DiffList(Trivial) { 15 | let front, back, value1 = diff() 16 | @connect(front, cons(sole(), cons(sole(), back))) 17 | let front, back, value2 = diff() 18 | @connect(front, cons(sole(), cons(sole(), back))) 19 | return diffAppend(value1, value2) 20 | } 21 | 22 | eval @inspect(@run(@inspect(twoTwoSoles()))) 23 | -------------------------------------------------------------------------------- /src/lang/builtins/run.ts: -------------------------------------------------------------------------------- 1 | import { type PrimitiveApply } from "../definition/index.ts" 2 | import { runHalfEdge } from "../run/runHalfEdge.ts" 3 | import { formatValue } from "../value/formatValue.ts" 4 | 5 | export const apply: PrimitiveApply = (env, args) => { 6 | if (args.length !== 1) { 7 | throw new Error([`[@run] I expect one argument.`].join("\n")) 8 | } 9 | 10 | const [value] = args 11 | 12 | if (value["@kind"] !== "HalfEdge") { 13 | throw new Error( 14 | [ 15 | `[@run] I expect the top value on the stack to be a HalfEdge.`, 16 | ``, 17 | ` value: ${formatValue(env, value)}`, 18 | ].join("\n"), 19 | ) 20 | } 21 | 22 | runHalfEdge(env.mod, env.net, value) 23 | 24 | return [value] 25 | } 26 | -------------------------------------------------------------------------------- /src/lang/cap/capType.ts: -------------------------------------------------------------------------------- 1 | import { type Mod } from "../mod/index.ts" 2 | import { addNode } from "../net/addNode.ts" 3 | import { findNodeEntryOrFail } from "../net/findNodeEntryOrFail.ts" 4 | import { findOutputPorts } from "../net/findOutputPorts.ts" 5 | import { type Net } from "../net/index.ts" 6 | import { type Port } from "../port/index.ts" 7 | import { type Value } from "../value/index.ts" 8 | 9 | export function capType(mod: Mod, net: Net, t: Value): Port { 10 | const parameter = { 11 | name: "covering", 12 | t, 13 | isPrincipal: false, 14 | } 15 | 16 | const node = addNode(net, mod, "@typeCap", [], [parameter]) 17 | const nodeEntry = findNodeEntryOrFail(net, node) 18 | nodeEntry.asTypeCap = {} 19 | 20 | return findOutputPorts(net, node)[0] 21 | } 22 | -------------------------------------------------------------------------------- /src/lang/node/addNodeFromDefinition.ts: -------------------------------------------------------------------------------- 1 | import { type Definition } from "../definition/index.ts" 2 | import { addNode } from "../net/addNode.ts" 3 | import { type Net } from "../net/index.ts" 4 | import { type Node } from "../node/index.ts" 5 | 6 | export function addNodeFromDefinition(net: Net, definition: Definition): Node { 7 | if (definition["@kind"] !== "NodeDefinition") { 8 | throw new Error( 9 | [ 10 | `[addNodeFromDefinition] I expect the definition to be NodeDefinition.`, 11 | ``, 12 | ` definition kind: ${definition["@kind"]}`, 13 | ].join("\n"), 14 | ) 15 | } 16 | 17 | return addNode( 18 | net, 19 | definition.mod, 20 | definition.name, 21 | definition.input, 22 | definition.output, 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /src/lang/cap/capNodeNonPrinciplePorts.ts: -------------------------------------------------------------------------------- 1 | import { type Mod } from "../mod/index.ts" 2 | import { findInputPorts } from "../net/findInputPorts.ts" 3 | import { findOutputPorts } from "../net/findOutputPorts.ts" 4 | import { type Net } from "../net/index.ts" 5 | import { type Node } from "../node/index.ts" 6 | import { capInputPort } from "./capInputPort.ts" 7 | import { capOutputPort } from "./capOutputPort.ts" 8 | 9 | export function capNodeNonPrinciplePorts(mod: Mod, net: Net, node: Node): void { 10 | for (const port of findInputPorts(net, node)) { 11 | if (!port.isPrincipal) { 12 | capInputPort(mod, net, port) 13 | } 14 | } 15 | 16 | for (const port of findOutputPorts(net, node)) { 17 | if (!port.isPrincipal) { 18 | capOutputPort(mod, net, port) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/lang/unify/deepWalkType.ts: -------------------------------------------------------------------------------- 1 | import { type Value } from "../value/index.ts" 2 | 3 | export function deepWalkType( 4 | substitution: Map, 5 | t: Value, 6 | ): Value { 7 | switch (t["@kind"]) { 8 | case "Symbol": { 9 | const found = substitution.get(t.name) 10 | if (found === undefined) { 11 | return t 12 | } else { 13 | return found 14 | } 15 | } 16 | 17 | case "TypeTerm": { 18 | return { 19 | "@type": "Value", 20 | "@kind": "TypeTerm", 21 | mod: t.mod, 22 | name: t.name, 23 | args: t.args.map((arg) => deepWalkType(substitution, arg)), 24 | } 25 | } 26 | 27 | default: { 28 | // TODO Maybe we need to handle nest values. 29 | return t 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/lang/exp/Exp.ts: -------------------------------------------------------------------------------- 1 | import { type Span } from "../span/index.ts" 2 | import { type BlockStmt } from "./BlockStmt.ts" 3 | 4 | export type Exp = Var | Builtin | Ap | QuoteSymbol | Block 5 | 6 | export type Var = { 7 | "@type": "Exp" 8 | "@kind": "Var" 9 | name: string 10 | span: Span 11 | } 12 | 13 | export type Builtin = { 14 | "@type": "Exp" 15 | "@kind": "Builtin" 16 | name: string 17 | span: Span 18 | } 19 | 20 | export type Ap = { 21 | "@type": "Exp" 22 | "@kind": "Ap" 23 | target: Exp 24 | args: Array 25 | span: Span 26 | } 27 | 28 | export type QuoteSymbol = { 29 | "@type": "Exp" 30 | "@kind": "QuoteSymbol" 31 | name: string 32 | span: Span 33 | } 34 | 35 | export type Block = { 36 | "@type": "Exp" 37 | "@kind": "Block" 38 | body: Array 39 | span: Span 40 | } 41 | -------------------------------------------------------------------------------- /src/lang/net/findPrincipalPort.ts: -------------------------------------------------------------------------------- 1 | import { formatNode, type Node } from "../node/index.ts" 2 | import { type Port } from "../port/index.ts" 3 | import { type Net } from "./Net.ts" 4 | import { createPortFromPortEntry } from "./createPortFromPortEntry.ts" 5 | import { findPortRecordOrFail } from "./findPortRecordOrFail.ts" 6 | 7 | export function findPrincipalPort(net: Net, node: Node): Port { 8 | const portRecord = findPortRecordOrFail(net, node) 9 | for (const portEntry of Object.values(portRecord)) { 10 | if (portEntry.isPrincipal) { 11 | return createPortFromPortEntry(node, portEntry) 12 | } 13 | } 14 | 15 | throw new Error( 16 | [ 17 | `[findPrincipalPort] I expect the node to have a principal port.`, 18 | ``, 19 | ` node: ${formatNode(net, node)}`, 20 | ].join("\n"), 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /src/lang/freshen/refreshNode.ts: -------------------------------------------------------------------------------- 1 | import { findPortRecordOrFail } from "../net/findPortRecordOrFail.ts" 2 | import { type Net } from "../net/index.ts" 3 | import { type Node } from "../node/index.ts" 4 | import { freshenType } from "./freshenType.ts" 5 | 6 | /* 7 | 8 | During `run`, no need to call `refreshNode` to refresh the types of a node. 9 | It will be expansive to do so. 10 | 11 | `refreshNode` is only called during `checking`. 12 | 13 | */ 14 | 15 | export function refreshNode( 16 | net: Net, 17 | typeVarCounters: Map, 18 | node: Node, 19 | ): void { 20 | const occurredNames = new Map() 21 | 22 | const portRecord = findPortRecordOrFail(net, node) 23 | for (const portEntry of Object.values(portRecord)) { 24 | portEntry.t = freshenType(typeVarCounters, portEntry.t, occurredNames) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/lang/syntax/grammars/import_binding.ts: -------------------------------------------------------------------------------- 1 | export const import_binding = { 2 | $grammar: { 3 | "import_binding:name": [{ name: "variable_name" }], 4 | // "import_binding:alias": [ 5 | // { name: "variable_name" }, 6 | // '"as"', 7 | // { alias: "variable_name" }, 8 | // ], 9 | }, 10 | } 11 | 12 | export const import_binding_comma = { 13 | $grammar: { 14 | "import_binding_comma:import_binding_comma": [ 15 | { binding: "import_binding" }, 16 | { $ap: ["optional", '","'] }, 17 | ], 18 | }, 19 | } 20 | 21 | export const import_bindings = { 22 | $grammar: { 23 | "import_bindings:import_bindings": [ 24 | { bindings: { $ap: ["zero_or_more", "import_binding_comma"] } }, 25 | { last_binding: "import_binding" }, 26 | { $ap: ["optional", '","'] }, 27 | ], 28 | }, 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, 2 | # cache/restore them, build the source code and run tests across 3 | # different versions of node For more information see: 4 | # https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 5 | 6 | name: Node.js CI 7 | 8 | on: 9 | push: 10 | branches: ["master"] 11 | pull_request: 12 | branches: ["master"] 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | 18 | strategy: 19 | matrix: 20 | node-version: [22.x] 21 | 22 | steps: 23 | - uses: actions/checkout@v3 24 | - uses: actions/setup-node@v3 25 | with: 26 | node-version: ${{ matrix.node-version }} 27 | - run: npm install 28 | - run: npm run build 29 | - run: npm test 30 | -------------------------------------------------------------------------------- /src/lang/half-edge/formatHalfEdgeOtherPort.ts: -------------------------------------------------------------------------------- 1 | import { findHalfEdgeEntryOrFail } from "../net/findHalfEdgeEntryOrFail.ts" 2 | import { type Net } from "../net/index.ts" 3 | import { formatPort } from "../port/formatPort.ts" 4 | import { type Port } from "../port/index.ts" 5 | import { type HalfEdge } from "./HalfEdge.ts" 6 | 7 | export function formatHalfEdgeOtherPort(net: Net, halfEdge: HalfEdge): string { 8 | const halfEdgeEntry = findHalfEdgeEntryOrFail(net, halfEdge) 9 | const otherHalfEdge = halfEdgeEntry.otherHalfEdge 10 | const otherHalfEdgeEntry = findHalfEdgeEntryOrFail(net, otherHalfEdge) 11 | const otherPort = otherHalfEdgeEntry.port 12 | return maybeFormatPort(net, otherPort) 13 | } 14 | 15 | function maybeFormatPort(net: Net, port: Port | undefined): string { 16 | return port === undefined ? `#unconnected` : formatPort(net, port) 17 | } 18 | -------------------------------------------------------------------------------- /src/lang/exp/formatExp.ts: -------------------------------------------------------------------------------- 1 | import { indent } from "../../utils/indent.ts" 2 | import { type Exp } from "./Exp.ts" 3 | import { formatBlockStmt } from "./formatBlockStmt.ts" 4 | 5 | export function formatExp(exp: Exp): string { 6 | switch (exp["@kind"]) { 7 | case "Var": { 8 | return exp.name 9 | } 10 | 11 | case "Builtin": { 12 | return `@${exp.name}` 13 | } 14 | 15 | case "Ap": { 16 | const target = formatExp(exp.target) 17 | const args = exp.args.map(formatExp) 18 | return `${target}(${args.join(", ")})` 19 | } 20 | 21 | case "QuoteSymbol": { 22 | return `'${exp.name}` 23 | } 24 | 25 | case "Block": { 26 | const body = exp.body.map(formatBlockStmt) 27 | if (body.length === 0) { 28 | return `{}` 29 | } else { 30 | return `{\n${indent(body.join("\n"))}\n}` 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/lang/mod/createMod.ts: -------------------------------------------------------------------------------- 1 | import { Loader } from "../../loader/index.ts" 2 | import { defineBuiltins } from "../builtins/defineBuiltins.ts" 3 | import { createChecking } from "../checking/createChecking.ts" 4 | import { createEnv } from "../env/createEnv.ts" 5 | import { type Stmt } from "../stmt/index.ts" 6 | import { type Mod } from "./Mod.ts" 7 | 8 | export function createMod(options: { 9 | url: URL 10 | text: string 11 | stmts: Array 12 | loader: Loader 13 | }): Mod { 14 | const mod = { 15 | loader: options.loader, 16 | url: options.url, 17 | text: options.text, 18 | stmts: options.stmts, 19 | definitions: new Map(), 20 | builtins: new Map(), 21 | ruleEntries: new Map(), 22 | requiredMods: new Map(), 23 | } as Mod 24 | 25 | mod.env = createEnv(mod) 26 | mod.checking = createChecking() 27 | 28 | defineBuiltins(mod) 29 | 30 | return mod 31 | } 32 | -------------------------------------------------------------------------------- /src/lang/evaluate/evaluateOne.ts: -------------------------------------------------------------------------------- 1 | import { type Env } from "../env/index.ts" 2 | import { formatExp, type Exp } from "../exp/index.ts" 3 | import { type Mod } from "../mod/index.ts" 4 | import { formatValue, type Value } from "../value/index.ts" 5 | import { evaluate, type EvaluateOptions } from "./evaluate.ts" 6 | 7 | export function evaluateOne( 8 | mod: Mod, 9 | env: Env, 10 | exp: Exp, 11 | options: EvaluateOptions, 12 | ): Value { 13 | const values = evaluate(mod, env, exp, options) 14 | if (values.length !== 1) { 15 | throw new Error( 16 | [ 17 | `[evaluateOne] I expect result of evaluating the exp to be one value.`, 18 | ``, 19 | ` exp: ${formatExp(exp)}`, 20 | ` values: [${values 21 | .map((value) => formatValue(env, value)) 22 | .join(", ")}]`, 23 | ].join("\n"), 24 | ) 25 | } 26 | 27 | return values[0] 28 | } 29 | -------------------------------------------------------------------------------- /examples/datatypes/Nat.test.i: -------------------------------------------------------------------------------- 1 | require "Nat.i" 2 | require "Nat.i" 3 | require "Nat.i" // Multiple `require` is fine. 4 | 5 | eval @inspect(@run(@inspect(add(zero(), zero())))) 6 | eval @inspect(@run(@inspect(two()))) 7 | eval @inspect(@run(@inspect(three()))) 8 | eval @inspect(@run(@inspect(four()))) 9 | eval @inspect(@run(@inspect(add(one(), zero())))) 10 | 11 | // eval @inspect(@run(@inspect(natErase(two())))) 12 | // eval @inspect(@run(@inspect(natDup(two())))) 13 | 14 | eval @inspect(@run(@inspect(mul(two(), two())))) 15 | eval @inspect(@run(@inspect(mul(three(), three())))) 16 | 17 | eval @inspect(@run(max(two(), zero()))) 18 | eval @inspect(@run(max(zero(), two()))) 19 | eval @inspect(@run(max(two(), one()))) 20 | eval @inspect(@run(max(two(), three()))) 21 | 22 | function addadd(x: Nat, y: Nat, z: Nat): Nat { 23 | return add(add(x, y), z) 24 | } 25 | 26 | eval @inspect(@run(addadd(one(), one(), one()))) 27 | -------------------------------------------------------------------------------- /src/lang/mod/findNodeRuleEntries.ts: -------------------------------------------------------------------------------- 1 | import { nodeKeyWithoutId, type NodeWithoutId } from "../node/index.ts" 2 | import { type Mod } from "./Mod.ts" 3 | import { type RuleEntry } from "./RuleEntry.ts" 4 | import { hasNodeDefinition } from "./hasNodeDefinition.ts" 5 | 6 | export function findNodeRuleEntries( 7 | mod: Mod, 8 | node: NodeWithoutId, 9 | ): Array { 10 | const nodeKey = nodeKeyWithoutId(node) 11 | const entries: Array = [] 12 | for (const [key, entry] of mod.ruleEntries) { 13 | const [firstKey, secondKey] = key.split(" ") 14 | 15 | if ( 16 | (firstKey === nodeKey || secondKey === nodeKey) && 17 | hasNodeDefinition(mod, entry.second) && 18 | hasNodeDefinition(mod, entry.first) 19 | ) { 20 | if (!entries.find(({ name }) => name === entry.name)) { 21 | entries.push(entry) 22 | } 23 | } 24 | } 25 | 26 | return entries 27 | } 28 | -------------------------------------------------------------------------------- /src/lang/errors/Report.ts: -------------------------------------------------------------------------------- 1 | import * as pt from "@xieyuheng/partech" 2 | import { type Span } from "../span/index.ts" 3 | 4 | export type ReportEntry = { 5 | message: string 6 | context?: { 7 | span: Span 8 | text: string 9 | } 10 | } 11 | 12 | export class Report extends Error { 13 | constructor(public entries: Array = []) { 14 | super() 15 | } 16 | 17 | format(): string { 18 | return this.entries 19 | .map(formatReportEntry) 20 | .map((s) => s.trim()) 21 | .join("\n\n") 22 | } 23 | 24 | get message(): string { 25 | return this.format() 26 | } 27 | } 28 | 29 | function formatReportEntry(entry: ReportEntry): string { 30 | if (entry.context === undefined) { 31 | return entry.message 32 | } else { 33 | return [ 34 | entry.message, 35 | "", 36 | pt.report(entry.context.span, entry.context.text), 37 | ].join("\n") 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/lang/present/presentNode.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest" 2 | import { Fetcher } from "../../fetcher/index.ts" 3 | import { Loader } from "../../loader/index.ts" 4 | import { formatNet } from "../net/formatNet.ts" 5 | import { presentNode } from "./presentNode.ts" 6 | 7 | test("presentNode", async () => { 8 | const text = ` 9 | 10 | type Nat 11 | 12 | node add( 13 | target!: Nat, 14 | addend: Nat 15 | -------- 16 | result: Nat 17 | ) 18 | 19 | ` 20 | 21 | const fetcher = new Fetcher() 22 | const loader = new Loader({ fetcher }) 23 | const url = new URL("test://presentNode") 24 | const mod = await loader.load(url, { text }) 25 | const net = presentNode(mod, "add") 26 | 27 | expect(formatNet(net)).toMatchInlineSnapshot(` 28 | "(add₀)-target covering-(@inputPortCap₀) 29 | (add₀)-addend covering-(@inputPortCap₁) 30 | (add₀)-result covering-(@ouputPortCap₀)" 31 | `) 32 | }) 33 | -------------------------------------------------------------------------------- /src/lang/net/disconnectNode.ts: -------------------------------------------------------------------------------- 1 | import { type HalfEdge } from "../half-edge/index.ts" 2 | import { type Node } from "../node/index.ts" 3 | import { type Net } from "./Net.ts" 4 | import { disconnectHalfEdge } from "./disconnectHalfEdge.ts" 5 | import { findInputPorts } from "./findInputPorts.ts" 6 | import { findOutputPorts } from "./findOutputPorts.ts" 7 | import { findPortEntryOrFail } from "./findPortEntryOrFail.ts" 8 | 9 | export function disconnectNode( 10 | net: Net, 11 | node: Node, 12 | ): Array { 13 | const ports = [...findInputPorts(net, node), ...findOutputPorts(net, node)] 14 | return ports.map((port) => { 15 | const portEntry = findPortEntryOrFail(net, port) 16 | 17 | if (portEntry.connection === undefined) { 18 | return undefined 19 | } 20 | 21 | const halfEdge = portEntry.connection.halfEdge 22 | disconnectHalfEdge(net, halfEdge) 23 | return halfEdge 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /src/lang/cap/capOutputPort.ts: -------------------------------------------------------------------------------- 1 | import { connectPorts } from "../connect/connectPorts.ts" 2 | import { type Mod } from "../mod/index.ts" 3 | import { addNode } from "../net/addNode.ts" 4 | import { findInputPorts } from "../net/findInputPorts.ts" 5 | import { findNodeEntryOrFail } from "../net/findNodeEntryOrFail.ts" 6 | import { type Net } from "../net/index.ts" 7 | import { type Port } from "../port/index.ts" 8 | 9 | export function capOutputPort(mod: Mod, net: Net, port: Port): Port { 10 | const parameter = { 11 | name: "covering", 12 | t: port.t, 13 | isPrincipal: false, 14 | } 15 | 16 | const node = addNode(net, mod, "@ouputPortCap", [parameter], []) 17 | const nodeEntry = findNodeEntryOrFail(net, node) 18 | nodeEntry.asPortCap = { 19 | nodeName: port.node.name, 20 | portName: port.name, 21 | } 22 | 23 | const capPort = findInputPorts(net, node)[0] 24 | connectPorts(net, port, capPort) 25 | return capPort 26 | } 27 | -------------------------------------------------------------------------------- /src/lang/cap/capInputPort.ts: -------------------------------------------------------------------------------- 1 | import { connectPorts } from "../connect/connectPorts.ts" 2 | import { type Mod } from "../mod/index.ts" 3 | import { addNode } from "../net/addNode.ts" 4 | import { findNodeEntryOrFail } from "../net/findNodeEntryOrFail.ts" 5 | import { findOutputPorts } from "../net/findOutputPorts.ts" 6 | import { type Net } from "../net/index.ts" 7 | import { type Port } from "../port/index.ts" 8 | 9 | export function capInputPort(mod: Mod, net: Net, port: Port): Port { 10 | const parameter = { 11 | name: "covering", 12 | t: port.t, 13 | isPrincipal: false, 14 | } 15 | 16 | const node = addNode(net, mod, "@inputPortCap", [], [parameter]) 17 | const nodeEntry = findNodeEntryOrFail(net, node) 18 | nodeEntry.asPortCap = { 19 | nodeName: port.node.name, 20 | portName: port.name, 21 | } 22 | 23 | const capPort = findOutputPorts(net, node)[0] 24 | connectPorts(net, port, capPort) 25 | return capPort 26 | } 27 | -------------------------------------------------------------------------------- /examples/datatypes/List.test.i.out: -------------------------------------------------------------------------------- 1 | net result-(append₄) { 2 | (append₄)-target!value-(cons₈) 3 | (append₄)-rest value-(cons₁₁) 4 | (cons₈)-head value-(sole₆) 5 | (cons₈)-tail value-(cons₉) 6 | (cons₉)-head value-(sole₇) 7 | (cons₉)-tail value-(cons₁₀) 8 | (cons₁₀)-head value-(sole₈) 9 | (cons₁₀)-tail value-(null₃) 10 | (cons₁₁)-head value-(sole₉) 11 | (cons₁₁)-tail value-(cons₁₂) 12 | (cons₁₂)-head value-(sole₁₀) 13 | (cons₁₂)-tail value-(cons₁₃) 14 | (cons₁₃)-head value-(sole₁₁) 15 | (cons₁₃)-tail value-(null₄) 16 | } 17 | net !value-(cons₁₄) { 18 | (cons₁₄)-head value-(sole₆) 19 | (cons₁₄)-tail value-(cons₁₅) 20 | (cons₁₅)-head value-(sole₇) 21 | (cons₁₅)-tail value-(cons₁₆) 22 | (cons₁₆)-head value-(sole₈) 23 | (cons₁₆)-tail value-(cons₁₁) 24 | (cons₁₁)-head value-(sole₉) 25 | (cons₁₁)-tail value-(cons₁₂) 26 | (cons₁₂)-head value-(sole₁₀) 27 | (cons₁₂)-tail value-(cons₁₃) 28 | (cons₁₃)-head value-(sole₁₁) 29 | (cons₁₃)-tail value-(null₄) 30 | } 31 | -------------------------------------------------------------------------------- /src/lang/check/checkRuleIsAboutOwnNode.ts: -------------------------------------------------------------------------------- 1 | import { findDefinitionOrFail } from "../mod/findDefinitionOrFail.ts" 2 | import { type Mod } from "../mod/index.ts" 3 | 4 | export function checkRuleIsAboutOwnNode( 5 | mod: Mod, 6 | firstName: string, 7 | secondName: string, 8 | ): void { 9 | const first = findDefinitionOrFail(mod, firstName) 10 | const second = findDefinitionOrFail(mod, secondName) 11 | 12 | const fetcher = mod.loader.fetcher 13 | 14 | if ( 15 | first.mod.url.href !== mod.url.href && 16 | second.mod.url.href !== mod.url.href 17 | ) { 18 | throw new Error( 19 | [ 20 | `[checkRuleIsAboutOwnNode] To define a rule, one of the node must be owned by this module.`, 21 | ``, 22 | ` loading module url: ${fetcher.formatURL(mod.url)}`, 23 | ` first node module url: ${fetcher.formatURL(first.mod.url)}`, 24 | ` second node module url: ${fetcher.formatURL(second.mod.url)}`, 25 | ].join("\n"), 26 | ) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/tests/builtin/connect-type.error.i.err: -------------------------------------------------------------------------------- 1 | [unifyTypes] I fail to unify types. 2 | 3 | left: Trivial 4 | right: Nat 5 | 6 | [evaluate] I fail to evaluate an exp. 7 | 8 | exp: @connect(sole(), result) 9 | 10 | 13 |) 11 | 14 | 12 | 15 |rule add(target!, addend, result) zero(value!) { 13 | 16 | @connect(sole(), result) 14 | 17 | 15 | 18 | // The correct definition is: 16 | 17 | [evaluateBlockStmt] I fail to evaluate a block stmt. 18 | 19 | block stmt: @connect(sole(), result) 20 | 21 | 13 |) 22 | 14 | 23 | 15 |rule add(target!, addend, result) zero(value!) { 24 | 16 | @connect(sole(), result) 25 | 17 | 26 | 18 | // The correct definition is: 27 | 28 | [execute] I fail to execute a statement. 29 | 30 | 11 | -------- 31 | 12 | result: Nat 32 | 13 |) 33 | 14 | 34 | 15 |rule add(target!, addend, result) zero(value!) { 35 | 16 | @connect(sole(), result) 36 | 17 | 37 | 18 | // The correct definition is: 38 | 19 | // @connect(addend, result) 39 | 20 |} 40 | 21 | 41 | -------------------------------------------------------------------------------- /examples/tests/linearity/function-variable-double-use.error.i.err: -------------------------------------------------------------------------------- 1 | [findDefinitionOrFail] I meet undefined name. 2 | 3 | name: n 4 | 5 | [evaluate] I fail to evaluate an exp. 6 | 7 | exp: n 8 | 9 | 18 |} 10 | 19 | 11 | 20 |function double(n: Nat): Nat { 12 | 21 | return add(n, n) 13 | 22 |} 14 | 23 | 15 | 16 | [evaluate] I fail to evaluate an exp. 17 | 18 | exp: add(n, n) 19 | 20 | 18 |} 21 | 19 | 22 | 20 |function double(n: Nat): Nat { 23 | 21 | return add(n, n) 24 | 22 |} 25 | 23 | 26 | 27 | [evaluateBlockStmt] I fail to evaluate a block stmt. 28 | 29 | block stmt: return add(n, n) 30 | 31 | 18 |} 32 | 19 | 33 | 20 |function double(n: Nat): Nat { 34 | 21 | return add(n, n) 35 | 22 |} 36 | 23 | 37 | 38 | [execute] I fail to execute a statement. 39 | 40 | 16 |rule add(target!, addend, result) add1(prev, value!) { 41 | 17 | add1(add(prev, addend), result) 42 | 18 |} 43 | 19 | 44 | 20 |function double(n: Nat): Nat { 45 | 21 | return add(n, n) 46 | 22 |} 47 | 23 | 48 | -------------------------------------------------------------------------------- /src/lang/connect/connectValues.ts: -------------------------------------------------------------------------------- 1 | import { type Env } from "../env/index.ts" 2 | import { formatValue, type Value } from "../value/index.ts" 3 | import { connectHalfEdges } from "./connectHalfEdges.ts" 4 | 5 | export function connectValues(env: Env, first: Value, second: Value): void { 6 | if (first["@kind"] !== "HalfEdge") { 7 | throw new Error( 8 | [ 9 | `[connectValues] I expect the first value to be a HalfEdge.`, 10 | ``, 11 | ` first value: ${formatValue(env, first)}`, 12 | ` second value: ${formatValue(env, second)}`, 13 | ].join("\n"), 14 | ) 15 | } 16 | 17 | if (second["@kind"] !== "HalfEdge") { 18 | throw new Error( 19 | [ 20 | `[connectValues] I expect the second value to be a HalfEdge.`, 21 | ``, 22 | ` first value: ${formatValue(env, first)}`, 23 | ` second value: ${formatValue(env, second)}`, 24 | ].join("\n"), 25 | ) 26 | } 27 | 28 | connectHalfEdges(env.net, first, second) 29 | } 30 | -------------------------------------------------------------------------------- /examples/datatypes/Pair.i: -------------------------------------------------------------------------------- 1 | type Trivial 2 | 3 | node sole(-- value!: Trivial) 4 | node trivialDrop(value!: Trivial --) 5 | 6 | rule trivialDrop(value!) sole(value!) {} 7 | 8 | type Pair 9 | 10 | node pair( 11 | left: 'L, 12 | right: 'R, 13 | ------ 14 | handle: Trivial, 15 | value!: Pair, 16 | ) 17 | 18 | node pairDup( 19 | target!: Pair, 20 | ---------- 21 | first: Pair, 22 | second: Pair, 23 | ) 24 | 25 | rule pairDup(target!, first, second) pair(left, right, handle, value!) { 26 | let leftFirst, leftSecond = pairDup(left) 27 | let rightFirst, rightSecond = pairDup(right) 28 | @connect(pair(leftFirst, rightFirst, trivialDrop()), first) 29 | @connect(pair(leftSecond, rightSecond, trivialDrop()), second) 30 | sole(handle) 31 | } 32 | 33 | function divergence(): Trivial { 34 | let left, right, handle, value = pair() 35 | let first, second = pairDup(value) 36 | @connect(first, left) 37 | @connect(second, right) 38 | return handle 39 | } 40 | 41 | eval @inspect(divergence()) 42 | -------------------------------------------------------------------------------- /src/lang/half-edge/formatHalfEdge.ts: -------------------------------------------------------------------------------- 1 | import { findHalfEdgeEntryOrFail } from "../net/findHalfEdgeEntryOrFail.ts" 2 | import { type Net } from "../net/index.ts" 3 | import { formatPort } from "../port/formatPort.ts" 4 | import { type Port } from "../port/index.ts" 5 | import { type HalfEdge } from "./HalfEdge.ts" 6 | 7 | export function formatHalfEdge(net: Net, halfEdge: HalfEdge): string { 8 | const halfEdgeEntry = findHalfEdgeEntryOrFail(net, halfEdge) 9 | const port = halfEdgeEntry.port 10 | const portText = maybeFormatPort(net, port) 11 | 12 | const otherHalfEdge = halfEdgeEntry.otherHalfEdge 13 | const otherHalfEdgeEntry = findHalfEdgeEntryOrFail(net, otherHalfEdge) 14 | const otherPort = otherHalfEdgeEntry.port 15 | const otherPortText = maybeFormatPort(net, otherPort) 16 | 17 | return `#HalfEdge(${portText}, ${otherPortText})` 18 | } 19 | 20 | function maybeFormatPort(net: Net, port: Port | undefined): string { 21 | return port === undefined ? `#unconnected` : formatPort(net, port) 22 | } 23 | -------------------------------------------------------------------------------- /src/lang/rule/exposeRuleTargets.ts: -------------------------------------------------------------------------------- 1 | import { type Env } from "../env/index.ts" 2 | import { type Node } from "../node/index.ts" 3 | import { type RuleTarget } from "./Rule.ts" 4 | import { disconnectNodeAndMatchParameters } from "./disconnectNodeAndMatchParameters.ts" 5 | 6 | export function exposeRuleTargets( 7 | env: Env, 8 | rule: { first: RuleTarget; second: RuleTarget }, 9 | [firstNode, secondNode]: [Node, Node], 10 | ): void { 11 | if ( 12 | rule.first.name === firstNode.name || 13 | rule.second.name === secondNode.name 14 | ) { 15 | disconnectNodeAndMatchParameters(env, firstNode, rule.first.parameters) 16 | disconnectNodeAndMatchParameters(env, secondNode, rule.second.parameters) 17 | return 18 | } 19 | 20 | if ( 21 | rule.first.name === secondNode.name || 22 | rule.second.name === firstNode.name 23 | ) { 24 | disconnectNodeAndMatchParameters(env, secondNode, rule.first.parameters) 25 | disconnectNodeAndMatchParameters(env, firstNode, rule.second.parameters) 26 | return 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/lang/check/checkPortSigns.ts: -------------------------------------------------------------------------------- 1 | import { type Net } from "../net/index.ts" 2 | import { formatPort } from "../port/formatPort.ts" 3 | import { type Port } from "../port/index.ts" 4 | 5 | export function checkPortSigns(net: Net, first: Port, second: Port): void { 6 | if (first.sign === 1 && second.sign === 1) { 7 | throw new Error( 8 | [ 9 | `[checkPortSigns] I expect the two ports to have opposite signs,`, 10 | ` but they all have positive sign.`, 11 | ``, 12 | ` first port: ${formatPort(net, first)}`, 13 | ` second port: ${formatPort(net, second)}`, 14 | ].join("\n"), 15 | ) 16 | } 17 | 18 | if (first.sign === -1 && second.sign === -1) { 19 | throw new Error( 20 | [ 21 | `[checkPortSigns] I expect the two ports to have opposite signs,`, 22 | ` but they all have negative sign.`, 23 | ``, 24 | ` first port: ${formatPort(net, first)}`, 25 | ` second port: ${formatPort(net, second)}`, 26 | ].join("\n"), 27 | ) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/lang/run/runHalfEdge.ts: -------------------------------------------------------------------------------- 1 | import { type HalfEdge } from "../half-edge/index.ts" 2 | import { type Mod } from "../mod/index.ts" 3 | import { createNet } from "../net/createNet.ts" 4 | import { findHalfEdgeEntryOrFail } from "../net/findHalfEdgeEntryOrFail.ts" 5 | import { findHalfEdgePortOrFail } from "../net/findHalfEdgePortOrFail.ts" 6 | import { type Net } from "../net/index.ts" 7 | import { moveConnectedComponent } from "../net/moveConnectedComponent.ts" 8 | import { runNet } from "./runNet.ts" 9 | 10 | export function runHalfEdge(mod: Mod, net: Net, halfEdge: HalfEdge): void { 11 | const component = createNet() 12 | 13 | const halfEdgeEntry = findHalfEdgeEntryOrFail(net, halfEdge) 14 | 15 | const otherPort = findHalfEdgePortOrFail(net, halfEdgeEntry.otherHalfEdge) 16 | moveConnectedComponent(net, component, otherPort.node) 17 | 18 | runNet(mod, component) 19 | 20 | const resultPort = findHalfEdgePortOrFail( 21 | component, 22 | halfEdgeEntry.otherHalfEdge, 23 | ) 24 | moveConnectedComponent(component, net, resultPort.node) 25 | } 26 | -------------------------------------------------------------------------------- /examples/tests/statement/rule-type.error.i.err: -------------------------------------------------------------------------------- 1 | [unifyTypes] I fail to unify types. 2 | 3 | left: Nat 4 | right: Trivial 5 | 6 | [evaluate] I fail to evaluate an exp. 7 | 8 | exp: add1(sole()) 9 | 10 | 17 |} 11 | 18 | 12 | 19 |rule add(target!, addend, result) add1(prev, value!) { 13 | 20 | add1(sole()) // Apply node to value of wrong type. 14 | 21 | add1(add(prev, addend), result) 15 | 22 |} 16 | 17 | [evaluateBlockStmt] I fail to evaluate a block stmt. 18 | 19 | block stmt: add1(sole()) 20 | 21 | 17 |} 22 | 18 | 23 | 19 |rule add(target!, addend, result) add1(prev, value!) { 24 | 20 | add1(sole()) // Apply node to value of wrong type. 25 | 21 | add1(add(prev, addend), result) 26 | 22 |} 27 | 28 | [execute] I fail to execute a statement. 29 | 30 | 15 |rule add(target!, addend, result) zero(value!) { 31 | 16 | @connect(addend, result) 32 | 17 |} 33 | 18 | 34 | 19 |rule add(target!, addend, result) add1(prev, value!) { 35 | 20 | add1(sole()) // Apply node to value of wrong type. 36 | 21 | add1(add(prev, addend), result) 37 | 22 |} 38 | 23 | 39 | -------------------------------------------------------------------------------- /examples/tests/statement/function-input-type.error.i.err: -------------------------------------------------------------------------------- 1 | [unifyTypes] I fail to unify types. 2 | 3 | left: Nat 4 | right: Trivial 5 | 6 | [evaluate] I fail to evaluate an exp. 7 | 8 | exp: add1(n) 9 | 10 | 6 |node sole(-- value!: Trivial) 11 | 7 | 12 | 8 |function add2(n: Trivial): Nat { 13 | 9 | return add1(add1(n)) 14 | 10 |} 15 | 11 | 16 | 17 | [evaluate] I fail to evaluate an exp. 18 | 19 | exp: add1(add1(n)) 20 | 21 | 6 |node sole(-- value!: Trivial) 22 | 7 | 23 | 8 |function add2(n: Trivial): Nat { 24 | 9 | return add1(add1(n)) 25 | 10 |} 26 | 11 | 27 | 28 | [evaluateBlockStmt] I fail to evaluate a block stmt. 29 | 30 | block stmt: return add1(add1(n)) 31 | 32 | 6 |node sole(-- value!: Trivial) 33 | 7 | 34 | 8 |function add2(n: Trivial): Nat { 35 | 9 | return add1(add1(n)) 36 | 10 |} 37 | 11 | 38 | 39 | [execute] I fail to execute a statement. 40 | 41 | 4 | 42 | 5 |type Trivial 43 | 6 |node sole(-- value!: Trivial) 44 | 7 | 45 | 8 |function add2(n: Trivial): Nat { 46 | 9 | return add1(add1(n)) 47 | 10 |} 48 | 11 | 49 | -------------------------------------------------------------------------------- /src/lang/env/formatEnv.ts: -------------------------------------------------------------------------------- 1 | import { indent } from "../../utils/indent.ts" 2 | import { formatNet } from "../net/formatNet.ts" 3 | import { netIsEmpty } from "../net/netIsEmpty.ts" 4 | import { formatValues } from "../value/formatValues.ts" 5 | import { formatValue } from "../value/index.ts" 6 | import { type Env } from "./Env.ts" 7 | 8 | export function formatEnv(env: Env): string { 9 | const netText = netIsEmpty(env.net) 10 | ? "net {}" 11 | : `net {\n${indent(formatNet(env.net))}\n}` 12 | 13 | const localsText = 14 | env.locals.size === 0 15 | ? "locals {}" 16 | : `locals {\n${indent( 17 | Array.from(env.locals.entries()) 18 | .map(([name, value]) => `${name}: ${formatValue(env, value)}.`) 19 | .join("\n"), 20 | )}\n}` 21 | 22 | const stackText = 23 | env.stack.length === 0 24 | ? "stack []" 25 | : `stack [${formatValues(env, env.stack)}]` 26 | 27 | return [ 28 | `env {`, 29 | indent(netText), 30 | indent(localsText), 31 | indent(stackText), 32 | `}`, 33 | ].join("\n") 34 | } 35 | -------------------------------------------------------------------------------- /src/lang/evaluate/evaluateBlock.ts: -------------------------------------------------------------------------------- 1 | import { type Env } from "../env/index.ts" 2 | import { type BlockStmt } from "../exp/BlockStmt.ts" 3 | import { type Mod } from "../mod/index.ts" 4 | import { type Value } from "../value/index.ts" 5 | import { type EvaluateOptions } from "./evaluate.ts" 6 | import { evaluateBlockStmt } from "./evaluateBlockStmt.ts" 7 | 8 | export function evaluateBlock( 9 | mod: Mod, 10 | env: Env, 11 | body: Array, 12 | options: EvaluateOptions, 13 | ): Array { 14 | for (const [index, stmt] of body.entries()) { 15 | const values = evaluateBlockStmt(mod, env, stmt, options) 16 | if (values !== null) { 17 | if (index !== body.length - 1) { 18 | throw new Error( 19 | [ 20 | `[evaluateBlock] I expect the return stmt to be at the end of the block.`, 21 | ``, 22 | ` return stmt index: ${index}`, 23 | ` lenght of block: ${body.length}`, 24 | ].join("\n"), 25 | ) 26 | } 27 | 28 | return values 29 | } 30 | } 31 | 32 | return [] 33 | } 34 | -------------------------------------------------------------------------------- /examples/datatypes/DiffList.i: -------------------------------------------------------------------------------- 1 | require "List.i" 2 | 3 | // Concatenation of lists is performed in linear time 4 | // with respect to its first argument. 5 | // Constant time concatenation is possible 6 | // with difference-lists: the idea consists in 7 | // plugging the front of the second argument 8 | // at the back of the first one. 9 | 10 | type DiffList(Element: @Type) 11 | 12 | node diff( 13 | front: List('A), 14 | ------- 15 | back: List('A), 16 | value!: DiffList('A), 17 | ) 18 | 19 | node diffAppend( 20 | target!: DiffList('A), 21 | rest: DiffList('A) 22 | -------- 23 | result: DiffList('A) 24 | ) 25 | 26 | node diffOpen( 27 | target!: DiffList('A), 28 | newBack: List('A) 29 | ---------- 30 | oldBack: List('A) 31 | ) 32 | 33 | rule diffAppend(target!, rest, result) 34 | diff(front, back, value!) { 35 | let newBack, value = diff(front) 36 | @connect(value, result) 37 | diffOpen(rest, newBack, back) 38 | } 39 | 40 | rule diffOpen(target!, newBack, oldBack) 41 | diff(front, back, value!) { 42 | @connect(back, newBack) 43 | @connect(front, oldBack) 44 | } 45 | -------------------------------------------------------------------------------- /examples/tests/currying/function-not-enough-args.i: -------------------------------------------------------------------------------- 1 | type Nat 2 | node zero(-- value!: Nat) 3 | node add1(prev: Nat -- value!: Nat) 4 | 5 | node add(target!: Nat, addend: Nat -- result: Nat) 6 | 7 | rule add(target!, addend, result) zero(value!) { 8 | @connect(addend, result) 9 | } 10 | 11 | rule add(target!, addend, result) add1(prev, value!) { 12 | add1(add(prev, addend), result) 13 | } 14 | 15 | function one(): Nat { 16 | return add1(zero()) 17 | } 18 | 19 | function addadd(x: Nat, y: Nat, z: Nat): Nat { 20 | return add(add(x, y), z) 21 | } 22 | 23 | eval { 24 | let x, y, z, result = addadd() 25 | @connect(x, one()) 26 | @connect(y, one()) 27 | @connect(z, one()) 28 | return @inspect(@run(result)) 29 | } 30 | 31 | eval { 32 | let y, z, result = addadd(one()) 33 | @connect(y, one()) 34 | @connect(z, one()) 35 | return @inspect(@run(result)) 36 | } 37 | 38 | eval { 39 | let z, result = addadd(one(), one()) 40 | @connect(z, one()) 41 | return @inspect(@run(result)) 42 | } 43 | 44 | eval { 45 | let result = addadd(one(), one(), one()) 46 | return @inspect(@run(result)) 47 | } 48 | -------------------------------------------------------------------------------- /src/lang/net/addEdge.ts: -------------------------------------------------------------------------------- 1 | import { type Edge } from "../edge/index.ts" 2 | import { createHalfEdgeId } from "../half-edge/createHalfEdgeId.ts" 3 | import { type HalfEdge } from "../half-edge/index.ts" 4 | import { type HalfEdgeEntry, type Net } from "../net/index.ts" 5 | 6 | export function addEdge(net: Net): Edge { 7 | const firstId = createHalfEdgeId() 8 | const firstHalfEdgeEntry = { id: firstId } as HalfEdgeEntry 9 | 10 | const secondId = createHalfEdgeId() 11 | const secondHalfEdgeEntry = { id: secondId } as HalfEdgeEntry 12 | 13 | const firstHalfEdge: HalfEdge = { 14 | "@type": "Value", 15 | "@kind": "HalfEdge", 16 | id: firstId, 17 | } 18 | 19 | const secondHalfEdge: HalfEdge = { 20 | "@type": "Value", 21 | "@kind": "HalfEdge", 22 | id: secondId, 23 | } 24 | 25 | firstHalfEdgeEntry.otherHalfEdge = secondHalfEdge 26 | secondHalfEdgeEntry.otherHalfEdge = firstHalfEdge 27 | 28 | net.halfEdgeEntries.set(firstId, firstHalfEdgeEntry) 29 | net.halfEdgeEntries.set(secondId, secondHalfEdgeEntry) 30 | 31 | return { 32 | first: firstHalfEdge, 33 | second: secondHalfEdge, 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/lang/syntax/grammars/parameter.ts: -------------------------------------------------------------------------------- 1 | export const parameter = { 2 | $grammar: { 3 | "parameter:normal": [{ name: "variable_name" }, '":"', { t: "exp" }], 4 | "parameter:is_principal": [ 5 | { name: "variable_name" }, 6 | '"!"', 7 | '":"', 8 | { t: "exp" }, 9 | ], 10 | }, 11 | } 12 | 13 | export const parameters = { 14 | $grammar: { 15 | "parameters:parameters": [ 16 | { entries: { $ap: ["zero_or_more", "parameter", '","'] } }, 17 | { last_entry: "parameter" }, 18 | { $ap: ["optional", '","'] }, 19 | ], 20 | }, 21 | } 22 | 23 | export const parameter_without_type = { 24 | $grammar: { 25 | "parameter_without_type:normal": [{ name: "variable_name" }], 26 | "parameter_without_type:is_principal": [{ name: "variable_name" }, '"!"'], 27 | }, 28 | } 29 | 30 | export const parameters_without_type = { 31 | $grammar: { 32 | "parameters_without_type:parameters_without_type": [ 33 | { entries: { $ap: ["zero_or_more", "parameter_without_type", '","'] } }, 34 | { last_entry: "parameter_without_type" }, 35 | { $ap: ["optional", '","'] }, 36 | ], 37 | }, 38 | } 39 | -------------------------------------------------------------------------------- /src/command-line/commands/Repl.ts: -------------------------------------------------------------------------------- 1 | import { Command, CommandRunner } from "@xieyuheng/command-line" 2 | import { ReadlineRepl } from "@xieyuheng/framework/lib/repls/readline-repl/index.js" 3 | import Path from "path" 4 | import { app } from "../../app/index.ts" 5 | 6 | type Args = {} 7 | 8 | export class Repl extends Command { 9 | name = "repl" 10 | 11 | description = "Start an interactive REPL" 12 | 13 | args = {} 14 | 15 | // prettier-ignore 16 | help(runner: CommandRunner): string { 17 | const { blue } = this.colors 18 | 19 | return [ 20 | `The ${blue(this.name)} command takes you into a rabbit hole`, 21 | ` called REPL -- "Read Evaluate Print Loop".`, 22 | ``, 23 | `In which you can try some ideas real quick.`, 24 | ``, 25 | blue(` ${runner.name} ${this.name}`), 26 | ``, 27 | ].join("\n") 28 | } 29 | 30 | async execute(argv: Args): Promise { 31 | const repl = await ReadlineRepl.create({ 32 | dir: Path.resolve(process.cwd()), 33 | handler: app.replEventHandler, 34 | files: app.home, 35 | commitOnDoubleNewline: true, 36 | }) 37 | 38 | await repl.run() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/lang/apply/apply.ts: -------------------------------------------------------------------------------- 1 | import { type Env } from "../env/index.ts" 2 | import { type EvaluateOptions } from "../evaluate/index.ts" 3 | import { formatValue, type Value } from "../value/index.ts" 4 | import { applyFunction } from "./applyFunction.ts" 5 | import { applyNode } from "./applyNode.ts" 6 | import { applyTypeCtor } from "./applyTypeCtor.ts" 7 | 8 | export function apply( 9 | env: Env, 10 | target: Value, 11 | args: Array, 12 | options: EvaluateOptions, 13 | ): Array { 14 | if (target["@kind"] === "Node") { 15 | return applyNode(env, target, args, options) 16 | } 17 | 18 | if (target["@kind"] === "TypeCtor") { 19 | return applyTypeCtor(env, target, args, options) 20 | } 21 | 22 | if (target["@kind"] === "Function") { 23 | return applyFunction(env, target, args, options) 24 | } 25 | 26 | if (target["@kind"] === "PrimitiveFunction") { 27 | return target.definition.apply(env, args, options) 28 | } 29 | 30 | throw new Error( 31 | [ 32 | `[apply] I can not apply target.`, 33 | ``, 34 | ` target: ${formatValue(env, target)}}`, 35 | ` args: [${args.map((arg) => formatValue(env, arg)).join(", ")}}]`, 36 | ].join("\n"), 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /src/lang/check/checkHalfEdges.ts: -------------------------------------------------------------------------------- 1 | import { checkPortSigns } from "../check/checkPortSigns.ts" 2 | import { type Checking } from "../checking/index.ts" 3 | import { type Env } from "../env/index.ts" 4 | import { type HalfEdge } from "../half-edge/index.ts" 5 | import { findHalfEdgeEntryOrFail } from "../net/findHalfEdgeEntryOrFail.ts" 6 | import { findHalfEdgePort } from "../net/findHalfEdgePort.ts" 7 | import { unifyTypes } from "../unify/unifyTypes.ts" 8 | 9 | export function checkHalfEdges( 10 | env: Env, 11 | first: HalfEdge, 12 | second: HalfEdge, 13 | checking: Checking, 14 | ): void { 15 | const firstHalfEdgeEntry = findHalfEdgeEntryOrFail(env.net, first) 16 | const secondHalfEdgeEntry = findHalfEdgeEntryOrFail(env.net, second) 17 | 18 | const firstOtherPort = findHalfEdgePort( 19 | env.net, 20 | firstHalfEdgeEntry.otherHalfEdge, 21 | ) 22 | const secondOtherPort = findHalfEdgePort( 23 | env.net, 24 | secondHalfEdgeEntry.otherHalfEdge, 25 | ) 26 | 27 | if (firstOtherPort !== undefined && secondOtherPort !== undefined) { 28 | checkPortSigns(env.net, firstOtherPort, secondOtherPort) 29 | unifyTypes(env, checking.substitution, firstOtherPort.t, secondOtherPort.t) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/lang/syntax/matchers/block_stmt_matcher.ts: -------------------------------------------------------------------------------- 1 | import * as pt from "@xieyuheng/partech" 2 | import { type BlockStmt } from "../../exp/BlockStmt.ts" 3 | import * as matchers from "../matchers/index.ts" 4 | 5 | export function block_stmt_matcher(tree: pt.Tree): BlockStmt { 6 | return pt.matcher({ 7 | "block_stmt:let": ({ names, exp }, { span }) => ({ 8 | "@type": "BlockStmt", 9 | "@kind": "Let", 10 | names: matchers.variable_names_matcher(names), 11 | exp: matchers.exp_matcher(exp), 12 | span, 13 | }), 14 | "block_stmt:evaluate": ({ exp }, { span }) => ({ 15 | "@type": "BlockStmt", 16 | "@kind": "Evaluate", 17 | exp: matchers.exp_matcher(exp), 18 | span, 19 | }), 20 | "block_stmt:return": ({ exp }, { span }) => ({ 21 | "@type": "BlockStmt", 22 | "@kind": "Return", 23 | exp: matchers.exp_matcher(exp), 24 | span, 25 | }), 26 | })(tree) 27 | } 28 | 29 | export function block_stmts_matcher(tree: pt.Tree): Array { 30 | return pt.matcher>({ 31 | "block_stmts:block_stmts": ({ entries }) => 32 | pt.matchers.zero_or_more_matcher(entries).map(block_stmt_matcher), 33 | })(tree) 34 | } 35 | -------------------------------------------------------------------------------- /src/lang/net/addNode.ts: -------------------------------------------------------------------------------- 1 | import { type Mod } from "../mod/index.ts" 2 | import { type Net, type PortRecord } from "../net/index.ts" 3 | import { createNodeId } from "../node/createNodeId.ts" 4 | import { type Node } from "../node/index.ts" 5 | import { nodeKey } from "../node/nodeKey.ts" 6 | import { type Parameter } from "../parameter/index.ts" 7 | 8 | export function addNode( 9 | net: Net, 10 | mod: Mod, 11 | name: string, 12 | input: Array, 13 | output: Array, 14 | ): Node { 15 | const modId = mod.url.href 16 | const id = createNodeId(name) 17 | 18 | const node: Node = { "@type": "Value", "@kind": "Node", modId, id, name } 19 | const ports: PortRecord = {} 20 | net.nodeEntries.set(nodeKey(node), { id, modId, name, ports }) 21 | 22 | input.map((parameter) => { 23 | ports[parameter.name] = { 24 | sign: -1, 25 | name: parameter.name, 26 | t: parameter.t, 27 | isPrincipal: parameter.isPrincipal, 28 | } 29 | }) 30 | 31 | output.map((parameter) => { 32 | ports[parameter.name] = { 33 | sign: 1, 34 | name: parameter.name, 35 | t: parameter.t, 36 | isPrincipal: parameter.isPrincipal, 37 | } 38 | }) 39 | 40 | return node 41 | } 42 | -------------------------------------------------------------------------------- /src/lang/rule/disconnectNodeAndMatchParameters.ts: -------------------------------------------------------------------------------- 1 | import { defineLocals } from "../env/defineLocals.ts" 2 | import { type Env } from "../env/index.ts" 3 | import { disconnectNode } from "../net/disconnectNode.ts" 4 | import { formatNode, type Node } from "../node/index.ts" 5 | import { 6 | formatParameterWithoutType, 7 | type ParameterWithoutType, 8 | } from "../parameter/index.ts" 9 | 10 | export function disconnectNodeAndMatchParameters( 11 | env: Env, 12 | node: Node, 13 | parameters: Array, 14 | ): void { 15 | const halfEdges = disconnectNode(env.net, node) 16 | 17 | for (const [index, parameter] of parameters.entries()) { 18 | if (!parameter.isPrincipal) { 19 | const halfEdge = halfEdges[index] 20 | if (halfEdge === undefined) { 21 | throw new Error( 22 | [ 23 | `[exposeRuleTarget] I expect a halfEdge at the index.`, 24 | ``, 25 | ` node: ${formatNode(env.net, node)}`, 26 | ` index: ${index}`, 27 | ` parameter: ${formatParameterWithoutType(parameter)}`, 28 | ].join("\n"), 29 | ) 30 | } 31 | 32 | defineLocals(env, [parameter.name], [halfEdge]) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/lang/syntax/matchers/import_binding_matcher.ts: -------------------------------------------------------------------------------- 1 | import * as pt from "@xieyuheng/partech" 2 | import { type ImportBinding } from "../../import/ImportBinding.ts" 3 | import * as matchers from "../matchers/index.ts" 4 | 5 | export function import_binding_matcher(tree: pt.Tree): ImportBinding { 6 | return pt.matcher({ 7 | "import_binding:name": ({ name }) => ({ 8 | name: pt.str(name), 9 | }), 10 | // "import_binding:alias": ({ name, alias }) => ({ 11 | // name: pt.str(name), 12 | // alias: pt.str(alias), 13 | // }), 14 | })(tree) 15 | } 16 | 17 | export function import_binding_comma_matcher(tree: pt.Tree): ImportBinding { 18 | return pt.matcher({ 19 | "import_binding_comma:import_binding_comma": ({ binding }) => 20 | import_binding_matcher(binding), 21 | })(tree) 22 | } 23 | 24 | export function import_bindings_matcher(tree: pt.Tree): Array { 25 | return pt.matcher({ 26 | "import_bindings:import_bindings": ({ bindings, last_binding }) => [ 27 | ...pt.matchers 28 | .zero_or_more_matcher(bindings) 29 | .map(matchers.import_binding_comma_matcher), 30 | import_binding_matcher(last_binding), 31 | ], 32 | })(tree) 33 | } 34 | -------------------------------------------------------------------------------- /src/lang/value/Value.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type FunctionDefinition, 3 | type PrimitiveFunctionDefinition, 4 | type TypeDefinition, 5 | } from "../definition/index.ts" 6 | import { type HalfEdge } from "../half-edge/index.ts" 7 | import { type Mod } from "../mod/index.ts" 8 | import { type Node } from "../node/index.ts" 9 | 10 | export type Value = 11 | | HalfEdge 12 | | Node 13 | | Function 14 | | PrimitiveFunction 15 | | TypeCtor 16 | | Type 17 | | Symbol 18 | | TypeTerm 19 | 20 | export type Type = { 21 | "@type": "Value" 22 | "@kind": "Type" 23 | } 24 | 25 | export type Function = { 26 | "@type": "Value" 27 | "@kind": "Function" 28 | definition: FunctionDefinition 29 | } 30 | 31 | export type PrimitiveFunction = { 32 | "@type": "Value" 33 | "@kind": "PrimitiveFunction" 34 | definition: PrimitiveFunctionDefinition 35 | } 36 | 37 | export type TypeCtor = { 38 | "@type": "Value" 39 | "@kind": "TypeCtor" 40 | definition: TypeDefinition 41 | } 42 | 43 | export type Symbol = { 44 | "@type": "Value" 45 | "@kind": "Symbol" 46 | name: string 47 | } 48 | 49 | export type TypeTerm = { 50 | "@type": "Value" 51 | "@kind": "TypeTerm" 52 | mod: Mod 53 | name: string 54 | args: Array 55 | } 56 | -------------------------------------------------------------------------------- /examples/tests/statement/rule-sign.error.i.err: -------------------------------------------------------------------------------- 1 | [checkPortSigns] I expect the two ports to have opposite signs, 2 | but they all have positive sign. 3 | 4 | first port: covering-(@inputPortCap₂) 5 | second port: covering-(@inputPortCap₁) 6 | 7 | [evaluate] I fail to evaluate an exp. 8 | 9 | exp: @connect(prev, addend) 10 | 11 | 17 |} 12 | 18 | 13 | 19 |rule add(target!, addend, result) add1(prev, value!) { 14 | 20 | @connect(prev, addend) // Connecting ports of the same sign. 15 | 21 | add1(add(prev, addend), result) 16 | 22 |} 17 | 18 | [evaluateBlockStmt] I fail to evaluate a block stmt. 19 | 20 | block stmt: @connect(prev, addend) 21 | 22 | 17 |} 23 | 18 | 24 | 19 |rule add(target!, addend, result) add1(prev, value!) { 25 | 20 | @connect(prev, addend) // Connecting ports of the same sign. 26 | 21 | add1(add(prev, addend), result) 27 | 22 |} 28 | 29 | [execute] I fail to execute a statement. 30 | 31 | 15 |rule add(target!, addend, result) zero(value!) { 32 | 16 | @connect(addend, result) 33 | 17 |} 34 | 18 | 35 | 19 |rule add(target!, addend, result) add1(prev, value!) { 36 | 20 | @connect(prev, addend) // Connecting ports of the same sign. 37 | 21 | add1(add(prev, addend), result) 38 | 22 |} 39 | 23 | 40 | -------------------------------------------------------------------------------- /src/lang/env/defineLocals.ts: -------------------------------------------------------------------------------- 1 | import { formatValue, type Value } from "../value/index.ts" 2 | import { type Env } from "./Env.ts" 3 | 4 | export function defineLocals( 5 | env: Env, 6 | names: Array, 7 | values: Array, 8 | ): void { 9 | if (names.length !== values.length) { 10 | throw new Error( 11 | [ 12 | `[defineLocals] I expect the number of names and values to be the same.`, 13 | ``, 14 | ` values length: ${values.length}`, 15 | ` names length: ${names.length}`, 16 | ` values: [${values 17 | .map((value) => formatValue(env, value)) 18 | .join(", ")}]`, 19 | ` names: [${names.join(", ")}]`, 20 | ].join("\n"), 21 | ) 22 | } 23 | 24 | for (const [index, name] of names.entries()) { 25 | const found = env.locals.get(name) 26 | if (found !== undefined) { 27 | throw new Error( 28 | [ 29 | `[defineLocals] The local name is already defined.`, 30 | ``, 31 | ` found value: ${formatValue(env, found)}`, 32 | ` name: ${name}`, 33 | ` index: ${index}`, 34 | ].join("\n"), 35 | ) 36 | } 37 | 38 | env.locals.set(name, values[index]) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/lang/apply/applyTypeCtor.ts: -------------------------------------------------------------------------------- 1 | import { checkTypeTermArgs } from "../check/checkTypeTermArgs.ts" 2 | import { type Env } from "../env/index.ts" 3 | import { type EvaluateOptions } from "../evaluate/index.ts" 4 | import { formatParameters } from "../parameter/index.ts" 5 | import { formatValues } from "../value/formatValues.ts" 6 | import { type TypeCtor, type Value } from "../value/index.ts" 7 | 8 | export function applyTypeCtor( 9 | env: Env, 10 | target: TypeCtor, 11 | args: Array, 12 | options: EvaluateOptions, 13 | ): Array { 14 | if (target.definition.input.length !== args.length) { 15 | throw new Error( 16 | [ 17 | `[applyTypeCtor] I expect the number of args`, 18 | ` to be the same as the length of input parameters.`, 19 | ``, 20 | ` args: [${formatValues(env, args)}]`, 21 | ` input parameters: { ${formatParameters( 22 | env, 23 | target.definition.input, 24 | )} }`, 25 | ].join("\n"), 26 | ) 27 | } 28 | 29 | checkTypeTermArgs(env, args) 30 | 31 | return [ 32 | { 33 | "@type": "Value", 34 | "@kind": "TypeTerm", 35 | mod: target.definition.mod, 36 | name: target.definition.name, 37 | args, 38 | }, 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /src/lang/check/checkNodeParameters.ts: -------------------------------------------------------------------------------- 1 | import { type Mod } from "../mod/index.ts" 2 | import { formatParameters, type Parameter } from "../parameter/index.ts" 3 | 4 | export function checkNodeParameters( 5 | mod: Mod, 6 | input: Array, 7 | output: Array, 8 | ): void { 9 | const parameters = [...input, ...output] 10 | for (const parameter of parameters) { 11 | if ( 12 | parameter.t["@kind"] !== "Symbol" && 13 | parameter.t["@kind"] !== "TypeTerm" 14 | ) { 15 | throw new Error( 16 | [ 17 | `[checkNodeParameters] I expect all parameters of a node to be Symbol or TypeTerm.`, 18 | ``, 19 | ` parameters: [${formatParameters(mod.env, parameters)}]`, 20 | ].join("\n"), 21 | ) 22 | } 23 | } 24 | 25 | const principalParameters = parameters.filter( 26 | (parameters) => parameters.isPrincipal, 27 | ) 28 | 29 | if (principalParameters.length !== 1) { 30 | throw new Error( 31 | [ 32 | `[checkNodeParameters] I expect a node to have one and only one principal port.`, 33 | ``, 34 | ` declared principal ports: [${formatParameters( 35 | mod.env, 36 | principalParameters, 37 | )}]`, 38 | ].join("\n"), 39 | ) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/lang/builtins/connect.ts: -------------------------------------------------------------------------------- 1 | import { checkHalfEdges } from "../check/checkHalfEdges.ts" 2 | import { connectHalfEdges } from "../connect/connectHalfEdges.ts" 3 | import { type PrimitiveApply } from "../definition/index.ts" 4 | import { formatValue } from "../value/formatValue.ts" 5 | 6 | export const apply: PrimitiveApply = (env, args, options) => { 7 | if (args.length !== 2) { 8 | throw new Error([`[@connect] I expect two arguments.`].join("\n")) 9 | } 10 | 11 | const [first, second] = args 12 | 13 | if (first["@kind"] !== "HalfEdge") { 14 | throw new Error( 15 | [ 16 | `[@connect] I expect the first arg to be a HalfEdge.`, 17 | ``, 18 | ` first: ${formatValue(env, first)}`, 19 | ` second: ${formatValue(env, first)}`, 20 | ].join("\n"), 21 | ) 22 | } 23 | 24 | if (second["@kind"] !== "HalfEdge") { 25 | throw new Error( 26 | [ 27 | `[@connect] I expect the second arg to be a HalfEdge.`, 28 | ``, 29 | ` first: ${formatValue(env, first)}`, 30 | ` second: ${formatValue(env, first)}`, 31 | ].join("\n"), 32 | ) 33 | } 34 | 35 | if (options.checking) { 36 | checkHalfEdges(env, first, second, options.checking) 37 | } 38 | 39 | connectHalfEdges(env.net, first, second) 40 | 41 | return [] 42 | } 43 | -------------------------------------------------------------------------------- /src/lang/value/formatValue.ts: -------------------------------------------------------------------------------- 1 | import { type Env } from "../env/index.ts" 2 | import { formatHalfEdge } from "../half-edge/formatHalfEdge.ts" 3 | import { formatNode } from "../node/formatNode.ts" 4 | import { type Value } from "./Value.ts" 5 | 6 | export function formatValue(env: Env, value: Value): string { 7 | switch (value["@kind"]) { 8 | case "HalfEdge": { 9 | return formatHalfEdge(env.net, value) 10 | } 11 | 12 | case "Node": { 13 | return `(${formatNode(env.net, value)})` 14 | } 15 | 16 | case "Function": { 17 | return `#Function(${value.definition.name})` 18 | } 19 | 20 | case "PrimitiveFunction": { 21 | return `#PrimitiveFunction(${value.definition.name})` 22 | } 23 | 24 | case "TypeCtor": { 25 | return `#TypeCtor(${value.definition.name})` 26 | } 27 | 28 | case "Type": { 29 | return "Type" 30 | } 31 | 32 | case "Symbol": { 33 | return `'${value.name}` 34 | } 35 | 36 | case "TypeTerm": { 37 | if (value.args.length === 0) { 38 | return `${value.name}` 39 | } else { 40 | const args = [...value.args] 41 | .reverse() 42 | .map((arg) => formatValue(env, arg)) 43 | .join(" ") 44 | 45 | return `${args} ${value.name}` 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /examples/tests/statement/rule-type-occur-check.error.i.err: -------------------------------------------------------------------------------- 1 | [unifyTypes] I find the right type variable occurs in the left type. 2 | 3 | right type variable: 'A₀ 4 | left type: 'A₂ List List 5 | 6 | [unifyTypes] I fail to unify types. 7 | 8 | left: 'A₂ List List 9 | right: 'A₀ 10 | 11 | [evaluate] I fail to evaluate an exp. 12 | 13 | exp: cons(append(tail, rest), head, result) 14 | 15 | 24 |} 16 | 25 | 17 | 26 |rule append(target!, rest, result) cons(head, tail, value!) { 18 | 27 | cons(append(tail, rest), head, result) 19 | 28 | 20 | 29 | // The correct definition is: 21 | 22 | [evaluateBlockStmt] I fail to evaluate a block stmt. 23 | 24 | block stmt: cons(append(tail, rest), head, result) 25 | 26 | 24 |} 27 | 25 | 28 | 26 |rule append(target!, rest, result) cons(head, tail, value!) { 29 | 27 | cons(append(tail, rest), head, result) 30 | 28 | 31 | 29 | // The correct definition is: 32 | 33 | [execute] I fail to execute a statement. 34 | 35 | 22 |rule append(target!, rest, result) null(value!) { 36 | 23 | @connect(rest, result) 37 | 24 |} 38 | 25 | 39 | 26 |rule append(target!, rest, result) cons(head, tail, value!) { 40 | 27 | cons(append(tail, rest), head, result) 41 | 28 | 42 | 29 | // The correct definition is: 43 | 30 | // cons(head, append(tail, rest), result) 44 | 31 |} 45 | 32 | 46 | -------------------------------------------------------------------------------- /src/loader/Loader.ts: -------------------------------------------------------------------------------- 1 | import { Fetcher } from "../fetcher/index.ts" 2 | import { execute } from "../lang/execute/index.ts" 3 | import { createMod } from "../lang/mod/createMod.ts" 4 | import { type Mod } from "../lang/mod/index.ts" 5 | import { parseStmts } from "../lang/syntax/index.ts" 6 | 7 | export class Loader { 8 | loaded: Map = new Map() 9 | loading: Set = new Set() 10 | fetcher: Fetcher 11 | onOutput = (output: string) => { 12 | console.log(output) 13 | } 14 | 15 | constructor(options: { fetcher: Fetcher }) { 16 | this.fetcher = options.fetcher 17 | } 18 | 19 | async load(url: URL, options?: { text?: string }): Promise { 20 | const found = this.loaded.get(url.href) 21 | if (found !== undefined) return found 22 | 23 | const text = 24 | options?.text === undefined 25 | ? await this.fetcher.fetchText(url) 26 | : options.text 27 | 28 | const stmts = parseStmts(text) 29 | const mod = createMod({ 30 | loader: this, 31 | url, 32 | text, 33 | stmts, 34 | }) 35 | 36 | this.loading.add(url.href) 37 | 38 | for (const stmt of stmts) { 39 | await execute(mod, stmt) 40 | } 41 | 42 | this.loading.delete(url.href) 43 | 44 | this.loaded.set(url.href, mod) 45 | 46 | return mod 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/command-line/commands/Default.ts: -------------------------------------------------------------------------------- 1 | import { Command, CommandRunner } from "@xieyuheng/command-line" 2 | import ty from "@xieyuheng/ty" 3 | import { version } from "../../version.ts" 4 | import * as Commands from "../commands/index.ts" 5 | 6 | type Args = { path?: string } 7 | type Opts = { help?: boolean; version?: boolean } 8 | 9 | export class Default extends Command { 10 | name = "default" 11 | 12 | description = "Start a REPL or run a file" 13 | 14 | args = { path: ty.optional(ty.string()) } 15 | opts = { 16 | help: ty.optional(ty.boolean()), 17 | version: ty.optional(ty.boolean()), 18 | } 19 | alias = { help: ["h"], version: ["v"] } 20 | 21 | async execute(argv: Args & Opts, runner: CommandRunner): Promise { 22 | if (argv["help"]) { 23 | const command = new Commands.CommonHelp() 24 | await command.execute({}, runner) 25 | return 26 | } 27 | 28 | if (argv["version"]) { 29 | console.log(version) 30 | return 31 | } 32 | 33 | const path = argv["path"] 34 | 35 | if (path === undefined) { 36 | const dir = process.cwd() 37 | const command = new Commands.Repl() 38 | await command.execute({ dir }) 39 | } else { 40 | const command = new Commands.Run() 41 | await command.execute({ path }) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/lang/import/importOne.ts: -------------------------------------------------------------------------------- 1 | import { type Mod } from "../mod/index.ts" 2 | import { type ImportBinding } from "./ImportBinding.ts" 3 | import { importNodeRules } from "./importNodeRules.ts" 4 | 5 | export function importOne( 6 | mod: Mod, 7 | targetMod: Mod, 8 | binding: ImportBinding, 9 | ): void { 10 | const { name } = binding 11 | 12 | const found = mod.definitions.get(name) 13 | if (found !== undefined) { 14 | throw new Error( 15 | [ 16 | `[importOne] I can not import already defined name.`, 17 | ``, 18 | ` name: ${name}`, 19 | ].join("\n"), 20 | ) 21 | } 22 | 23 | const definition = targetMod.definitions.get(name) 24 | if (definition === undefined) { 25 | const fetcher = mod.loader.fetcher 26 | 27 | throw new Error( 28 | [ 29 | `[importOne] I can not import undefined name.`, 30 | ``, 31 | ` name: ${name}`, 32 | ` current module url: ${fetcher.formatURL(mod.url)}`, 33 | ` imported module url: ${fetcher.formatURL(targetMod.url)}`, 34 | ].join("\n"), 35 | ) 36 | } 37 | 38 | mod.definitions.set(name, definition) 39 | 40 | if (definition["@kind"] === "NodeDefinition") { 41 | importNodeRules(mod, targetMod, { 42 | modId: definition.mod.url.href, 43 | name: definition.name, 44 | }) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/lang/present/presentRule.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest" 2 | import { Fetcher } from "../../fetcher/index.ts" 3 | import { Loader } from "../../loader/index.ts" 4 | import { formatNet } from "../net/formatNet.ts" 5 | import { presentRule } from "./presentRule.ts" 6 | 7 | test("presentRule", async () => { 8 | const text = ` 9 | 10 | type Nat 11 | node zero(-- value!: Nat) 12 | node add1(prev: Nat -- value!: Nat) 13 | 14 | node add(target!: Nat, addend: Nat -- result: Nat) 15 | 16 | rule add(target!, addend, result) zero(value!) { 17 | @connect(addend, result) 18 | } 19 | 20 | rule add(target!, addend, result) add1(prev, value!) { 21 | add1(add(prev, addend), result) 22 | } 23 | 24 | ` 25 | 26 | const fetcher = new Fetcher() 27 | const loader = new Loader({ fetcher }) 28 | const url = new URL("test://presentRule") 29 | const mod = await loader.load(url, { text }) 30 | const [initial, final] = presentRule(mod, "add1 add") 31 | 32 | expect(formatNet(initial)).toMatchInlineSnapshot(` 33 | "(add1₂)-prev covering-(@inputPortCap₃) 34 | (add1₂)-value!target-(add₃) 35 | (add₃)-addend covering-(@inputPortCap₄) 36 | (add₃)-result covering-(@ouputPortCap₂)" 37 | `) 38 | 39 | expect(formatNet(final)).toMatchInlineSnapshot(` 40 | "(@inputPortCap₃)-covering target-(add₄) 41 | (@inputPortCap₄)-covering addend-(add₄) 42 | (@ouputPortCap₂)-covering value-(add1₃) 43 | (add1₃)-prev result-(add₄)" 44 | `) 45 | }) 46 | -------------------------------------------------------------------------------- /examples/datatypes/DiffList.test.i.out: -------------------------------------------------------------------------------- 1 | net result-(diffAppend₂) { 2 | (diffAppend₂)-target!value-(diff₅) 3 | (diffAppend₂)-rest value-(diff₆) 4 | (diff₅)-front value-(cons₅) 5 | (diff₅)-back tail-(cons₅) 6 | (cons₅)-head value-(sole₃) 7 | (diff₆)-front value-(cons₆) 8 | (diff₆)-back tail-(cons₇) 9 | (cons₆)-head value-(sole₄) 10 | (cons₆)-tail value-(cons₇) 11 | (cons₇)-head value-(sole₅) 12 | } 13 | net !value-(diff₇) { 14 | (diff₇)-front value-(cons₅) 15 | (diff₇)-back tail-(cons₇) 16 | (cons₅)-head value-(sole₃) 17 | (cons₅)-tail value-(cons₆) 18 | (cons₆)-head value-(sole₄) 19 | (cons₆)-tail value-(cons₇) 20 | (cons₇)-head value-(sole₅) 21 | } 22 | net result-(diffAppend₄) { 23 | (diffAppend₄)-target!value-(diff₁₀) 24 | (diffAppend₄)-rest value-(diff₁₁) 25 | (diff₁₀)-front value-(cons₁₂) 26 | (diff₁₀)-back tail-(cons₁₃) 27 | (cons₁₂)-head value-(sole₁₀) 28 | (cons₁₂)-tail value-(cons₁₃) 29 | (cons₁₃)-head value-(sole₁₁) 30 | (diff₁₁)-front value-(cons₁₄) 31 | (diff₁₁)-back tail-(cons₁₅) 32 | (cons₁₄)-head value-(sole₁₂) 33 | (cons₁₄)-tail value-(cons₁₅) 34 | (cons₁₅)-head value-(sole₁₃) 35 | } 36 | net !value-(diff₁₂) { 37 | (diff₁₂)-front value-(cons₁₂) 38 | (diff₁₂)-back tail-(cons₁₅) 39 | (cons₁₂)-head value-(sole₁₀) 40 | (cons₁₂)-tail value-(cons₁₃) 41 | (cons₁₃)-head value-(sole₁₁) 42 | (cons₁₃)-tail value-(cons₁₄) 43 | (cons₁₄)-head value-(sole₁₂) 44 | (cons₁₄)-tail value-(cons₁₅) 45 | (cons₁₅)-head value-(sole₁₃) 46 | } 47 | -------------------------------------------------------------------------------- /src/lang/builtins/inspect.ts: -------------------------------------------------------------------------------- 1 | import { indent } from "../../utils/indent.ts" 2 | import { type PrimitiveApply } from "../definition/index.ts" 3 | import { findConnectedComponent } from "../net/findConnectedComponent.ts" 4 | import { findHalfEdgeEntryOrFail } from "../net/findHalfEdgeEntryOrFail.ts" 5 | import { findHalfEdgePortOrFail } from "../net/findHalfEdgePortOrFail.ts" 6 | import { formatNet } from "../net/formatNet.ts" 7 | import { formatPort } from "../port/formatPort.ts" 8 | import { formatValue } from "../value/formatValue.ts" 9 | 10 | export const apply: PrimitiveApply = (env, args) => { 11 | if (args.length !== 1) { 12 | throw new Error([`[@inspect] I expect one argument.`].join("\n")) 13 | } 14 | 15 | const [value] = args 16 | 17 | if (value["@kind"] === "HalfEdge") { 18 | const valueHalfEdgeEntry = findHalfEdgeEntryOrFail(env.net, value) 19 | const otherPort = findHalfEdgePortOrFail( 20 | env.net, 21 | valueHalfEdgeEntry.otherHalfEdge, 22 | ) 23 | const connectedcomponent = findConnectedComponent(env.net, otherPort.node) 24 | const netText = formatNet(connectedcomponent) 25 | if (netText.length === 0) { 26 | env.mod.loader.onOutput(`net ${formatPort(env.net, otherPort)} {}`) 27 | } else { 28 | env.mod.loader.onOutput( 29 | `net ${formatPort(env.net, otherPort)} {\n${indent(netText)}\n}`, 30 | ) 31 | } 32 | } else { 33 | env.mod.loader.onOutput(formatValue(env, value)) 34 | } 35 | 36 | return [value] 37 | } 38 | -------------------------------------------------------------------------------- /src/lang/evaluate/evaluateDefinition.ts: -------------------------------------------------------------------------------- 1 | import { type Definition } from "../definition/index.ts" 2 | import { type Env } from "../env/index.ts" 3 | import { addNodeFromDefinition } from "../node/addNodeFromDefinition.ts" 4 | import { type Value } from "../value/index.ts" 5 | import { type EvaluateOptions } from "./evaluate.ts" 6 | 7 | export function evaluateDefinition( 8 | env: Env, 9 | definition: Definition, 10 | options: EvaluateOptions, 11 | ): Value { 12 | switch (definition["@kind"]) { 13 | case "NodeDefinition": { 14 | return addNodeFromDefinition(env.net, definition) 15 | } 16 | 17 | case "TypeDefinition": { 18 | if (definition.input.length === 0) { 19 | return { 20 | "@type": "Value", 21 | "@kind": "TypeTerm", 22 | mod: definition.mod, 23 | name: definition.name, 24 | args: [], 25 | } 26 | } 27 | 28 | return { 29 | "@type": "Value", 30 | "@kind": "TypeCtor", 31 | definition, 32 | } 33 | } 34 | 35 | case "FunctionDefinition": { 36 | return { 37 | "@type": "Value", 38 | "@kind": "Function", 39 | definition, 40 | } 41 | } 42 | 43 | case "PrimitiveFunctionDefinition": { 44 | return { 45 | "@type": "Value", 46 | "@kind": "PrimitiveFunction", 47 | definition, 48 | } 49 | } 50 | 51 | case "ValueDefinition": { 52 | return definition.value 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/lang/connect/connectPorts.ts: -------------------------------------------------------------------------------- 1 | import { checkPortSigns } from "../check/checkPortSigns.ts" 2 | import { addEdge } from "../net/addEdge.ts" 3 | import { findPortEntry } from "../net/findPortEntry.ts" 4 | import { type Net } from "../net/index.ts" 5 | import { formatPort } from "../port/formatPort.ts" 6 | import { type Port } from "../port/index.ts" 7 | import { connectHalfEdgeWithPort } from "./connectHalfEdgeWithPort.ts" 8 | 9 | export function connectPorts(net: Net, first: Port, second: Port): void { 10 | const firstPortEntry = findPortEntry(net, first) 11 | if (firstPortEntry?.connection !== undefined) { 12 | console.trace() 13 | throw new Error( 14 | [ 15 | `[connectPorts] The first port is already connected.`, 16 | ``, 17 | ` first port: ${formatPort(net, first)}`, 18 | ` second port: ${formatPort(net, second)}`, 19 | ].join("\n"), 20 | ) 21 | } 22 | 23 | const secondPortEntry = findPortEntry(net, second) 24 | if (secondPortEntry?.connection !== undefined) { 25 | console.trace() 26 | throw new Error( 27 | [ 28 | `[connectPorts] The second port is already connected.`, 29 | ``, 30 | ` first port: ${formatPort(net, first)}`, 31 | ` second port: ${formatPort(net, second)}`, 32 | ].join("\n"), 33 | ) 34 | } 35 | 36 | checkPortSigns(net, first, second) 37 | 38 | const edge = addEdge(net) 39 | 40 | connectHalfEdgeWithPort(net, edge.first, first) 41 | connectHalfEdgeWithPort(net, edge.second, second) 42 | } 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@xieyuheng/inet-js", 3 | "version": "0.4.0", 4 | "repository": "github:xieyuheng/inet-js", 5 | "license": "GPL-3.0-or-later", 6 | "type": "module", 7 | "main": "./lib/index.js", 8 | "files": [ 9 | "src", 10 | "lib" 11 | ], 12 | "bin": { 13 | "inet-js": "bin/inet-js" 14 | }, 15 | "scripts": { 16 | "build": "tsc", 17 | "build:watch": "tsc --watch", 18 | "test:ts": "vitest --dir src --run", 19 | "test:inet-parse": "test-runner test 'node ./bin/inet-js parse --no-color' 'examples/**/*.i'", 20 | "test:inet-format": "test-runner test 'node ./bin/inet-js format --no-color' 'examples/**/*.i'", 21 | "test:inet-run": "test-runner snapshot 'node ./bin/inet-js run --no-color' 'examples/**/*.i' --exclude 'examples/**/*.error.i'", 22 | "test:inet-run-error": "test-runner snapshot-error 'node ./bin/inet-js run --no-color' 'examples/**/*.error.i'", 23 | "test": "npm run test:ts && npm run test:inet-run && npm run test:inet-run-error", 24 | "format": "prettier src docs --write" 25 | }, 26 | "dependencies": { 27 | "@xieyuheng/framework": "^0.2.0", 28 | "@xieyuheng/partech": "^0.2.5", 29 | "@xieyuheng/command-line": "^0.1.3", 30 | "@xieyuheng/ty": "^0.3.1", 31 | "picocolors": "^1.1.1" 32 | }, 33 | "devDependencies": { 34 | "@types/node": "^22.15.27", 35 | "@xieyuheng/test-runner": "^0.2.10", 36 | "prettier": "^3.5.3", 37 | "prettier-plugin-organize-imports": "^4.1.0", 38 | "typescript": "^5.8.3", 39 | "vitest": "^3.1.4" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/lang/present/presentFunction.ts: -------------------------------------------------------------------------------- 1 | import { capType } from "../cap/index.ts" 2 | import { connectHalfEdgeWithPort } from "../connect/connectHalfEdgeWithPort.ts" 3 | import { createEnv } from "../env/createEnv.ts" 4 | import { defineLocals } from "../env/defineLocals.ts" 5 | import { type Env } from "../env/index.ts" 6 | import { evaluateBlock } from "../evaluate/evaluateBlock.ts" 7 | import { findDefinitionOrFail, type Mod } from "../mod/index.ts" 8 | import { addEdge } from "../net/addEdge.ts" 9 | 10 | export function presentFunction(mod: Mod, name: string): Env { 11 | const definition = findDefinitionOrFail(mod, name) 12 | 13 | if (definition["@kind"] !== "FunctionDefinition") { 14 | throw new Error( 15 | [ 16 | `[presentFunction] I expect to find a FunctionDefinition from the name.`, 17 | ``, 18 | ` name: ${name}`, 19 | ` definition kind: ${definition["@kind"]}`, 20 | ].join("\n"), 21 | ) 22 | } 23 | 24 | const env = createEnv(mod) 25 | 26 | const { input, body } = definition 27 | 28 | const capOutputPorts = input 29 | .reverse() 30 | .map((parameter) => capType(mod, env.net, parameter.t)) 31 | 32 | for (const [index, port] of capOutputPorts.entries()) { 33 | const parameter = input[index] 34 | const edge = addEdge(env.net) 35 | connectHalfEdgeWithPort(env.net, edge.first, port) 36 | defineLocals(env, [parameter.name], [edge.second]) 37 | } 38 | 39 | const values = evaluateBlock(mod, env, body, {}) 40 | 41 | env.stack.push(...values) 42 | 43 | return env 44 | } 45 | -------------------------------------------------------------------------------- /src/lang/connect/connectHalfEdgeWithPort.ts: -------------------------------------------------------------------------------- 1 | import { checkPortSigns } from "../check/checkPortSigns.ts" 2 | import { edgeEqual } from "../edge/index.ts" 3 | import { type HalfEdge } from "../half-edge/index.ts" 4 | import { findHalfEdgeEntryOrFail } from "../net/findHalfEdgeEntryOrFail.ts" 5 | import { findHalfEdgePort } from "../net/findHalfEdgePort.ts" 6 | import { findPortRecordOrFail } from "../net/findPortRecordOrFail.ts" 7 | import { type Net } from "../net/index.ts" 8 | import { type Port } from "../port/index.ts" 9 | 10 | export function connectHalfEdgeWithPort( 11 | net: Net, 12 | halfEdge: HalfEdge, 13 | port: Port, 14 | ): void { 15 | const portRecord = findPortRecordOrFail(net, port.node) 16 | portRecord[port.name].connection = { halfEdge } 17 | 18 | const halfEdgeEntry = findHalfEdgeEntryOrFail(net, halfEdge) 19 | if (halfEdgeEntry.port !== undefined) { 20 | throw new Error( 21 | `[connectPortWithHalfEdge] halfEdgeEntry is already connected`, 22 | ) 23 | } 24 | 25 | halfEdgeEntry.port = port 26 | 27 | const otherHalfEdge = halfEdgeEntry.otherHalfEdge 28 | const otherPort = findHalfEdgePort(net, otherHalfEdge) 29 | 30 | if (otherPort !== undefined) { 31 | checkPortSigns(net, port, otherPort) 32 | 33 | if (port.isPrincipal && otherPort.isPrincipal) { 34 | const edge = { 35 | first: halfEdge, 36 | second: otherHalfEdge, 37 | } 38 | 39 | if (!net.activeEdges.find((activeEdge) => edgeEqual(activeEdge, edge))) { 40 | net.activeEdges.push(edge) 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/lang/net/Net.ts: -------------------------------------------------------------------------------- 1 | import { type Edge } from "../edge/index.ts" 2 | import { type HalfEdge } from "../half-edge/index.ts" 3 | import { type Port } from "../port/index.ts" 4 | import { type Sign } from "../sign/index.ts" 5 | import { type Value } from "../value/index.ts" 6 | 7 | /* 8 | 9 | A net is an undirected graph where each edge 10 | must be connected through ports. 11 | 12 | We implement a net like adjacency list, 13 | but the list of nodes is replaced by a record of ports. 14 | 15 | */ 16 | 17 | export type Net = { 18 | activeEdges: Array 19 | nodeEntries: Map 20 | halfEdgeEntries: Map 21 | } 22 | 23 | export type HalfEdgeEntry = { 24 | id: string 25 | port?: Port 26 | otherHalfEdge: HalfEdge 27 | } 28 | 29 | export type NodeEntry = { 30 | modId: string 31 | id: string 32 | name: string 33 | ports: PortRecord 34 | asTypeCap?: {} 35 | asPortCap?: { 36 | nodeName: string 37 | portName: string 38 | } 39 | } 40 | 41 | export type PortRecord = Record 42 | 43 | export type PortEntry = { 44 | name: string 45 | sign: Sign 46 | t: Value 47 | isPrincipal: boolean 48 | connection?: Connection 49 | } 50 | 51 | // For a shared-memory multithread implementation. 52 | // - We must define `EdgeEntry`. 53 | // - `Net` must have `edgeEntries: Map`. 54 | // - Port must connect to edge instead of port, 55 | // so that parallel updates of the net will not 56 | // interfere with each other. 57 | 58 | export type Connection = { 59 | halfEdge: HalfEdge 60 | } 61 | -------------------------------------------------------------------------------- /src/lang/mod/defineRule.ts: -------------------------------------------------------------------------------- 1 | import { checkRuleIsAboutOwnNode } from "../check/checkRuleIsAboutOwnNode.ts" 2 | import { checkRuleNodeOrder } from "../check/checkRuleNodeOrder.ts" 3 | import { type BlockStmt } from "../exp/BlockStmt.ts" 4 | import { nodeKeyWithoutId } from "../node/nodeKeyWithoutId.ts" 5 | import { type RuleTarget } from "../rule/index.ts" 6 | import { type Mod } from "./Mod.ts" 7 | import { findDefinitionOrFail } from "./findDefinitionOrFail.ts" 8 | 9 | export function defineRule( 10 | mod: Mod, 11 | first: RuleTarget, 12 | second: RuleTarget, 13 | body: Array, 14 | ): void { 15 | checkRuleIsAboutOwnNode(mod, first.name, second.name) 16 | checkRuleNodeOrder(mod, first.name, second.name) 17 | 18 | const firstDefinition = findDefinitionOrFail(mod, first.name) 19 | const secondDefinition = findDefinitionOrFail(mod, second.name) 20 | 21 | const firstKey = nodeKeyWithoutId({ 22 | modId: firstDefinition.mod.url.href, 23 | name: firstDefinition.name, 24 | }) 25 | 26 | const secondKey = nodeKeyWithoutId({ 27 | modId: secondDefinition.mod.url.href, 28 | name: secondDefinition.name, 29 | }) 30 | 31 | const key = `${firstKey} ${secondKey}` 32 | const name = `${first.name} ${second.name}` 33 | 34 | mod.ruleEntries.set(key, { 35 | name, 36 | mod, 37 | first: { 38 | modId: firstDefinition.mod.url.href, 39 | name: first.name, 40 | parameters: first.parameters, 41 | }, 42 | second: { 43 | modId: secondDefinition.mod.url.href, 44 | name: second.name, 45 | parameters: second.parameters, 46 | }, 47 | body, 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /src/lang/check/checkRule.ts: -------------------------------------------------------------------------------- 1 | import { capNodeNonPrinciplePorts } from "../cap/index.ts" 2 | import { createChecking } from "../checking/createChecking.ts" 3 | import { createEnv } from "../env/createEnv.ts" 4 | import { evaluateBlock } from "../evaluate/evaluateBlock.ts" 5 | import { type BlockStmt } from "../exp/BlockStmt.ts" 6 | import { refreshNode } from "../freshen/refreshNode.ts" 7 | import { findDefinitionOrFail } from "../mod/findDefinitionOrFail.ts" 8 | import { type Mod } from "../mod/index.ts" 9 | import { addNodeFromDefinition } from "../node/addNodeFromDefinition.ts" 10 | import { exposeRuleTargets } from "../rule/exposeRuleTargets.ts" 11 | import { type RuleTarget } from "../rule/index.ts" 12 | import { checkAllLocalsAreUsed } from "./checkAllLocalsAreUsed.ts" 13 | 14 | export function checkRule( 15 | mod: Mod, 16 | first: RuleTarget, 17 | second: RuleTarget, 18 | body: Array, 19 | ): void { 20 | const checking = createChecking() 21 | const env = createEnv(mod) 22 | 23 | const firstNode = addNodeFromDefinition( 24 | env.net, 25 | findDefinitionOrFail(mod, first.name), 26 | ) 27 | 28 | refreshNode(env.net, checking.typeVarCounters, firstNode) 29 | 30 | const secondNode = addNodeFromDefinition( 31 | env.net, 32 | findDefinitionOrFail(mod, second.name), 33 | ) 34 | 35 | refreshNode(env.net, checking.typeVarCounters, secondNode) 36 | 37 | capNodeNonPrinciplePorts(mod, env.net, firstNode) 38 | capNodeNonPrinciplePorts(mod, env.net, secondNode) 39 | 40 | exposeRuleTargets(env, { first, second }, [firstNode, secondNode]) 41 | evaluateBlock(mod, env, body, { checking }) 42 | 43 | checkAllLocalsAreUsed(env.locals) 44 | } 45 | -------------------------------------------------------------------------------- /src/lang/connect/connectHalfEdges.ts: -------------------------------------------------------------------------------- 1 | import { checkPortSigns } from "../check/checkPortSigns.ts" 2 | import { type HalfEdge } from "../half-edge/index.ts" 3 | import { deleteHalfEdgeEntry } from "../net/deleteHalfEdgeEntry.ts" 4 | import { findHalfEdgeEntryOrFail } from "../net/findHalfEdgeEntryOrFail.ts" 5 | import { type Net } from "../net/index.ts" 6 | 7 | export function connectHalfEdges( 8 | net: Net, 9 | firstHalfEdge: HalfEdge, 10 | secondHalfEdge: HalfEdge, 11 | ): void { 12 | const firstHalfEdgeEntry = findHalfEdgeEntryOrFail(net, firstHalfEdge) 13 | const secondHalfEdgeEntry = findHalfEdgeEntryOrFail(net, secondHalfEdge) 14 | 15 | const firstOtherHalfEdge = firstHalfEdgeEntry.otherHalfEdge 16 | const secondOtherHalfEdge = secondHalfEdgeEntry.otherHalfEdge 17 | 18 | const firstOtherHalfEdgeEntry = findHalfEdgeEntryOrFail( 19 | net, 20 | firstOtherHalfEdge, 21 | ) 22 | const secondOtherHalfEdgeEntry = findHalfEdgeEntryOrFail( 23 | net, 24 | secondOtherHalfEdge, 25 | ) 26 | 27 | firstOtherHalfEdgeEntry.otherHalfEdge = secondOtherHalfEdge 28 | secondOtherHalfEdgeEntry.otherHalfEdge = firstOtherHalfEdge 29 | 30 | deleteHalfEdgeEntry(net, firstHalfEdge) 31 | deleteHalfEdgeEntry(net, secondHalfEdge) 32 | 33 | const firstPort = firstOtherHalfEdgeEntry.port 34 | const secondPort = secondOtherHalfEdgeEntry.port 35 | 36 | if (firstPort !== undefined && secondPort !== undefined) { 37 | checkPortSigns(net, firstPort, secondPort) 38 | if (firstPort.isPrincipal && secondPort.isPrincipal) { 39 | net.activeEdges.push({ 40 | first: firstOtherHalfEdge, 41 | second: secondOtherHalfEdge, 42 | }) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/lang/definition/Definition.ts: -------------------------------------------------------------------------------- 1 | import { type Env } from "../env/index.ts" 2 | import { type EvaluateOptions } from "../evaluate/index.ts" 3 | import { type BlockStmt } from "../exp/BlockStmt.ts" 4 | import { type Mod } from "../mod/index.ts" 5 | import { type Parameter } from "../parameter/index.ts" 6 | import { type Span } from "../span/index.ts" 7 | import { type Value } from "../value/index.ts" 8 | 9 | export type Definition = 10 | | NodeDefinition 11 | | TypeDefinition 12 | | FunctionDefinition 13 | | PrimitiveFunctionDefinition 14 | | ValueDefinition 15 | 16 | export type NodeDefinition = { 17 | "@type": "Definition" 18 | "@kind": "NodeDefinition" 19 | mod: Mod 20 | name: string 21 | input: Array 22 | output: Array 23 | span: Span 24 | } 25 | 26 | export type TypeDefinition = { 27 | "@type": "Definition" 28 | "@kind": "TypeDefinition" 29 | mod: Mod 30 | name: string 31 | input: Array 32 | span: Span 33 | } 34 | 35 | export type FunctionDefinition = { 36 | "@type": "Definition" 37 | "@kind": "FunctionDefinition" 38 | mod: Mod 39 | input: Array 40 | retType: Value 41 | body: Array 42 | name: string 43 | span: Span 44 | } 45 | 46 | export type PrimitiveApply = ( 47 | env: Env, 48 | args: Array, 49 | options: EvaluateOptions, 50 | ) => Array 51 | 52 | export type PrimitiveFunctionDefinition = { 53 | "@type": "Definition" 54 | "@kind": "PrimitiveFunctionDefinition" 55 | mod: Mod 56 | name: string 57 | apply: PrimitiveApply 58 | } 59 | 60 | export type ValueDefinition = { 61 | "@type": "Definition" 62 | "@kind": "ValueDefinition" 63 | mod: Mod 64 | name: string 65 | value: Value 66 | } 67 | -------------------------------------------------------------------------------- /src/lang/present/presentFunction.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest" 2 | import { Fetcher } from "../../fetcher/index.ts" 3 | import { Loader } from "../../loader/index.ts" 4 | import { formatEnv } from "../env/index.ts" 5 | import { presentFunction } from "./presentFunction.ts" 6 | 7 | test("presentFunction", async () => { 8 | const text = ` 9 | 10 | type Nat 11 | node zero(-- value!: Nat) 12 | node add1(prev: Nat -- value!: Nat) 13 | 14 | node add(target!: Nat, addend: Nat -- result: Nat) 15 | 16 | function one(): Nat { 17 | return add1(zero()) 18 | } 19 | 20 | function two(): Nat { 21 | return add(one(), one()) 22 | } 23 | 24 | function addadd(x: Nat, y: Nat, z: Nat): Nat { 25 | return add(add(x, y), z) 26 | } 27 | 28 | ` 29 | 30 | const fetcher = new Fetcher() 31 | const loader = new Loader({ fetcher }) 32 | const url = new URL("test://presentFunction") 33 | const mod = await loader.load(url, { text }) 34 | 35 | expect(formatEnv(presentFunction(mod, "two"))).toMatchInlineSnapshot(` 36 | "env { 37 | net { 38 | (add₃)-target!value-(add1₃) 39 | (add₃)-addend value-(add1₄) 40 | (add1₃)-prev value-(zero₃) 41 | (add1₄)-prev value-(zero₄) 42 | } 43 | locals {} 44 | stack [#HalfEdge(#unconnected, result-(add₃))] 45 | }" 46 | `) 47 | 48 | expect(formatEnv(presentFunction(mod, "addadd"))).toMatchInlineSnapshot(` 49 | "env { 50 | net { 51 | (@typeCap₃)-covering addend-(add₄) 52 | (@typeCap₄)-covering addend-(add₅) 53 | (@typeCap₅)-covering target-(add₅) 54 | (add₄)-target result-(add₅) 55 | } 56 | locals {} 57 | stack [#HalfEdge(#unconnected, result-(add₄))] 58 | }" 59 | `) 60 | }) 61 | -------------------------------------------------------------------------------- /src/lang/check/checkRuleNodeOrder.ts: -------------------------------------------------------------------------------- 1 | import { findDefinitionOrFail } from "../mod/findDefinitionOrFail.ts" 2 | import { type Mod } from "../mod/index.ts" 3 | 4 | export function checkRuleNodeOrder( 5 | mod: Mod, 6 | firstName: string, 7 | secondName: string, 8 | ): void { 9 | const first = findDefinitionOrFail(mod, firstName) 10 | 11 | if (first["@kind"] !== "NodeDefinition") { 12 | throw new Error( 13 | [ 14 | `[checkRuleNodeOrder] I expect the first name to be a NodeDefinition.`, 15 | ``, 16 | ` first node: ${firstName}`, 17 | ` second node: ${secondName}`, 18 | ].join("\n"), 19 | ) 20 | } 21 | 22 | if (!first.input.some((port) => port.isPrincipal)) { 23 | throw new Error( 24 | [ 25 | `[checkRuleNodeOrder] The first node of a rule must have its principal port in the input.`, 26 | ``, 27 | ` first node: ${firstName}`, 28 | ` second node: ${secondName}`, 29 | ].join("\n"), 30 | ) 31 | } 32 | 33 | const second = findDefinitionOrFail(mod, secondName) 34 | 35 | if (second["@kind"] !== "NodeDefinition") { 36 | throw new Error( 37 | [ 38 | `[checkRuleNodeOrder] I expect the first name to be a NodeDefinition.`, 39 | ``, 40 | ` first node: ${firstName}`, 41 | ` second node: ${secondName}`, 42 | ].join("\n"), 43 | ) 44 | } 45 | 46 | if (!second.output.some((port) => port.isPrincipal)) { 47 | throw new Error( 48 | [ 49 | `[checkRuleNodeOrder] The second node of a rule must have its principal port in the output.`, 50 | ``, 51 | ` first node: ${firstName}`, 52 | ` second node: ${secondName}`, 53 | ].join("\n"), 54 | ) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/lang/net/allEdges.ts: -------------------------------------------------------------------------------- 1 | import { type Edge } from "../edge/index.ts" 2 | import { nodeKey } from "../node/nodeKey.ts" 3 | import { type Net } from "./Net.ts" 4 | import { createNodeFromNodeEntry } from "./createNodeFromNodeEntry.ts" 5 | import { findHalfEdgeEntryOrFail } from "./findHalfEdgeEntryOrFail.ts" 6 | 7 | export function allEdges(net: Net): Array { 8 | const edges: Array = [] 9 | const occurred: Set = new Set() 10 | 11 | for (const nodeEntry of net.nodeEntries.values()) { 12 | const node = createNodeFromNodeEntry(nodeEntry) 13 | 14 | for (const portEntry of Object.values(nodeEntry.ports)) { 15 | if (portEntry.connection) { 16 | const firstHalfEdge = portEntry.connection.halfEdge 17 | const firstHalfEdgeEntry = findHalfEdgeEntryOrFail(net, firstHalfEdge) 18 | 19 | const secondHalfEdge = firstHalfEdgeEntry.otherHalfEdge 20 | const secondHalfEdgeEntry = findHalfEdgeEntryOrFail(net, secondHalfEdge) 21 | 22 | const firstPort = firstHalfEdgeEntry.port 23 | const secondPort = secondHalfEdgeEntry.port 24 | 25 | if (firstPort !== undefined && secondPort !== undefined) { 26 | const firstOccur = `${nodeKey(node)}-${portEntry.name}` 27 | const secondOccur = `${nodeKey(secondPort.node)}-${secondPort.name}` 28 | 29 | if ( 30 | !occurred.has(firstOccur + secondOccur) && 31 | !occurred.has(secondOccur + firstOccur) 32 | ) { 33 | occurred.add(firstOccur + secondOccur) 34 | occurred.add(secondOccur + firstOccur) 35 | edges.push({ first: firstHalfEdge, second: secondHalfEdge }) 36 | } 37 | } 38 | } 39 | } 40 | } 41 | 42 | return edges 43 | } 44 | -------------------------------------------------------------------------------- /src/fetcher/Fetcher.ts: -------------------------------------------------------------------------------- 1 | export type FetcherHandler = { 2 | fetchText: (url: URL) => string | Promise 3 | formatURL?: (url: URL) => string 4 | } 5 | 6 | export class Fetcher { 7 | handlers: Record = {} 8 | 9 | constructor() { 10 | this.register("http", { 11 | fetchText: async (url) => await (await fetch(url)).text(), 12 | }) 13 | 14 | this.register("https", { 15 | fetchText: async (url) => await (await fetch(url)).text(), 16 | }) 17 | } 18 | 19 | findHandler(url: URL): FetcherHandler | undefined { 20 | const scheme = url.protocol.slice(0, url.protocol.length - 1) 21 | return this.handlers[scheme] 22 | } 23 | 24 | formatURL(url: URL): string { 25 | const handler = this.findHandler(url) 26 | if (handler === undefined) { 27 | throw new Error( 28 | [ 29 | `[Fetcher.formatURL] I meet unknown protocol.`, 30 | ``, 31 | ` protocol: ${url.protocol}`, 32 | ` url: ${url.href}`, 33 | ].join("\n"), 34 | ) 35 | } 36 | 37 | if (handler.formatURL) { 38 | return handler.formatURL(url) 39 | } else { 40 | return url.href 41 | } 42 | } 43 | 44 | async fetchText(url: URL): Promise { 45 | const handler = this.findHandler(url) 46 | if (handler === undefined) { 47 | throw new Error( 48 | [ 49 | `[Fetcher.fetchText] I meet unknown protocol.`, 50 | ``, 51 | ` protocol: ${url.protocol}`, 52 | ` url: ${url.href}`, 53 | ].join("\n"), 54 | ) 55 | } 56 | 57 | return handler.fetchText(url) 58 | } 59 | 60 | register(scheme: string, handler: FetcherHandler): void { 61 | this.handlers[scheme] = handler 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/lang/freshen/freshenType.ts: -------------------------------------------------------------------------------- 1 | import { stringToSubscript } from "../../utils/stringToSubscript.ts" 2 | import { type Value } from "../value/index.ts" 3 | 4 | export function freshenType( 5 | typeVarCounters: Map, 6 | t: Value, 7 | occurredNames: Map, 8 | ): Value { 9 | switch (t["@kind"]) { 10 | case "Symbol": { 11 | const foundName = occurredNames.get(t.name) 12 | if (foundName === undefined) { 13 | const subscript = tickSymbolCounter(typeVarCounters, t.name) 14 | const newName = t.name + stringToSubscript(subscript.toString()) 15 | occurredNames.set(t.name, newName) 16 | return { 17 | "@type": "Value", 18 | "@kind": "Symbol", 19 | name: newName, 20 | } 21 | } else { 22 | return { 23 | "@type": "Value", 24 | "@kind": "Symbol", 25 | name: foundName, 26 | } 27 | } 28 | } 29 | 30 | case "TypeTerm": { 31 | return { 32 | "@type": "Value", 33 | "@kind": "TypeTerm", 34 | mod: t.mod, 35 | name: t.name, 36 | args: t.args.map((arg) => 37 | freshenType(typeVarCounters, arg, occurredNames), 38 | ), 39 | } 40 | } 41 | 42 | default: { 43 | // TODO Maybe we need to handle other values. 44 | return t 45 | } 46 | } 47 | } 48 | 49 | export function tickSymbolCounter( 50 | typeVarCounters: Map, 51 | name: string, 52 | ): number { 53 | const foundCounter = typeVarCounters.get(name) 54 | if (foundCounter === undefined) { 55 | typeVarCounters.set(name, 0) 56 | return 0 57 | } else { 58 | typeVarCounters.set(name, foundCounter + 1) 59 | return foundCounter + 1 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/lang/syntax/matchers/exp_matcher.ts: -------------------------------------------------------------------------------- 1 | import * as pt from "@xieyuheng/partech" 2 | import { type Exp } from "../../exp/index.ts" 3 | import * as matchers from "../matchers/index.ts" 4 | 5 | export function exp_matcher(tree: pt.Tree): Exp { 6 | return pt.matcher({ 7 | "exp:operator": ({ operator }) => operator_matcher(operator), 8 | "exp:operand": ({ operand }) => operand_matcher(operand), 9 | })(tree) 10 | } 11 | 12 | export function operator_matcher(tree: pt.Tree): Exp { 13 | return pt.matcher({ 14 | "operator:var": ({ name }, { span }) => ({ 15 | "@type": "Exp", 16 | "@kind": "Var", 17 | name: pt.str(name), 18 | span, 19 | }), 20 | "operator:builtin": ({ name }, { span }) => ({ 21 | "@type": "Exp", 22 | "@kind": "Builtin", 23 | name: pt.str(name), 24 | span, 25 | }), 26 | })(tree) 27 | } 28 | 29 | export function operand_matcher(tree: pt.Tree): Exp { 30 | return pt.matcher({ 31 | "operand:ap": ({ target, args }, { span }) => ({ 32 | "@type": "Exp", 33 | "@kind": "Ap", 34 | target: operator_matcher(target), 35 | args: matchers.args_matcher(args), 36 | span, 37 | }), 38 | "operand:ap_nullary": ({ target }, { span }) => ({ 39 | "@type": "Exp", 40 | "@kind": "Ap", 41 | target: operator_matcher(target), 42 | args: [], 43 | span, 44 | }), 45 | "operand:quote_symbol": ({ name }, { span }) => ({ 46 | "@type": "Exp", 47 | "@kind": "QuoteSymbol", 48 | name: pt.str(name), 49 | span, 50 | }), 51 | "operand:block": ({ body }, { span }) => ({ 52 | "@type": "Exp", 53 | "@kind": "Block", 54 | body: matchers.block_stmts_matcher(body), 55 | span, 56 | }), 57 | })(tree) 58 | } 59 | -------------------------------------------------------------------------------- /src/lang/edge/formatEdge.ts: -------------------------------------------------------------------------------- 1 | import { type Edge } from "../edge/index.ts" 2 | import { findHalfEdgeEntryOrFail } from "../net/findHalfEdgeEntryOrFail.ts" 3 | import { type Net } from "../net/index.ts" 4 | import { formatNode } from "../node/formatNode.ts" 5 | 6 | export function formatEdge(net: Net, edge: Edge): string { 7 | const firstHalfEdgeEntry = findHalfEdgeEntryOrFail(net, edge.first) 8 | const secondHalfEdgeEntry = findHalfEdgeEntryOrFail(net, edge.second) 9 | 10 | const firstPort = firstHalfEdgeEntry.port 11 | const secondPort = secondHalfEdgeEntry.port 12 | 13 | if (firstPort === undefined && secondPort === undefined) { 14 | return `-- --` 15 | } 16 | 17 | if (firstPort === undefined && secondPort !== undefined) { 18 | const secondNodeText = formatNode(net, secondPort.node) 19 | if (secondPort.isPrincipal) { 20 | return `--!${secondPort.name}-(${secondNodeText})` 21 | } else { 22 | return `-- ${secondPort.name}-(${secondNodeText})` 23 | } 24 | } 25 | 26 | if (firstPort !== undefined && secondPort === undefined) { 27 | const firstNodeText = formatNode(net, firstPort.node) 28 | if (firstPort.isPrincipal) { 29 | return `(${firstNodeText})-${firstPort.name}!--` 30 | } else { 31 | return `(${firstNodeText})-${firstPort.name} --` 32 | } 33 | } 34 | 35 | if (firstPort !== undefined && secondPort !== undefined) { 36 | const firstNodeText = formatNode(net, firstPort.node) 37 | const secondNodeText = formatNode(net, secondPort.node) 38 | if (firstPort.isPrincipal && secondPort.isPrincipal) { 39 | return `(${firstNodeText})-${firstPort.name}!${secondPort.name}-(${secondNodeText})` 40 | } else { 41 | return `(${firstNodeText})-${firstPort.name} ${secondPort.name}-(${secondNodeText})` 42 | } 43 | } 44 | 45 | throw new Error(`[formatEdge] unreachable`) 46 | } 47 | -------------------------------------------------------------------------------- /src/lang/stmt/Stmt.ts: -------------------------------------------------------------------------------- 1 | import { type BlockStmt } from "../exp/BlockStmt.ts" 2 | import { type Exp } from "../exp/index.ts" 3 | import { type ImportBinding } from "../import/ImportBinding.ts" 4 | import { type ParameterExp } from "../parameter/index.ts" 5 | import { type RuleTarget } from "../rule/index.ts" 6 | import { type Span } from "../span/index.ts" 7 | 8 | export type Stmt = 9 | | DefineNode 10 | | DefineType 11 | | DefineFunction 12 | | DefineRule 13 | | TopLevelEvaluate 14 | | TopLevelLet 15 | | Require 16 | | Import 17 | 18 | export type DefineNode = { 19 | "@type": "Stmt" 20 | "@kind": "DefineNode" 21 | name: string 22 | input: Array 23 | output: Array 24 | span: Span 25 | } 26 | 27 | export type DefineType = { 28 | "@type": "Stmt" 29 | "@kind": "DefineType" 30 | name: string 31 | input: Array 32 | span: Span 33 | } 34 | 35 | export type DefineFunction = { 36 | "@type": "Stmt" 37 | "@kind": "DefineFunction" 38 | name: string 39 | input: Array 40 | retType: Exp 41 | body: Array 42 | span: Span 43 | } 44 | 45 | export type DefineRule = { 46 | "@type": "Stmt" 47 | "@kind": "DefineRule" 48 | first: RuleTarget 49 | second: RuleTarget 50 | body: Array 51 | span: Span 52 | } 53 | 54 | export type TopLevelEvaluate = { 55 | "@type": "Stmt" 56 | "@kind": "TopLevelEvaluate" 57 | exp: Exp 58 | span: Span 59 | } 60 | 61 | export type TopLevelLet = { 62 | "@type": "Stmt" 63 | "@kind": "TopLevelLet" 64 | names: Array 65 | exp: Exp 66 | span: Span 67 | } 68 | 69 | export type Require = { 70 | "@type": "Stmt" 71 | "@kind": "Require" 72 | path: string 73 | span: Span 74 | } 75 | 76 | export type Import = { 77 | "@type": "Stmt" 78 | "@kind": "Import" 79 | bindings: Array 80 | path: string 81 | span: Span 82 | } 83 | -------------------------------------------------------------------------------- /src/lang/syntax/matchers/parameter_matcher.ts: -------------------------------------------------------------------------------- 1 | import * as pt from "@xieyuheng/partech" 2 | import { 3 | type ParameterExp, 4 | type ParameterWithoutType, 5 | } from "../../parameter/index.ts" 6 | import * as matchers from "../matchers/index.ts" 7 | 8 | export function parameter_matcher(tree: pt.Tree): ParameterExp { 9 | return pt.matcher({ 10 | "parameter:normal": ({ name, t }) => ({ 11 | name: pt.str(name), 12 | t: matchers.exp_matcher(t), 13 | isPrincipal: false, 14 | }), 15 | "parameter:is_principal": ({ name, t }) => ({ 16 | name: pt.str(name), 17 | t: matchers.exp_matcher(t), 18 | isPrincipal: true, 19 | }), 20 | })(tree) 21 | } 22 | 23 | export function parameters_matcher(tree: pt.Tree): Array { 24 | return pt.matcher({ 25 | "parameters:parameters": ({ entries, last_entry }) => [ 26 | ...pt.matchers.zero_or_more_matcher(entries).map(parameter_matcher), 27 | parameter_matcher(last_entry), 28 | ], 29 | })(tree) 30 | } 31 | 32 | export function parameter_without_type_matcher( 33 | tree: pt.Tree, 34 | ): ParameterWithoutType { 35 | return pt.matcher({ 36 | "parameter_without_type:normal": ({ name, t }) => ({ 37 | name: pt.str(name), 38 | isPrincipal: false, 39 | }), 40 | "parameter_without_type:is_principal": ({ name, t }) => ({ 41 | name: pt.str(name), 42 | isPrincipal: true, 43 | }), 44 | })(tree) 45 | } 46 | 47 | export function parameters_without_type_matcher( 48 | tree: pt.Tree, 49 | ): Array { 50 | return pt.matcher({ 51 | "parameters_without_type:parameters_without_type": ({ 52 | entries, 53 | last_entry, 54 | }) => [ 55 | ...pt.matchers 56 | .zero_or_more_matcher(entries) 57 | .map(parameter_without_type_matcher), 58 | parameter_without_type_matcher(last_entry), 59 | ], 60 | })(tree) 61 | } 62 | -------------------------------------------------------------------------------- /src/lang/evaluate/evaluateBlockStmt.ts: -------------------------------------------------------------------------------- 1 | import { defineLocals } from "../env/defineLocals.ts" 2 | import { type Env } from "../env/index.ts" 3 | import { appendReport } from "../errors/index.ts" 4 | import { type BlockStmt } from "../exp/BlockStmt.ts" 5 | import { formatBlockStmt } from "../exp/formatBlockStmt.ts" 6 | import { type Mod } from "../mod/index.ts" 7 | import { formatValue, type Value } from "../value/index.ts" 8 | import { evaluate, type EvaluateOptions } from "./evaluate.ts" 9 | 10 | export function evaluateBlockStmt( 11 | mod: Mod, 12 | env: Env, 13 | stmt: BlockStmt, 14 | options: EvaluateOptions, 15 | ): Array | null { 16 | try { 17 | switch (stmt["@kind"]) { 18 | case "Let": { 19 | const values = evaluate(mod, env, stmt.exp, options) 20 | defineLocals(env, stmt.names, values) 21 | return null 22 | } 23 | 24 | case "Evaluate": { 25 | const values = evaluate(mod, env, stmt.exp, options) 26 | if (values.length !== 0) { 27 | throw new Error( 28 | [ 29 | `[evaluateBlockStmt / Evaluate] I expect the result of the evalutaion to have zero values.`, 30 | ``, 31 | ` block stmt: ${formatBlockStmt(stmt)}`, 32 | ` values: [${values 33 | .map((value) => formatValue(env, value)) 34 | .join(", ")}]`, 35 | ].join("\n"), 36 | ) 37 | } 38 | 39 | return null 40 | } 41 | 42 | case "Return": { 43 | return evaluate(mod, env, stmt.exp, options) 44 | } 45 | } 46 | } catch (error) { 47 | throw appendReport(error, { 48 | message: [ 49 | `[evaluateBlockStmt] I fail to evaluate a block stmt.`, 50 | ``, 51 | ` block stmt: ${formatBlockStmt(stmt)}`, 52 | ].join("\n"), 53 | context: { 54 | span: stmt.span, 55 | text: mod.text, 56 | }, 57 | }) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/lang/apply/applyFunction.ts: -------------------------------------------------------------------------------- 1 | import { manyTimes } from "../../utils/manyTimes.ts" 2 | import { connectValues } from "../connect/connectValues.ts" 3 | import { defineLocals } from "../env/defineLocals.ts" 4 | import { type Env } from "../env/index.ts" 5 | import { evaluateBlock } from "../evaluate/evaluateBlock.ts" 6 | import { type EvaluateOptions } from "../evaluate/index.ts" 7 | import { addEdge } from "../net/addEdge.ts" 8 | import { formatValues } from "../value/formatValues.ts" 9 | import { formatValue, type Function, type Value } from "../value/index.ts" 10 | 11 | export function applyFunction( 12 | env: Env, 13 | target: Function, 14 | args: Array, 15 | options: EvaluateOptions, 16 | ): Array { 17 | const { mod, input, body } = target.definition 18 | 19 | if (args.length === input.length) { 20 | const inputNames = input.map((parameter) => parameter.name) 21 | defineLocals(env, inputNames, args) 22 | return evaluateBlock(mod, env, body, options) 23 | } 24 | 25 | if (args.length === input.length + 1) { 26 | const inputNames = input.map((parameter) => parameter.name) 27 | const inputArgs = args.slice(0, args.length - 1) 28 | const lastArg = args[args.length - 1] 29 | defineLocals(env, inputNames, inputArgs) 30 | const [value] = evaluateBlock(mod, env, body, options) 31 | connectValues(env, value, lastArg) 32 | return [] 33 | } 34 | 35 | if (args.length < input.length) { 36 | const edges = manyTimes(input.length - args.length, () => addEdge(env.net)) 37 | const inputNames = input.map((parameter) => parameter.name) 38 | defineLocals(env, inputNames, [...args, ...edges.map((edge) => edge.first)]) 39 | return [ 40 | ...edges.map((edge) => edge.second), 41 | ...evaluateBlock(mod, env, body, options), 42 | ] 43 | } 44 | 45 | throw new Error( 46 | [ 47 | `[applyFunction] Too many args.`, 48 | ``, 49 | ` function: ${formatValue(env, target)}`, 50 | ` args: [${formatValues(env, args)}]`, 51 | ].join("\n"), 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /src/lang/interact/interact.ts: -------------------------------------------------------------------------------- 1 | import { type Checking } from "../checking/index.ts" 2 | import { type Edge } from "../edge/index.ts" 3 | import { type Env } from "../env/index.ts" 4 | import { evaluateBlock } from "../evaluate/evaluateBlock.ts" 5 | import { findRuleByPorts } from "../mod/findRuleByPorts.ts" 6 | import { deleteHalfEdgeEntry } from "../net/deleteHalfEdgeEntry.ts" 7 | import { deleteNodeEntry } from "../net/deleteNodeEntry.ts" 8 | import { findHalfEdgeEntryOrFail } from "../net/findHalfEdgeEntryOrFail.ts" 9 | import { findPortEntryOrFail } from "../net/findPortEntryOrFail.ts" 10 | import { exposeRuleTargets } from "../rule/exposeRuleTargets.ts" 11 | 12 | export type InteractOptions = { 13 | checking?: Checking 14 | } 15 | 16 | export function interact( 17 | env: Env, 18 | activeEdge: Edge, 19 | options: InteractOptions, 20 | ): void { 21 | const firstHaldEdgeEntry = findHalfEdgeEntryOrFail(env.net, activeEdge.first) 22 | const secondHaldEdgeEntry = findHalfEdgeEntryOrFail( 23 | env.net, 24 | activeEdge.second, 25 | ) 26 | 27 | const firstPort = firstHaldEdgeEntry.port 28 | const secondPort = secondHaldEdgeEntry.port 29 | 30 | if (firstPort === undefined) { 31 | throw new Error(`[interact] I expect firstPort`) 32 | } 33 | 34 | if (secondPort === undefined) { 35 | throw new Error(`[interact] I expect secondPort`) 36 | } 37 | 38 | const rule = findRuleByPorts(env.mod, firstPort, secondPort) 39 | 40 | if (rule === undefined) return 41 | 42 | exposeRuleTargets(env, rule, [firstPort.node, secondPort.node]) 43 | 44 | evaluateBlock(rule.mod, env, rule.body, options) 45 | 46 | const firstPortEntry = findPortEntryOrFail(env.net, firstPort) 47 | const secondPortEntry = findPortEntryOrFail(env.net, secondPort) 48 | 49 | if (firstPortEntry.connection !== undefined) { 50 | deleteHalfEdgeEntry(env.net, firstPortEntry.connection.halfEdge) 51 | } 52 | 53 | if (secondPortEntry.connection !== undefined) { 54 | deleteHalfEdgeEntry(env.net, secondPortEntry.connection.halfEdge) 55 | } 56 | 57 | deleteNodeEntry(env.net, firstPort.node) 58 | deleteNodeEntry(env.net, secondPort.node) 59 | } 60 | --------------------------------------------------------------------------------