From 6fd45bf5c73a51a6dac8b40fca56cb7c49845d6e Mon Sep 17 00:00:00 2001 From: Sam Estep Date: Wed, 11 May 2022 10:21:10 -0400 Subject: [PATCH 1/4] refactor: Remove children field from ASTNode --- .../src/analysis/SubstanceAnalysis.test.ts | 8 +----- .../core/src/analysis/SubstanceAnalysis.ts | 12 ++------ packages/core/src/compiler/Domain.ts | 4 --- packages/core/src/compiler/Style.ts | 28 ++++--------------- packages/core/src/compiler/Substance.ts | 2 -- packages/core/src/engine/EngineUtils.ts | 3 -- packages/core/src/engine/Evaluator.ts | 2 -- packages/core/src/parser/ParserUtil.ts | 1 - packages/core/src/synthesis/Synthesizer.ts | 3 -- packages/core/src/types/ast.ts | 2 -- 10 files changed, 9 insertions(+), 56 deletions(-) diff --git a/packages/core/src/analysis/SubstanceAnalysis.test.ts b/packages/core/src/analysis/SubstanceAnalysis.test.ts index dea6000446..43b5750c94 100644 --- a/packages/core/src/analysis/SubstanceAnalysis.test.ts +++ b/packages/core/src/analysis/SubstanceAnalysis.test.ts @@ -104,16 +104,14 @@ describe("Substance AST queries", () => { // const id2 = dummyIdentifier("B", "SyntheticSubstance"); // expect(nodesEqual(id1, id2)).toBe(false); // expect(nodesEqual(id2, id2)).toBe(true); - // // create two SubStmts with different source locs and children and the equality check should still return true + // // create two SubStmts with different source locs and the equality check should still return true // const prog1: DefaultLabels = { - // children: [], // tag: "DefaultLabels", // nodeType: "Substance", // start: { line: 0, col: 0 }, // end: { line: 0, col: 0 }, // }; // const prog2: DefaultLabels = { - // children: [prog1], // tag: "DefaultLabels", // nodeType: "Substance", // start: { line: 5, col: 0 }, @@ -156,13 +154,11 @@ Set C`; const originalAST = compileSubstance(original, env).unsafelyUnwrap()[0].ast; const newStmt: SubStmt = { nodeType: "SyntheticSubstance", - children: [], tag: "Decl", name: dummyIdentifier("C", "SyntheticSubstance"), type: { tag: "TypeConstructor", nodeType: "SyntheticSubstance", - children: [], args: [], name: dummyIdentifier("Set", "SyntheticSubstance"), }, @@ -190,13 +186,11 @@ Set ZZZ`; const toReplace = originalAST.statements[1]; const newStmt: SubStmt = { nodeType: "SyntheticSubstance", - children: [], tag: "Decl", name: dummyIdentifier("ZZZ", "SyntheticSubstance"), type: { tag: "TypeConstructor", nodeType: "SyntheticSubstance", - children: [], args: [], name: dummyIdentifier("Set", "SyntheticSubstance"), }, diff --git a/packages/core/src/analysis/SubstanceAnalysis.ts b/packages/core/src/analysis/SubstanceAnalysis.ts index cc7fdb9920..4eda5fc843 100644 --- a/packages/core/src/analysis/SubstanceAnalysis.ts +++ b/packages/core/src/analysis/SubstanceAnalysis.ts @@ -380,7 +380,6 @@ export const applyConstructor = ( tag: "ApplyConstructor", name, nodeType: "SyntheticSubstance", - children: [], args, }; }; @@ -394,7 +393,6 @@ export const applyFunction = ( tag: "ApplyFunction", name, nodeType: "SyntheticSubstance", - children: [], args, }; }; @@ -408,7 +406,6 @@ export const applyPredicate = ( tag: "ApplyPredicate", name, nodeType: "SyntheticSubstance", - children: [], args, }; }; @@ -416,7 +413,6 @@ export const applyPredicate = ( export const subProg = (statements: SubStmt[]): SubProg => ({ tag: "SubProg", statements, - children: statements, nodeType: "SyntheticSubstance", }); @@ -431,7 +427,6 @@ export const applyBind = ( expr: SubExpr ): Bind => ({ tag: "Bind", - children: [], nodeType: "SyntheticSubstance", variable, expr, @@ -440,7 +435,6 @@ export const applyBind = ( export const nullaryTypeCons = (name: Identifier): TypeConsApp => ({ tag: "TypeConstructor", nodeType: "SyntheticSubstance", - children: [], name, args: [], }); @@ -450,14 +444,12 @@ export const autoLabelStmt: AutoLabel = { option: { tag: "DefaultLabels", nodeType: "SyntheticSubstance", - children: [], }, nodeType: "SyntheticSubstance", - children: [], }; /** - * Compare two AST nodes by their contents, ignoring structural properties such as `children` and positional properties like `start` and `end`. + * Compare two AST nodes by their contents, ignoring structural properties such as `nodeType` and positional properties like `start` and `end`. * * @param node1 the first AST node * @param node2 the second AST node @@ -469,7 +461,7 @@ export const nodesEqual = (node1: AbstractNode, node2: AbstractNode): boolean => }); /** - * Compare all statements of two ASTs by their contents, ignoring structural properties such as `children` and positional properties like `start` and `end`. + * Compare all statements of two ASTs by their contents, ignoring structural properties such as `nodeType` and positional properties like `start` and `end`. * * @param left the first Substance program * @param right the second Substance program diff --git a/packages/core/src/compiler/Domain.ts b/packages/core/src/compiler/Domain.ts index 6edf473471..269a7f8bbb 100644 --- a/packages/core/src/compiler/Domain.ts +++ b/packages/core/src/compiler/Domain.ts @@ -89,7 +89,6 @@ const builtinTypes: [string, TypeDecl][] = [ start: { line: 1, col: 1 }, end: { line: 1, col: 1 }, nodeType: "Substance", - children: [], tag: "TypeDecl", name: idOf("String", "Domain"), params: [], @@ -145,7 +144,6 @@ const checkStmt = (stmt: DomainStmt, env: Env): CheckerResult => { const subType: TypeConstructor = { tag: "TypeConstructor", nodeType: "Substance", - children: [name], start: stmt.start, end: stmt.end, name, @@ -388,7 +386,6 @@ const topName = idOf("type", "Domain"); export const topType: TypeConsApp = { tag: "TypeConstructor", nodeType: "Domain", - children: [topName], name: topName, args: [], }; @@ -397,7 +394,6 @@ const bottomName = idOf("void", "Domain"); export const bottomType: TypeConsApp = { tag: "TypeConstructor", nodeType: "Domain", - children: [bottomName], name: bottomName, args: [], }; diff --git a/packages/core/src/compiler/Style.ts b/packages/core/src/compiler/Style.ts index 8fb42bba1e..27028197f0 100644 --- a/packages/core/src/compiler/Style.ts +++ b/packages/core/src/compiler/Style.ts @@ -503,7 +503,6 @@ const toSubstanceType = (styT: StyT): TypeConsApp => { return { tag: "TypeConstructor", nodeType: "Substance", - children: [styT], name: styT, args: [], }; @@ -809,10 +808,8 @@ const substitutePath = ( case "LocalVar": { return { nodeType: "SyntheticStyle", - children: [], tag: "FieldPath", name: { - children: [], nodeType: "SyntheticStyle", tag: "SubVar", contents: { @@ -830,11 +827,9 @@ const substitutePath = ( // COMBAK / HACK: Is there some way to get rid of all these dummy values? return { nodeType: "SyntheticStyle", - children: [], tag: "FieldPath", name: { nodeType: "SyntheticStyle", - children: [], tag: "SubVar", contents: { ...dummyId(mkLocalVarName(lv)), @@ -1577,14 +1572,12 @@ const nameAnonStatement = (i: number, s: Stmt): [number, Stmt] => { type: { tag: "TypeOf", nodeType: "SyntheticStyle", - children: [], contents: "Nothing", }, // TODO: Why is it parsed like this? path: { tag: "InternalLocalVar", contents: `\$${ANON_KEYWORD}_${i}`, nodeType: "SyntheticStyle", - children: [], // Unused bc compiler internal }, value: s.contents, }; @@ -2265,10 +2258,8 @@ const mkPath = (strs: string[]): Path => { return { tag: "FieldPath", nodeType: "SyntheticStyle", - children: [], name: { nodeType: "SyntheticStyle", - children: [], tag: "SubVar", contents: { ...dummyId(name), @@ -2281,10 +2272,8 @@ const mkPath = (strs: string[]): Path => { return { tag: "PropertyPath", nodeType: "SyntheticStyle", - children: [], name: { nodeType: "SyntheticStyle", - children: [], tag: "SubVar", contents: { ...dummyId(name), @@ -2332,7 +2321,6 @@ const findPropertyVarying = ( tag: "OptEval", contents: { nodeType: "SyntheticStyle", - children: [], tag: "Vector", contents: [ dummyASTNode({ tag: "Vary" }, "SyntheticStyle") as Expr, @@ -2366,16 +2354,12 @@ const findNestedVarying = (e: TagExpr, p: Path): Path[] => { .map((e: Expr, i): [Expr, number] => [e, i]) .filter((e: [Expr, number]): boolean => isVarying(e[0])) .map( - ([, i]: [Expr, number]): IAccessPath => - ({ - nodeType: "SyntheticStyle", - children: [], - tag: "AccessPath", - path: p, - indices: [ - dummyASTNode({ tag: "Fix", contents: i }, "SyntheticStyle"), - ], - } as IAccessPath) + ([, i]: [Expr, number]): IAccessPath => ({ + nodeType: "SyntheticStyle", + tag: "AccessPath", + path: p, + indices: [{ tag: "Fix", nodeType: "SyntheticStyle", contents: i }], + }) ); return indices; diff --git a/packages/core/src/compiler/Substance.ts b/packages/core/src/compiler/Substance.ts index 0b53a03cd9..1844495e24 100644 --- a/packages/core/src/compiler/Substance.ts +++ b/packages/core/src/compiler/Substance.ts @@ -135,7 +135,6 @@ export const postprocessSubstance = ( const toSubDecl = (idString: string, decl: TypeConstructor): Decl => ({ nodeType: "SyntheticSubstance", - children: [], tag: "Decl", type: { ...decl, @@ -216,7 +215,6 @@ const stringName = idOf("String", "Substance"); const stringType: TypeConsApp = { tag: "TypeConstructor", nodeType: "SyntheticSubstance", - children: [stringName], name: stringName, args: [], }; diff --git a/packages/core/src/engine/EngineUtils.ts b/packages/core/src/engine/EngineUtils.ts index 5e99058606..d1f4e12620 100644 --- a/packages/core/src/engine/EngineUtils.ts +++ b/packages/core/src/engine/EngineUtils.ts @@ -434,7 +434,6 @@ export const dummyIdentifier = ( ): Identifier => { return { nodeType, - children: [], type: "value", value: name, tag: "Identifier", @@ -448,7 +447,6 @@ const floatValToExpr = (e: Value): Expr => { return { nodeType: "SyntheticStyle", - children: [], tag: "VaryAD", contents: e.contents, }; @@ -1060,7 +1058,6 @@ export const exprToNumber = (e: Expr): number => { export const numToExpr = (n: number): Expr => { return { nodeType: "SyntheticStyle", - children: [], tag: "Fix", contents: n, }; diff --git a/packages/core/src/engine/Evaluator.ts b/packages/core/src/engine/Evaluator.ts index b481cd6e98..06427ab67a 100644 --- a/packages/core/src/engine/Evaluator.ts +++ b/packages/core/src/engine/Evaluator.ts @@ -615,7 +615,6 @@ export const evalExpr = ( p = { // convert to AccessPath schema nodeType: "SyntheticStyle", - children: [], tag: "AccessPath", // contents: [p.contents[0], [p.contents[1].contents]], path: p.contents[0], @@ -625,7 +624,6 @@ export const evalExpr = ( p = { // convert to AccessPath schema nodeType: "SyntheticStyle", - children: [], tag: "AccessPath", path: p.contents[0], indices: p.contents[1], diff --git a/packages/core/src/parser/ParserUtil.ts b/packages/core/src/parser/ParserUtil.ts index fc806ce468..f3f221b8ac 100644 --- a/packages/core/src/parser/ParserUtil.ts +++ b/packages/core/src/parser/ParserUtil.ts @@ -174,7 +174,6 @@ export const tokensIn = ( // HACK: locations for dummy AST nodes. Revisit if this pattern becomes widespread. export const idOf = (value: string, nodeType: NodeType): Identifier => ({ nodeType, - children: [], start: { line: 1, col: 1 }, end: { line: 1, col: 1 }, tag: "Identifier", diff --git a/packages/core/src/synthesis/Synthesizer.ts b/packages/core/src/synthesis/Synthesizer.ts index 4fbb4d16b3..507ad1e770 100644 --- a/packages/core/src/synthesis/Synthesizer.ts +++ b/packages/core/src/synthesis/Synthesizer.ts @@ -332,7 +332,6 @@ export class Synthesizer { this.template = { tag: "SubProg", statements: [], - children: [], nodeType: "SyntheticSubstance", }; } @@ -761,7 +760,6 @@ const generateDecl = ( const stmt: Decl = { tag: "Decl", nodeType: "SyntheticSubstance", - children: [], type: typeCons, name, }; @@ -779,7 +777,6 @@ const generateDeclFromType = ( const stmt: Decl = { tag: "Decl", nodeType: "SyntheticSubstance", - children: [], type: typeCons, name, }; diff --git a/packages/core/src/types/ast.ts b/packages/core/src/types/ast.ts index ecbbe7f8c0..2190feaec8 100644 --- a/packages/core/src/types/ast.ts +++ b/packages/core/src/types/ast.ts @@ -12,7 +12,6 @@ export interface SourceRange { export type ASTNode = T & { tag: string; nodeType: NodeType; - children: ASTNode[]; }; export type A = unknown; @@ -35,7 +34,6 @@ export type ConcreteNode = ASTNode; // metaObj causes TypeScript to enforce that metaProps is correct const metaObj: { [k in keyof Omit]: undefined } = { nodeType: undefined, - children: undefined, start: undefined, end: undefined, }; From 179463d6d8ba798f115cf8de2789080271bb94e2 Mon Sep 17 00:00:00 2001 From: Sam Estep Date: Wed, 11 May 2022 10:47:14 -0400 Subject: [PATCH 2/4] Update parsers and their tests --- packages/core/src/parser/Domain.ne | 35 ++++---- packages/core/src/parser/DomainParser.test.ts | 15 ---- packages/core/src/parser/Style.ne | 89 +++++++++---------- packages/core/src/parser/StyleParser.test.ts | 15 +--- packages/core/src/parser/Substance.ne | 45 +++++----- .../core/src/parser/SubstanceParser.test.ts | 14 --- 6 files changed, 81 insertions(+), 132 deletions(-) diff --git a/packages/core/src/parser/Domain.ne b/packages/core/src/parser/Domain.ne index e9e752330e..a3d579316e 100644 --- a/packages/core/src/parser/Domain.ne +++ b/packages/core/src/parser/Domain.ne @@ -29,10 +29,7 @@ const lexer = moo.compile({ } }); -const nodeData = (children: ConcreteNode[]) => ({ - nodeType: "Domain" as const, - children -}); +const nodeData = { nodeType: "Domain" as const }; %} # end of lexer @@ -46,7 +43,7 @@ const nodeData = (children: ConcreteNode[]) => ({ input -> statements {% ([statements]): DomainProg => ({ - ...nodeData(statements), + ...nodeData, ...rangeFrom(statements), tag: "DomainProg", statements @@ -78,7 +75,7 @@ type_decl -> "type" __ identifier (_ "(" _ type_params _ ")"):? (_ "<:" _ sepBy1 const params = ps ? ps[3] : []; const superTypes = sub ? sub[3] : []; return { - ...nodeData([name, ...params, ...superTypes]), + ...nodeData, ...rangeBetween(typ, name), tag: "TypeDecl", name, params, superTypes }; @@ -87,7 +84,7 @@ type_decl -> "type" __ identifier (_ "(" _ type_params _ ")"):? (_ "<:" _ sepBy1 predicate -> "predicate" __ identifier type_params_list args_list {% ([kw, , name, params, args]): PredicateDecl => ({ - ...nodeData([name, ...params, ...args]), + ...nodeData, ...rangeFrom([rangeOf(kw), ...args, ...params]), tag: "PredicateDecl", name, params, args }) @@ -100,7 +97,7 @@ function const params = optional(ps, []); const args = optional(as, []); return { - ...nodeData([name, output, ...params, ...args]), + ...nodeData, ...rangeBetween(rangeOf(kw), output), tag: "FunctionDecl", name, output, params, args }; @@ -115,7 +112,7 @@ constructor_decl const params = optional(ps, []); const args = optional(as, []); return { - ...nodeData([name, output, ...params, ...args]), + ...nodeData, ...rangeBetween(rangeOf(kw), output), tag: "ConstructorDecl", name, output, params, args } @@ -124,7 +121,7 @@ constructor_decl prelude -> "value" __ var _ ":" _ type {% ([kw, , name, , , , type]): PreludeDecl => ({ - ...nodeData([name, type]), + ...nodeData, ...rangeBetween(rangeOf(kw), type), tag: "PreludeDecl", name, type }) @@ -132,7 +129,7 @@ prelude -> "value" __ var _ ":" _ type {% notation -> "notation" _ string_lit _ "~" _ string_lit {% ([kw, , from, , , , to]): NotationDecl => ({ - ...nodeData([from, to]), + ...nodeData, ...rangeBetween(rangeOf(kw), to), tag: "NotationDecl", from, to }) @@ -140,7 +137,7 @@ notation -> "notation" _ string_lit _ "~" _ string_lit {% subtype -> type _ "<:" _ type {% ([subType, , , , superType]): SubTypeDecl => ({ - ...nodeData([subType, superType]), + ...nodeData, ...rangeBetween(subType, superType), tag: "SubTypeDecl", subType, superType }) @@ -165,7 +162,7 @@ var -> identifier {% id %} # TODO: without `'`, type_var will look the same as 0-arg type_constructor type_var -> "'" identifier {% ([a, name]) => ({ - ...nodeData([name]), + ...nodeData, ...rangeBetween(a, name), tag: "TypeVar", name }) @@ -180,7 +177,7 @@ type_constructor -> identifier type_arg_list:? {% ([name, a]): TypeConstructor => { const args = optional(a, []); return { - ...nodeData([name, ...args]), + ...nodeData, ...rangeFrom([name, ...args]), tag: "TypeConstructor", name, args }; @@ -203,7 +200,7 @@ arg -> type (__ var):? {% const variable = v ? v[1] : undefined; const range = variable ? rangeBetween(variable, type) : rangeOf(type); return { - ...nodeData(variable ? [variable, type] : [type]), + ...nodeData, ...range, tag: "Arg", variable, type }; @@ -213,7 +210,7 @@ named_args_list -> _ "(" _ sepBy[named_arg, ","] _ ")" {% ([, , , d]): Arg[] => flatten(d) %} named_arg -> type __ var {% ([type, , variable]): Arg => ({ - ...nodeData([type, variable]), + ...nodeData, ...rangeBetween(type, variable), tag: "Arg", variable, type }) @@ -221,7 +218,7 @@ named_arg -> type __ var {% prop -> "Prop" {% ([kw]): Prop => ({ - ...nodeData([]), + ...nodeData, ...rangeOf(kw), tag: "Prop" }) @@ -231,7 +228,7 @@ prop -> "Prop" {% string_lit -> %string_literal {% ([d]): IStringLit => ({ - ...nodeData([]), + ...nodeData, ...rangeOf(d), tag: 'StringLit', contents: d.value @@ -240,7 +237,7 @@ string_lit -> %string_literal {% identifier -> %identifier {% ([d]) => ({ - ...nodeData([]), + ...nodeData, ...rangeOf(d), tag: 'Identifier', value: d.text, diff --git a/packages/core/src/parser/DomainParser.test.ts b/packages/core/src/parser/DomainParser.test.ts index 84a7fa69ed..05588aeaf0 100644 --- a/packages/core/src/parser/DomainParser.test.ts +++ b/packages/core/src/parser/DomainParser.test.ts @@ -2,7 +2,6 @@ import { examples } from "@penrose/examples"; import * as fs from "fs"; import * as nearley from "nearley"; import * as path from "path"; -import { ConcreteNode } from "types/ast"; import grammar from "./DomainParser"; const outputDir = "/tmp/asts"; @@ -20,18 +19,6 @@ const printAST = (ast: any) => { console.log(JSON.stringify(ast)); }; -export const traverseTree = (root: ConcreteNode) => { - const { nodeType, children } = root; - if (!nodeType) console.log(root); - expect(nodeType).toEqual("Domain"); - expect(children).not.toBe(undefined); - children.map((node) => { - if (!node) console.log(root); - expect(node).not.toBe(undefined); - traverseTree(node); - }); -}; - const domainPaths = [ "linear-algebra-domain/linear-algebra.dsl", "set-theory-domain/setTheory.dsl", @@ -102,7 +89,6 @@ List('T) <: List('U) `; const { results } = parser.feed(prog); sameASTs(results); - traverseTree(results[0]); }); }); @@ -229,7 +215,6 @@ describe("Real Programs", () => { test(examplePath, () => { const { results } = parser.feed(prog); sameASTs(results); - traverseTree(results[0]); // write to output folder if (saveASTs) { const exampleName = path.basename(examplePath, ".dsl"); diff --git a/packages/core/src/parser/Style.ne b/packages/core/src/parser/Style.ne index 8dfaa293a0..8cd78196f3 100644 --- a/packages/core/src/parser/Style.ne +++ b/packages/core/src/parser/Style.ne @@ -59,16 +59,13 @@ const lexer = moo.compile({ } }); -const nodeData = (children: ConcreteNode[]) => ({ - nodeType: "Style" as const, - children -}); +const nodeData = { nodeType: "Style" as const }; // Node constructors const declList = (type: StyT, ids: BindingForm[]): DeclPattern[] => { const decl = (t: StyT, i: BindingForm): DeclPattern => ({ - ...nodeData([t, i]), + ...nodeData, ...rangeFrom([t, i]), tag: "DeclPattern", type: t, @@ -85,7 +82,7 @@ const selector = ( namespace?: Namespace ): Selector => { return { - ...nodeData(compact([hd, wth, whr, namespace])), + ...nodeData, ...rangeFrom(compact([hd, wth, whr, namespace])), tag: "Selector", head: hd, @@ -96,13 +93,13 @@ const selector = ( } const layering = (kw: any, below: Path, above: Path): ILayering => ({ - ...nodeData([above, below]), + ...nodeData, ...rangeFrom(kw ? [rangeOf(kw), above, below] : [above, below]), tag: 'Layering', above, below }) const binop = (op: BinaryOp, left: Expr, right: Expr): IBinOp => ({ - ...nodeData([left, right]), + ...nodeData, ...rangeBetween(left, right), tag: 'BinOp', op, left, right }) @@ -122,7 +119,7 @@ const binop = (op: BinaryOp, left: Expr, right: Expr): IBinOp => ({ # TODO: header_blocks gets called twice here. Investigate why input -> _ml header_blocks {% ([, blocks]): StyProg => ({ - ...nodeData(blocks), + ...nodeData, ...rangeFrom(blocks), tag: "StyProg", blocks }) @@ -132,7 +129,7 @@ header_blocks -> header_block:* {% id %} header_block -> header block _ml {% ([header, block]): HeaderBlock => ({ - ...nodeData([header, block]), + ...nodeData, ...rangeFrom([header, block]), tag:"HeaderBlock", header, block }) @@ -165,7 +162,7 @@ decl_patterns -> sepBy1[decl_list, ";"] {% ([d]): DeclPatterns => { const contents = flatten(d) as DeclPattern[]; return { - ...nodeData([...contents]), + ...nodeData, ...rangeFrom(contents), tag: "DeclPatterns", contents }; @@ -182,7 +179,7 @@ select_where -> "where" __ relation_list _ml {% d => d[2] %} relation_list -> sepBy1[relation, ";"] {% ([d]): RelationPatterns => ({ - ...nodeData([...d]), + ...nodeData, ...rangeFrom(d), tag: "RelationPatterns", contents: d @@ -196,7 +193,7 @@ relation rel_bind -> binding_form _ ":=" _ sel_expr {% ([id, , , , expr]): RelBind => ({ - ...nodeData([id, expr]), + ...nodeData, ...rangeFrom([id, expr]), tag: "RelBind", id, expr }) @@ -204,7 +201,7 @@ rel_bind -> binding_form _ ":=" _ sel_expr {% rel_pred -> identifier _ "(" pred_arg_list ")" {% ([name, , , args, ]): RelPred => ({ - ...nodeData([name, ...args]), + ...nodeData, ...rangeFrom([name, ...args]), tag: "RelPred", name, args }) @@ -212,7 +209,7 @@ rel_pred -> identifier _ "(" pred_arg_list ")" {% rel_field -> binding_form __ "has" __ (field_desc __):? identifier {% ([name, , , , field_desc, field]): RelField => ({ - ...nodeData(field_desc ? [name, field_desc[0], field] : [name, field]), + ...nodeData, ...rangeBetween(name, field), tag: "RelField", fieldDescriptor: field_desc ? field_desc[0] : undefined, @@ -231,14 +228,14 @@ sel_expr_list sel_expr -> identifier _ "(" sel_expr_list ")" {% ([name, , , args, ]): SEFuncOrValCons => ({ - ...nodeData([name, ...args]), + ...nodeData, ...rangeFrom([name, ...args]), tag: "SEFuncOrValCons", name, args }) %} | binding_form {% ([d]): SEBind => ({ - ...nodeData([d]), + ...nodeData, ...rangeFrom([d]), tag: "SEBind", contents: d }) @@ -254,7 +251,7 @@ pred_arg_list pred_arg -> rel_pred {% id %} | binding_form {% ([d]): SEBind => ({ - ...nodeData([d]), + ...nodeData, ...rangeFrom([d]), tag: "SEBind", contents: d }) @@ -267,7 +264,7 @@ binding_form # HACK: tokens like "`" don't really have start and end points, just line and col. How do you merge a heterogenrous list of tokens and nodes? subVar -> "`" identifier "`" {% ([ltick, contents, rtick]): SubVar => ({ - ...nodeData([contents]), + ...nodeData, ...rangeBetween(rangeOf(ltick), rangeOf(rtick)), tag: "SubVar", contents }) @@ -275,7 +272,7 @@ subVar -> "`" identifier "`" {% styVar -> identifier {% ([contents]): StyVar => ({ - ...nodeData([contents]), + ...nodeData, ...rangeOf(contents), tag: "StyVar", contents }) @@ -286,7 +283,7 @@ select_as -> "as" __ namespace {% nth(2) %} namespace -> styVar _ml {% ([contents]): Namespace => ({ - ...nodeData([contents]), + ...nodeData, ...rangeOf(contents), tag: "Namespace", contents @@ -299,7 +296,7 @@ namespace -> styVar _ml {% block -> "{" statements "}" {% ([lbrace, stmts, rbrace]): Block => ({ - ...nodeData(stmts), + ...nodeData, ...rangeBetween(lbrace, rbrace), tag: "Block", statements: stmts @@ -322,7 +319,7 @@ statement | path_assign {% id %} | anonymous_expr {% ([contents]): IAnonAssign => ({ - ...nodeData([contents]), + ...nodeData, ...rangeOf(contents), tag: "AnonAssign", contents }) @@ -331,7 +328,7 @@ statement delete -> "delete" __ path {% ([kw, , contents]): Delete => { return { - ...nodeData([contents]), + ...nodeData, ...rangeBetween(rangeOf(kw), contents), tag: "Delete", contents }} @@ -339,7 +336,7 @@ delete -> "delete" __ path {% override -> "override" __ path _ "=" _ assign_expr {% ([kw, , path, , , , value]): IOverride => ({ - ...nodeData([path, value]), + ...nodeData, ...rangeBetween(kw, value), tag: "Override", path, value }) @@ -347,7 +344,7 @@ override -> "override" __ path _ "=" _ assign_expr {% path_assign -> type:? __ path _ "=" _ assign_expr {% ([type, , path, , , , value]): PathAssign => ({ - ...nodeData(type ? [type, path, value] : [path, value]), + ...nodeData, ...rangeBetween(type ? type : path, value), tag: "PathAssign", type, path, value @@ -356,13 +353,13 @@ path_assign -> type:? __ path _ "=" _ assign_expr {% type -> identifier {% ([contents]): StyType => ({ - ...nodeData([contents]), + ...nodeData, ...rangeOf(contents), tag: 'TypeOf', contents }) %} | identifier "[]" {% ([contents]): StyType => ({ - ...nodeData([contents]), + ...nodeData, ...rangeOf(contents), tag: 'ListOf', contents }) @@ -379,7 +376,7 @@ entity_path propertyPath -> binding_form "." identifier "." identifier {% ([name, , field, , property]): IPropertyPath => ({ - ...nodeData([name, field, property]), + ...nodeData, ...rangeFrom([name, field, property]), tag: "PropertyPath", name, field, property }) @@ -387,7 +384,7 @@ propertyPath -> binding_form "." identifier "." identifier {% fieldPath -> binding_form "." identifier {% ([name, , field]): IFieldPath => ({ - ...nodeData([name, field]), + ...nodeData, ...rangeFrom([name, field]), tag: "FieldPath", name, field }) @@ -395,7 +392,7 @@ fieldPath -> binding_form "." identifier {% localVar -> identifier {% ([contents]): LocalVar => ({ - ...nodeData([contents]), + ...nodeData, ...rangeOf(contents), tag: "LocalVar", contents }) @@ -407,7 +404,7 @@ access_path -> entity_path _ access_ops {% ([path, , indices]): IAccessPath => { const lastIndex: moo.Token = last(indices)!; return { - ...nodeData([path]), // TODO: model access op as an AST node to solve the range and child node issue + ...nodeData, // TODO: model access op as an AST node to solve the range and child node issue ...rangeBetween(path, lastIndex), tag: "AccessPath", path, indices } @@ -448,7 +445,7 @@ parenthesized unary -> "-" _ parenthesized {% (d): IUOp => ({ - ...nodeData([d[2]]), + ...nodeData, ...rangeBetween(d[0], d[2]), tag: 'UOp', op: "UMinus", arg: d[2] }) @@ -488,7 +485,7 @@ expr_literal list -> "[" _ expr_list _ "]" {% ([lbracket, , exprs, , rbracket]): IList => ({ - ...nodeData(exprs), + ...nodeData, ...rangeBetween(lbracket, rbracket), tag: 'List', contents: exprs @@ -498,7 +495,7 @@ list -> "[" _ expr_list _ "]" {% # NOTE: we only allow 2 tuples and enforce this rule during parsing tuple -> "{" _ expr _ "," _ expr _ "}" {% ([lbrace, , e1, , , , e2, , rbrace]): ITuple => ({ - ...nodeData([e1, e2]), + ...nodeData, ...rangeBetween(lbrace, rbrace), tag: 'Tuple', contents: [e1, e2] @@ -508,7 +505,7 @@ tuple -> "{" _ expr _ "," _ expr _ "}" {% # NOTE: the extra expr makes sure a vector will always have >1 components. vector -> "(" _ expr _ "," expr_list ")" {% ([lparen, , first, , , rest, rparen]): IVector => ({ - ...nodeData([first, ...rest]), + ...nodeData, ...rangeBetween(lparen, rparen), tag: 'Vector', contents: [first, ...rest] @@ -526,7 +523,7 @@ vector -> "(" _ expr _ "," expr_list ")" {% bool_lit -> ("true" | "false") {% ([[d]]): IBoolLit => ({ - ...nodeData([]), + ...nodeData, ...rangeOf(d), tag: 'BoolLit', contents: d.text === 'true' // https://stackoverflow.com/questions/263965/how-can-i-convert-a-string-to-boolean-in-javascript @@ -535,7 +532,7 @@ bool_lit -> ("true" | "false") {% string_lit -> %string_literal {% ([d]): IStringLit => ({ - ...nodeData([]), + ...nodeData, ...rangeOf(d), tag: 'StringLit', contents: d.value @@ -543,9 +540,9 @@ string_lit -> %string_literal {% %} annotated_float - -> "?" {% ([d]): IVary => ({ ...nodeData([]), ...rangeOf(d), tag: 'Vary' }) %} + -> "?" {% ([d]): IVary => ({ ...nodeData, ...rangeOf(d), tag: 'Vary' }) %} | %float_literal {% - ([d]): IFix => ({ ...nodeData([]), ...rangeOf(d), tag: 'Fix', contents: parseFloat(d) }) + ([d]): IFix => ({ ...nodeData, ...rangeOf(d), tag: 'Fix', contents: parseFloat(d) }) %} layering @@ -558,7 +555,7 @@ layer_keyword -> "layer" __ {% nth(0) %} computation_function -> identifier _ "(" expr_list ")" {% ([name, , , args, rparen]): ICompApp => ({ - ...nodeData([name, ...args]), + ...nodeData, ...rangeBetween(name, rparen), tag: "CompApp", name, args @@ -567,7 +564,7 @@ computation_function -> identifier _ "(" expr_list ")" {% objective -> "encourage" __ identifier _ "(" expr_list ")" {% ([kw, , name, , , args, rparen]): IObjFn => ({ - ...nodeData([name, ...args]), + ...nodeData, ...rangeBetween(kw, rparen), tag: "ObjFn", name, args @@ -576,7 +573,7 @@ objective -> "encourage" __ identifier _ "(" expr_list ")" {% constraint -> "ensure" __ identifier _ "(" expr_list ")" {% ([kw, , name, , , args, rparen]): IConstrFn => ({ - ...nodeData([name, ...args]), + ...nodeData, ...rangeBetween(kw, rparen), tag: "ConstrFn", name, args @@ -589,7 +586,7 @@ expr_list gpi_decl -> identifier _ml "{" property_decl_list "}" {% ([shapeName, , , properties, rbrace]): GPIDecl => ({ - ...nodeData([shapeName, ...properties]), + ...nodeData, ...rangeBetween(shapeName, rbrace), tag: "GPIDecl", shapeName, properties @@ -609,7 +606,7 @@ property_decl_list property_decl -> identifier _ ":" _ expr {% ([name, , , , value]): PropertyDecl => ({ - ...nodeData([name, value]), + ...nodeData, ...rangeBetween(name, value), tag: "PropertyDecl", name, value @@ -620,7 +617,7 @@ property_decl -> identifier _ ":" _ expr {% identifier -> %identifier {% ([d]): Identifier => ({ - ...nodeData([]), + ...nodeData, ...rangeOf(d), tag: 'Identifier', value: d.text, diff --git a/packages/core/src/parser/StyleParser.test.ts b/packages/core/src/parser/StyleParser.test.ts index 2242e7ebeb..61430ddec5 100644 --- a/packages/core/src/parser/StyleParser.test.ts +++ b/packages/core/src/parser/StyleParser.test.ts @@ -3,7 +3,7 @@ import { parseStyle } from "compiler/Style"; import * as fs from "fs"; import * as nearley from "nearley"; import * as path from "path"; -import { C, ConcreteNode } from "types/ast"; +import { C } from "types/ast"; import { StyProg } from "types/style"; import grammar from "./StyleParser"; @@ -22,18 +22,6 @@ const printAST = (ast: any) => { console.log(JSON.stringify(ast)); }; -export const traverseTree = (root: ConcreteNode) => { - const { nodeType, children } = root; - if (!nodeType) console.log(root); - expect(nodeType).toEqual("Style"); - expect(children).not.toBe(undefined); - children.map((node) => { - if (!node) console.log(root); - expect(node).not.toBe(undefined); - traverseTree(node); - }); -}; - const styPaths = [ "linear-algebra-domain/linear-algebra-paper-simple.sty", "set-theory-domain/venn.sty", @@ -467,7 +455,6 @@ describe("Real Programs", () => { test(examplePath, () => { const { results } = parser.feed(prog); sameASTs(results); - traverseTree(results[0]); // write to output folder if (saveASTs) { const exampleName = path.basename(examplePath, ".sty"); diff --git a/packages/core/src/parser/Substance.ne b/packages/core/src/parser/Substance.ne index b8e9405ba8..1ff3821c45 100644 --- a/packages/core/src/parser/Substance.ne +++ b/packages/core/src/parser/Substance.ne @@ -31,10 +31,7 @@ const lexer = moo.compile({ } }); -const nodeData = (children: ConcreteNode[]) => ({ - nodeType: "Substance" as const, - children -}); +const nodeData = { nodeType: "Substance" as const }; %} # end of lexer @@ -49,7 +46,7 @@ const nodeData = (children: ConcreteNode[]) => ({ input -> statements {% ([d]): SubProg => { const statements = flatten(d) as SubStmt[]; - return { ...nodeData(statements), ...rangeFrom(statements), tag: "SubProg", statements }; + return { ...nodeData, ...rangeFrom(statements), tag: "SubProg", statements }; } %} @@ -75,7 +72,7 @@ statement decl -> type_constructor __ sepBy1[identifier, ","] {% ([type, , ids]): Decl[] => ids.map((name: Identifier): Decl => ({ - ...nodeData([type, name]), + ...nodeData, ...rangeBetween(type, name), tag: "Decl", type, name })) @@ -83,7 +80,7 @@ decl -> type_constructor __ sepBy1[identifier, ","] {% bind -> identifier _ ":=" _ sub_expr {% ([variable, , , , expr]): Bind => ({ - ...nodeData([variable, expr]), + ...nodeData, ...rangeBetween(variable, expr), tag: "Bind", variable, expr }) @@ -92,12 +89,12 @@ bind -> identifier _ ":=" _ sub_expr {% decl_bind -> type_constructor __ identifier _ ":=" _ sub_expr {% ([type, , variable, , , , expr]): [Decl, Bind] => { const decl: Decl = { - ...nodeData([type, variable]), + ...nodeData, ...rangeBetween(type, variable), tag: "Decl", type, name: variable }; const bind: Bind = { - ...nodeData([variable, expr]), + ...nodeData, ...rangeBetween(variable, expr), tag: "Bind", variable, expr }; @@ -107,7 +104,7 @@ decl_bind -> type_constructor __ identifier _ ":=" _ sub_expr {% apply_predicate -> identifier _ "(" _ sepBy1[pred_arg, ","] _ ")" {% ([name, , , , args]): ApplyPredicate => ({ - ...nodeData([name, ...args]), + ...nodeData, ...rangeFrom([name, ...args]), tag: "ApplyPredicate", name, args }) @@ -123,7 +120,7 @@ sub_expr deconstructor -> identifier _ "." _ identifier {% ([variable, , , , field]): Deconstructor => ({ - ...nodeData([variable, field]), + ...nodeData, ...rangeBetween(variable, field), tag: "Deconstructor", variable, field }) @@ -132,7 +129,7 @@ deconstructor -> identifier _ "." _ identifier {% # NOTE: generic func type for consturction, predicate, or function func -> identifier _ "(" _ sepBy[sub_expr, ","] _ ")" {% ([name, , , , args]): Func => ({ - ...nodeData([name, ...args]), + ...nodeData, ...rangeFrom([name, ...args]), tag: "Func", name, args }) @@ -140,7 +137,7 @@ func -> identifier _ "(" _ sepBy[sub_expr, ","] _ ")" {% equal_exprs -> sub_expr _ "=" _ sub_expr {% ([left, , , , right]): EqualExprs => ({ - ...nodeData([left, right]), + ...nodeData, ...rangeBetween(left, right), tag: "EqualExprs", left, right }) @@ -148,7 +145,7 @@ equal_exprs -> sub_expr _ "=" _ sub_expr {% equal_predicates -> apply_predicate _ "<->" _ apply_predicate {% ([left, , , , right]): EqualPredicates => ({ - ...nodeData([left, right]), + ...nodeData, ...rangeBetween(left, right), tag: "EqualPredicates", left, right }) @@ -162,7 +159,7 @@ label_stmt label_decl -> "Label" __ identifier __ tex_literal {% ([kw, , variable, , label]): LabelDecl => ({ - ...nodeData([variable, label]), + ...nodeData, ...rangeBetween(rangeOf(kw), label), tag: "LabelDecl", labelType: "MathLabel", @@ -171,7 +168,7 @@ label_decl %} | "Label" __ identifier __ string_lit {% ([kw, , variable, , label]): LabelDecl => ({ - ...nodeData([variable, label]), + ...nodeData, ...rangeBetween(rangeOf(kw), label), tag: "LabelDecl", labelType: "TextLabel", @@ -181,7 +178,7 @@ label_decl no_label -> "NoLabel" __ sepBy1[identifier, ","] {% ([kw, , args]): NoLabel => ({ - ...nodeData([...args]), + ...nodeData, ...rangeFrom([rangeOf(kw), ...args]), tag: "NoLabel", args }) @@ -189,16 +186,16 @@ no_label -> "NoLabel" __ sepBy1[identifier, ","] {% auto_label -> "AutoLabel" __ label_option {% ([kw, , option]): AutoLabel => ({ - ...nodeData([option]), + ...nodeData, ...rangeBetween(kw, option), tag: "AutoLabel", option }) %} label_option - -> "All" {% ([kw]): LabelOption => ({ ...nodeData([]), ...rangeOf(kw), tag: "DefaultLabels" }) %} + -> "All" {% ([kw]): LabelOption => ({ ...nodeData, ...rangeOf(kw), tag: "DefaultLabels" }) %} | sepBy1[identifier, ","] {% - ([variables]): LabelOption => ({ ...nodeData([]), ...rangeFrom(variables), tag: "LabelIDs", variables }) + ([variables]): LabelOption => ({ ...nodeData, ...rangeFrom(variables), tag: "LabelIDs", variables }) %} # Grammar from Domain @@ -207,7 +204,7 @@ type_constructor -> identifier type_arg_list:? {% ([name, a]): TypeConsApp => { const args = optional(a, []); return { - ...nodeData([name, ...args]), + ...nodeData, ...rangeFrom([name, ...args]), tag: "TypeConstructor", name, args }; @@ -223,7 +220,7 @@ type_arg_list -> _ "(" _ sepBy1[type_constructor, ","] _ ")" {% string_lit -> %string_literal {% ([d]): IStringLit => ({ - ...nodeData([]), + ...nodeData, ...rangeOf(d), tag: 'StringLit', contents: d.value @@ -232,7 +229,7 @@ string_lit -> %string_literal {% tex_literal -> %tex_literal {% ([d]): IStringLit => ({ - ...nodeData([]), + ...nodeData, ...rangeOf(d), tag: 'StringLit', contents: d.text.substring(1, d.text.length - 1), // NOTE: remove dollars @@ -241,7 +238,7 @@ tex_literal -> %tex_literal {% identifier -> %identifier {% ([d]) => ({ - ...nodeData([]), + ...nodeData, ...rangeOf(d), tag: 'Identifier', value: d.text, diff --git a/packages/core/src/parser/SubstanceParser.test.ts b/packages/core/src/parser/SubstanceParser.test.ts index 7ace4343c5..e039538679 100644 --- a/packages/core/src/parser/SubstanceParser.test.ts +++ b/packages/core/src/parser/SubstanceParser.test.ts @@ -2,7 +2,6 @@ import { examples } from "@penrose/examples"; import * as fs from "fs"; import * as nearley from "nearley"; import * as path from "path"; -import { ConcreteNode } from "types/ast"; import grammar from "./SubstanceParser"; const outputDir = "/tmp/asts"; @@ -20,18 +19,6 @@ const printAST = (ast: any) => { console.log(JSON.stringify(ast)); }; -export const traverseTree = (root: ConcreteNode) => { - const { nodeType, children } = root; - if (!nodeType) console.log(root); - expect(nodeType).toEqual("Substance"); - expect(children).not.toBe(undefined); - children.map((node) => { - if (!node) console.log(root); - expect(node).not.toBe(undefined); - traverseTree(node); - }); -}; - const subPaths = [ // "linear-algebra-domain/twoVectorsPerp.sub", "set-theory-domain/tree.sub", @@ -203,7 +190,6 @@ describe("Real Programs", () => { test(examplePath, () => { const { results } = parser.feed(prog); sameASTs(results); - traverseTree(results[0]); // write to output folder if (saveASTs) { const exampleName = path.basename(examplePath, ".sub"); From 9eb147d57faed0422caa914b21bfc7d3c77f0a0c Mon Sep 17 00:00:00 2001 From: Sam Estep Date: Wed, 11 May 2022 12:29:03 -0400 Subject: [PATCH 3/4] Implement children function for synthesis/Search --- packages/core/src/synthesis/Search.ts | 65 ++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/packages/core/src/synthesis/Search.ts b/packages/core/src/synthesis/Search.ts index 561e5b3320..21ff99dce7 100644 --- a/packages/core/src/synthesis/Search.ts +++ b/packages/core/src/synthesis/Search.ts @@ -32,8 +32,8 @@ import { MutationGroup, } from "synthesis/Mutation"; import { A, AbstractNode, Identifier, metaProps } from "types/ast"; -import { Env } from "types/domain"; -import { SubProg, SubStmt } from "types/substance"; +import { Env, Type } from "types/domain"; +import { LabelOption, SubExpr, SubProg, SubStmt } from "types/substance"; import { filterContext, initContext, @@ -116,6 +116,54 @@ export const diffSubStmts = ( return exactDiffs.map((d) => toStmtDiff(d, leftSorted)); }; +type SubNode = LabelOption | SubExpr | SubStmt | Type; + +const children = (node: SubNode): SubNode[] => { + switch (node.tag) { + case "ApplyConstructor": + case "ApplyFunction": + case "ApplyPredicate": + case "Func": + case "TypeConstructor": { + return [node.name, ...node.args]; + } + case "AutoLabel": { + return [node.option]; + } + case "Bind": { + return [node.variable, node.expr]; + } + case "Decl": { + return [node.type, node.name]; + } + case "Deconstructor": { + return [node.variable, node.field]; + } + case "DefaultLabels": + case "Identifier": + case "Prop": + case "StringLit": { + return []; + } + case "EqualExprs": + case "EqualPredicates": { + return [node.left, node.right]; + } + case "LabelDecl": { + return [node.variable, node.label]; + } + case "LabelIDs": { + return [...node.variables]; + } + case "NoLabel": { + return [...node.args]; + } + case "TypeVar": { + return [node.name]; + } + } +}; + /** * Determine if two Substance AST nodes are similar. The metric is whether the nodes have common descendents or are equal themselves. * @@ -123,19 +171,16 @@ export const diffSubStmts = ( * @param right a node in the Substance AST * @returns if the nodes have common descendents */ -export const similarNodes = ( - left: AbstractNode, - right: AbstractNode -): boolean => { +export const similarNodes = (left: SubNode, right: SubNode): boolean => { const equalNodes = nodesEqual(left, right); const similarChildren = intersectionWith( - left.children, - right.children, + children(left), + children(right), similarNodes ); - const similarLeft = intersectionWith([left], right.children, similarNodes); - const similarRight = intersectionWith(left.children, [right], similarNodes); + const similarLeft = intersectionWith([left], children(right), similarNodes); + const similarRight = intersectionWith(children(left), [right], similarNodes); // console.log( // prettySubNode(left as any), From a82da0f80e16d3bb5ba4287d23445220ac18ea81 Mon Sep 17 00:00:00 2001 From: Sam Estep Date: Wed, 11 May 2022 12:36:16 -0400 Subject: [PATCH 4/4] Fix Substance AST queries test --- packages/core/src/analysis/SubstanceAnalysis.test.ts | 8 ++++---- packages/core/src/synthesis/Search.ts | 10 +++++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/core/src/analysis/SubstanceAnalysis.test.ts b/packages/core/src/analysis/SubstanceAnalysis.test.ts index 43b5750c94..1be27693fc 100644 --- a/packages/core/src/analysis/SubstanceAnalysis.test.ts +++ b/packages/core/src/analysis/SubstanceAnalysis.test.ts @@ -6,8 +6,8 @@ import { } from "compiler/Substance"; import { dummyIdentifier } from "engine/EngineUtils"; import { intersectionWith } from "lodash"; -import { similarMappings, similarNodes } from "synthesis/Search"; -import { A, ASTNode } from "types/ast"; +import { similarMappings, similarNodes, SubNode } from "synthesis/Search"; +import { A } from "types/ast"; import { Env } from "types/domain"; import { SubProg, SubStmt } from "types/substance"; import { @@ -33,8 +33,8 @@ const compile = (src: string): SubProg => describe("Substance AST queries", () => { test("Similar AST nodes", () => { - let node1: ASTNode; - let node2: ASTNode; + let node1: SubNode; + let node2: SubNode; node1 = compile("Set A"); node2 = compile("Set B"); expect(similarNodes(node1, node2)).toBe(true); diff --git a/packages/core/src/synthesis/Search.ts b/packages/core/src/synthesis/Search.ts index 21ff99dce7..7769104063 100644 --- a/packages/core/src/synthesis/Search.ts +++ b/packages/core/src/synthesis/Search.ts @@ -116,7 +116,12 @@ export const diffSubStmts = ( return exactDiffs.map((d) => toStmtDiff(d, leftSorted)); }; -type SubNode = LabelOption | SubExpr | SubStmt | Type; +export type SubNode = + | LabelOption + | SubExpr + | SubProg + | SubStmt + | Type; const children = (node: SubNode): SubNode[] => { switch (node.tag) { @@ -158,6 +163,9 @@ const children = (node: SubNode): SubNode[] => { case "NoLabel": { return [...node.args]; } + case "SubProg": { + return [...node.statements]; + } case "TypeVar": { return [node.name]; }