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,
|
||||
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<Syntax> {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export type Decl
|
||||
= Sentence
|
||||
| FuncDecl
|
||||
| ImportDecl
|
||||
| VarDecl
|
||||
|
||||
|
||||
export type Syntax
|
||||
= Decl
|
||||
| Expr
|
||||
|
|
|
@ -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' })
|
||||
}
|
||||
|
||||
)
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
|
||||
import {
|
||||
Syntax,
|
||||
SyntaxKind
|
||||
SyntaxKind,
|
||||
ImportDecl,
|
||||
} from "./ast"
|
||||
|
||||
class Type {
|
||||
|
@ -27,7 +28,7 @@ export class TypeChecker {
|
|||
|
||||
protected scopes = new Map<Syntax, Scope>();
|
||||
|
||||
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!;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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])
|
||||
}
|
||||
|
||||
|
|
|
@ -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()));
|
||||
|
|
Loading…
Reference in a new issue