diff --git a/Makefile b/Makefile index acfd8d9dd..23827da5e 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,14 @@ -TREEGEN_FILES = spec/ast.txt lib/bin/bolt-treegen.js lib/treegen/parser.js lib/treegen/index.js lib/treegen/util.js src/treegen/ast-template.js +TREEGEN_FILES = src/ast-spec.txt lib/bin/bolt-treegen.js lib/treegen/parser.js lib/treegen/index.js lib/treegen/util.js src/treegen/ast-template.js all: lib/ast.js - bolt bundle test.bolt + bolt bundle stdlib lib/ast.js: $(TREEGEN_FILES) @echo "Generating AST definitions ..." @mkdir -p lib/ @chmod +x lib/bin/*.js - @bolt-treegen --js-file=lib/ast.js --dts-file src/ast.d.ts spec/ast.txt + @bolt-treegen --js-file=lib/ast.js --dts-file src/ast.d.ts src/ast-spec.txt lib/treegen/parser.js: src/treegen/parser.pegjs @echo "Generating parser ..." diff --git a/package-lock.json b/package-lock.json index 3ed725d4f..6da4308fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,6 +38,11 @@ "@types/node": "*" } }, + "@types/js-yaml": { + "version": "3.12.4", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.12.4.tgz", + "integrity": "sha512-fYMgzN+9e28R81weVN49inn/u798ruU91En1ZnGvSZzCRc5jXx9B2EDhlRaWmcO1RIxFHL8AajRXzxDuJu93+A==" + }, "@types/microtime": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/microtime/-/microtime-2.1.0.tgz", @@ -64,6 +69,14 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.5.tgz", "integrity": "sha512-90hiq6/VqtQgX8Sp0EzeIsv3r+ellbGj4URKj5j30tLlZvRUpnAe9YbYnjl3pJM93GyXU0tghHhvXHq+5rnCKA==" }, + "@types/semver": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.2.0.tgz", + "integrity": "sha512-TbB0A8ACUWZt3Y6bQPstW9QNbhNeebdgLX4T/ZfkrswAfUzRiXrgd9seol+X379Wa589Pu4UEx9Uok0D4RjRCQ==", + "requires": { + "@types/node": "*" + } + }, "@types/yargs": { "version": "15.0.5", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz", @@ -111,7 +124,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "requires": { "sprintf-js": "~1.0.2" } @@ -353,8 +365,7 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "fill-range": { "version": "7.0.1", @@ -576,10 +587,9 @@ "dev": true }, "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -741,6 +751,16 @@ "path-is-absolute": "^1.0.0" } }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, "string-width": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", @@ -825,6 +845,14 @@ "requires": { "object.getownpropertydescriptors": "^2.0.3", "semver": "^5.7.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } } }, "node-gyp-build": { @@ -955,10 +983,9 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" }, "set-blocking": { "version": "2.0.0", @@ -982,8 +1009,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "string-width": { "version": "4.2.0", diff --git a/package.json b/package.json index 1d4699819..842a784c3 100644 --- a/package.json +++ b/package.json @@ -16,18 +16,22 @@ "dependencies": { "@types/fs-extra": "^9.0.1", "@types/glob": "^7.1.1", + "@types/js-yaml": "^3.12.4", "@types/microtime": "^2.1.0", "@types/minimist": "^1.2.0", "@types/node": "^14.0.5", + "@types/semver": "^7.2.0", "@types/yargs": "^15.0.5", "chalk": "^4.0.0", "fs-extra": "^9.0.0", "glob": "^7.1.6", + "js-yaml": "^3.14.0", "microtime": "^3.0.0", "minimist": "^1.2.5", "moment": "^2.26.0", "pegjs": "^0.11.0-master.b7b87ea", "reflect-metadata": "^0.1.13", + "semver": "^7.3.2", "source-map-support": "^0.5.19", "yargs": "^15.3.1" }, diff --git a/spec/ast.txt b/src/ast-spec.txt similarity index 94% rename from spec/ast.txt rename to src/ast-spec.txt index 94bd12c0b..a8a46662f 100644 --- a/spec/ast.txt +++ b/src/ast-spec.txt @@ -67,6 +67,7 @@ node BoltLoopKeyword > BoltToken, BoltKeyword; node BoltYieldKeyword > BoltToken, BoltKeyword; node BoltMatchKeyword > BoltToken, BoltKeyword; node BoltImportKeyword > BoltToken, BoltKeyword; +node BoltExportKeyword > BoltToken, BoltKeyword; node BoltPubKeyword > BoltToken, BoltKeyword; node BoltModKeyword > BoltToken, BoltKeyword; node BoltMutKeyword > BoltToken, BoltKeyword; @@ -86,6 +87,7 @@ node BoltBracketed > BoltPunctuated; node BoltSourceFile > SourceFile { elements: Vec, + package: Package, } node BoltQualName { @@ -237,15 +239,13 @@ node BoltDeclaration > BoltSourceElement; node BoltTypeDeclaration > BoltSourceElement; -enum BoltDeclarationModifiers { - Mutable = 0x1, - Public = 0x2, - IsType = 0x4, - IsForeign = 0x8, +enum BoltModifiers { + IsMutable = 0x1, + IsPublic = 0x2, } -node BoltModule > BoltDeclaration { - modifiers: BoltDeclarationModifiers, +node BoltModule > BoltSourceElement { + modifiers: BoltModifiers, name: BoltQualName, elements: Vec, } @@ -253,7 +253,7 @@ node BoltModule > BoltDeclaration { node BoltFunctionBodyElement; node BoltFunctionDeclaration > BoltFunctionBodyElement, BoltDeclaration { - modifiers: BoltDeclarationModifiers, + modifiers: BoltModifiers, target: String, name: BoltSymbol, params: Vec, @@ -263,7 +263,7 @@ node BoltFunctionDeclaration > BoltFunctionBodyElement, BoltDeclaration { } node BoltVariableDeclaration > BoltFunctionBodyElement, BoltDeclaration { - modifiers: BoltDeclarationModifiers, + modifiers: BoltModifiers, bindings: BoltPattern, type: Option, value: Option, @@ -275,20 +275,32 @@ node BoltPlainImportSymbol > BoltImportSymbol { name: BoltQualName, } -node BoltImportDeclaration > BoltDeclaration { +node BoltImportDirective > BoltSourceElement { + modifiers: BoltModifiers, file: String, symbols: Vec, } +node BoltExportSymbol; + +node BoltPlainExportSymbol { + name: BoltQualName, +} + +node BoltExportDirective > BoltSourceElement { + file: String, + symbols: Option>, +} + node BoltTraitDeclaration > BoltDeclaration, BoltTypeDeclaration { - modifiers: BoltDeclarationModifiers, + modifiers: BoltModifiers, name: BoltIdentifier, typeParams: Option>, elements: Vec, } node BoltImplDeclaration > BoltDeclaration { - modifiers: BoltDeclarationModifiers, + modifiers: BoltModifiers, name: BoltIdentifier, trait: BoltTypeExpression, typeParams: Option>, @@ -296,7 +308,7 @@ node BoltImplDeclaration > BoltDeclaration { } node BoltTypeAliasDeclaration > BoltDeclaration, BoltTypeDeclaration { - modifiers: BoltDeclarationModifiers, + modifiers: BoltModifiers, name: BoltIdentifier, typeParams: Option>, typeExpr: BoltTypeExpression, @@ -310,7 +322,7 @@ node BoltRecordField > BoltRecordMember { } node BoltRecordDeclaration > BoltDeclaration, BoltTypeDeclaration { - modifiers: BoltDeclarationModifiers, + modifiers: BoltModifiers, name: BoltIdentifier, typeParms: Option>, members: Option>, diff --git a/src/ast.d.ts b/src/ast.d.ts index 04ff283a3..3948a2abf 100644 --- a/src/ast.d.ts +++ b/src/ast.d.ts @@ -1,4 +1,9 @@ +import { Package } from "./common" + +export function isSyntax(value: any): value is Syntax; + + export const enum SyntaxKind { EndOfFile = 2, FunctionBody = 6, @@ -32,121 +37,125 @@ export const enum SyntaxKind { BoltYieldKeyword = 38, BoltMatchKeyword = 39, BoltImportKeyword = 40, - BoltPubKeyword = 41, - BoltModKeyword = 42, - BoltMutKeyword = 43, - BoltEnumKeyword = 44, - BoltStructKeyword = 45, - BoltTypeKeyword = 46, - BoltTraitKeyword = 47, - BoltImplKeyword = 48, - BoltParenthesized = 50, - BoltBraced = 51, - BoltBracketed = 52, - BoltSourceFile = 53, - BoltQualName = 54, - BoltReferenceTypeExpression = 56, - BoltFunctionTypeExpression = 57, - BoltTypeParameter = 58, - BoltBindPattern = 60, - BoltTypePattern = 61, - BoltExpressionPattern = 62, - BoltTuplePatternElement = 63, - BoltTuplePattern = 64, - BoltRecordFieldPattern = 65, - BoltRecordPattern = 66, - BoltQuoteExpression = 68, - BoltReferenceExpression = 69, - BoltMemberExpression = 70, - BoltFunctionExpression = 71, - BoltCallExpression = 72, - BoltYieldExpression = 73, - BoltMatchArm = 74, - BoltMatchExpression = 75, - BoltCase = 76, - BoltCaseExpression = 77, - BoltBlockExpression = 78, - BoltConstantExpression = 79, - BoltReturnStatement = 81, - BoltConditionalCase = 82, - BoltConditionalStatement = 83, - BoltResumeStatement = 84, - BoltExpressionStatement = 85, - BoltParameter = 86, - BoltModule = 90, - BoltFunctionDeclaration = 92, - BoltVariableDeclaration = 93, - BoltPlainImportSymbol = 95, - BoltImportDeclaration = 96, - BoltTraitDeclaration = 97, - BoltImplDeclaration = 98, - BoltTypeAliasDeclaration = 99, - BoltRecordField = 101, - BoltRecordDeclaration = 102, - BoltMacroCall = 104, - JSOperator = 107, - JSIdentifier = 108, - JSString = 109, - JSInteger = 110, - JSFromKeyword = 111, - JSReturnKeyword = 112, - JSTryKeyword = 113, - JSFinallyKeyword = 114, - JSCatchKeyword = 115, - JSImportKeyword = 116, - JSAsKeyword = 117, - JSConstKeyword = 118, - JSLetKeyword = 119, - JSExportKeyword = 120, - JSFunctionKeyword = 121, - JSWhileKeyword = 122, - JSForKeyword = 123, - JSCloseBrace = 124, - JSCloseBracket = 125, - JSCloseParen = 126, - JSOpenBrace = 127, - JSOpenBracket = 128, - JSOpenParen = 129, - JSSemi = 130, - JSComma = 131, - JSDot = 132, - JSDotDotDot = 133, - JSMulOp = 134, - JSAddOp = 135, - JSDivOp = 136, - JSSubOp = 137, - JSLtOp = 138, - JSGtOp = 139, - JSBOrOp = 140, - JSBXorOp = 141, - JSBAndOp = 142, - JSBNotOp = 143, - JSNotOp = 144, - JSBindPattern = 146, - JSConstantExpression = 148, - JSMemberExpression = 149, - JSCallExpression = 150, - JSBinaryExpression = 151, - JSUnaryExpression = 152, - JSNewExpression = 153, - JSSequenceExpression = 154, - JSConditionalExpression = 155, - JSLiteralExpression = 157, - JSReferenceExpression = 158, - JSCatchBlock = 162, - JSTryCatchStatement = 163, - JSExpressionStatement = 164, - JSConditionalCase = 165, - JSConditionalStatement = 166, - JSReturnStatement = 167, - JSParameter = 168, - JSImportStarBinding = 172, - JSImportAsBinding = 173, - JSImportDeclaration = 174, - JSFunctionDeclaration = 175, - JSArrowFunctionDeclaration = 176, - JSLetDeclaration = 177, - JSSourceFile = 178, + BoltExportKeyword = 41, + BoltPubKeyword = 42, + BoltModKeyword = 43, + BoltMutKeyword = 44, + BoltEnumKeyword = 45, + BoltStructKeyword = 46, + BoltTypeKeyword = 47, + BoltTraitKeyword = 48, + BoltImplKeyword = 49, + BoltParenthesized = 51, + BoltBraced = 52, + BoltBracketed = 53, + BoltSourceFile = 54, + BoltQualName = 55, + BoltReferenceTypeExpression = 57, + BoltFunctionTypeExpression = 58, + BoltTypeParameter = 59, + BoltBindPattern = 61, + BoltTypePattern = 62, + BoltExpressionPattern = 63, + BoltTuplePatternElement = 64, + BoltTuplePattern = 65, + BoltRecordFieldPattern = 66, + BoltRecordPattern = 67, + BoltQuoteExpression = 69, + BoltReferenceExpression = 70, + BoltMemberExpression = 71, + BoltFunctionExpression = 72, + BoltCallExpression = 73, + BoltYieldExpression = 74, + BoltMatchArm = 75, + BoltMatchExpression = 76, + BoltCase = 77, + BoltCaseExpression = 78, + BoltBlockExpression = 79, + BoltConstantExpression = 80, + BoltReturnStatement = 82, + BoltConditionalCase = 83, + BoltConditionalStatement = 84, + BoltResumeStatement = 85, + BoltExpressionStatement = 86, + BoltParameter = 87, + BoltModule = 91, + BoltFunctionDeclaration = 93, + BoltVariableDeclaration = 94, + BoltPlainImportSymbol = 96, + BoltImportDirective = 97, + BoltExportSymbol = 98, + BoltPlainExportSymbol = 99, + BoltExportDirective = 100, + BoltTraitDeclaration = 101, + BoltImplDeclaration = 102, + BoltTypeAliasDeclaration = 103, + BoltRecordField = 105, + BoltRecordDeclaration = 106, + BoltMacroCall = 108, + JSOperator = 111, + JSIdentifier = 112, + JSString = 113, + JSInteger = 114, + JSFromKeyword = 115, + JSReturnKeyword = 116, + JSTryKeyword = 117, + JSFinallyKeyword = 118, + JSCatchKeyword = 119, + JSImportKeyword = 120, + JSAsKeyword = 121, + JSConstKeyword = 122, + JSLetKeyword = 123, + JSExportKeyword = 124, + JSFunctionKeyword = 125, + JSWhileKeyword = 126, + JSForKeyword = 127, + JSCloseBrace = 128, + JSCloseBracket = 129, + JSCloseParen = 130, + JSOpenBrace = 131, + JSOpenBracket = 132, + JSOpenParen = 133, + JSSemi = 134, + JSComma = 135, + JSDot = 136, + JSDotDotDot = 137, + JSMulOp = 138, + JSAddOp = 139, + JSDivOp = 140, + JSSubOp = 141, + JSLtOp = 142, + JSGtOp = 143, + JSBOrOp = 144, + JSBXorOp = 145, + JSBAndOp = 146, + JSBNotOp = 147, + JSNotOp = 148, + JSBindPattern = 150, + JSConstantExpression = 152, + JSMemberExpression = 153, + JSCallExpression = 154, + JSBinaryExpression = 155, + JSUnaryExpression = 156, + JSNewExpression = 157, + JSSequenceExpression = 158, + JSConditionalExpression = 159, + JSLiteralExpression = 161, + JSReferenceExpression = 162, + JSCatchBlock = 166, + JSTryCatchStatement = 167, + JSExpressionStatement = 168, + JSConditionalCase = 169, + JSConditionalStatement = 170, + JSReturnStatement = 171, + JSParameter = 172, + JSImportStarBinding = 176, + JSImportAsBinding = 177, + JSImportDeclaration = 178, + JSFunctionDeclaration = 179, + JSArrowFunctionDeclaration = 180, + JSLetDeclaration = 181, + JSSourceFile = 182, } @@ -158,6 +167,7 @@ export function setParents(node: Syntax): void; export type SyntaxRange = [Syntax, Syntax]; interface SyntaxBase { + id: number; kind: K; parentNode: ParentTypesOf | null; span: TextSpan | null; @@ -207,6 +217,7 @@ export type Token | BoltYieldKeyword | BoltMatchKeyword | BoltImportKeyword + | BoltExportKeyword | BoltPubKeyword | BoltModKeyword | BoltMutKeyword @@ -304,6 +315,7 @@ export type BoltToken | BoltYieldKeyword | BoltMatchKeyword | BoltImportKeyword + | BoltExportKeyword | BoltPubKeyword | BoltModKeyword | BoltMutKeyword @@ -426,6 +438,7 @@ export type BoltKeyword | BoltYieldKeyword | BoltMatchKeyword | BoltImportKeyword + | BoltExportKeyword | BoltPubKeyword | BoltModKeyword | BoltMutKeyword @@ -480,6 +493,10 @@ export interface BoltImportKeyword extends SyntaxBase { + kind: SyntaxKind.BoltExportKeyword; +} + export interface BoltPubKeyword extends SyntaxBase { kind: SyntaxKind.BoltPubKeyword; } @@ -536,6 +553,7 @@ export interface BoltBracketed extends SyntaxBase { export interface BoltSourceFile extends SyntaxBase { kind: SyntaxKind.BoltSourceFile; elements: BoltSourceElement[]; + package: Package; } export interface BoltQualName extends SyntaxBase { @@ -740,10 +758,8 @@ export interface BoltParameter extends SyntaxBase { } export type BoltDeclaration - = BoltModule - | BoltFunctionDeclaration + = BoltFunctionDeclaration | BoltVariableDeclaration - | BoltImportDeclaration | BoltTraitDeclaration | BoltImplDeclaration | BoltTypeAliasDeclaration @@ -757,12 +773,12 @@ export type BoltTypeDeclaration | BoltRecordDeclaration -export const enum BoltDeclarationModifiers { - Mutable = 1,Public = 2,IsType = 4,IsForeign = 8,} +export const enum BoltModifiers { + IsMutable = 1,IsPublic = 2,} export interface BoltModule extends SyntaxBase { kind: SyntaxKind.BoltModule; - modifiers: BoltDeclarationModifiers; + modifiers: BoltModifiers; name: BoltQualName; elements: BoltSourceElement[]; } @@ -779,7 +795,7 @@ export type BoltFunctionBodyElement export interface BoltFunctionDeclaration extends SyntaxBase { kind: SyntaxKind.BoltFunctionDeclaration; - modifiers: BoltDeclarationModifiers; + modifiers: BoltModifiers; target: string; name: BoltSymbol; params: BoltParameter[]; @@ -790,7 +806,7 @@ export interface BoltFunctionDeclaration extends SyntaxBase { kind: SyntaxKind.BoltVariableDeclaration; - modifiers: BoltDeclarationModifiers; + modifiers: BoltModifiers; bindings: BoltPattern; type: BoltTypeExpression | null; value: BoltExpression | null; @@ -805,15 +821,31 @@ export interface BoltPlainImportSymbol extends SyntaxBase { - kind: SyntaxKind.BoltImportDeclaration; +export interface BoltImportDirective extends SyntaxBase { + kind: SyntaxKind.BoltImportDirective; + modifiers: BoltModifiers; file: string; symbols: BoltImportSymbol[]; } +export interface BoltExportSymbol extends SyntaxBase { + kind: SyntaxKind.BoltExportSymbol; +} + +export interface BoltPlainExportSymbol extends SyntaxBase { + kind: SyntaxKind.BoltPlainExportSymbol; + name: BoltQualName; +} + +export interface BoltExportDirective extends SyntaxBase { + kind: SyntaxKind.BoltExportDirective; + file: string; + symbols: BoltExportSymbol[] | null; +} + export interface BoltTraitDeclaration extends SyntaxBase { kind: SyntaxKind.BoltTraitDeclaration; - modifiers: BoltDeclarationModifiers; + modifiers: BoltModifiers; name: BoltIdentifier; typeParams: BoltTypeParameter[] | null; elements: BoltDeclaration[]; @@ -821,7 +853,7 @@ export interface BoltTraitDeclaration extends SyntaxBase { kind: SyntaxKind.BoltImplDeclaration; - modifiers: BoltDeclarationModifiers; + modifiers: BoltModifiers; name: BoltIdentifier; trait: BoltTypeExpression; typeParams: BoltTypeParameter[] | null; @@ -830,7 +862,7 @@ export interface BoltImplDeclaration extends SyntaxBase { kind: SyntaxKind.BoltTypeAliasDeclaration; - modifiers: BoltDeclarationModifiers; + modifiers: BoltModifiers; name: BoltIdentifier; typeParams: BoltTypeParameter[] | null; typeExpr: BoltTypeExpression; @@ -849,7 +881,7 @@ export interface BoltRecordField extends SyntaxBase export interface BoltRecordDeclaration extends SyntaxBase { kind: SyntaxKind.BoltRecordDeclaration; - modifiers: BoltDeclarationModifiers; + modifiers: BoltModifiers; name: BoltIdentifier; typeParms: BoltTypeParameter[] | null; members: BoltRecordMember[] | null; @@ -861,10 +893,8 @@ export type BoltSourceElement | BoltResumeStatement | BoltExpressionStatement | BoltMacroCall - | BoltModule | BoltFunctionDeclaration | BoltVariableDeclaration - | BoltImportDeclaration | BoltTraitDeclaration | BoltImplDeclaration | BoltTypeAliasDeclaration @@ -873,6 +903,9 @@ export type BoltSourceElement | BoltTraitDeclaration | BoltTypeAliasDeclaration | BoltRecordDeclaration + | BoltModule + | BoltImportDirective + | BoltExportDirective export interface BoltMacroCall extends SyntaxBase { @@ -1314,6 +1347,7 @@ export type BoltSyntax | BoltYieldKeyword | BoltMatchKeyword | BoltImportKeyword + | BoltExportKeyword | BoltPubKeyword | BoltModKeyword | BoltMutKeyword @@ -1359,7 +1393,10 @@ export type BoltSyntax | BoltFunctionDeclaration | BoltVariableDeclaration | BoltPlainImportSymbol - | BoltImportDeclaration + | BoltImportDirective + | BoltExportSymbol + | BoltPlainExportSymbol + | BoltExportDirective | BoltTraitDeclaration | BoltImplDeclaration | BoltTypeAliasDeclaration @@ -1467,6 +1504,7 @@ export type Syntax | BoltYieldKeyword | BoltMatchKeyword | BoltImportKeyword + | BoltExportKeyword | BoltPubKeyword | BoltModKeyword | BoltMutKeyword @@ -1512,7 +1550,10 @@ export type Syntax | BoltFunctionDeclaration | BoltVariableDeclaration | BoltPlainImportSymbol - | BoltImportDeclaration + | BoltImportDirective + | BoltExportSymbol + | BoltPlainExportSymbol + | BoltExportDirective | BoltTraitDeclaration | BoltImplDeclaration | BoltTypeAliasDeclaration @@ -1618,6 +1659,7 @@ export function createBoltLoopKeyword(span?: TextSpan | null): BoltLoopKeyword; export function createBoltYieldKeyword(span?: TextSpan | null): BoltYieldKeyword; export function createBoltMatchKeyword(span?: TextSpan | null): BoltMatchKeyword; export function createBoltImportKeyword(span?: TextSpan | null): BoltImportKeyword; +export function createBoltExportKeyword(span?: TextSpan | null): BoltExportKeyword; export function createBoltPubKeyword(span?: TextSpan | null): BoltPubKeyword; export function createBoltModKeyword(span?: TextSpan | null): BoltModKeyword; export function createBoltMutKeyword(span?: TextSpan | null): BoltMutKeyword; @@ -1629,7 +1671,7 @@ export function createBoltImplKeyword(span?: TextSpan | null): BoltImplKeyword; export function createBoltParenthesized(text: string, span?: TextSpan | null): BoltParenthesized; export function createBoltBraced(text: string, span?: TextSpan | null): BoltBraced; export function createBoltBracketed(text: string, span?: TextSpan | null): BoltBracketed; -export function createBoltSourceFile(elements: BoltSourceElement[], span?: TextSpan | null): BoltSourceFile; +export function createBoltSourceFile(elements: BoltSourceElement[], package: Package, span?: TextSpan | null): BoltSourceFile; export function createBoltQualName(modulePath: BoltIdentifier[] | null, name: BoltSymbol, span?: TextSpan | null): BoltQualName; export function createBoltReferenceTypeExpression(name: BoltQualName, arguments: BoltTypeExpression[] | null, span?: TextSpan | null): BoltReferenceTypeExpression; export function createBoltFunctionTypeExpression(params: BoltParameter[], returnType: BoltTypeExpression | null, span?: TextSpan | null): BoltFunctionTypeExpression; @@ -1659,16 +1701,19 @@ export function createBoltConditionalStatement(cases: BoltConditionalCase[], spa export function createBoltResumeStatement(value: BoltExpression, span?: TextSpan | null): BoltResumeStatement; export function createBoltExpressionStatement(expression: BoltExpression, span?: TextSpan | null): BoltExpressionStatement; export function createBoltParameter(index: number, bindings: BoltPattern, type: BoltTypeExpression | null, defaultValue: BoltExpression | null, span?: TextSpan | null): BoltParameter; -export function createBoltModule(modifiers: BoltDeclarationModifiers, name: BoltQualName, elements: BoltSourceElement[], span?: TextSpan | null): BoltModule; -export function createBoltFunctionDeclaration(modifiers: BoltDeclarationModifiers, target: string, name: BoltSymbol, params: BoltParameter[], returnType: BoltTypeExpression | null, typeParams: BoltTypeParameter[] | null, body: BoltFunctionBodyElement[], span?: TextSpan | null): BoltFunctionDeclaration; -export function createBoltVariableDeclaration(modifiers: BoltDeclarationModifiers, bindings: BoltPattern, type: BoltTypeExpression | null, value: BoltExpression | null, span?: TextSpan | null): BoltVariableDeclaration; +export function createBoltModule(modifiers: BoltModifiers, name: BoltQualName, elements: BoltSourceElement[], span?: TextSpan | null): BoltModule; +export function createBoltFunctionDeclaration(modifiers: BoltModifiers, target: string, name: BoltSymbol, params: BoltParameter[], returnType: BoltTypeExpression | null, typeParams: BoltTypeParameter[] | null, body: BoltFunctionBodyElement[], span?: TextSpan | null): BoltFunctionDeclaration; +export function createBoltVariableDeclaration(modifiers: BoltModifiers, bindings: BoltPattern, type: BoltTypeExpression | null, value: BoltExpression | null, span?: TextSpan | null): BoltVariableDeclaration; export function createBoltPlainImportSymbol(name: BoltQualName, span?: TextSpan | null): BoltPlainImportSymbol; -export function createBoltImportDeclaration(file: string, symbols: BoltImportSymbol[], span?: TextSpan | null): BoltImportDeclaration; -export function createBoltTraitDeclaration(modifiers: BoltDeclarationModifiers, name: BoltIdentifier, typeParams: BoltTypeParameter[] | null, elements: BoltDeclaration[], span?: TextSpan | null): BoltTraitDeclaration; -export function createBoltImplDeclaration(modifiers: BoltDeclarationModifiers, name: BoltIdentifier, trait: BoltTypeExpression, typeParams: BoltTypeParameter[] | null, elements: BoltDeclaration[], span?: TextSpan | null): BoltImplDeclaration; -export function createBoltTypeAliasDeclaration(modifiers: BoltDeclarationModifiers, name: BoltIdentifier, typeParams: BoltTypeParameter[] | null, typeExpr: BoltTypeExpression, span?: TextSpan | null): BoltTypeAliasDeclaration; +export function createBoltImportDirective(modifiers: BoltModifiers, file: string, symbols: BoltImportSymbol[], span?: TextSpan | null): BoltImportDirective; +export function createBoltExportSymbol(span?: TextSpan | null): BoltExportSymbol; +export function createBoltPlainExportSymbol(name: BoltQualName, span?: TextSpan | null): BoltPlainExportSymbol; +export function createBoltExportDirective(file: string, symbols: BoltExportSymbol[] | null, span?: TextSpan | null): BoltExportDirective; +export function createBoltTraitDeclaration(modifiers: BoltModifiers, name: BoltIdentifier, typeParams: BoltTypeParameter[] | null, elements: BoltDeclaration[], span?: TextSpan | null): BoltTraitDeclaration; +export function createBoltImplDeclaration(modifiers: BoltModifiers, name: BoltIdentifier, trait: BoltTypeExpression, typeParams: BoltTypeParameter[] | null, elements: BoltDeclaration[], span?: TextSpan | null): BoltImplDeclaration; +export function createBoltTypeAliasDeclaration(modifiers: BoltModifiers, name: BoltIdentifier, typeParams: BoltTypeParameter[] | null, typeExpr: BoltTypeExpression, span?: TextSpan | null): BoltTypeAliasDeclaration; export function createBoltRecordField(name: BoltIdentifier, type: BoltTypeExpression, span?: TextSpan | null): BoltRecordField; -export function createBoltRecordDeclaration(modifiers: BoltDeclarationModifiers, name: BoltIdentifier, typeParms: BoltTypeParameter[] | null, members: BoltRecordMember[] | null, span?: TextSpan | null): BoltRecordDeclaration; +export function createBoltRecordDeclaration(modifiers: BoltModifiers, name: BoltIdentifier, typeParms: BoltTypeParameter[] | null, members: BoltRecordMember[] | null, span?: TextSpan | null): BoltRecordDeclaration; export function createBoltMacroCall(name: BoltIdentifier, text: string, span?: TextSpan | null): BoltMacroCall; export function createJSOperator(text: string, span?: TextSpan | null): JSOperator; export function createJSIdentifier(text: string, span?: TextSpan | null): JSIdentifier; @@ -1772,6 +1817,7 @@ export function isBoltLoopKeyword(value: any): value is BoltLoopKeyword; export function isBoltYieldKeyword(value: any): value is BoltYieldKeyword; export function isBoltMatchKeyword(value: any): value is BoltMatchKeyword; export function isBoltImportKeyword(value: any): value is BoltImportKeyword; +export function isBoltExportKeyword(value: any): value is BoltExportKeyword; export function isBoltPubKeyword(value: any): value is BoltPubKeyword; export function isBoltModKeyword(value: any): value is BoltModKeyword; export function isBoltMutKeyword(value: any): value is BoltMutKeyword; @@ -1826,7 +1872,10 @@ export function isBoltFunctionDeclaration(value: any): value is BoltFunctionDecl export function isBoltVariableDeclaration(value: any): value is BoltVariableDeclaration; export function isBoltImportSymbol(value: any): value is BoltImportSymbol; export function isBoltPlainImportSymbol(value: any): value is BoltPlainImportSymbol; -export function isBoltImportDeclaration(value: any): value is BoltImportDeclaration; +export function isBoltImportDirective(value: any): value is BoltImportDirective; +export function isBoltExportSymbol(value: any): value is BoltExportSymbol; +export function isBoltPlainExportSymbol(value: any): value is BoltPlainExportSymbol; +export function isBoltExportDirective(value: any): value is BoltExportDirective; export function isBoltTraitDeclaration(value: any): value is BoltTraitDeclaration; export function isBoltImplDeclaration(value: any): value is BoltImplDeclaration; export function isBoltTypeAliasDeclaration(value: any): value is BoltTypeAliasDeclaration; diff --git a/src/bin/bolt.ts b/src/bin/bolt.ts index 27dc5f8b9..fe680f4ce 100644 --- a/src/bin/bolt.ts +++ b/src/bin/bolt.ts @@ -7,11 +7,17 @@ import { sync as globSync } from "glob" import * as path from "path" import * as fs from "fs" import yargs from "yargs" +import yaml from "js-yaml" +import semver from "semver" import { Program } from "../program" import { parseSourceFile } from "../parser" -import { BoltSourceFile} from "../ast" import { Frontend } from "../frontend" +import { Package } from "../common" +import {hasOwnProperty} from "../util" +import {isString} from "util" +import {DiagnosticPrinter, E_FIELD_NOT_PRESENT, E_FIELD_MUST_BE_STRING, E_FIELD_HAS_INVALID_VERSION_NUMBER} from "../diagnostics" +import {BoltSourceFileModifiers} from "../ast" //global.print = function (value: any) { // console.error(require('util').inspect(value, { depth: Infinity, colors: true })) @@ -43,16 +49,99 @@ function flatMap(array: T[], proc: (element: T) => T[]) { return out } -function loadAllSourceFiles(filenames: string[]): BoltSourceFile[] { - const sourceFiles = []; - for (const filename of filenames) { - if (fs.statSync(filename).isDirectory()) { - for (const filepath of globSync(path.join(filename, '**/*.bolt'))) { - sourceFiles.push(parseSourceFile(filepath)); +const diagnostics = new DiagnosticPrinter(); + +function loadPackageMetadata(rootDir: string) { + + let name = null + let version = null; + + let hasVersionErrors = false; + let hasNameErrors = false; + + const filepath = path.join(rootDir, 'Boltfile'); + if (fs.existsSync(filepath)) { + const data = yaml.safeLoad(fs.readFileSync(filepath, 'utf8')); + if (data !== undefined) { + if (hasOwnProperty(data, 'name')) { + if (!isString(data.name)) { + diagnostics.add({ + message: E_FIELD_MUST_BE_STRING, + severity: 'error', + args: { name: 'name' }, + }); + hasNameErrors = true; + } else { + name = data.name; + } + } + if (hasOwnProperty(data, 'version')) { + if (!isString(data.version)) { + diagnostics.add({ + message: E_FIELD_MUST_BE_STRING, + args: { name: 'version' }, + severity: 'error', + }); + hasVersionErrors = true; + } else { + if (!semver.valid(data.version)) { + diagnostics.add({ + message: E_FIELD_HAS_INVALID_VERSION_NUMBER, + args: { name: 'version' }, + severity: 'error', + }); + hasVersionErrors = true; + } else { + version = data.version; + } + } } } } - return sourceFiles; + + if (name === null && !hasNameErrors) { + diagnostics.add({ + message: E_FIELD_NOT_PRESENT, + severity: 'warning', + args: { name: 'name' }, + }); + } + + if (version === null && !hasVersionErrors) { + diagnostics.add({ + message: E_FIELD_NOT_PRESENT, + severity: 'warning', + args: { name: 'version' }, + }); + } + + return { + name, + version + }; + +} + +function loadPackage(rootDir: string): Package { + const data = loadPackageMetadata(rootDir); + const pkg = new Package(rootDir, data.name, data.version, []); + for (const filepath of globSync(path.join(rootDir, '**/*.bolt'))) { + pkg.addSourceFile(parseSourceFile(filepath, pkg)); + } + return pkg; +} + +function loadPackagesAndSourceFiles(filenames: string[], cwd = '.'): Package[] { + const anonPkg = new Package(cwd, null, null, []); + const pkgs = [ anonPkg ]; + for (const filename of filenames) { + if (fs.statSync(filename).isDirectory()) { + pkgs.push(loadPackage(filename)); + } else { + anonPkg.addSourceFile(parseSourceFile(filename, anonPkg, 0)); + } + } + return pkgs; } yargs @@ -90,8 +179,8 @@ yargs , args => { - const sourceFiles = loadAllSourceFiles(toArray(args.files as string[] | string)); - const program = new Program(sourceFiles); + const pkgs = loadPackagesAndSourceFiles(toArray(args.files as string[] | string)); + const program = new Program(pkgs); const frontend = new Frontend(); frontend.typeCheck(program); if (frontend.diagnostics.hasErrors && !args.force) { diff --git a/src/checker.ts b/src/checker.ts index ce93708f9..bf02f80da 100644 --- a/src/checker.ts +++ b/src/checker.ts @@ -24,13 +24,13 @@ */ import { + isSyntax, Syntax, SyntaxKind, BoltReferenceExpression, BoltDeclaration, BoltSourceFile, BoltSyntax, - BoltReferenceTypeExpression, BoltTypeDeclaration, BoltExpression, BoltFunctionDeclaration, @@ -40,9 +40,11 @@ import { BoltTypeExpression, BoltSourceElement, isBoltStatement, - isBoltDeclaration + isBoltDeclaration, + isSourceFile, + BoltReferenceTypeExpression } from "./ast"; -import {FastStringMap, memoize, assert} from "./util"; +import {FastStringMap, memoize, assert, verbose} from "./util"; import { DiagnosticPrinter, E_TYPES_NOT_ASSIGNABLE, @@ -53,15 +55,13 @@ import { E_INVALID_ARGUMENTS } from "./diagnostics"; import { createAnyType, isOpaqueType, createOpaqueType, Type, createVoidType, createVariantType, isVoidType } from "./types"; -import { getReturnStatementsInFunctionBody } from "./common"; +import { getReturnStatementsInFunctionBody, isAutoImported, toDeclarationPath, createDeclarationPath, hasRelativeModulePath, hasAbsoluteModulePath, DeclarationPath, getModulePath, getSymbolNameOfDeclarationPath } from "./common"; import {emit} from "./emitter"; -interface SymbolInfo { - declarations: BoltDeclaration[]; -} +const PACKAGE_SCOPE_ID = 0; -interface TypeSymbolInfo { - declarations: BoltTypeDeclaration[]; +interface SymbolInfo { + declarations: N[]; } function introducesNewScope(kind: SyntaxKind): boolean { @@ -79,12 +79,120 @@ function introducesNewTypeScope(kind: SyntaxKind): boolean { type Scope = unknown; type TypeScope = unknown; +function getScopeId(scope: Scope | TypeScope) { + if (isSyntax(scope)) { + return scope.id; + } + return PACKAGE_SCOPE_ID; +} + function createSymbol(node: BoltDeclaration): SymbolInfo { return { declarations: [ node ] }; } -function createTypeSymbol(node: BoltTypeDeclaration): TypeSymbolInfo { - return { declarations: [ node ] }; +class SymbolResolver { + + private symbols = new FastStringMap>(); + + constructor(private introducesNewScope: (kind: SyntaxKind) => boolean) { + + } + + public addSymbol(name: string, node: N): void { + const scope = this.getScopeSurroundingNode(node) + verbose(`Adding symbol ${name} in scope #${getScopeId(scope)}`); + const sym = { declarations: [ node ] }; + this.symbols.set(`${name}@${getScopeId(scope)}`, sym); + } + + public getParentScope(scope: Scope): Scope | null { + if (!isSyntax(scope)) { + // Scope is the global package scope + return null; + } + if (scope.kind === SyntaxKind.BoltSourceFile) { + return scope.package; + } + return this.getScopeForNode(scope.parentNode!) + } + + public getScopeSurroundingNode(node: Syntax): Scope { + assert(node.parentNode !== null); + return this.getScopeForNode(node.parentNode!); + } + + public getScopeForNode(node: Syntax): Scope { + let currNode = node; + while (!this.introducesNewScope(currNode.kind)) { + if (currNode.kind === SyntaxKind.BoltSourceFile) { + return currNode.package; + } + currNode = currNode.parentNode!; + } + return currNode; + } + + private lookupSymbolInScope(name: string, scope: Scope): SymbolInfo | null { + const key = `${name}@${getScopeId(scope)}`; + if (!this.symbols.has(key)) { + return null; + } + return this.symbols.get(key); + } + + public findSymbolInScopeOf(name: string, scope: Scope): SymbolInfo | null { + while (true) { + const sym = this.lookupSymbolInScope(name, scope); + if (sym !== null) { + return sym; + } + const parentScope = this.getParentScope(scope); + if (parentScope === null) { + break; + } + scope = parentScope; + } + return null; + } + + public resolve(path: DeclarationPath, node: BoltSyntax): BoltSyntax | null { + let scope = this.getScopeSurroundingNode(node); + if (hasAbsoluteModulePath(path)) { + // TODO + } else if (hasRelativeModulePath(path)) { + while (true) { + let shouldSearchParentScopes = false; + let currScope = scope; + for (const name of getModulePath(path)) { + const sym = this.lookupSymbolInScope(name, currScope); + if (sym === null) { + shouldSearchParentScopes = true; + break; + } + if (sym.declarations[0].kind !== SyntaxKind.BoltModule) { + shouldSearchParentScopes = true; + break; + } + currScope = this.getScopeForNode(sym.declarations[0]); + } + if (!shouldSearchParentScopes) { + scope = currScope; + break; + } + const parentScope = this.getParentScope(scope); + if (parentScope === null) { + return null; + } + scope = parentScope; + } + } + const sym = this.findSymbolInScopeOf(getSymbolNameOfDeclarationPath(path), scope); + if (sym === null) { + return null; + } + return sym.declarations[0]!; + } + } export class TypeChecker { @@ -93,8 +201,8 @@ export class TypeChecker { } - private symbols = new FastStringMap(); - private typeSymbols = new FastStringMap(); + private varResolver = new SymbolResolver(introducesNewScope); + private typeResolver = new SymbolResolver(introducesNewTypeScope); public checkSourceFile(node: BoltSourceFile): void { @@ -107,12 +215,15 @@ export class TypeChecker { switch (node.kind) { + case SyntaxKind.BoltConstantExpression: + break; + case SyntaxKind.BoltReferenceExpression: { if (self.resolveReferenceExpression(node) === null) { self.diagnostics.add({ message: E_DECLARATION_NOT_FOUND, - args: { name: node.name.name.text }, + args: { name: emit(node.name.name) }, severity: 'error', node: node, }) @@ -186,7 +297,7 @@ export class TypeChecker { if (self.resolveTypeReferenceExpression(node) === null) { self.diagnostics.add({ message: E_TYPE_DECLARATION_NOT_FOUND, - args: { name: node.name.name.text }, + args: { name: emit(node.name.name) }, severity: 'error', node: node, }) @@ -338,15 +449,19 @@ export class TypeChecker { visitStatement(node); } else if (isBoltDeclaration(node)) { visitDeclaration(node); - } else { - throw new Error(`Unknown node of kind ${kindToString(node)}`); + } else if (node.kind === SyntaxKind.BoltModule) { + for (const element of node.elements) { + visitSourceElement(element); + } + } else if (node.kind !== SyntaxKind.BoltImportDirective) { + throw new Error(`Unknown node of kind ${kindToString(node.kind)}`); } } } private resolveType(name: string, node: BoltSyntax): Type | null { - const sym = this.findSymbolInTypeScopeOf(name, this.getTypeScopeSurroundingNode(node)) + const sym = this.typeResolver.findSymbolInScopeOf(name, this.typeResolver.getScopeSurroundingNode(node)) if (sym === null) { return null; } @@ -429,11 +544,13 @@ export class TypeChecker { function visitExpression(node: BoltExpression) { switch (node.kind) { case SyntaxKind.BoltReferenceExpression: + { const resolved = self.resolveReferenceExpression(node); if (resolved !== null) { visitFunctionBodyElement(resolved); } break; + } default: throw new Error(`Unexpected node type ${kindToString(node.kind)}`); } @@ -475,182 +592,25 @@ export class TypeChecker { case SyntaxKind.BoltFunctionDeclaration: { - const scope = this.getScopeSurroundingNode(node); - const sym = createSymbol(node); - this.addSymbol(emit(node.name), scope, sym); + this.varResolver.addSymbol(emit(node.name), node); break; } case SyntaxKind.BoltRecordDeclaration: { - const typeScope = this.getTypeScopeSurroundingNode(node); - const typeSym = createTypeSymbol(node); - this.addTypeSymbol(node.name.text, typeScope, typeSym); + this.typeResolver.addSymbol(node.name.text, node); } } } - private addSymbol(name: string, scope: Scope, sym: SymbolInfo): void { - console.error(`Adding symbol ${name}`); - this.symbols.set(`${name}@${(scope as any).id}`, sym); + private resolveReferenceExpression(node: BoltReferenceExpression): BoltDeclaration | null { + return this.varResolver.resolve(createDeclarationPath(node.name), node); } - private addTypeSymbol(name: string, scope: TypeScope, sym: TypeSymbolInfo): void { - console.error(`Adding type symbol ${name}`); - this.typeSymbols.set(`${name}@${(scope as any).id}`, sym); - } - - public getParentScope(scope: Scope): Scope | null { - let node = scope as Syntax; - if (node.kind === SyntaxKind.BoltSourceFile) { - return null; - } - node = node.parentNode!; - while (!introducesNewScope(node.kind)) { - node = node.parentNode!; - } - return node; - } - - public getParentTypeScope(scope: TypeScope): TypeScope | null { - let node = scope as Syntax; - if (node.kind === SyntaxKind.BoltSourceFile) { - return null; - } - node = node.parentNode!; - while (!introducesNewTypeScope(node.kind)) { - node = node.parentNode!; - } - return node; - } - - private getScopeSurroundingNode(node: Syntax): Scope { - if (node.kind === SyntaxKind.BoltSourceFile) { - return node; - } - return this.getScopeForNode(node.parentNode); - } - - private getTypeScopeSurroundingNode(node: Syntax): TypeScope { - if (node.kind === SyntaxKind.BoltSourceFile) { - return node; - } - return this.getScopeForNode(node.parentNode); - } - - private getScopeForNode(node: Syntax): Scope { - if (node.kind === SyntaxKind.BoltSourceFile) { - return node; - } - let currNode = node; - while (!introducesNewScope(currNode.kind)) { - currNode = currNode.parentNode!; - } - return currNode; - } - - private getTypeScopeForNode(node: Syntax): TypeScope { - if (node.kind === SyntaxKind.BoltSourceFile) { - return node; - } - let currNode = node; - while (!introducesNewTypeScope(currNode.kind)) { - currNode = currNode.parentNode!; - } - return currNode; - } - - private lookupSymbolInScope(name: string, scope: Scope): SymbolInfo | null { - const key = `${name}@${(scope as any).id}`; - if (!this.symbols.has(key)) { - return null; - } - return this.symbols.get(key); - } - - private lookupSymbolInTypeScope(name: string, scope: TypeScope): TypeSymbolInfo | null { - const key = `${name}@${(scope as any).id}`; - if (!this.typeSymbols.has(key)) { - return null; - } - return this.typeSymbols.get(key); - } - - public findSymbolInScopeOf(name: string, scope: Scope): SymbolInfo | null { - while (true) { - const sym = this.lookupSymbolInScope(name, scope); - if (sym !== null) { - return sym; - } - const parentScope = this.getParentScope(scope); - if (parentScope === null) { - break; - } - scope = parentScope; - } - return null; - } - - public findSymbolInTypeScopeOf(name: string, scope: TypeScope): TypeSymbolInfo | null { - while (true) { - const sym = this.lookupSymbolInTypeScope(name, scope); - if (sym !== null) { - return sym; - } - const parentTypeScope = this.getParentTypeScope(scope); - if (parentTypeScope === null) { - break; - } - scope = parentTypeScope; - } - return null; - } - - public resolveReferenceExpression(node: BoltReferenceExpression): BoltDeclaration | null { - let scope = this.getScopeSurroundingNode(node); - if (node.name.modulePath !== null) { - while (true) { - let shouldSearchParentScopes = false; - let currScope = scope; - for (const name of node.name.modulePath) { - const sym = this.lookupSymbolInScope(name.text, currScope); - if (sym === null) { - shouldSearchParentScopes = true; - break; - } - if (sym.declarations[0].kind !== SyntaxKind.BoltModule) { - shouldSearchParentScopes = true; - break; - } - currScope = this.getScopeForNode(sym.declarations[0]); - } - if (!shouldSearchParentScopes) { - scope = currScope; - break; - } - const parentScope = this.getParentScope(scope); - if (parentScope === null) { - return null; - } - scope = parentScope; - } - } - const sym = this.findSymbolInScopeOf(emit(node.name.name), scope); - if (sym === null) { - return null; - } - return sym.declarations[0]!; - } - - public resolveTypeReferenceExpression(node: BoltReferenceTypeExpression): BoltTypeDeclaration | null { - const typeScope = this.getTypeScopeSurroundingNode(node); - const typeSym = this.findSymbolInTypeScopeOf(emit(node.name.name), typeScope); - if (typeSym === null) { - return null; - } - return typeSym.declarations[0]!; + private resolveTypeReferenceExpression(node: BoltReferenceTypeExpression): BoltTypeDeclaration | null { + return this.typeResolver.resolve(createDeclarationPath(node.name), node); } } diff --git a/src/common.ts b/src/common.ts index 18c6f59d5..549bced46 100644 --- a/src/common.ts +++ b/src/common.ts @@ -7,7 +7,10 @@ import { kindToString, Syntax, Token, - isBoltPunctuated + isBoltPunctuated, + SourceFile, + BoltSourceFile, + BoltSourceFileModifiers } from "./ast"; import { BOLT_SUPPORTED_LANGUAGES } from "./constants" import {emit} from "./emitter"; @@ -15,6 +18,30 @@ import {FastStringMap, enumerate, escapeChar} from "./util"; import {TextSpan, TextPos, TextFile} from "./text"; import {Scanner} from "./scanner"; +export class Package { + + constructor( + public rootDir: string, + public name: string | null, + public version: string | null, + public sourceFiles: SourceFile[], + ) { + + } + + public addSourceFile(sourceFile: SourceFile) { + this.sourceFiles.push(sourceFile); + } + +} + +export function isAutoImported(node: BoltSourceFile): boolean { + //if (node.kind !== SyntaxKind.BoltSourceFile) { + // node = node.getParentOfKind(SyntaxKind.BoltSourceFile)! + //} + return (node.modifiers & BoltSourceFileModifiers.AutoImport) > 0; +} + export function getLanguage(node: Syntax): string { const kindStr = kindToString(node.kind); for (const prefix of BOLT_SUPPORTED_LANGUAGES) { @@ -164,6 +191,10 @@ export class OperatorTable { export function describeKind(kind: SyntaxKind): string { switch (kind) { + case SyntaxKind.BoltImportKeyword: + return "'import'"; + case SyntaxKind.BoltExportKeyword: + return "'export'"; case SyntaxKind.BoltExMark: return "'!'"; case SyntaxKind.JSIdentifier: @@ -257,7 +288,7 @@ export function describeKind(kind: SyntaxKind): string { case SyntaxKind.BoltTraitKeyword: return "'impl'"; case SyntaxKind.BoltImplKeyword: - return "'trait'"; + return "'impl'"; case SyntaxKind.BoltForKeyword: return "'for'"; case SyntaxKind.JSMulOp: @@ -297,11 +328,37 @@ export function describeKind(kind: SyntaxKind): string { } } -export function toDeclarationPath(node: BoltQualName): string[] { - const lastElement = emit(node.name); - if (node.modulePath === null) { - return [ lastElement ]; - } - return [...node.modulePath.map(id => id.text), lastElement]; +export type DeclarationPath = unknown; + +type DeclarationPathInfo = { + modulePath: string[], + isAbsolute: boolean, + name: string +}; + +export function getModulePath(path: DeclarationPath): string[] { + return (path as DeclarationPathInfo).modulePath; +} + +export function hasAbsoluteModulePath(path: DeclarationPath): boolean { + return (path as DeclarationPathInfo).modulePath.length > 0 + && (path as DeclarationPathInfo).isAbsolute; +} + +export function hasRelativeModulePath(path: DeclarationPath): boolean { + return (path as DeclarationPathInfo).modulePath.length > 0 + && !(path as DeclarationPathInfo).isAbsolute; +} + +export function getSymbolNameOfDeclarationPath(path: DeclarationPath): string { + return (path as DeclarationPathInfo).name; +} + +export function createDeclarationPath(node: BoltQualName): DeclarationPath { + const name = emit(node.name); + if (node.modulePath === null) { + return { modulePath: [], isAbsolute: false, name }; + } + return { modulePath: node.modulePath.map(id => id.text), isAbsolute: false, name }; } diff --git a/src/diagnostics.ts b/src/diagnostics.ts index e5507fa4c..ceeee21fc 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -3,6 +3,9 @@ import chalk from "chalk" import {Syntax} from "./ast"; import {format, MapLike, FormatArg} from "./util"; +export const E_FIELD_HAS_INVALID_VERSION_NUMBER = "Field '{name}' contains an invalid version nunmber." +export const E_FIELD_MUST_BE_STRING = "Field '{name}' must be a string." +export const E_FIELD_NOT_PRESENT = "Field '{name}' is not present." export const E_TYPE_DECLARATION_NOT_FOUND = "A type declaration named '{name}' was not found." export const E_DECLARATION_NOT_FOUND = "Reference to an undefined declaration '{name}'."; export const E_TYPES_NOT_ASSIGNABLE = "Types {left} and {right} are not assignable."; @@ -84,6 +87,9 @@ export class DiagnosticPrinter { case 'error': this.hasErrors = true; out += chalk.bold.red('error: '); + case 'warning': + this.hasErrors = true; + out += chalk.bold.red('warning: '); } if (diagnostic.args !== undefined) { out += format(diagnostic.message, diagnostic.args) + '\n'; diff --git a/src/frontend.ts b/src/frontend.ts index 57f0b1957..b5a5009f1 100644 --- a/src/frontend.ts +++ b/src/frontend.ts @@ -8,7 +8,7 @@ import { Program } from "./program" import { TypeChecker } from "./checker" import { Evaluator } from "./evaluator" import { emit } from "./emitter" -import { Syntax, BoltSourceFile } from "./ast" +import { Syntax, BoltSourceFile, SourceFile } from "./ast" import { upsearchSync, FastStringMap, getFileStem, getLanguage } from "./util" import { Package } from "./package" import { verbose, memoize } from "./util" @@ -115,18 +115,13 @@ export class Frontend { } for (const sourceFile of program.getAllSourceFiles()) { - //const filepath = rootNode.span!.file.fullPath; - //const pkg = this.getPackage(filepath); - //if (pkg !== null) { - // - //} fs.mkdirp('.bolt-work'); fs.writeFileSync(this.mapToTargetFile(sourceFile), emit(sourceFile), 'utf8'); } } - private mapToTargetFile(node: Syntax) { + private mapToTargetFile(node: SourceFile) { return path.join('.bolt-work', getFileStem(node.span!.file.fullPath) + getDefaultExtension(getLanguage(node))); } diff --git a/src/package.ts b/src/package.ts deleted file mode 100644 index 992d9b94e..000000000 --- a/src/package.ts +++ /dev/null @@ -1,9 +0,0 @@ - -export class Package { - - constructor(public rootDir: string) { - - } - -} - diff --git a/src/parser.ts b/src/parser.ts index b0c3fd47b..222260672 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -16,7 +16,7 @@ import { BoltQualName, BoltPattern, createBoltBindPattern, - BoltImportDeclaration, + BoltImportDirective, BoltTypeExpression, createBoltReferenceTypeExpression, createBoltConstantExpression, @@ -25,8 +25,8 @@ import { BoltBindPattern, createBoltRecordDeclaration, createBoltRecordField, - createBoltImportDeclaration, - BoltDeclarationModifiers, + createBoltImportDirective, + BoltModifiers, BoltStringLiteral, BoltImportSymbol, BoltExpressionStatement, @@ -77,6 +77,7 @@ import { BoltMacroCall, createBoltMacroCall, createBoltMemberExpression, + BoltSourceFileModifiers, } from "./ast" import { parseForeignLanguage } from "./foreign" @@ -88,6 +89,7 @@ import { ParseError, setOrigNodeRange, createTokenStream, + Package, } from "./common" import { Stream, uniq } from "./util" @@ -140,6 +142,7 @@ const KIND_DECLARATION_T0 = uniq([ const KIND_SOURCEELEMENT_T0 = uniq([ SyntaxKind.BoltModKeyword, + SyntaxKind.BoltImportKeyword, ...KIND_EXPRESSION_T0, ...KIND_STATEMENT_T0, ...KIND_DECLARATION_T0, @@ -321,7 +324,13 @@ export class Parser { } } - public parseImportDeclaration(tokens: BoltTokenStream): BoltImportDeclaration { + public parseImportDirective(tokens: BoltTokenStream): BoltImportDirective { + + let modifiers = 0; + if (tokens.peek().kind === SyntaxKind.BoltPubKeyword) { + tokens.get(); + modifiers |= BoltModifiers.IsPublic; + } const t0 = tokens.get(); assertToken(t0, SyntaxKind.BoltImportKeyword); @@ -331,9 +340,12 @@ export class Parser { const filename = (t1 as BoltStringLiteral).value; const symbols: BoltImportSymbol[] = []; - // TODO implement grammar and parsing logic for symbols + const t2 = tokens.get(); + if (t2.kind === SyntaxKind.BoltParenthesized) { + // TODO implement grammar and parsing logic for symbols + } - const node = createBoltImportDeclaration(filename, symbols); + const node = createBoltImportDirective(modifiers, filename, symbols); setOrigNodeRange(node, t0, t1); return node; } @@ -701,7 +713,7 @@ export class Parser { const t1 = tokens.peek(); if (t1.kind === SyntaxKind.BoltMutKeyword) { tokens.get(); - modifiers |= BoltDeclarationModifiers.Mutable; + modifiers |= BoltModifiers.Mutable; } const bindings = this.parsePattern(tokens) @@ -821,7 +833,7 @@ export class Parser { let t0 = tokens.get(); const firstToken = t0; if (t0.kind === SyntaxKind.BoltPubKeyword) { - modifiers |= BoltDeclarationModifiers.Public; + modifiers |= BoltModifiers.IsPublic; t0 = tokens.get(); } @@ -915,7 +927,7 @@ export class Parser { const firstToken = t0; if (t0.kind === SyntaxKind.BoltPubKeyword) { tokens.get(); - modifiers |= BoltDeclarationModifiers.Public; + modifiers |= BoltModifiers.IsPublic; t0 = tokens.peek(); } @@ -946,7 +958,7 @@ export class Parser { const firstToken = t0; if (t0.kind === SyntaxKind.BoltPubKeyword) { - modifiers |= BoltDeclarationModifiers.Public; + modifiers |= BoltModifiers.IsPublic; t0 = tokens.get(); } @@ -985,13 +997,12 @@ export class Parser { if (k0.kind === SyntaxKind.BoltPubKeyword) { tokens.get(); - modifiers |= BoltDeclarationModifiers.Public; + modifiers |= BoltModifiers.IsPublic; k0 = tokens.peek(); } if (k0.kind === SyntaxKind.BoltForeignKeyword) { tokens.get(); - modifiers |= BoltDeclarationModifiers.IsForeign; const l1 = tokens.get(); if (l1.kind !== SyntaxKind.BoltStringLiteral) { throw new ParseError(l1, [SyntaxKind.BoltStringLiteral]) @@ -1150,7 +1161,7 @@ export class Parser { let t0 = tokens.get(); const firstToken = t0; if (t0.kind === SyntaxKind.BoltPubKeyword) { - modifiers |= BoltDeclarationModifiers.Public; + modifiers |= BoltModifiers.IsPublic; t0 = tokens.get(); } assertToken(t0, SyntaxKind.BoltTraitKeyword); @@ -1181,7 +1192,7 @@ export class Parser { let t0 = tokens.get(); const firstToken = t0; if (t0.kind === SyntaxKind.BoltPubKeyword) { - modifiers |= BoltDeclarationModifiers.Public; + modifiers |= BoltModifiers.IsPublic; t0 = tokens.get(); } assertToken(t0, SyntaxKind.BoltImplKeyword); @@ -1244,12 +1255,12 @@ export class Parser { } private getFirstTokenAfterModifiers(tokens: BoltTokenStream): BoltToken { - let mustBeDecl = false; + let mustBeDeclOrImport = false; let mustBeFunctionOrVariable = false; let i = 1; let t0 = tokens.peek(i); if (t0.kind === SyntaxKind.BoltPubKeyword) { - mustBeDecl = true; + mustBeDeclOrImport = true; t0 = tokens.peek(++i); } if (t0.kind === SyntaxKind.BoltForeignKeyword) { @@ -1262,7 +1273,7 @@ export class Parser { && t0.kind !== SyntaxKind.BoltFnKeyword) { throw new ParseError(t0, [SyntaxKind.BoltStructKeyword, SyntaxKind.BoltFnKeyword]); } - if (mustBeDecl && KIND_DECLARATION_T0.indexOf(t0.kind) === -1) { + if (mustBeDeclOrImport && KIND_DECLARATION_T0.indexOf(t0.kind) === -1 && t0.kind !== SyntaxKind.BoltImportKeyword) { throw new ParseError(t0, KIND_DECLARATION_KEYWORD); } return t0; @@ -1278,9 +1289,12 @@ export class Parser { return this.parseMacroCall(tokens); } const t0 = this.getFirstTokenAfterModifiers(tokens); - if (KIND_STATEMENT_T0.indexOf(t0.kind) !== -1) { + if (t0.kind === SyntaxKind.BoltImportKeyword) { + return this.parseImportDirective(tokens); + } else if (KIND_STATEMENT_T0.indexOf(t0.kind) !== -1) { return this.parseStatement(tokens); } + // FIXME This should only parse when the tokens are valid and otherwise display a friendly error message. return this.parseDeclaration(tokens); } @@ -1372,12 +1386,13 @@ export class Parser { // return lhs //} - public parseSourceFile(tokens: BoltTokenStream): BoltSourceFile { + public parseSourceFile(tokens: BoltTokenStream, pkg: Package): BoltSourceFile { const elements = this.parseSourceElements(tokens); const t1 = tokens.peek(); assertToken(t1, SyntaxKind.EndOfFile); return createBoltSourceFile( elements, + pkg, new TextSpan(t1.span!.file, new TextPos(0,1,1), t1.span!.end.clone()) ); } @@ -1518,12 +1533,12 @@ import * as fs from "fs" import {JSScanner} from "./foreign/js/scanner"; import {emit} from "./emitter"; -export function parseSourceFile(filepath: string): BoltSourceFile { +export function parseSourceFile(filepath: string, pkg: Package): BoltSourceFile { const file = new TextFile(filepath); const contents = fs.readFileSync(file.origPath, 'utf8'); const scanner = new Scanner(file, contents) const parser = new Parser(); - const sourceFile = parser.parseSourceFile(scanner); + const sourceFile = parser.parseSourceFile(scanner, pkg); setParents(sourceFile); return sourceFile; } diff --git a/src/program.ts b/src/program.ts index 9d2779fdf..df1d2b19d 100644 --- a/src/program.ts +++ b/src/program.ts @@ -1,4 +1,5 @@ +import { Package } from "./common" import { SourceFile } from "./ast" import { FastStringMap } from "./util"; @@ -7,10 +8,12 @@ export class Program { private transformed = new FastStringMap(); constructor( - sourceFiles: SourceFile[] + pkgs: Package[] ) { - for (const sourceFile of sourceFiles) { - this.transformed.set(sourceFile.span!.file.fullPath, sourceFile); + for (const pkg of pkgs) { + for (const sourceFile of pkg.sourceFiles) { + this.transformed.set(sourceFile.span!.file.fullPath, sourceFile); + } } } diff --git a/src/scanner.ts b/src/scanner.ts index 613465b23..f267c3490 100644 --- a/src/scanner.ts +++ b/src/scanner.ts @@ -9,6 +9,8 @@ import { import { BoltToken, + createBoltImportKeyword, + createBoltExportKeyword, createBoltRArrowAlt, createEndOfFile, createBoltIdentifier, @@ -308,6 +310,8 @@ export class Scanner { case 'trait': return createBoltTraitKeyword(span); case 'impl': return createBoltImplKeyword(span); case 'type': return createBoltTypeKeyword(span); + //case 'export': return createBoltExportKeyword(span); + case 'import': return createBoltImportKeyword(span); case 'foreign': return createBoltForeignKeyword(span); case 'let': return createBoltLetKeyword(span); case 'mut': return createBoltMutKeyword(span); diff --git a/src/treegen/ast-template.js b/src/treegen/ast-template.js index 46f9fc135..15d9a72ea 100644 --- a/src/treegen/ast-template.js +++ b/src/treegen/ast-template.js @@ -58,6 +58,8 @@ function isSyntax(value) { && value.__NODE_TYPE !== undefined; } +exported.isSyntax = isSyntax; + let nextNodeId = 1; function createNode(nodeType) { diff --git a/src/treegen/index.ts b/src/treegen/index.ts index c58121bd4..d1b8d6d12 100644 --- a/src/treegen/index.ts +++ b/src/treegen/index.ts @@ -4,6 +4,8 @@ import * as path from "path" const PACKAGE_ROOT = path.resolve(__dirname, '..', '..'); +const CUSTOM_TYPES = ['Package']; + import { Syntax, Declaration, NodeDeclaration, TypeDeclaration, EnumDeclaration, TypeNode, NodeField } from "./ast" import { MapLike } from "../util" import { FileWriter } from "./util" @@ -96,6 +98,13 @@ export function generateAST(decls: Declaration[]) { // Write corresponding TypeScript declarations + + // FIXME These imports are specific to our project and should somehow + // form part of the user specification. + dtsFile.write('\nimport { Package } from "./common"\n\n'); + + dtsFile.write('export function isSyntax(value: any): value is Syntax;\n\n'); + dtsFile.write(`\nexport const enum SyntaxKind {\n`); for (const decl of leafNodes) { dtsFile.write(` ${decl.name} = ${decl.index},\n`); @@ -111,6 +120,7 @@ export function setParents(node: Syntax): void; export type SyntaxRange = [Syntax, Syntax]; interface SyntaxBase { + id: number; kind: K; parentNode: ParentTypesOf | null; span: TextSpan | null; @@ -227,6 +237,8 @@ export type ResolveSyntaxKind = Extract { }; export interface JsonObject { [key: string]: Json } export type Json = null | string | boolean | number | JsonArray | JsonObject; +export function isString(value: any): boolean { + return typeof value === 'string'; +} + +export function hasOwnProperty(obj: T, key: K): boolean { + return Object.prototype.hasOwnProperty.call(obj, key); +} + export function uniq(elements: T[]): T[] { const out: T[] = []; const visited = new Set();