From 9b5cb6ad38ef75894a7643f02816ec9380a75cd1 Mon Sep 17 00:00:00 2001 From: Sam Vervaeck Date: Sun, 24 May 2020 17:47:04 +0200 Subject: [PATCH] Add a first version of Lang.Bolt module --- spec/ast.txt | 28 ++- src/ast.d.ts | 203 ++++++++++++-------- src/common.ts | 8 +- src/frontend.ts | 1 - src/program.ts | 7 +- src/transforms/boltToJS.ts | 93 +++++---- src/transforms/eliminateMatchExpressions.ts | 0 src/transforms/eliminateModules.ts | 55 ------ stdlib/lang/bolt.bolt | 52 ++++- stdlib/syntax.bolt | 14 +- 10 files changed, 265 insertions(+), 196 deletions(-) delete mode 100644 src/transforms/eliminateMatchExpressions.ts delete mode 100644 src/transforms/eliminateModules.ts diff --git a/spec/ast.txt b/spec/ast.txt index c9ed59a2f..ac606b300 100644 --- a/spec/ast.txt +++ b/spec/ast.txt @@ -180,6 +180,15 @@ node BoltReturnStatement > BoltStatement { value: Option, } +node BoltConditionalCase { + test: Option, + body: Vec, +} + +node BoltConditionalStatement > BoltStatement { + cases: Vec, +} + node BoltResumeStatement > BoltStatement { value: BoltExpression, } @@ -402,7 +411,9 @@ node JSReferenceExpression > JSExpression { node JSSourceElement; -node JSStatement > JSSourceElement; +node JSFunctionBodyElement; + +node JSStatement > JSSourceElement, JSFunctionBodyElement; node JSCatchBlock { bindings: Option, @@ -419,10 +430,13 @@ node JSExpressionStatement > JSStatement { expression: JSExpression, } +node JSConditionalCase { + test: Option, + body: Vec, +} + node JSConditionalStatement > JSStatement { - test: JSExpression, - consequent: Vec, - alternate: Vec, + cases: Vec, } node JSReturnStatement > JSStatement { @@ -457,20 +471,20 @@ node JSImportDeclaration > JSDeclaration { filename: JSString, } -node JSFunctionDeclaration > JSDeclaration { +node JSFunctionDeclaration > JSDeclaration, JSFunctionBodyElement { modifiers: JSDeclarationModifiers, name: JSIdentifier, params: Vec, body: Vec, } -node JSArrowFunctionDeclaration > JSDeclaration { +node JSArrowFunctionDeclaration > JSDeclaration, JSFunctionBodyElement { name: JSIdentifier, params: Vec, body: JSExpression, } -node JSLetDeclaration > JSDeclaration { +node JSLetDeclaration > JSDeclaration, JSFunctionBodyElement { bindings: JSPattern, value: Option, } diff --git a/src/ast.d.ts b/src/ast.d.ts index 498ffde9f..1bf9224b3 100644 --- a/src/ast.d.ts +++ b/src/ast.d.ts @@ -59,82 +59,85 @@ export const enum SyntaxKind { BoltBlockExpression = 66, BoltConstantExpression = 67, BoltReturnStatement = 69, - BoltResumeStatement = 70, - BoltExpressionStatement = 71, - BoltParameter = 72, - BoltModule = 76, - BoltFunctionDeclaration = 78, - BoltVariableDeclaration = 79, - BoltPlainImportSymbol = 81, - BoltImportDeclaration = 82, - BoltTraitDeclaration = 83, - BoltImplDeclaration = 84, - BoltTypeAliasDeclaration = 85, - BoltRecordField = 87, - BoltRecordDeclaration = 88, - BoltMacroCall = 90, - JSOperator = 93, - JSIdentifier = 94, - JSString = 95, - JSInteger = 96, - JSFromKeyword = 97, - JSReturnKeyword = 98, - JSTryKeyword = 99, - JSFinallyKeyword = 100, - JSCatchKeyword = 101, - JSImportKeyword = 102, - JSAsKeyword = 103, - JSConstKeyword = 104, - JSLetKeyword = 105, - JSExportKeyword = 106, - JSFunctionKeyword = 107, - JSWhileKeyword = 108, - JSForKeyword = 109, - JSCloseBrace = 110, - JSCloseBracket = 111, - JSCloseParen = 112, - JSOpenBrace = 113, - JSOpenBracket = 114, - JSOpenParen = 115, - JSSemi = 116, - JSComma = 117, - JSDot = 118, - JSDotDotDot = 119, - JSMulOp = 120, - JSAddOp = 121, - JSDivOp = 122, - JSSubOp = 123, - JSLtOp = 124, - JSGtOp = 125, - JSBOrOp = 126, - JSBXorOp = 127, - JSBAndOp = 128, - JSBNotOp = 129, - JSNotOp = 130, - JSBindPattern = 132, - JSConstantExpression = 134, - JSMemberExpression = 135, - JSCallExpression = 136, - JSBinaryExpression = 137, - JSUnaryExpression = 138, - JSNewExpression = 139, - JSSequenceExpression = 140, - JSConditionalExpression = 141, - JSLiteralExpression = 143, - JSReferenceExpression = 144, - JSCatchBlock = 147, - JSTryCatchStatement = 148, - JSExpressionStatement = 149, - JSConditionalStatement = 150, - JSReturnStatement = 151, - JSParameter = 152, - JSImportStarBinding = 156, - JSImportAsBinding = 157, - JSImportDeclaration = 158, - JSFunctionDeclaration = 159, - JSArrowFunctionDeclaration = 160, - JSLetDeclaration = 161, - JSSourceFile = 162, + BoltConditionalCase = 70, + BoltConditionalStatement = 71, + BoltResumeStatement = 72, + BoltExpressionStatement = 73, + BoltParameter = 74, + BoltModule = 78, + BoltFunctionDeclaration = 80, + BoltVariableDeclaration = 81, + BoltPlainImportSymbol = 83, + BoltImportDeclaration = 84, + BoltTraitDeclaration = 85, + BoltImplDeclaration = 86, + BoltTypeAliasDeclaration = 87, + BoltRecordField = 89, + BoltRecordDeclaration = 90, + BoltMacroCall = 92, + JSOperator = 95, + JSIdentifier = 96, + JSString = 97, + JSInteger = 98, + JSFromKeyword = 99, + JSReturnKeyword = 100, + JSTryKeyword = 101, + JSFinallyKeyword = 102, + JSCatchKeyword = 103, + JSImportKeyword = 104, + JSAsKeyword = 105, + JSConstKeyword = 106, + JSLetKeyword = 107, + JSExportKeyword = 108, + JSFunctionKeyword = 109, + JSWhileKeyword = 110, + JSForKeyword = 111, + JSCloseBrace = 112, + JSCloseBracket = 113, + JSCloseParen = 114, + JSOpenBrace = 115, + JSOpenBracket = 116, + JSOpenParen = 117, + JSSemi = 118, + JSComma = 119, + JSDot = 120, + JSDotDotDot = 121, + JSMulOp = 122, + JSAddOp = 123, + JSDivOp = 124, + JSSubOp = 125, + JSLtOp = 126, + JSGtOp = 127, + JSBOrOp = 128, + JSBXorOp = 129, + JSBAndOp = 130, + JSBNotOp = 131, + JSNotOp = 132, + JSBindPattern = 134, + JSConstantExpression = 136, + JSMemberExpression = 137, + JSCallExpression = 138, + JSBinaryExpression = 139, + JSUnaryExpression = 140, + JSNewExpression = 141, + JSSequenceExpression = 142, + JSConditionalExpression = 143, + JSLiteralExpression = 145, + JSReferenceExpression = 146, + JSCatchBlock = 150, + JSTryCatchStatement = 151, + JSExpressionStatement = 152, + JSConditionalCase = 153, + JSConditionalStatement = 154, + JSReturnStatement = 155, + JSParameter = 156, + JSImportStarBinding = 160, + JSImportAsBinding = 161, + JSImportDeclaration = 162, + JSFunctionDeclaration = 163, + JSArrowFunctionDeclaration = 164, + JSLetDeclaration = 165, + JSSourceFile = 166, } @@ -523,6 +526,7 @@ export interface BoltConstantExpression extends SyntaxBase { + kind: SyntaxKind.BoltConditionalCase; + test: BoltExpression | null; + body: BoltFunctionBodyElement[]; +} + +export interface BoltConditionalStatement extends SyntaxBase { + kind: SyntaxKind.BoltConditionalStatement; + cases: BoltConditionalCase[]; +} + export interface BoltResumeStatement extends SyntaxBase { kind: SyntaxKind.BoltResumeStatement; value: BoltExpression; @@ -581,6 +596,7 @@ export interface BoltModule extends SyntaxBase { export type BoltFunctionBodyElement = BoltReturnStatement + | BoltConditionalStatement | BoltResumeStatement | BoltExpressionStatement | BoltMacroCall @@ -667,6 +683,7 @@ export interface BoltRecordDeclaration extends SyntaxBase { + kind: SyntaxKind.JSConditionalCase; + test: JSExpression | null; + body: JSFunctionBodyElement[]; +} + export interface JSConditionalStatement extends SyntaxBase { kind: SyntaxKind.JSConditionalStatement; - test: JSExpression; - consequent: JSStatement[]; - alternate: JSStatement[]; + cases: JSConditionalCase[]; } export interface JSReturnStatement extends SyntaxBase { @@ -1137,6 +1167,8 @@ export type BoltSyntax | BoltBlockExpression | BoltConstantExpression | BoltReturnStatement + | BoltConditionalCase + | BoltConditionalStatement | BoltResumeStatement | BoltExpressionStatement | BoltParameter @@ -1206,6 +1238,7 @@ export type JSSyntax | JSCatchBlock | JSTryCatchStatement | JSExpressionStatement + | JSConditionalCase | JSConditionalStatement | JSReturnStatement | JSParameter @@ -1278,6 +1311,8 @@ export type Syntax | BoltBlockExpression | BoltConstantExpression | BoltReturnStatement + | BoltConditionalCase + | BoltConditionalStatement | BoltResumeStatement | BoltExpressionStatement | BoltParameter @@ -1344,6 +1379,7 @@ export type Syntax | JSCatchBlock | JSTryCatchStatement | JSExpressionStatement + | JSConditionalCase | JSConditionalStatement | JSReturnStatement | JSParameter @@ -1417,6 +1453,8 @@ export function createBoltCaseExpression(cases: BoltCase[], span?: TextSpan | nu export function createBoltBlockExpression(elements: BoltFunctionBodyElement[], span?: TextSpan | null): BoltBlockExpression; export function createBoltConstantExpression(value: BoltValue, span?: TextSpan | null): BoltConstantExpression; export function createBoltReturnStatement(value: BoltExpression | null, span?: TextSpan | null): BoltReturnStatement; +export function createBoltConditionalCase(test: BoltExpression | null, body: BoltFunctionBodyElement[], span?: TextSpan | null): BoltConditionalCase; +export function createBoltConditionalStatement(cases: BoltConditionalCase[], span?: TextSpan | null): BoltConditionalStatement; 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; @@ -1483,7 +1521,8 @@ export function createJSReferenceExpression(name: string, span?: TextSpan | null export function createJSCatchBlock(bindings: JSPattern | null, elements: JSSourceElement[], span?: TextSpan | null): JSCatchBlock; export function createJSTryCatchStatement(tryBlock: JSSourceElement[], catchBlock: JSCatchBlock | null, finalBlock: JSSourceElement[] | null, span?: TextSpan | null): JSTryCatchStatement; export function createJSExpressionStatement(expression: JSExpression, span?: TextSpan | null): JSExpressionStatement; -export function createJSConditionalStatement(test: JSExpression, consequent: JSStatement[], alternate: JSStatement[], span?: TextSpan | null): JSConditionalStatement; +export function createJSConditionalCase(test: JSExpression | null, body: JSFunctionBodyElement[], span?: TextSpan | null): JSConditionalCase; +export function createJSConditionalStatement(cases: JSConditionalCase[], span?: TextSpan | null): JSConditionalStatement; export function createJSReturnStatement(value: JSExpression | null, span?: TextSpan | null): JSReturnStatement; export function createJSParameter(index: number, bindings: JSPattern, defaultValue: JSExpression | null, span?: TextSpan | null): JSParameter; export function createJSImportStarBinding(local: JSIdentifier, span?: TextSpan | null): JSImportStarBinding; @@ -1561,6 +1600,8 @@ export function isBoltBlockExpression(value: any): value is BoltBlockExpression; export function isBoltConstantExpression(value: any): value is BoltConstantExpression; export function isBoltStatement(value: any): value is BoltStatement; export function isBoltReturnStatement(value: any): value is BoltReturnStatement; +export function isBoltConditionalCase(value: any): value is BoltConditionalCase; +export function isBoltConditionalStatement(value: any): value is BoltConditionalStatement; export function isBoltResumeStatement(value: any): value is BoltResumeStatement; export function isBoltExpressionStatement(value: any): value is BoltExpressionStatement; export function isBoltParameter(value: any): value is BoltParameter; @@ -1634,10 +1675,12 @@ export function isJSConditionalExpression(value: any): value is JSConditionalExp export function isJSLiteralExpression(value: any): value is JSLiteralExpression; export function isJSReferenceExpression(value: any): value is JSReferenceExpression; export function isJSSourceElement(value: any): value is JSSourceElement; +export function isJSFunctionBodyElement(value: any): value is JSFunctionBodyElement; export function isJSStatement(value: any): value is JSStatement; export function isJSCatchBlock(value: any): value is JSCatchBlock; export function isJSTryCatchStatement(value: any): value is JSTryCatchStatement; export function isJSExpressionStatement(value: any): value is JSExpressionStatement; +export function isJSConditionalCase(value: any): value is JSConditionalCase; export function isJSConditionalStatement(value: any): value is JSConditionalStatement; export function isJSReturnStatement(value: any): value is JSReturnStatement; export function isJSParameter(value: any): value is JSParameter; diff --git a/src/common.ts b/src/common.ts index 218b4d152..780a4bb70 100644 --- a/src/common.ts +++ b/src/common.ts @@ -2,9 +2,15 @@ import { BoltFunctionBodyElement, BoltReturnStatement, SyntaxKind, - BoltExpression + BoltExpression, + BoltSourceFile, + JSSourceFile } from "./ast"; +export type SourceFile + = BoltSourceFile + | JSSourceFile + export type BoltFunctionBody = BoltFunctionBodyElement[]; export function getReturnStatementsInFunctionBody(body: BoltFunctionBody): BoltReturnStatement[] { diff --git a/src/frontend.ts b/src/frontend.ts index 659b043f4..57f0b1957 100644 --- a/src/frontend.ts +++ b/src/frontend.ts @@ -104,7 +104,6 @@ export class Frontend { case "JS": const transforms = new TransformManager(this.container); transforms.register(ExpandBoltTransform); - transforms.register(EliminateModulesTransform); transforms.register(CompileBoltToJSTransform); transforms.register(ConstFoldTransform); transforms.apply(program); diff --git a/src/program.ts b/src/program.ts index c86598f4c..ce0ca2ed4 100644 --- a/src/program.ts +++ b/src/program.ts @@ -1,11 +1,8 @@ -import { BoltSourceFile, JSSourceFile } from "./ast" +import { SourceFile } from "./common" +import { BoltSourceFile } from "./ast" import { FastStringMap } from "./util"; -export type SourceFile - = BoltSourceFile - | JSSourceFile - export class Program { private transformed = new FastStringMap(); diff --git a/src/transforms/boltToJS.ts b/src/transforms/boltToJS.ts index ed3a3f76d..bf714812e 100644 --- a/src/transforms/boltToJS.ts +++ b/src/transforms/boltToJS.ts @@ -39,6 +39,7 @@ import { isBoltStatement, JSBindPattern, BoltSourceElement, + createJSParameter, } from "../ast" import { hasPublicModifier, setOrigNodeRange } from "../util" @@ -69,7 +70,7 @@ class CompileContext { private generatedNodes: JSSyntax[] = []; - constructor(public scope: Scope) { + constructor() { } @@ -98,7 +99,7 @@ export class BoltToJSTransform implements Transformer { } public transform(sourceFile: BoltSourceFile): JSSourceFile { - const ctx = new CompileContext(this.checker.getScope(sourceFile)) + const ctx = new CompileContext() for (const element of sourceFile.elements) { this.compileSourceElement(element, ctx); } @@ -110,6 +111,7 @@ export class BoltToJSTransform implements Transformer { switch (node.kind) { case SyntaxKind.BoltCallExpression: + { const compiledOperator = this.compileExpression(node.operator, ctx); const compiledArgs = node.operands.map(arg => this.compileExpression(arg, ctx)) return createJSCallExpression( @@ -117,19 +119,22 @@ export class BoltToJSTransform implements Transformer { compiledArgs, node.span, ); + } case SyntaxKind.BoltReferenceExpression: + { assert(node.name.modulePath === null); - return createJSReferenceExpression( - node.name.name.text, - node.span, - ); + const result = createJSReferenceExpression(node.name.name.text); + setOrigNodeRange(result, node, node); + return result; + } case SyntaxKind.BoltConstantExpression: - return createJSConstantExpression( - node.value, - node.span, - ); + { + const result = createJSConstantExpression(node.value); + setOrigNodeRange(result, node, node); + return result; + } default: throw new Error(`Could not compile expression node ${kindToString(node.kind)}`) @@ -147,7 +152,7 @@ export class BoltToJSTransform implements Transformer { return jsBindPatt; } - protected compileSourceElement(node: BoltSourceElement, ctx: CompileContext) { + private compileSourceElement(node: BoltSourceElement, ctx: CompileContext) { switch (node.kind) { @@ -167,6 +172,7 @@ export class BoltToJSTransform implements Transformer { break; case SyntaxKind.BoltVariableDeclaration: + { const jsValue = node.value !== null ? this.compileExpression(node.value, ctx) : null; const jsValueBindPatt = this.convertPattern(node.bindings); const jsValueDecl = createJSLetDeclaration( @@ -175,35 +181,50 @@ export class BoltToJSTransform implements Transformer { ); ctx.appendNode(jsValueDecl); break; + } case SyntaxKind.BoltFunctionDeclaration: - if (node.body === null) { - break; - } - if (node.target === "JS") { - const params: JSParameter[] = []; - let body: JSStatement[] = []; - for (const param of node.params) { - assert(param.defaultValue === null); - const jsPatt = this.convertPattern(param.bindings) - params.push(jsPatt); - } - let result = createJSFunctionDeclaration( - 0, - createJSIdentifier(node.name.text, node.name.span), - params, - body, - node.span, - ); - if (hasPublicModifier(node)) { - result.modifiers |= JSDeclarationModifiers.IsExported;; - } - ctx.appendNode(result) - } else { - // TODO - throw new Error(`Compiling native functions is not yet implemented.`); + { + if (node.body === null) { + break; } + const params: JSParameter[] = []; + let body: JSStatement[] = []; + let modifiers = 0; + if (hasPublicModifier(node)) { + modifiers |= JSDeclarationModifiers.IsExported;; + } + let i = 0; + for (const param of node.params) { + assert(param.defaultValue === null); + const jsPatt = this.convertPattern(param.bindings) + const jsParam = createJSParameter(i, jsPatt, null); + params.push(jsParam); + i++; + } + const name = createJSIdentifier(node.name.text) + setOrigNodeRange(name, node.name, node.name); + const bodyCtx = new CompileContext(); + if (node.target === "JS") { + for (const element of node.body) { + this.compileJSStatement(element, bodyCtx); + } + } else { + for (const element of node.body) { + this.compileSourceElement(element, bodyCtx); + } + } + const result = createJSFunctionDeclaration( + modifiers, + name, + params, + bodyCtx.getGeneratedNodes() as JSStatement[], + node.span, + ); + setOrigNodeRange(result, node, node); + ctx.appendNode(result) break; + } default: throw new Error(`Could not compile node ${kindToString(node.kind)}`); diff --git a/src/transforms/eliminateMatchExpressions.ts b/src/transforms/eliminateMatchExpressions.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/transforms/eliminateModules.ts b/src/transforms/eliminateModules.ts deleted file mode 100644 index d994be649..000000000 --- a/src/transforms/eliminateModules.ts +++ /dev/null @@ -1,55 +0,0 @@ -import {TransformManager} from "."; -import {SourceFile} from "../program"; -import {isBoltSourceFile, createBoltSourceFile, BoltSourceElement, SyntaxKind, BoltModule, isBoltModule} from "../ast"; -import {setOrigNodeRange} from "../util"; - -export class EliminateModulesTransform { - - constructor(private transformers: TransformManager) { - - } - - public isApplicable(sourceFile: SourceFile) { - return isBoltSourceFile(sourceFile); - } - - public transform(sourceFile: SourceFile): SourceFile { - - let needsUpdate = false; - const elements: BoltSourceElement[] = []; - - for (const element of sourceFile.elements) { - if (element.kind === SyntaxKind.BoltModule) { - this.extractModuleElements(element, elements); - needsUpdate = true; - } else { - elements.push(element); - } - } - - if (!needsUpdate) { - return sourceFile; - } - - const newSourceFile = createBoltSourceFile(elements); - setOrigNodeRange(newSourceFile, sourceFile, sourceFile); - return newSourceFile; - } - - public extractModuleElements(node: BoltModule, out: BoltSourceElement[]) { - for (const element of node.elements) { - switch (element.kind) { - case SyntaxKind.BoltModule: - this.extractModuleElements(node, out); - break; - case SyntaxKind.BoltRecordDeclaration: - // TODO - break; - } - } - } - -} - -export default EliminateModulesTransform; - diff --git a/stdlib/lang/bolt.bolt b/stdlib/lang/bolt.bolt index 49a4b7bb8..c0c25a243 100644 --- a/stdlib/lang/bolt.bolt +++ b/stdlib/lang/bolt.bolt @@ -1,5 +1,5 @@ -mod Lang.Bolt.AST { +mod Bolt.Lang { pub struct Pos { offset: Int, @@ -20,5 +20,55 @@ mod Lang.Bolt.AST { parent: Option, } + pub type Token + = Identifier + + pub struct ConditionalCase { + test: Option, + result: Vec, + } + + pub struct ConditionalStatement { + cases: Vec, + } + + pub type Statement + = ReturnStatement + | ConditionalStatement + + pub type Expression + = ReferenceExpression + + pub type Transformer = fn (node: Node) -> Node; + + fn build_predicate_from_pattern(pattern: Pattern) -> Expression { + match pattern { + BindPattern { name } => quote(true).taint(pattern), + VariantPatten { elements } => quote(or($(elements),*)), + RecordPattern { members } => quote(and($elements),*)), + } + } + + fn eliminate_match_rule(stx: Syntax) { + match stx { + quote { + match $value: Expression { + $patterns @ ( $pattern: Pattern => $expression: Expression ),* (,)? + } + } => { + let cases = patterns.map(|pattern| { + ConditionalCase::from_node( + pattern, + build_predicate_from_pattern(pattern), + value + ) + }); + return ConditionalStatement::from_node(stx, cases); + } + } + } + + register_transformer!(elminate_match_rule); + } diff --git a/stdlib/syntax.bolt b/stdlib/syntax.bolt index b1bef73b3..bc019d8de 100644 --- a/stdlib/syntax.bolt +++ b/stdlib/syntax.bolt @@ -1,11 +1,5 @@ -syntax { - quote { - macro $name: QualName { - - } - } => { - - } -} - +import "lang/bolt" ( + Identifier, + Syntax, +);