From efa3c4d835571ebdbb4eec4423097bb6ed366759 Mon Sep 17 00:00:00 2001 From: Sam Vervaeck Date: Tue, 25 Feb 2020 18:34:17 +0100 Subject: [PATCH] Fix some bugs and add minimal support for imports --- src/ast.ts | 24 +++++++++++++++++++++++- src/bin/bolt.ts | 9 +++++---- src/checker.ts | 9 +++++++-- src/compiler.ts | 25 +++++++++++++++++++++++++ src/expander.ts | 1 + src/parser.ts | 46 +++++++++++++++++++++++++++++++++++++++++++--- src/scanner.ts | 4 ++++ 7 files changed, 108 insertions(+), 10 deletions(-) diff --git a/src/ast.ts b/src/ast.ts index 3e95ff3be..d2138c075 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -70,7 +70,7 @@ export enum SyntaxKind { VarDecl, FuncDecl, - ForeignDecl, + ImportDecl, } @@ -920,6 +920,7 @@ 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, @@ -952,11 +953,32 @@ export class VarDecl extends SyntaxBase { } +export class ImportDecl { + + kind: SyntaxKind.ImportDecl = SyntaxKind.ImportDecl; + + constructor( + public file: string, + public span: TextSpan | null = null, + public origNode: [Syntax, Syntax] | Syntax | null = null, + public parentNode: Syntax | null = null + ) { + + } + + *getChildren(): IterableIterator { + + } + +} + export type Decl = Sentence | FuncDecl + | ImportDecl | VarDecl + export type Syntax = Decl | Expr diff --git a/src/bin/bolt.ts b/src/bin/bolt.ts index 79c5d4970..4c2c8bdeb 100644 --- a/src/bin/bolt.ts +++ b/src/bin/bolt.ts @@ -183,14 +183,15 @@ yargs const compiler = new Compiler(checker, { target: "JS" }) const bundle = compiler.compile(sourceFiles) const emitter = new Emitter() - for (const file of bundle) { + const outfiles = bundle.map(file => { const text = emitter.emit(file); fs.mkdirpSync('.bolt-work') - const filepath = path.join('.bolt-work', path.relative(process.cwd(), stripExtension(path.resolve(file.loc.source)) + '.js')) + const filepath = path.join('.bolt-work', path.relative(process.cwd(), stripExtension(path.resolve(file.loc.source)) + '.mjs')) fs.writeFileSync(filepath, text, 'utf8') - spawnSync('node', [filepath], { stdio: 'inherit' }) - } + return filepath + }) + spawnSync('node', [outfiles[0]], { stdio: 'inherit' }) } ) diff --git a/src/checker.ts b/src/checker.ts index 89782a2dc..920b587a8 100644 --- a/src/checker.ts +++ b/src/checker.ts @@ -1,7 +1,8 @@ import { Syntax, - SyntaxKind + SyntaxKind, + ImportDecl, } from "./ast" class Type { @@ -27,7 +28,7 @@ export class TypeChecker { protected scopes = new Map(); - createType(node: Syntax) { + protected createType(node: Syntax) { switch (node.kind) { case SyntaxKind.ConstExpr: if (typeof node.value === 'bigint') { @@ -38,6 +39,10 @@ export class TypeChecker { } } + getImportedSymbols(node: ImportDecl) { + return [{ name: 'fac' }] + } + getScope(node: Syntax): Scope { while (node.kind !== SyntaxKind.FuncDecl && node.kind !== SyntaxKind.SourceFile) { node = node.parentNode!; diff --git a/src/compiler.ts b/src/compiler.ts index e89516952..060ab21c8 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -96,6 +96,31 @@ export class Compiler { switch (node.kind) { + case SyntaxKind.ImportDecl: + preamble.push({ + type: 'ImportDeclaration', + source: { type: 'Literal', value: node.file }, + specifiers: this.checker.getImportedSymbols(node).map(s => ({ + type: 'ImportSpecifier', + imported: { type: 'Identifier', name: s.name }, + local: { type: 'Identifier', name: s.name }, + })), + }); + break; + + case SyntaxKind.VarDecl: + const compiledValue = node.value !== null ? this.compileExpr(node.value, preamble) : null; + preamble.push({ + type: 'VariableDeclaration', + kind: 'let', + declarations: [{ + type: 'VariableDeclarator', + id: { type: 'Identifier', name: node.bindings.name.text }, + init: compiledValue + }] + }); + break; + case SyntaxKind.FuncDecl: const params = []; if (node.target === this.target) { diff --git a/src/expander.ts b/src/expander.ts index 57a6e8ffd..740ac4f94 100644 --- a/src/expander.ts +++ b/src/expander.ts @@ -18,6 +18,7 @@ export class Expander { constructor(public parser: Parser) { this.transformers.set('fn', parser.parseFuncDecl.bind(parser)) + this.transformers.set('import', parser.parseImportDecl.bind(parser)) this.transformers.set('foreign', parser.parseFuncDecl.bind(parser)) this.transformers.set('let', parser.parseVarDecl.bind(parser)) this.transformers.set('return', parser.parseRetStmt.bind(parser)) diff --git a/src/parser.ts b/src/parser.ts index b95e031d1..b1f2283d9 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -20,8 +20,8 @@ import { TypeDecl, ConstExpr, QualName, - ForeignDecl, CallExpr, + ImportDecl, } from "./ast" function describeKind(kind: SyntaxKind): string { @@ -116,6 +116,20 @@ export class Parser { } } + parseImportDecl(tokens: TokenStream) { + + // Assuming first keyword is 'import' + tokens.get(); + + const t0 = tokens.get(); + if (t0.kind !== SyntaxKind.StringLiteral) { + throw new ParseError(t0, [SyntaxKind.StringLiteral]) + } + + return new ImportDecl(t0.value, null, t0); + + } + parseTypeDecl(tokens: TokenStream): TypeDecl { const t0 = tokens.peek(); if (t0.kind === SyntaxKind.Identifier) { @@ -170,9 +184,35 @@ export class Parser { parseVarDecl(tokens: TokenStream): VarDecl { + let isMutable = false; + let typeDecl = null; + let value = null; + // Assuming first token is 'let' tokens.get(); + const t0 = tokens.peek(); + if (t0.kind === SyntaxKind.Identifier && t0.text === 'mut') { + tokens.get(); + isMutable = true; + } + + const bindings = this.parsePattern(tokens) + + const t1 = tokens.peek(); + if (t1.kind === SyntaxKind.Colon) { + tokens.get(); + typeDecl = this.parseTypeDecl(tokens); + } + + const t2 = tokens.peek(); + if (t2.kind === SyntaxKind.EqSign) { + tokens.get(); + value = this.parseExpr(tokens); + } + + return new VarDecl(isMutable, bindings, typeDecl, value, null) + } parseRetStmt(tokens: TokenStream): RetStmt { @@ -206,7 +246,7 @@ export class Parser { let target = "Bolt"; - const k0 = tokens.get(); + const k0 = tokens.peek(); if (k0.kind !== SyntaxKind.Identifier) { throw new ParseError(k0, [SyntaxKind.ForeignKeyword, SyntaxKind.FunctionKeyword]) } @@ -218,7 +258,7 @@ export class Parser { target = l1.value; } const k1 = tokens.get(); - if (k1.text !== 'fn') { + if (k1.kind !== SyntaxKind.Identifier || k1.text !== 'fn') { throw new ParseError(k1, [SyntaxKind.FunctionKeyword]) } diff --git a/src/scanner.ts b/src/scanner.ts index 5c9aa4a8b..273cb22d4 100644 --- a/src/scanner.ts +++ b/src/scanner.ts @@ -23,6 +23,7 @@ import { IntegerLiteral, Colon, EOS, + EqSign, } from "./ast" function escapeChar(ch: string) { @@ -200,6 +201,9 @@ export class Scanner { } switch (c0) { + case '=': + this.getChar(); + return new EqSign(new TextSpan(this.file, startPos, this.currPos.clone())); case ';': this.getChar(); return new Semi(new TextSpan(this.file, startPos, this.currPos.clone()));