Fix some bugs and add minimal support for imports
This commit is contained in:
parent
5e111d8a65
commit
efa3c4d835
7 changed files with 108 additions and 10 deletions
24
src/ast.ts
24
src/ast.ts
|
@ -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
|
||||||
|
|
|
@ -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' })
|
||||||
}
|
}
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -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!;
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()));
|
||||||
|
|
Loading…
Reference in a new issue