Undo creation of workspaces and merge treegen with main source

This commit is contained in:
Sam Vervaeck 2020-05-10 11:58:07 +02:00
parent fee9139427
commit f64da05a36
21 changed files with 654 additions and 1292 deletions

197
package-lock.json generated
View file

@ -5,12 +5,12 @@
"requires": true,
"dependencies": {
"@babel/runtime-corejs3": {
"version": "7.8.4",
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.8.4.tgz",
"integrity": "sha512-+wpLqy5+fbQhvbllvlJEVRIpYj+COUWnnsm+I4jZlA8Lo7/MJmBhGTCHyk1/RWfOqBRJ2MbadddG6QltTKTlrg==",
"version": "7.9.6",
"resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.9.6.tgz",
"integrity": "sha512-6toWAfaALQjt3KMZQc6fABqZwUDDuWzz+cAfPhqyEnzxvdWOAkjwPNxgF8xlmo7OWLsSjaKjsskpKHRLaMArOA==",
"requires": {
"core-js-pure": "^3.0.0",
"regenerator-runtime": "^0.13.2"
"regenerator-runtime": "^0.13.4"
}
},
"@types/chai": {
@ -73,10 +73,9 @@
"dev": true
},
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
},
"ansi-styles": {
"version": "3.2.1",
@ -112,11 +111,6 @@
"integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
"dev": true
},
"astring": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/astring/-/astring-1.4.3.tgz",
"integrity": "sha512-yJlJU/bmN820vL+cbWShu2YQU87dBP5V7BH2N4wODapRv27A2dZtUD0LgjP9lZENvPe9XRoSyWx+pZR6qKqNBw=="
},
"at-least-node": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
@ -233,36 +227,6 @@
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^6.2.0"
},
"dependencies": {
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"string-width": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
}
},
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"requires": {
"ansi-regex": "^5.0.0"
}
}
}
},
"color-convert": {
@ -286,9 +250,9 @@
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"core-js-pure": {
"version": "3.6.4",
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.4.tgz",
"integrity": "sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw=="
"version": "3.6.5",
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz",
"integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA=="
},
"debug": {
"version": "3.2.6",
@ -750,6 +714,15 @@
"ansi-regex": "^4.1.0"
}
},
"supports-color": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz",
"integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
},
"wrap-ansi": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
@ -885,9 +858,9 @@
"dev": true
},
"pegjs": {
"version": "0.11.0-master.b7b87ea",
"resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.11.0-master.b7b87ea.tgz",
"integrity": "sha512-fwjzNiYHRUEUe/86Aaslb/ocbbsAupOcsJz+dlPYtgp3feCDRQOLChHO924XGh7fzSJBTdFCQTzmSOQaWjCTew=="
"version": "0.11.0-master.f69239d",
"resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.11.0-master.f69239d.tgz",
"integrity": "sha512-8PXSaXnLh9hw0R/SfOL1b8JSgiPv1KSl8B1h+sP0uwngmx3Vk+QdB2cuBAT1lXPbP5necVZ2Dl2t4DfR3KKbZg=="
},
"picomatch": {
"version": "2.2.2",
@ -910,9 +883,9 @@
"integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg=="
},
"regenerator-runtime": {
"version": "0.13.3",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
"version": "0.13.5",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
"integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA=="
},
"require-directory": {
"version": "2.1.1",
@ -956,13 +929,20 @@
"dev": true
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"dev": true,
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
"requires": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
},
"dependencies": {
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
}
}
},
"string.prototype.trimend": {
@ -1008,12 +988,11 @@
}
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"dev": true,
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"requires": {
"ansi-regex": "^3.0.0"
"ansi-regex": "^5.0.0"
}
},
"strip-json-comments": {
@ -1022,15 +1001,6 @@
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"dev": true
},
"supports-color": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz",
"integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
},
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@ -1072,6 +1042,33 @@
"dev": true,
"requires": {
"string-width": "^1.0.2 || 2"
},
"dependencies": {
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"dev": true
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"dev": true,
"requires": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
}
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"dev": true,
"requires": {
"ansi-regex": "^3.0.0"
}
}
}
},
"wrap-ansi": {
@ -1084,11 +1081,6 @@
"strip-ansi": "^6.0.0"
},
"dependencies": {
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
},
"ansi-styles": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
@ -1110,29 +1102,6 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"string-width": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
}
},
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"requires": {
"ansi-regex": "^5.0.0"
}
}
}
},
@ -1172,11 +1141,6 @@
"yargs-parser": "^18.1.1"
},
"dependencies": {
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
},
"find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
@ -1186,11 +1150,6 @@
"path-exists": "^4.0.0"
}
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@ -1212,24 +1171,6 @@
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
},
"string-width": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
}
},
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"requires": {
"ansi-regex": "^5.0.0"
}
},
"yargs-parser": {
"version": "18.1.3",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",

View file

@ -4,7 +4,8 @@
"description": "A programming language for rapid prototyping",
"main": "lib/index.js",
"bin": {
"bolt": "lib/bin/bolt.js"
"bolt": "lib/bin/bolt.js",
"bolt-treegen": "lib/bin/bolt-treegen.js"
},
"scripts": {
"test": "mocha lib/test"

148
src/ast.d.ts vendored Normal file
View file

@ -0,0 +1,148 @@
export const enum SyntaxKind {
FunctionBody = 1
BoltStringLiteral = 4
BoltIntegerLiteral = 5
BoltIdentifier = 7
BoltOperator = 8
BoltEOS = 9
BoltComma = 10
BoltSemi = 11
BoltDot = 12
BoltDotDot = 13
BoltRArrow = 14
BoltLArrow = 15
BoltEqSign = 16
BoltFnKeyword = 18
BoltForeignKeyword = 19
BoltLetKeyword = 20
BoltImportKeyword = 21
BoltPubKeyword = 22
BoltModKeyword = 23
BoltEnumKeyword = 24
BoltStructKeyword = 25
BoltNewTypeKeyword = 26
BoltParenthesized = 28
BoltBraced = 29
BoltBracketed = 30
BoltSourceElement = 31
BoltSourceFile = 32
BoltQualName = 33
BoltReferenceTypeNode = 35
BoltBindPattern = 37
BoltTypePattern = 38
BoltExpressionPattern = 39
BoltTuplePatternElement = 40
BoltTuplePattern = 41
BoltRecordPatternField = 42
BoltRecordPattern = 43
BoltCallExpression = 45
BoltYieldExpression = 46
BoltMatchArm = 47
BoltMatchExpression = 48
BoltCase = 49
BoltCaseExpression = 50
BoltBlockExpression = 51
BoltConstantExpression = 52
BoltReturnStatement = 54
BoltResumeStatement = 55
BoltExpressionStatement = 56
BoltModule = 57
BoltParameter = 58
BoltFunctionDeclaration = 60
BoltForeignFunctionDeclaration = 61
BoltVariableDeclaration = 62
BoltPlainImportSymbol = 64
BoltImportDeclaration = 65
BoltRecordDeclarationField = 66
BoltRecordDeclaration = 67
JSOperator = 70
JSIdentifier = 71
JSPattern = 72
JSBindPattern = 73
JSConstantExpression = 75
JSMemberExpression = 77
JSCallExpression = 78
JSBinaryExpression = 79
JSUnaryExpression = 80
JSNewExpression = 81
JSSequenceExpression = 82
JSConditionalExpression = 83
JSReferenceExpression = 84
JSConditionalStatement = 86
JSSourceFile = 88
JSSourceElement = 89
}
export function createFunctionBody(span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltStringLiteral(value: string, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltIntegerLiteral(value: number, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltIdentifier(text: string, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltOperator(text: string, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltEOS(span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltComma(span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltSemi(span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltDot(span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltDotDot(span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltRArrow(span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltLArrow(span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltEqSign(span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltFnKeyword(span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltForeignKeyword(span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltLetKeyword(span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltImportKeyword(span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltPubKeyword(span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltModKeyword(span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltEnumKeyword(span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltStructKeyword(span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltNewTypeKeyword(span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltParenthesized(text: string, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltBraced(text: string, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltBracketed(text: string, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltSourceElement(span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltSourceFile(elements: BoltSourceElement[], span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltQualName(modulePath: BoltIdentifier[], name: BoltSymbol, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltReferenceTypeNode(name: BoltQualName, arguments: BoltTypeNode[] | null, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltBindPattern(name: string, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltTypePattern(typeNode: BoltTypeNode, nestedPattern: BoltPattern, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltExpressionPattern(expression: BoltExpression, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltTuplePatternElement(index: number, pattern: BoltPattern, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltTuplePattern(elements: BoltTuplePatternElement[], span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltRecordPatternField(name: BoltIdentifier, pattern: BoltPattern, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltRecordPattern(fields: BoltRecordPatternField[], span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltCallExpression(operator: BoltExpression, operands: BoltExpression[], span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltYieldExpression(value: BoltExpression, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltMatchArm(pattern: BoltPattern, body: BoltExpression, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltMatchExpression(value: BoltExpression, arms: BoltMatchArm[], span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltCase(test: BoltExpression, result: BoltExpression, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltCaseExpression(cases: BoltCase[], span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltBlockExpression(statements: BoltStatement[], span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltConstantExpression(value: BoltValue, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltReturnStatement(value: BoltExpression | null, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltResumeStatement(value: BoltExpression, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltExpressionStatement(expression: BoltExpression, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltModule(modifiers: BoltDeclarationModifiers, name: BoltQualName, elements: BoltSourceElement, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltParameter(index: number, bindings: BoltPattern, typeNode: BoltTypeNode | null, defaultValue: BoltExpression | null, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltFunctionDeclaration(modifiers: BoltDeclarationModifiers, name: BoltSymbol, params: BoltParameter[], type: BoltTypeNode | null, body: BoltExpression, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltForeignFunctionDeclaration(modifiers: BoltDeclarationModifiers, name: BoltSymbol, params: BoltParameter[], type: BoltTypeNode | null, body: FunctionBody, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltVariableDeclaration(modifiers: BoltDeclarationModifiers, name: BoltSymbol, type: BoltTypeNode | null, value: BoltExpression | null, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltPlainImportSymbol(name: BoltQualName, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltImportDeclaration(file: string, symbols: BoltImportSymbol[], span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltRecordDeclarationField(name: BoltIdentifier, type: BoltTypeNode, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createBoltRecordDeclaration(name: BoltQualName, fields: BoltRecordDeclarationField[], span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createJSOperator(text: string, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createJSIdentifier(text: string, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createJSPattern(span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createJSBindPattern(name: JSIdentifier, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createJSConstantExpression(value: BoltValue, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createJSMemberExpression(value: JSExpression, property: JSExpression, modifiers: JSMemberExpressionModifiers, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createJSCallExpression(operator: JSExpression, operands: JSExpression[], span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createJSBinaryExpression(left: JSExpression, operator: JSOperator, right: JSExpression, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createJSUnaryExpression(operator: JSOperator, operand: JSExpression, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createJSNewExpression(target: JSExpression, arguments: JSExpression[], span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createJSSequenceExpression(expressions: JSExpression[], span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createJSConditionalExpression(test: JSExpression, consequent: JSExpression, alternate: JSExpression, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createJSReferenceExpression(name: string, span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createJSConditionalStatement(test: JSExpression, consequent: JSStatement[], alternate: JSStatement[], span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createJSSourceFile(elements: JSSourceElement[], span: TextSpan | null = null, origNodes: SyntaxRange | null = null);
export function createJSSourceElement(span: TextSpan | null = null, origNodes: SyntaxRange | null = null);

View file

@ -1,973 +0,0 @@
// FIXME SyntaxBase.getSpan() does not work then [n1, n2] is given as origNode
import * as path from "path"
import { Stream, StreamWrapper, FastStringMap } from "./util"
import { Scanner } from "./scanner"
import { RecordType, PrimType, OptionType, VariantType, stringType, intType, boolType } from "./checker"
import { bind } from "./bindings"
import { Value } from "./evaluator"
interface JsonArray extends Array<Json> { };
interface JsonObject { [key: string]: Json }
type Json = null | string | boolean | number | JsonArray | JsonObject;
export type TokenStream = Stream<Token>;
export enum SyntaxKind {
// Bolt language AST
// Tokens
StringLiteral,
IntegerLiteral,
Identifier,
Operator,
Parenthesized,
Braced,
Bracketed,
Semi,
Comma,
Colon,
Dot,
RArrow,
EqSign,
// Keywords
FnKeyword,
ForeignKeyword,
LetKeyword,
ImportKeyword,
PubKeyword,
ModKeyword,
EnumKeyword,
StructKeyword,
NewTypeKeyword,
// Special nodes
SourceFile,
Module,
QualName,
Sentence,
Param,
EOS,
// Patterns
BindPatt,
TypePatt,
RecordPatt,
TuplePatt,
// Expressions
CallExpr,
ConstExpr,
RefExpr,
MatchExpr,
// Statements
RetStmt,
CondStmt,
// Type declarations
TypeRef,
// Declaration nodes
VarDecl,
FuncDecl,
ImportDecl,
RecordDecl,
VariantDecl,
NewTypeDecl,
// JavaScript AST
// Expressions
JSReferenceExpression,
JSCallExpression,
JSYieldExpression,
// Statements
JSReturnStatement,
// Special nodes
JSSourceFile,
// C language AST
// Miscellaneous elements
CSourceFile,
}
export class TextFile {
constructor(public origPath: string) {
}
get fullPath() {
return path.resolve(this.origPath)
}
}
export class TextPos {
constructor(
public offset: number,
public line: number,
public column: number
) {
}
clone() {
return new TextPos(this.offset, this.line, this.column)
}
}
export class TextSpan {
constructor(
public file: TextFile,
public start: TextPos,
public end: TextPos
) {
}
clone() {
return new TextSpan(this.file, this.start.clone(), this.end.clone());
}
}
export type SyntaxRange = [Syntax, Syntax];
abstract class SyntaxBase {
abstract kind: SyntaxKind;
public parentNode: Syntax | null = null;
constructor(
public span: TextSpan | null,
public origNode: SyntaxRange | null,
) {
}
}
export const fileType = new PrimType();
@bind('Bolt.AST.StringLiteral')
export class StringLiteral extends SyntaxBase {
kind: SyntaxKind.StringLiteral = SyntaxKind.StringLiteral;
constructor(
public value: string,
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
}
export class IntegerLiteral extends SyntaxBase {
kind: SyntaxKind.IntegerLiteral = SyntaxKind.IntegerLiteral;
constructor(
public value: bigint,
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
toJSON(): Json {
return {
value: ['bigint', this.value.toString()],
span: this.span !== null ? this.span.toJSON() : null,
}
}
*getChildren(): IterableIterator<Syntax> {
}
}
export enum PunctType {
Paren,
Bracket,
Brace,
}
export class EOS extends SyntaxBase {
kind: SyntaxKind.EOS = SyntaxKind.EOS;
}
export abstract class Punctuated extends SyntaxBase {
constructor(
public text: string,
span: TextSpan,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
toSentences() {
const scanner = new Scanner(this.span!.file, this.text)
return scanner.scanTokens()
}
toTokenStream() {
const startPos = this.span!.start;
return new Scanner(this.span!.file, this.text, new TextPos(startPos.offset+1, startPos.line, startPos.column+1));
}
}
export class Parenthesized extends Punctuated {
kind: SyntaxKind.Parenthesized = SyntaxKind.Parenthesized;
}
export class Braced extends Punctuated {
kind: SyntaxKind.Braced = SyntaxKind.Braced;
}
export class Bracketed extends Punctuated {
kind: SyntaxKind.Bracketed = SyntaxKind.Bracketed;
}
export class Identifier extends SyntaxBase {
kind: SyntaxKind.Identifier = SyntaxKind.Identifier;
constructor(
public text: string,
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
}
export class Operator extends SyntaxBase {
kind: SyntaxKind.Operator = SyntaxKind.Operator;
constructor(
public text: string,
public span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
parentNode: Syntax | null = null
) {
super(span, origNode);
}
}
export class Semi extends SyntaxBase {
kind: SyntaxKind.Semi = SyntaxKind.Semi;
}
export class Colon extends SyntaxBase {
kind: SyntaxKind.Colon = SyntaxKind.Colon;
}
export class Comma extends SyntaxBase {
kind: SyntaxKind.Comma = SyntaxKind.Comma;
}
export class RArrow extends SyntaxBase {
kind: SyntaxKind.RArrow = SyntaxKind.RArrow;
}
export class EqSign extends SyntaxBase {
kind: SyntaxKind.EqSign = SyntaxKind.EqSign;
}
export class Dot extends SyntaxBase {
kind: SyntaxKind.Dot = SyntaxKind.Dot;
}
export type Token
= Semi
| Comma
| Colon
| EqSign
| Dot
| RArrow
| EOS
| Identifier
| Operator
| StringLiteral
| IntegerLiteral
| Parenthesized
| Braced
| Bracketed
export class Sentence extends SyntaxBase {
kind: SyntaxKind.Sentence = SyntaxKind.Sentence;
constructor(
public tokens: Token[],
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
toTokenStream() {
return new StreamWrapper(
this.tokens,
() => new EOS(new TextSpan(this.getSpan().file, this.getSpan().end.clone(), this.getSpan().end.clone()))
);
}
}
export class QualName extends SyntaxBase {
kind: SyntaxKind.QualName = SyntaxKind.QualName;
constructor(
public name: Identifier | Operator,
public path: Identifier[],
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
get fullText() {
let out = ''
for (const chunk of this.path) {
out += chunk.text + '.'
}
return out + this.name.text
}
}
export class Param extends SyntaxBase {
kind: SyntaxKind.Param = SyntaxKind.Param;
constructor(
public bindings: Patt,
public typeDecl: TypeDecl | null,
public defaultValue: Expr | null,
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
}
export class BindPatt extends SyntaxBase {
kind: SyntaxKind.BindPatt = SyntaxKind.BindPatt;
constructor(
public name: Identifier,
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
}
// FIXME make this a node
export interface RecordPattField {
name: Identifier;
pattern: Patt,
}
export class RecordPatt extends SyntaxBase {
kind: SyntaxKind.RecordPatt = SyntaxKind.RecordPatt;
constructor(
public typeDecl: TypeDecl,
public fields: RecordPattField[],
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
}
export class TuplePatt extends SyntaxBase {
kind: SyntaxKind.TuplePatt = SyntaxKind.TuplePatt;
constructor(
public elements: Patt[],
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
}
export class TypePatt extends SyntaxBase {
kind: SyntaxKind.TypePatt = SyntaxKind.TypePatt;
constructor(
public typeDecl: TypeDecl,
public pattern: Patt,
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
}
export type Patt
= BindPatt
| Expr
| TypePatt
| TuplePatt
| RecordPatt
export class RefExpr extends SyntaxBase {
kind: SyntaxKind.RefExpr = SyntaxKind.RefExpr;
constructor(
public name: QualName,
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
}
export class CallExpr extends SyntaxBase {
kind: SyntaxKind.CallExpr = SyntaxKind.CallExpr;
constructor(
public operator: Expr,
public args: Expr[],
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
}
// FIXME make this a node
export type MatchArm = [Patt, Expr | Stmt[]];
export class MatchExpr extends SyntaxBase {
kind: SyntaxKind.MatchExpr = SyntaxKind.MatchExpr;
constructor(
public value: Expr,
public arms: MatchArm[],
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
}
export class ConstExpr extends SyntaxBase {
kind: SyntaxKind.ConstExpr = SyntaxKind.ConstExpr;
constructor(
public value: Value,
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
}
export type Expr
= ConstExpr
| RefExpr
| CallExpr
| MatchExpr
export class RetStmt extends SyntaxBase {
kind: SyntaxKind.RetStmt = SyntaxKind.RetStmt;
constructor(
public value: Expr | null,
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
}
export interface Case {
conditional: Expr,
consequent: Stmt[]
}
export class CondStmt extends SyntaxBase {
kind: SyntaxKind.CondStmt = SyntaxKind.CondStmt
constructor(
public cases: Case[],
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
}
export type Stmt
= RetStmt
| CondStmt
export class TypeRef extends SyntaxBase {
kind: SyntaxKind.TypeRef = SyntaxKind.TypeRef;
constructor(
public name: QualName,
public typeArgs: TypeDecl[],
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
}
export type TypeDecl
= TypeRef
export class FuncDecl extends SyntaxBase {
kind: SyntaxKind.FuncDecl = SyntaxKind.FuncDecl;
constructor(
public isPublic: boolean,
public target: string,
public name: QualName,
public params: Param[],
public returnType: TypeDecl | null,
public body: Body | null,
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
}
export class VarDecl extends SyntaxBase {
kind: SyntaxKind.VarDecl = SyntaxKind.VarDecl;
constructor(
public isMutable: boolean,
public bindings: Patt,
public typeDecl: TypeDecl | null,
public value: Expr | null,
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
}
export class ImportDecl extends SyntaxBase {
kind: SyntaxKind.ImportDecl = SyntaxKind.ImportDecl;
constructor(
public file: string,
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
}
export class RecordDecl extends SyntaxBase {
kind: SyntaxKind.RecordDecl = SyntaxKind.RecordDecl;
fields: Map<Identifier, TypeDecl>;
constructor(
public isPublic: boolean,
public name: QualName,
fields: Iterable<[Identifier, TypeDecl]>,
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode)
this.fields = new Map(fields);
}
}
export class NewTypeDecl extends SyntaxBase {
kind: SyntaxKind.NewTypeDecl = SyntaxKind.NewTypeDecl;
constructor(
public isPublic: boolean,
public name: Identifier,
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
}
export type Decl
= Sentence
| FuncDecl
| ImportDecl
| NewTypeDecl
| VarDecl
| RecordDecl
export type Syntax
= Decl
| Expr
| Token
| Stmt
| Patt
| TypeDecl
| Module
| SourceFile
| QualName
| Param
| EOS
export type SourceElement = (Module | Decl | Stmt | Expr);
export class Module extends SyntaxBase {
kind: SyntaxKind.Module = SyntaxKind.Module;
constructor(
public isPublic: boolean,
public name: QualName,
public elements: SourceElement[],
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
}
export class SourceFile extends SyntaxBase {
kind: SyntaxKind.SourceFile = SyntaxKind.SourceFile;
constructor(
public elements: SourceElement[],
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
}
export function isExpr(node: Syntax): node is Expr {
return node.kind === SyntaxKind.ConstExpr
|| node.kind === SyntaxKind.CallExpr
|| node.kind === SyntaxKind.MatchExpr
|| node.kind === SyntaxKind.RefExpr;
}
export class JSYieldExpression extends SyntaxBase {
kind: SyntaxKind.JSYieldExpression = SyntaxKind.JSYieldExpression;
constructor(
public value: JSExpression,
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
}
export class JSReferenceExpression extends SyntaxBase {
kind: SyntaxKind.JSReferenceExpression = SyntaxKind.JSReferenceExpression;
constructor(
public name: string,
span: TextSpan | null = null,
origNode: SyntaxRange | null = null
) {
super(span, origNode);
}
}
export class JSCallExpression extends SyntaxBase {
kind: SyntaxKind.JSCallExpression = JSCallExpression;
constructor(
public operator: JSExpression,
public operands: JSExpression[],
) {
}
}
export type JSExpression
= JSYieldExpression
| JSReferenceExpression
| JSCallExpression
export type JSStatement
= JSReturnStatement
export type JSDeclaration
= JSVariableDeclaration
| JSFunctionDeclaration
export type JSSourceElement
= JSStatement
| JSDeclaration
export class JSSourceFile extends SyntaxBase {
kind: SyntaxKind.JSSourceFile = SyntaxKind.JSSourceFile;
constructor(
public elements: JSSourceElement[],
span: TextSpan | null = null,
origNode: SyntaxRange | null = null,
) {
super(span, origNode);
}
}
export type JSSyntax
= JSExpression
| JSStatement
| JSDeclaration
| JSSourceFile
export type CSourceElement
= never
export class CSourceFile extends SyntaxBase {
kind: SyntaxKind.CSourceFile = SyntaxKind.CSourceFile;
constructor(
public elements: CSourceElement,
span: TextSpan | null = null,
origNode: SyntaxRange | null = null
) {
super(span, origNode);
}
}
export type CSyntax
= CSourceFile
export type AnySyntax
= Syntax
| JSSyntax
| CSyntax
export type AnySourceFile
= SourceFile
| JSSourceFile
| CSourceFile
export function nodeToJSON(node: Syntax) {
const obj: Json = {
kind: SyntaxKind[node.kind],
};
for (const key of Object.keys(node)) {
if (key !== 'kind' && key !== 'origNode' && key !== 'parentNode') {
const value = (node as any)[key];
if (Array.isArray(value)) {
obj[key] = value.map(visit)
} else {
obj[key] = visit(value);
}
}
}
return obj;
function visit(value: any) {
if (isNode(value)) {
return nodeToJSON(node);
} else {
return value;
}
}
}
export function isJSNode(node: Syntax): node is JSSyntax {
return SyntaxKind[node.kind].startsWith('JS');
}
export function getChildren(node: Syntax) {
const out: Syntax[] = [];
for (const key of Object.keys(node)) {
if (key !== 'kind' && key !== 'origNode' && key !== 'parentNode') {
const value = (node as any)[key];
if (Array.isArray(value)) {
for (const element of value) {
visit(element)
}
} else {
visit(value)
}
}
}
return out;
function visit(value: any) {
if (isNode(value)) {
out.push(value);
}
}
}
export function kindToString(kind: SyntaxKind) {
return SyntaxKind[kind];
}
export function isNode(value: any): value is Syntax {
return typeof value.kind === 'number'
&& Object.prototype.hasOwnProperty.call(SyntaxKind, value.kind)
}
export function getTargetLanguage(node: AnySyntax) {
switch (node.kind) {
case SyntaxKind.StringLiteral:
case SyntaxKind.IntegerLiteral:
case SyntaxKind.Identifier:
case SyntaxKind.Operator:
case SyntaxKind.Parenthesized:
case SyntaxKind.Braced:
case SyntaxKind.Bracketed:
case SyntaxKind.Semi:
case SyntaxKind.Comma:
case SyntaxKind.Colon:
case SyntaxKind.Dot:
case SyntaxKind.RArrow:
case SyntaxKind.EqSign:
case SyntaxKind.FnKeyword:
case SyntaxKind.ForeignKeyword:
case SyntaxKind.LetKeyword:
case SyntaxKind.ImportKeyword:
case SyntaxKind.PubKeyword:
case SyntaxKind.ModKeyword:
case SyntaxKind.EnumKeyword:
case SyntaxKind.StructKeyword:
case SyntaxKind.NewTypeKeyword:
case SyntaxKind.SourceFile:
case SyntaxKind.Module:
case SyntaxKind.QualName:
case SyntaxKind.Sentence:
case SyntaxKind.Param:
case SyntaxKind.EOS:
case SyntaxKind.BindPatt:
case SyntaxKind.TypePatt:
case SyntaxKind.RecordPatt:
case SyntaxKind.TuplePatt:
case SyntaxKind.CallExpr:
case SyntaxKind.ConstExpr:
case SyntaxKind.RefExpr:
case SyntaxKind.MatchExpr:
case SyntaxKind.RetStmt:
case SyntaxKind.CondStmt:
case SyntaxKind.TypeRef:
case SyntaxKind.VarDecl:
case SyntaxKind.FuncDecl:
case SyntaxKind.ImportDecl:
case SyntaxKind.RecordDecl:
case SyntaxKind.VariantDecl:
case SyntaxKind.NewTypeDecl:
return "Bolt";
case SyntaxKind.JSReferenceExpression:
case SyntaxKind.JSCallExpression:
case SyntaxKind.JSYieldExpression:
case SyntaxKind.JSReturnStatement:
case SyntaxKind.JSSourceFile:
return "JS";
case SyntaxKind.CSourceFile:
return "C";
default:
throw new Error(`Could not determine language of ${SyntaxKind[node.kind]}.`);
}
}
export function setParents(node: Syntax) {
for (const child of getChildren(node)) {
child.parentNode = node
setParents(child)
}
}

View file

@ -3,15 +3,23 @@
import * as path from "path"
import * as fs from "fs"
import { parse, SyntaxError } from "../parser"
import { Declaration, NodeDeclaration, TypeDeclaration, EnumDeclaration, TypeNode, NodeField } from "../ast"
import { parse, SyntaxError } from "../treegen/parser"
import { Syntax, Declaration, NodeDeclaration, TypeDeclaration, EnumDeclaration, TypeNode, NodeField } from "../ast"
import { FileWriter } from "../util"
import minimist from "minimist"
for (const filename of process.argv.slice(2)) {
const PACKAGE_ROOT = path.join(__dirname, '..', '..');
const argv = minimist(process.argv.slice(2));
const jsFilePath = argv['js-file'] ?? 'lib/ast.js';
const dtsFilePath = argv['dts-file'] ?? 'src/ast.d.ts';
for (const filename of argv._) {
const contents = fs.readFileSync(filename, 'utf8');
let decls: Declaration[];
try {
decls = parse(contents);
decls = parse(contents, { prefix: getFileStem(filename) });
} catch (e) {
if (e instanceof SyntaxError) {
console.error(`${filename}:${e.location.start.line}:${e.location.start.column}: ${e.message}`);
@ -20,22 +28,29 @@ for (const filename of process.argv.slice(2)) {
throw e;
}
}
const generated = generateAST(decls, getFileStem(filename));
fs.writeFileSync('src/ast-generated.js', generated, 'utf8');
const { jsFile, dtsFile } = generateAST(decls);
fs.writeFileSync(jsFilePath, jsFile, 'utf8');
fs.writeFileSync(dtsFilePath, dtsFile, 'utf8');
}
interface FastStringMap<T> { [key: string]: T }
function generateAST(decls: Declaration[], langName: string) {
function generateAST(decls: Declaration[]) {
let jsFile = new FileWriter();
let dtsFile = new FileWriter();
let i;
// Sort declarations by category
const nodeDecls: NodeDeclaration[] = decls.filter(decl => decl.type === 'NodeDeclaration') as NodeDeclaration[];
const typeDecls: TypeDeclaration[] = decls.filter(decl => decl.type === 'TypeDeclaration') as TypeDeclaration[];
const enumDecls: EnumDeclaration[] = decls.filter(decl => decl.type === 'EnumDeclaration') as EnumDeclaration[];
const declByName: FastStringMap<Declaration> = Object.create(null);
i = 0;
for (const decl of decls) {
decl.index = i++;
declByName[decl.name] = decl;
}
@ -52,48 +67,91 @@ function generateAST(decls: Declaration[], langName: string) {
}
}
// After we're done mappping parents to children, we can use isLeafNode()
// to store the nodes we will be iterating most frequently on.
const leafNodes: NodeDeclaration[] = nodeDecls.filter(decl => isLeafNode(decl.name));
// Write a JavaScript file that contains all AST definitions.
jsFile.write(`\nconst NODE_TYPES = [\n`);
jsFile.write(`\nconst NODE_TYPES = {\n`);
jsFile.indent();
for (const decl of decls) {
for (const decl of leafNodes) {
if (decl.type === 'NodeDeclaration' && isLeafNode(decl.name)) {
jsFile.write(`'${decl.name}': new Map([\n`);
jsFile.write(`'${decl.name}': {\n`);
jsFile.indent();
jsFile.write(`index: ${decl.index},\n`);
jsFile.write(`fields: new Map([\n`);
jsFile.indent();
for (const field of getAllFields(decl)) {
jsFile.write(`[${field.name}, ${emitTypeNode(field.typeNode)}],\n`);
jsFile.write(`['${field.name}', ${JSON.stringify(jsonify(field.typeNode))}],\n`);
}
jsFile.dedent();
jsFile.write(']),\n');
jsFile.dedent();
jsFile.write('},\n');
}
}
jsFile.dedent();
jsFile.write('];\n\n');
jsFile.write('};\n\n');
function emitTypeNode(typeNode: TypeNode): string {
console.error(typeNode);
if (typeNode.type === 'ReferenceTypeNode') {
if (hasDeclarationNamed(typeNode.name)) {
return typeNode.name;
} else if (typeNode.name === 'Bool') {
return 'boolean';
} else if (typeNode.name === 'String') {
return 'string';
} else if (typeNode.name === 'usize') {
return 'bigint';
} else if (typeNode.name === 'Vec') {
return `${emitTypeNode(typeNode.typeArgs[0])}[]`;
} else if (typeNode.name === 'Option') {
return `${emitTypeNode(typeNode.typeArgs[0])} | null`;
}
}
throw new Error(`Could not emit TypeScript type for type node ${typeNode.type}.`);
jsFile.write(fs.readFileSync(path.join(PACKAGE_ROOT, 'src', 'treegen', 'ast-template.js'), 'utf8'));
jsFile.write(`if (typeof module !== 'undefined') {\n module.exports = exported;\n}\n\n`)
// Write corresponding TypeScript declarations
dtsFile.write(`\nexport const enum SyntaxKind {\n`);
for (const decl of leafNodes) {
dtsFile.write(` ${decl.name} = ${decl.index}\n`);
}
dtsFile.write(`}\n\n`);
for (const decl of leafNodes) {
dtsFile.write(`export function create${decl.name}(`);
for (const field of getAllFields(decl)) {
dtsFile.write(`${field.name}: ${emitTypeScriptType(field.typeNode)}, `);
}
dtsFile.write(`span: TextSpan | null = null, origNodes: SyntaxRange | null = null);\n`);
}
return {
jsFile: jsFile.currentText,
dtsFile: dtsFile.currentText,
};
// Below are some useful functions
function hasDeclarationNamed(name: string): boolean {
return name in declByName;
}
function emitTypeScriptType(typeNode: TypeNode): string {
if (typeNode.type === 'ReferenceTypeNode') {
if (hasDeclarationNamed(typeNode.name)) {
return typeNode.name;
} else if (typeNode.name === 'Option') {
return `${emitTypeScriptType(typeNode.typeArgs[0])} | null`;
} else if (typeNode.name === 'Vec') {
return `${emitTypeScriptType(typeNode.typeArgs[0])}[]`;
} else if (typeNode.name === 'String') {
return `string`;
} else if (typeNode.name === 'Int') {
return `bigint`;
} else if (typeNode.name === 'usize') {
return `number`;
} else if (typeNode.name === 'bool') {
return `boolean`;
} else {
throw new Error(`Could not emit TypeScript type for reference type node named ${typeNode.name}`);
}
} else if (typeNode.type === 'UnionTypeNode') {
return typeNode.elements.map(emitTypeScriptType).join(' | ');
}
//throw new Error(`Could not emit TypeScript type for type node ${typeNode.type}`);
}
function getAllFields(nodeDecl: NodeDeclaration) {
let out: NodeField[] = [];
pushAll(out, nodeDecl.fields);
@ -123,8 +181,6 @@ function generateAST(decls: Declaration[], langName: string) {
return childrenOf[name] === undefined || childrenOf[name].length === 0;
}
return jsFile.currentText;
}
function pushAll<T>(arr: T[], els: T[]): void {
@ -133,6 +189,10 @@ function pushAll<T>(arr: T[], els: T[]): void {
}
}
function isNode(value: any): value is Syntax {
return typeof value === 'object' && value !== null && value.__IS_NODE;
}
function jsonify(value: any) {
function visitNode(node: any) {
@ -140,7 +200,7 @@ function jsonify(value: any) {
const obj: any = {};
for (const key of Object.keys(node)) {
if (key !== 'type' && key !== 'span') {
if (key !== 'type' && key !== 'span' && key !== '__IS_NODE') {
const value = node[key];
if (Array.isArray(value)) {
obj[key] = value.map(visit);
@ -154,7 +214,7 @@ function jsonify(value: any) {
}
function visit(value: any) {
if (value.__IS_NODE) {
if (isNode(value)) {
return visitNode(value);
} else {
return value;
@ -164,6 +224,13 @@ function jsonify(value: any) {
return visit(value);
}
function stripSuffix(str: string, suffix: string): string {
if (!str.endsWith(suffix)) {
return str;
}
return str.substring(0, str.length-suffix.length);
}
function getFileStem(filepath: string) {
return path.basename(filepath).split('.')[0];
}

View file

@ -5,11 +5,9 @@ import "source-map-support/register"
import * as path from "path"
import * as fs from "fs-extra"
import { spawnSync } from "child_process"
import yargs from "yargs"
import { Package, loadPackage } from "../package"
import { Program } from "../program"
import { TextFile } from "../ast"
@ -74,7 +72,7 @@ yargs
const files = toArray(args.path as string[] | string).map(filepath => new TextFile(filepath, args['work-dir']));
const program = new Program(files)
program.compile({ target: "JS" });
program.compile("JS");
})

View file

@ -3,7 +3,7 @@ import {
Syntax,
SyntaxKind,
ImportDecl,
isNode,
Patt,
} from "./ast"
import { FastStringMap } from "./util"
@ -130,6 +130,9 @@ export class TypeChecker {
switch (node.kind) {
case SyntaxKind.RefExpr:
return anyType;
case SyntaxKind.ConstExpr:
return node.value.type;
@ -222,6 +225,10 @@ export class TypeChecker {
}
break;
case SyntaxKind.RefExpr:
// TODO implement this
break;
case SyntaxKind.Module:
case SyntaxKind.SourceFile:
for (const element of node.elements) {

View file

@ -16,6 +16,8 @@ import {
isExpr,
} from "./ast"
import { Program } from "./program"
export interface CompilerOptions {
target: string;
}
@ -30,7 +32,7 @@ export class Compiler {
readonly target: string;
constructor(public checker: TypeChecker, options: CompilerOptions) {
constructor(public program: Program, public checker: TypeChecker, options: CompilerOptions) {
this.target = options.target
}
@ -44,14 +46,14 @@ export class Compiler {
type: 'Program',
body,
loc: {
source: s.getFile().path
source: s.span!.file.path
}
}
});
}
protected compileExpr(node: Syntax, preamble: Syntax[]): Expr {
protected compileExpr(node: Syntax, preamble: Syntax[]): JSExpression {
switch (node.kind) {
case SyntaxKind.CallExpr:

View file

@ -1,16 +1,10 @@
import * as astring from "astring"
import { Syntax, SyntaxKind, isJSNode } from "./ast"
export class Emitter {
emit(node: Syntax) {
if (isJSNode(node)) {
return astring.generate(node)
}
switch (node.kind) {
case SyntaxKind.SourceFile:
@ -28,3 +22,11 @@ export class Emitter {
}
}
/**
* A wrapper around `Emitter` for quick emission of AST nodes with sane defaults.
*/
export function emit(node: Syntax) {
const emitter = new Emitter();
return emitter.emit(node);
}

View file

@ -65,6 +65,43 @@ export class RecordWrapper extends RecordValue {
}
class Environment {
private symbols: FastStringMap<Value> = Object.create(null);
constructor(public parentEnv: Environment | null = null) {
}
setValue(name: string, value: Value) {
if (name in this.symbols) {
throw new Error(`A variable with the name '${name}' already exists.`);
}
this.symbols[name] = value;
}
updateValue(name: string, newValue: Value) {
if (!(name in this.symbols)) {
throw new Error(`Trying to update a variable '${name}' that has not been declared.`);
}
}
lookup(name: string) {
let curr = this as Environment;
while (true) {
if (name in curr.symbols) {
return curr.symbols[name];
}
if (curr.parentEnv === null) {
break;
}
curr = curr.parentEnv;
}
throw new Error(`A variable named '${name}' was not found.`);
}
}
export class Evaluator {
constructor(public checker: TypeChecker) {
@ -99,27 +136,30 @@ export class Evaluator {
}
}
eval(node: Syntax): Value {
eval(node: Syntax, env: Environment = new Environment()): Value {
switch (node.kind) {
case SyntaxKind.SourceFile:
case SyntaxKind.Module:
for (const element of node.elements) {
this.eval(element);
this.eval(element, env);
}
break;
case SyntaxKind.RefExpr:
return env.lookup(node.name.fullText);
case SyntaxKind.NewTypeDecl:
case SyntaxKind.RecordDecl:
case SyntaxKind.FuncDecl:
break;
case SyntaxKind.MatchExpr:
const value = this.eval(node.value);
const value = this.eval(node.value, env);
for (const [pattern, result] of node.arms) {
if (this.match(value, pattern)) {
return this.eval(result as Expr)
return this.eval(result as Expr, env)
}
}
return new PrimValue(this.checker.getTypeNamed('Void')!, null);

View file

@ -2,38 +2,32 @@
// FIXME Actually, the syntax expander could make use of algebraic effects to
// easily specify how the next expansion should happen. Just a thought.
import {
TokenStream,
SyntaxKind,
Syntax,
SourceFile,
Decl,
RecordPatt,
Identifier,
TypeRef,
Patt,
ConstExpr,
QualName,
TuplePatt,
BindPatt,
TypePatt,
MatchExpr,
Stmt,
Module,
import {
BoltSyntax,
createBoltRecordPattern,
createBoltIdentifier,
createBoltReferenceTypeNode,
createBoltConstantExpression,
createBoltTuplePattern,
createBoltQualName,
createBoltTypePattern,
createBoltBindPattern,
BoltPattern,
} from "./ast"
import { BoltTokenStream } from "./util"
import { TypeChecker } from "./checker"
import { Parser, ParseError } from "./parser"
import { Evaluator, TRUE, FALSE } from "./evaluator"
interface Transformer {
pattern: Patt;
transform: (node: TokenStream) => Syntax;
pattern: BoltPattern;
transform: (node: BoltTokenStream) => BoltSyntax;
}
function createTypeRef(text: string) {
const ids = text.split('.').map(name => new Identifier(name))
return new TypeRef(new QualName(ids[ids.length-1], ids.slice(0, -1)), [])
function createSimpleBoltReferenceTypeNode(text: string) {
const ids = text.split('.').map(name => createBoltIdentifier(name))
return createBoltReferenceTypeNode(createBoltQualName(ids[ids.length-1], ids.slice(0, -1)), [])
}
/// This is actually a hand-parsed version of the following:
@ -50,27 +44,27 @@ function createTypeRef(text: string) {
/// }
/// ],
/// }
const PATTERN_SYNTAX: Patt =
new RecordPatt(
createTypeRef('Bolt.AST.Sentence'),
const PATTERN_SYNTAX: Pattern =
createBoltRecordPattern(
createSimpleBoltReferenceTypeNode('Bolt.AST.Sentence'),
[{
name: new Identifier('elements'),
pattern: new TuplePatt([
new RecordPatt(
createTypeRef('Bolt.AST.Identifier'),
name: createBoltIdentifier('elements'),
pattern: createBoltTuplePattern([
createBoltRecordPattern(
createSimpleBoltReferenceTypeNode('Bolt.AST.Identifier'),
[{
name: new Identifier('text'),
pattern: new ConstExpr('syntax')
name: createBoltIdentifier('text'),
pattern: createBoltConstantExpression('syntax')
}]
),
new RecordPatt(
createTypeRef('Bolt.AST.Braced'),
createBoltRecordPattern(
createSimpleBoltReferenceTypeNode('Bolt.AST.Braced'),
[{
name: new Identifier('elements'),
pattern: new TuplePatt([
new TypePatt(createTypeRef('Bolt.AST.Pattern'), new BindPatt(new Identifier('pattern'))),
new TypePatt(createTypeRef('Bolt.AST.RArrow'), new BindPatt(new Identifier('_'))),
new TypePatt(createTypeRef('Bolt.AST.Expr'), new BindPatt(new Identifier('expression')))
name: createBoltIdentifier('elements'),
pattern: createBoltTuplePattern([
createBoltTypePattern(createSimpleBoltReferenceTypeNode('Bolt.AST.Pattern'), createBoltBindPattern(createBoltIdentifier('pattern'))),
createBoltTypePattern(createSimpleBoltReferenceTypeNode('Bolt.AST.RArrow'), createBoltBindPattern(createBoltIdentifier('_'))),
createBoltTypePattern(createSimpleBoltReferenceTypeNode('Bolt.AST.Expr'), createBoltBindPattern(createBoltIdentifier('expression')))
])
}]
)

View file

@ -1,12 +1,7 @@
import { TextFile } from "./ast"
export class Package {
constructor(
public name: string | null,
public files: TextFile[],
) {
constructor(public rootDir: string) {
}

View file

@ -1,26 +1,34 @@
import * as path from "path"
import * as fs from "fs"
import * as fs from "fs-extra"
import * as crypto from "crypto"
import { FastStringMap } from "./util"
import { Parser } from "./parser"
import { TypeChecker } from "./checker"
import { Evaluator } from "./evaluator"
import { Expander } from "./expander"
import { Scanner } from "./scanner"
import { Compiler } from "./compiler"
import { TextFile, SourceFile } from "./ast"
import { emit } from "./emitter"
import { TextFile, SourceFile, AnySourceFile } from "./ast"
import { upsearchSync, FastStringMap } from "./util"
import { Package } from "./package"
const targetExtensions: FastStringMap<string> = {
'JS': '.mjs'
};
export class Program {
parser: Parser
evaluator: Evaluator;
checker: TypeChecker;
expander: Expander;
public parser: Parser
public evaluator: Evaluator;
public checker: TypeChecker;
public expander: Expander;
sourceFiles = new Map<TextFile, SourceFile>();
private sourceFiles = new Map<string, SourceFile>();
private packages: FastStringMap<Package> = Object.create(null);
constructor(public files: TextFile[]) {
constructor(files: TextFile[]) {
this.checker = new TypeChecker();
this.parser = new Parser();
this.evaluator = new Evaluator(this.checker);
@ -28,19 +36,42 @@ export class Program {
for (const file of files) {
const contents = fs.readFileSync(file.fullPath, 'utf8');
const scanner = new Scanner(file, contents)
this.sourceFiles.set(file, scanner.scan());
this.sourceFiles.set(file.fullPath, scanner.scan());
}
}
compile(file: TextFile) {
const original = this.sourceFiles.get(file);
if (original === undefined) {
throw new Error(`File ${file.path} does not seem to be part of this Program.`)
public getPackage(filepath: string) {
filepath = path.resolve(filepath);
const projectFile = upsearchSync('Boltfile', path.dirname(filepath));
if (projectFile === null) {
return null;
}
const expanded = this.expander.getFullyExpanded(original) as SourceFile;
const compiler = new Compiler(this.checker, { target: "JS" })
const compiled = compiler.compile(expanded)
return compiled
const projectDir = path.resolve(path.dirname(projectFile));
if (this.packages[projectDir] !== undefined) {
return this.packages[projectDir];
}
return this.packages[projectDir] = new Package(projectDir);
}
public compile(target: string) {
const compiler = new Compiler(this, this.checker, { target })
const expanded: SourceFile[] = [];
for (const [filepath, sourceFile] of this.sourceFiles) {
expanded.push(this.expander.getFullyExpanded(sourceFile) as SourceFile);
}
const compiled = compiler.compile(expanded) as AnySourceFile[];
for (const rootNode of compiled) {
const filepath = rootNode.span!.file.fullPath;
const pkg = this.getPackage(filepath);
if (pkg !== null) {
}
fs.writeFileSync(this.mapToTargetFile(rootNode), emit(node), 'utf8');
}
}
private mapToTargetFile(node: AnySourceFile) {
getFileStem(node.span.file.fullPath) + '.' + getDefaultExtension(getTargetLanguage(node.kind));
}
eval(file: TextFile) {
@ -54,3 +85,15 @@ export class Program {
}
function getDefaultExtension(target: string) {
if (targetExtensions[target] === undefined) {
throw new Error(`Could not derive an appropriate extension for target "${target}".`)
}
return targetExtensions[target];
}
function getFileStem(filepath: string): string {
const chunks = path.basename(filepath).split('.')
return chunks[chunks.length-1];
}

View file

@ -0,0 +1,75 @@
const exported = {};
const nodeProto = {
preorder() {
}
}
function createNode(nodeType) {
const obj = Object.create(nodeProto);
Object.defineProperty(obj, '__IS_NODE', {
enumerable: false,
writable: false,
configurable: true,
value: true,
});
Object.defineProperty(obj, '__NODE_TYPE', {
enumerable: false,
writable: false,
configurable: true,
value: nodeType,
});
Object.defineProperty(obj, 'kind', {
enumerable: false,
writable: false,
configurable: true,
getter() {
return this.__NODE_TYPE.index;
}
});
obj.span = null;
obj.origNodes = null;
return obj;
}
for (const nodeName of Object.keys(NODE_TYPES)) {
exported[`create${nodeName}`] = function (...args) {
const nodeType = NODE_TYPES[nodeName];
const node = createNode(nodeType);
let i = 0;
const iter = nodeType.fields[Symbol.iterator]();
for (; i < args.length; i++) {
const { done, value } = iter.next();
if (done) {
break;
}
const [fieldName, fieldType] = value;
node[fieldName] = args[i];
}
while (true) {
const { done, value } = iter.next();
if (done) {
break;
}
const [fieldName, fieldType] = value;
throw new Error(`No argument provided for field '${fieldName}'`);
}
if (i < args.length) {
node.span = args[i++];
}
if (i < args.length) {
node.origNodes = i < args.length ? args[i++] : null;
}
if (i < args.length) {
throw new Error(`Too many arguments provided to function create${nodeName}`);
}
return node;
}
}
if (typeof module !== 'undefined') {
module.exports = exports;
}

View file

@ -20,6 +20,7 @@ export interface NodeField {
}
export interface NodeDeclaration {
index?: number;
type: 'NodeDeclaration';
name: string;
parents: string[];
@ -32,12 +33,14 @@ export interface EnumField {
}
export interface EnumDeclaration {
index?: number;
type: 'EnumDeclaration';
name: string;
fields: EnumField[];
}
export interface TypeDeclaration {
index?: number;
type: 'TypeDeclaration';
name: string;
typeNode: TypeNode;
@ -48,3 +51,25 @@ export type Declaration
| TypeDeclaration
| EnumDeclaration
export type Syntax
= Declaration
| TypeNode
| NodeField
| EnumField
export function hasArrayType(typeNode: TypeNode) {
if (typeNode.type === 'ReferenceTypeNode') {
return typeNode.name === 'Vec'
} else if (typeNode.type === 'UnionTypeNode') {
return typeNode.elements.some(hasArrayType);
}
}
export function isTypeOptional(typeNode: TypeNode) {
if (typeNode.type === 'ReferenceTypeNode') {
return typeNode.name === 'Option';
} else if (typeNode.type === 'UnionTypeNode') {
return typeNode.elements.every(isTypeOptional);
}
}

View file

@ -1,4 +1,9 @@
export function parse(input:string):any;
export interface ParseOptions {
[key: string]: any;
}
export function parse(input: string, opts?: ParseOptions): any;
export interface Location {
line: number;

View file

@ -1,4 +1,7 @@
import * as path from "path"
import * as fs from "fs"
export interface FastStringMap<T> {
[key: string]: T
}
@ -33,3 +36,66 @@ export class StreamWrapper<T> {
}
export function upsearchSync(filename: string, startDir = '.') {
let currDir = startDir;
while (true) {
const filePath = path.join(currDir, filename);
if (fs.existsSync(filePath)) {
return filePath
}
const { root, dir } = path.parse(currDir);
if (dir === root) {
return null;
}
currDir = dir;
}
}
function isWhiteSpace(ch: string) {
return /[\r\t ]/.test(ch);
}
export interface FileWriterOptions {
indentStr?: string;
startIndent?: number;
indentWidth?: number;
}
export class FileWriter {
public currentText = '';
private atBlankLine = true;
private currentIndent: number;
private indentStr: string;
private indentWidth: number;
constructor(opts: FileWriterOptions = {}) {
this.indentStr = opts.indentStr ?? ' ';
this.indentWidth = opts.indentWidth ?? 2;
this.currentIndent = (opts.startIndent ?? 0) * this.indentWidth;
}
public indent(count = 1) {
this.currentIndent += this.indentWidth * count;
}
public dedent(count = 1) {
this.currentIndent -= this.indentWidth * count;
}
public write(str: string) {
for (const ch of str) {
if (ch === '\n') {
this.atBlankLine = true;
} else if (!(this.atBlankLine && isWhiteSpace(ch))) {
if (this.atBlankLine) {
this.currentText += this.indentStr.repeat(this.currentIndent)
}
this.atBlankLine = false;
}
this.currentText += ch;
}
}
}

View file

@ -1,25 +0,0 @@
{
"name": "treegen",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@types/minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=",
"dev": true
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"pegjs": {
"version": "0.11.0-master.b7b87ea",
"resolved": "https://registry.npmjs.org/pegjs/-/pegjs-0.11.0-master.b7b87ea.tgz",
"integrity": "sha512-fwjzNiYHRUEUe/86Aaslb/ocbbsAupOcsJz+dlPYtgp3feCDRQOLChHO924XGh7fzSJBTdFCQTzmSOQaWjCTew==",
"dev": true
}
}
}

View file

@ -1,49 +0,0 @@
function isWhiteSpace(ch: string) {
return /[\r\t ]/.test(ch);
}
export interface FileWriterOptions {
indentStr?: string;
startIndent?: number;
indentWidth?: number;
}
export class FileWriter {
public currentText = '';
private atBlankLine = true;
private currentIndent: number;
private indentStr: string;
private indentWidth: number;
constructor(opts: FileWriterOptions = {}) {
this.indentStr = opts.indentStr ?? ' ';
this.indentWidth = opts.indentWidth ?? 2;
this.currentIndent = (opts.startIndent ?? 0) * this.indentWidth;
}
public indent(count = 1) {
this.currentIndent += this.indentWidth * count;
}
public dedent(count = 1) {
this.currentIndent -= this.indentWidth * count;
}
public write(str: string) {
for (const ch of str) {
if (ch === '\n') {
this.atBlankLine = true;
} else if (!(this.atBlankLine && isWhiteSpace(ch))) {
if (this.atBlankLine) {
this.currentText += this.indentStr.repeat(this.currentIndent)
}
this.atBlankLine = false;
}
this.currentText += ch;
}
}
}