Fix some bugs and add minimal support for imports

This commit is contained in:
Sam Vervaeck 2020-02-25 18:34:17 +01:00
parent 5e111d8a65
commit efa3c4d835
7 changed files with 108 additions and 10 deletions

View file

@ -70,7 +70,7 @@ export enum SyntaxKind {
VarDecl, VarDecl,
FuncDecl, FuncDecl,
ForeignDecl, ImportDecl,
} }
@ -920,6 +920,7 @@ export class VarDecl extends SyntaxBase {
kind: SyntaxKind.VarDecl = SyntaxKind.VarDecl; kind: SyntaxKind.VarDecl = SyntaxKind.VarDecl;
constructor( constructor(
public isMutable: boolean,
public bindings: Patt, public bindings: Patt,
public typeDecl: TypeDecl | null, public typeDecl: TypeDecl | null,
public value: Expr | 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<Syntax> {
}
}
export type Decl export type Decl
= Sentence = Sentence
| FuncDecl | FuncDecl
| ImportDecl
| VarDecl | VarDecl
export type Syntax export type Syntax
= Decl = Decl
| Expr | Expr

View file

@ -183,14 +183,15 @@ yargs
const compiler = new Compiler(checker, { target: "JS" }) const compiler = new Compiler(checker, { target: "JS" })
const bundle = compiler.compile(sourceFiles) const bundle = compiler.compile(sourceFiles)
const emitter = new Emitter() const emitter = new Emitter()
for (const file of bundle) { const outfiles = bundle.map(file => {
const text = emitter.emit(file); const text = emitter.emit(file);
fs.mkdirpSync('.bolt-work') 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') fs.writeFileSync(filepath, text, 'utf8')
spawnSync('node', [filepath], { stdio: 'inherit' }) return filepath
} })
spawnSync('node', [outfiles[0]], { stdio: 'inherit' })
} }
) )

View file

@ -1,7 +1,8 @@
import { import {
Syntax, Syntax,
SyntaxKind SyntaxKind,
ImportDecl,
} from "./ast" } from "./ast"
class Type { class Type {
@ -27,7 +28,7 @@ export class TypeChecker {
protected scopes = new Map<Syntax, Scope>(); protected scopes = new Map<Syntax, Scope>();
createType(node: Syntax) { protected createType(node: Syntax) {
switch (node.kind) { switch (node.kind) {
case SyntaxKind.ConstExpr: case SyntaxKind.ConstExpr:
if (typeof node.value === 'bigint') { if (typeof node.value === 'bigint') {
@ -38,6 +39,10 @@ export class TypeChecker {
} }
} }
getImportedSymbols(node: ImportDecl) {
return [{ name: 'fac' }]
}
getScope(node: Syntax): Scope { getScope(node: Syntax): Scope {
while (node.kind !== SyntaxKind.FuncDecl && node.kind !== SyntaxKind.SourceFile) { while (node.kind !== SyntaxKind.FuncDecl && node.kind !== SyntaxKind.SourceFile) {
node = node.parentNode!; node = node.parentNode!;

View file

@ -96,6 +96,31 @@ export class Compiler {
switch (node.kind) { 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: case SyntaxKind.FuncDecl:
const params = []; const params = [];
if (node.target === this.target) { if (node.target === this.target) {

View file

@ -18,6 +18,7 @@ export class Expander {
constructor(public parser: Parser) { constructor(public parser: Parser) {
this.transformers.set('fn', parser.parseFuncDecl.bind(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('foreign', parser.parseFuncDecl.bind(parser))
this.transformers.set('let', parser.parseVarDecl.bind(parser)) this.transformers.set('let', parser.parseVarDecl.bind(parser))
this.transformers.set('return', parser.parseRetStmt.bind(parser)) this.transformers.set('return', parser.parseRetStmt.bind(parser))

View file

@ -20,8 +20,8 @@ import {
TypeDecl, TypeDecl,
ConstExpr, ConstExpr,
QualName, QualName,
ForeignDecl,
CallExpr, CallExpr,
ImportDecl,
} from "./ast" } from "./ast"
function describeKind(kind: SyntaxKind): string { 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 { parseTypeDecl(tokens: TokenStream): TypeDecl {
const t0 = tokens.peek(); const t0 = tokens.peek();
if (t0.kind === SyntaxKind.Identifier) { if (t0.kind === SyntaxKind.Identifier) {
@ -170,9 +184,35 @@ export class Parser {
parseVarDecl(tokens: TokenStream): VarDecl { parseVarDecl(tokens: TokenStream): VarDecl {
let isMutable = false;
let typeDecl = null;
let value = null;
// Assuming first token is 'let' // Assuming first token is 'let'
tokens.get(); 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 { parseRetStmt(tokens: TokenStream): RetStmt {
@ -206,7 +246,7 @@ export class Parser {
let target = "Bolt"; let target = "Bolt";
const k0 = tokens.get(); const k0 = tokens.peek();
if (k0.kind !== SyntaxKind.Identifier) { if (k0.kind !== SyntaxKind.Identifier) {
throw new ParseError(k0, [SyntaxKind.ForeignKeyword, SyntaxKind.FunctionKeyword]) throw new ParseError(k0, [SyntaxKind.ForeignKeyword, SyntaxKind.FunctionKeyword])
} }
@ -218,7 +258,7 @@ export class Parser {
target = l1.value; target = l1.value;
} }
const k1 = tokens.get(); const k1 = tokens.get();
if (k1.text !== 'fn') { if (k1.kind !== SyntaxKind.Identifier || k1.text !== 'fn') {
throw new ParseError(k1, [SyntaxKind.FunctionKeyword]) throw new ParseError(k1, [SyntaxKind.FunctionKeyword])
} }

View file

@ -23,6 +23,7 @@ import {
IntegerLiteral, IntegerLiteral,
Colon, Colon,
EOS, EOS,
EqSign,
} from "./ast" } from "./ast"
function escapeChar(ch: string) { function escapeChar(ch: string) {
@ -200,6 +201,9 @@ export class Scanner {
} }
switch (c0) { switch (c0) {
case '=':
this.getChar();
return new EqSign(new TextSpan(this.file, startPos, this.currPos.clone()));
case ';': case ';':
this.getChar(); this.getChar();
return new Semi(new TextSpan(this.file, startPos, this.currPos.clone())); return new Semi(new TextSpan(this.file, startPos, this.currPos.clone()));