Another set of fixes

This commit is contained in:
Sam Vervaeck 2020-05-10 23:50:42 +02:00
parent 12c9317ea0
commit 9e139c40a9
19 changed files with 600 additions and 209 deletions

111
package-lock.json generated
View file

@ -32,6 +32,16 @@
"@types/node": "*"
}
},
"@types/microtime": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@types/microtime/-/microtime-2.1.0.tgz",
"integrity": "sha1-rb2Z9QGoXIhpXrHv01FYEPJWOTI="
},
"@types/minimist": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY="
},
"@types/mocha": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-7.0.2.tgz",
@ -176,24 +186,35 @@
}
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz",
"integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==",
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"dependencies": {
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"ansi-styles": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz",
"integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==",
"requires": {
"has-flag": "^3.0.0"
"@types/color-name": "^1.1.1",
"color-convert": "^2.0.1"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
}
}
},
@ -600,6 +621,37 @@
"dev": true,
"requires": {
"chalk": "^2.4.2"
},
"dependencies": {
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
"microtime": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/microtime/-/microtime-3.0.0.tgz",
"integrity": "sha512-SirJr7ZL4ow2iWcb54bekS4aWyBQNVcEDBiwAz9D/sTgY59A+uE8UJU15cp5wyZmPBwg/3zf8lyCJ5NUe1nVlQ==",
"requires": {
"node-addon-api": "^1.2.0",
"node-gyp-build": "^3.8.0"
}
},
"minimatch": {
@ -613,8 +665,7 @@
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"mkdirp": {
"version": "0.5.5",
@ -754,12 +805,22 @@
}
}
},
"moment": {
"version": "2.25.3",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.25.3.tgz",
"integrity": "sha512-PuYv0PHxZvzc15Sp8ybUCoQ+xpyPWvjOuK72a5ovzp2LI32rJXOiIfyoFoYvG3s6EwwrdkMyWuRiEHSZRLJNdg=="
},
"ms": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
"dev": true
},
"node-addon-api": {
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.1.tgz",
"integrity": "sha512-2+DuKodWvwRTrCfKOeR24KIc5unKjOh8mz17NCzVnHWfjAdDqbfbjqh7gUT+BkXBRQM52+xCHciKWonJ3CbJMQ=="
},
"node-environment-flags": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz",
@ -770,6 +831,11 @@
"semver": "^5.7.0"
}
},
"node-gyp-build": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.9.0.tgz",
"integrity": "sha512-zLcTg6P4AbcHPq465ZMFNXx7XpKKJh+7kkN699NiQWisR2uWYOWNWqRHAmbnmKiL4e9aLSlmy5U7rEMUXV59+A=="
},
"normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@ -1001,6 +1067,21 @@
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
"dev": true
},
"supports-color": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz",
"integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==",
"requires": {
"has-flag": "^4.0.0"
},
"dependencies": {
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
}
}
},
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",

View file

@ -15,12 +15,18 @@
"repository": "https://github.com/samvv/Bolt",
"dependencies": {
"@types/fs-extra": "^8.1.0",
"@types/microtime": "^2.1.0",
"@types/minimist": "^1.2.0",
"@types/node": "^13.13.5",
"@types/xregexp": "^4.3.0",
"@types/yargs": "^15.0.4",
"acorn": "^7.2.0",
"chalk": "^4.0.0",
"fs-extra": "^9.0.0",
"glob": "^7.1.6",
"microtime": "^3.0.0",
"minimist": "^1.2.5",
"moment": "^2.25.3",
"pegjs": "^0.11.0-master.b7b87ea",
"reflect-metadata": "^0.1.13",
"source-map-support": "^0.5.19",

View file

@ -30,6 +30,10 @@ node BoltOperator > BoltSymbol {
text: String,
}
node BoltAssignment > BoltToken {
operator: Option<String>,
}
node BoltEOS > BoltToken;
node BoltComma > BoltToken;

173
src/ast.d.ts vendored
View file

@ -5,92 +5,95 @@ export const enum SyntaxKind {
BoltIntegerLiteral = 6,
BoltIdentifier = 8,
BoltOperator = 9,
BoltEOS = 10,
BoltComma = 11,
BoltSemi = 12,
BoltColon = 13,
BoltDot = 14,
BoltDotDot = 15,
BoltRArrow = 16,
BoltLArrow = 17,
BoltEqSign = 18,
BoltGtSign = 19,
BoltLtSign = 20,
BoltFnKeyword = 22,
BoltForeignKeyword = 23,
BoltLetKeyword = 24,
BoltReturnKeyword = 25,
BoltLoopKeyword = 26,
BoltYieldKeyword = 27,
BoltMatchKeyword = 28,
BoltImportKeyword = 29,
BoltPubKeyword = 30,
BoltModKeyword = 31,
BoltMutKeyword = 32,
BoltEnumKeyword = 33,
BoltStructKeyword = 34,
BoltNewTypeKeyword = 35,
BoltParenthesized = 37,
BoltBraced = 38,
BoltBracketed = 39,
BoltSourceFile = 40,
BoltQualName = 41,
BoltSentence = 42,
BoltReferenceTypeNode = 44,
BoltBindPattern = 46,
BoltTypePattern = 47,
BoltExpressionPattern = 48,
BoltTuplePatternElement = 49,
BoltTuplePattern = 50,
BoltRecordPatternField = 51,
BoltRecordPattern = 52,
BoltReferenceExpression = 54,
BoltCallExpression = 55,
BoltYieldExpression = 56,
BoltMatchArm = 57,
BoltMatchExpression = 58,
BoltCase = 59,
BoltCaseExpression = 60,
BoltBlockExpression = 61,
BoltConstantExpression = 62,
BoltReturnStatement = 64,
BoltResumeStatement = 65,
BoltExpressionStatement = 66,
BoltParameter = 67,
BoltNewTypeDeclaration = 70,
BoltModule = 71,
BoltFunctionDeclaration = 72,
BoltVariableDeclaration = 73,
BoltPlainImportSymbol = 75,
BoltImportDeclaration = 76,
BoltRecordDeclarationField = 77,
BoltRecordDeclaration = 79,
JSOperator = 82,
JSIdentifier = 83,
JSBindPattern = 85,
JSConstantExpression = 87,
JSMemberExpression = 89,
JSCallExpression = 90,
JSBinaryExpression = 91,
JSUnaryExpression = 92,
JSNewExpression = 93,
JSSequenceExpression = 94,
JSConditionalExpression = 95,
JSReferenceExpression = 96,
JSExpressionStatement = 98,
JSConditionalStatement = 99,
JSParameter = 100,
JSFunctionDeclaration = 103,
JSArrowFunctionDeclaration = 104,
JSLetDeclaration = 105,
JSSourceFile = 106,
JSSourceElement = 107,
BoltAssignment = 10,
BoltEOS = 11,
BoltComma = 12,
BoltSemi = 13,
BoltColon = 14,
BoltDot = 15,
BoltDotDot = 16,
BoltRArrow = 17,
BoltLArrow = 18,
BoltEqSign = 19,
BoltGtSign = 20,
BoltLtSign = 21,
BoltFnKeyword = 23,
BoltForeignKeyword = 24,
BoltLetKeyword = 25,
BoltReturnKeyword = 26,
BoltLoopKeyword = 27,
BoltYieldKeyword = 28,
BoltMatchKeyword = 29,
BoltImportKeyword = 30,
BoltPubKeyword = 31,
BoltModKeyword = 32,
BoltMutKeyword = 33,
BoltEnumKeyword = 34,
BoltStructKeyword = 35,
BoltNewTypeKeyword = 36,
BoltParenthesized = 38,
BoltBraced = 39,
BoltBracketed = 40,
BoltSourceFile = 41,
BoltQualName = 42,
BoltSentence = 43,
BoltReferenceTypeNode = 45,
BoltBindPattern = 47,
BoltTypePattern = 48,
BoltExpressionPattern = 49,
BoltTuplePatternElement = 50,
BoltTuplePattern = 51,
BoltRecordPatternField = 52,
BoltRecordPattern = 53,
BoltReferenceExpression = 55,
BoltCallExpression = 56,
BoltYieldExpression = 57,
BoltMatchArm = 58,
BoltMatchExpression = 59,
BoltCase = 60,
BoltCaseExpression = 61,
BoltBlockExpression = 62,
BoltConstantExpression = 63,
BoltReturnStatement = 65,
BoltResumeStatement = 66,
BoltExpressionStatement = 67,
BoltParameter = 68,
BoltNewTypeDeclaration = 71,
BoltModule = 72,
BoltFunctionDeclaration = 73,
BoltVariableDeclaration = 74,
BoltPlainImportSymbol = 76,
BoltImportDeclaration = 77,
BoltRecordDeclarationField = 78,
BoltRecordDeclaration = 80,
JSOperator = 83,
JSIdentifier = 84,
JSBindPattern = 86,
JSConstantExpression = 88,
JSMemberExpression = 90,
JSCallExpression = 91,
JSBinaryExpression = 92,
JSUnaryExpression = 93,
JSNewExpression = 94,
JSSequenceExpression = 95,
JSConditionalExpression = 96,
JSReferenceExpression = 97,
JSExpressionStatement = 99,
JSConditionalStatement = 100,
JSParameter = 101,
JSFunctionDeclaration = 104,
JSArrowFunctionDeclaration = 105,
JSLetDeclaration = 106,
JSSourceFile = 107,
JSSourceElement = 108,
}
import { TextSpan } from "./text"
export function setParents(node: Syntax): void;
export type SyntaxRange = [Syntax, Syntax];
interface SyntaxBase {
@ -107,6 +110,7 @@ export type BoltToken
| BoltIntegerLiteral
| BoltIdentifier
| BoltOperator
| BoltAssignment
| BoltEOS
| BoltComma
| BoltSemi
@ -162,6 +166,11 @@ export interface BoltOperator extends SyntaxBase {
text: string;
}
export interface BoltAssignment extends SyntaxBase {
kind: SyntaxKind.BoltAssignment;
operator: string | null;
}
export interface BoltEOS extends SyntaxBase {
kind: SyntaxKind.BoltEOS;
}
@ -708,6 +717,7 @@ export type BoltSyntax
| BoltIntegerLiteral
| BoltIdentifier
| BoltOperator
| BoltAssignment
| BoltEOS
| BoltComma
| BoltSemi
@ -799,6 +809,7 @@ export type Syntax
| BoltIntegerLiteral
| BoltIdentifier
| BoltOperator
| BoltAssignment
| BoltEOS
| BoltComma
| BoltSemi
@ -888,6 +899,7 @@ export function createBoltStringLiteral(value: string, span?: TextSpan | null):
export function createBoltIntegerLiteral(value: bigint, span?: TextSpan | null): BoltIntegerLiteral;
export function createBoltIdentifier(text: string, span?: TextSpan | null): BoltIdentifier;
export function createBoltOperator(text: string, span?: TextSpan | null): BoltOperator;
export function createBoltAssignment(operator: string | null, span?: TextSpan | null): BoltAssignment;
export function createBoltEOS(span?: TextSpan | null): BoltEOS;
export function createBoltComma(span?: TextSpan | null): BoltComma;
export function createBoltSemi(span?: TextSpan | null): BoltSemi;
@ -976,6 +988,7 @@ export function isBoltIntegerLiteral(value: any): value is BoltIntegerLiteral;
export function isBoltSymbol(value: any): value is BoltSymbol;
export function isBoltIdentifier(value: any): value is BoltIdentifier;
export function isBoltOperator(value: any): value is BoltOperator;
export function isBoltAssignment(value: any): value is BoltAssignment;
export function isBoltEOS(value: any): value is BoltEOS;
export function isBoltComma(value: any): value is BoltComma;
export function isBoltSemi(value: any): value is BoltSemi;

View file

@ -6,7 +6,7 @@ import * as fs from "fs"
import { parse, SyntaxError } from "../treegen/parser"
import { Declaration } from "../treegen/ast"
import { generateAST } from "../treegen/index"
import { getFileStem } from "../util"
import { getFileStem } from "../treegen/util"
import minimist from "minimist"
const PACKAGE_ROOT = path.join(__dirname, '..', '..');

View file

@ -3,9 +3,6 @@
import "reflect-metadata"
import "source-map-support/register"
import * as path from "path"
import * as fs from "fs-extra"
import yargs from "yargs"
import { Program } from "../program"
@ -66,12 +63,17 @@ yargs
yargs => yargs
.string('work-dir')
.describe('work-dir', 'The working directory where files will be resolved against.')
.default('work-dir', '.'),
.default('work-dir', '.')
.string('inspect-server'),
args => {
const files = toArray(args.files as string[] | string).map(filepath => new TextFile(filepath, args['work-dir']));
const program = new Program(files)
const files = toArray(args.files as string[] | string);
const opts = {};
if (args['inspect-server'] !== undefined) {
opts.inspector = connectToInspector(args['inspect-server'] as string);
}
const program = new Program(files, opts);
program.compile("JS");
})

View file

@ -129,6 +129,8 @@ export class TypeChecker {
protected createType(node: Syntax): Type {
console.error(`creating type for ${kindToString(node.kind)}`);
switch (node.kind) {
case SyntaxKind.BoltReferenceExpression:
@ -145,6 +147,10 @@ export class TypeChecker {
case SyntaxKind.BoltExpressionStatement:
return voidType;
case SyntaxKind.BoltCallExpression:
// TODO
return anyType;
case SyntaxKind.BoltFunctionDeclaration:
let returnType = anyType;
if (node.returnType !== null) {
@ -182,8 +188,8 @@ export class TypeChecker {
return typ;
case SyntaxKind.BoltParameter:
if (node.typeNode !== null) {
return this.getTypeOfNode(node.typeNode)
if (node.type !== null) {
return this.getTypeOfNode(node.type)
}
return anyType;
@ -218,12 +224,21 @@ export class TypeChecker {
case SyntaxKind.BoltSentence:
case SyntaxKind.BoltRecordDeclaration:
case SyntaxKind.BoltNewTypeDeclaration:
case SyntaxKind.BoltConstantExpression:
break;
case SyntaxKind.BoltExpressionStatement:
this.check(node.expression);
break;
case SyntaxKind.BoltCallExpression:
this.check(node.operator);
for (const operand of node.operands) {
this.check(operand);
}
// TODO check whether the overload matches the referenced operator
break;
case SyntaxKind.BoltFunctionDeclaration:
if (node.body !== null) {
if (Array.isArray(node.body)) {

View file

@ -30,6 +30,8 @@ import {
createJSBindPattern,
JSDeclarationModifiers,
JSParameter,
BoltSyntax,
BoltPattern,
} from "./ast"
import { getFullTextOfQualName, hasPublicModifier } from "./util"
@ -95,9 +97,11 @@ export class Compiler {
}
protected compileDecl(node: Syntax, preamble: Syntax[]) {
private compilePattern(node: BoltPattern) {
console.log(`compiling ${kindToString(node.kind)}`)
}
protected compileDecl(node: BoltSyntax, preamble: Syntax[]) {
//if (isBoltExpression(node)) {
// const compiled = this.compileExpr(node, preamble);
@ -121,6 +125,9 @@ export class Compiler {
// TODO
break;
case SyntaxKind.BoltNewTypeDeclaration:
break;
case SyntaxKind.BoltVariableDeclaration:
const compiledValue = node.value !== null ? this.compileExpr(node.value, preamble) : null;
preamble.push(
@ -132,8 +139,11 @@ export class Compiler {
);
break;
case SyntaxKind.BoltForeignFunctionDeclaration:
if (node.target === this.target && node.body !== null) {
case SyntaxKind.BoltFunctionDeclaration:
if (node.target === this.target) {
if (node.body === null) {
break;
}
const params: JSParameter[] = [];
let body: JSStatement[] = [];
for (const param of node.params) {
@ -150,6 +160,9 @@ export class Compiler {
result.modifiers |= JSDeclarationModifiers.IsExported;;
}
preamble.push(result)
} else {
// TODO
throw new Error(`Compiling native functions is not yet implemented.`);
}
break;

7
src/constants.ts Normal file
View file

@ -0,0 +1,7 @@
export const TYPES = {
Program: Symbol('a Bolt program'),
TypeChecker: Symbol('the Bolt type checking system'),
FileManager: Symbol('the file manager'),
}

View file

@ -5,25 +5,50 @@ export class Emitter {
emit(node: Syntax) {
debug(node);
let out = '';
switch (node.kind) {
case SyntaxKind.JSReferenceExpression:
return node.name;
out += node.name;
break;
case SyntaxKind.JSConstantExpression:
if (typeof node.value === 'string') {
out += '"' + node.value + '"';
} else if (typeof node.value === 'bigint') {
out += node.value.toString();
} else {
throw new Error(`Could not emit the value of a specific JSConstantExpression.`);
}
break;
case SyntaxKind.JSFunctionDeclaration:
out += 'function ' + node.name.text + '(';
//out += node.params.map(p => this.emit(p)).join(', ');
out += ') {\n';
out += '}\n\n'
break;
case SyntaxKind.JSCallExpression:
out += this.emit(node.operator) + '(';
out += node.operands.map(op => this.emit(op)).join(', ');
out += ')'
break;
case SyntaxKind.JSSourceFile:
let out = ''
for (const element of node.elements) {
out += this.emit(element);
}
return out;
break;
default:
throw new Error(`Could not emit source code for ${kindToString(node.kind)}`)
}
return out;
}
}

View file

@ -4,6 +4,7 @@
import {
SyntaxKind,
setParents,
kindToString,
BoltSyntax,
BoltSentence,
@ -35,20 +36,13 @@ import { TextSpan } from "./text"
import { TypeChecker } from "./checker"
import { Parser, ParseError } from "./parser"
import { Evaluator, TRUE, FALSE } from "./evaluator"
import { StreamWrapper, setOrigNodeRange, BoltTokenStream } from "./util"
import { StreamWrapper, setOrigNodeRange, BoltTokenStream, createTokenStream } from "./util"
interface Transformer {
pattern: BoltPattern;
transform: (node: BoltTokenStream) => BoltSyntax;
}
function createTokenStream(node: BoltSentence) {
return new StreamWrapper(
node.tokens,
() => createBoltEOS(new TextSpan(node.span!.file, node.span!.end.clone(), node.span!.end.clone()))
);
}
function createSimpleBoltReferenceTypeNode(text: string): BoltReferenceTypeNode {
const ids = text.split('.').map(name => createBoltIdentifier(name))
return createBoltReferenceTypeNode(createBoltQualName(ids.slice(0, -1), ids[ids.length-1]), [])
@ -145,6 +139,7 @@ export class Expander {
const newSourceFile = createBoltSourceFile(expanded);
setOrigNodeRange(newSourceFile, node, node);
setParents(newSourceFile);
return newSourceFile;
} else if (node.kind == SyntaxKind.BoltModule) {
@ -170,6 +165,7 @@ export class Expander {
const newModule = createBoltModule(0, node.name, expanded);
setOrigNodeRange(newModule, node, node);
setParents(newModule);
return newModule;
} else if (node.kind === SyntaxKind.BoltSentence) {

View file

@ -1,4 +1,6 @@
import * as acorn from "acorn"
import {
SyntaxKind,
kindToString,
@ -45,9 +47,12 @@ import {
BoltFunctionDeclaration,
createBoltFunctionDeclaration,
createBoltCallExpression,
BoltSymbol,
} from "./ast"
import { BoltTokenStream, setOrigNodeRange } from "./util"
import { Stream, setOrigNodeRange, createTokenStream, uniq } from "./util"
export type BoltTokenStream = Stream<BoltToken>;
function describeKind(kind: SyntaxKind): string {
switch (kind) {
@ -148,10 +153,10 @@ const KIND_EXPRESSION_T0 = [
SyntaxKind.BoltYieldKeyword,
]
const KIND_STATEMENT_T0 = [
const KIND_STATEMENT_T0 = uniq([
SyntaxKind.BoltReturnKeyword,
...KIND_EXPRESSION_T0,
]
])
const KIND_DECLARATION_KEYWORD = [
SyntaxKind.BoltFnKeyword,
@ -162,18 +167,18 @@ const KIND_DECLARATION_KEYWORD = [
SyntaxKind.BoltStructKeyword,
]
const KIND_DECLARATION_T0 = [
const KIND_DECLARATION_T0 = uniq([
SyntaxKind.BoltPubKeyword,
SyntaxKind.BoltForeignKeyword,
...KIND_DECLARATION_KEYWORD,
]
])
const KIND_SOURCEELEMENT_T0 = [
const KIND_SOURCEELEMENT_T0 = uniq([
SyntaxKind.BoltModKeyword,
...KIND_EXPRESSION_T0,
...KIND_STATEMENT_T0,
...KIND_DECLARATION_T0,
]
])
export class Parser {
@ -213,6 +218,10 @@ export class Parser {
}
}
public parse(kind: SyntaxKind, tokens: BoltTokenStream): BoltSyntax {
return (this as any)['parse' + kindToString(kind).substring('Bolt'.length)](tokens);
}
public parseQualName(tokens: BoltTokenStream): BoltQualName {
const path: BoltIdentifier[] = [];
@ -373,7 +382,7 @@ export class Parser {
//}
public parseExpression(tokens: BoltTokenStream): BoltExpression {
return this.parsePrimitiveExpression(tokens)
return this.parseCallOrPrimitiveExpression(tokens)
}
public parseParameter(tokens: BoltTokenStream): BoltParameter {
@ -627,7 +636,7 @@ export class Parser {
let lastToken: BoltSyntax;
const firstToken = k0;
if (k0.kind !== SyntaxKind.BoltPubKeyword) {
if (k0.kind === SyntaxKind.BoltPubKeyword) {
tokens.get();
modifiers |= BoltDeclarationModifiers.Public;
k0 = tokens.peek();
@ -650,9 +659,9 @@ export class Parser {
tokens.get();
let name: BoltQualName;
let name: BoltSymbol;
let returnType = null;
let body = null;
let body: any = null; // FIXME type-checking should not be disabled
let params: BoltParameter[] = [];
// Parse parameters
@ -687,16 +696,14 @@ export class Parser {
if (t0.kind === SyntaxKind.BoltOperator) {
name = createBoltQualName([], t0);
setOrigNodeRange(name, t0, t0);
name = t0;
tokens.get();
params.push(parseParamLike(tokens))
} else if (isParamLike(t0) && t1.kind == SyntaxKind.BoltOperator) {
params.push(parseParamLike(tokens));
name = createBoltQualName([], t1);
setOrigNodeRange(name, t1, t1);
name = t1;
while (true) {
const t2 = tokens.peek();
if (t2.kind !== SyntaxKind.BoltOperator) {
@ -711,7 +718,8 @@ export class Parser {
} else if (t0.kind === SyntaxKind.BoltIdentifier) {
name = this.parseQualName(tokens)
name = t0;
tokens.get();
const t2 = tokens.get();
if (t2.kind === SyntaxKind.BoltParenthesized) {
const innerTokens = createTokenStream(t2);
@ -762,8 +770,7 @@ export class Parser {
body = this.parseStatements(tokens);
break;
case "JS":
// TODO
//body = acorn.parse(t3.text).body;
body = acorn.parse(t3.text);
break;
default:
throw new Error(`Unrecognised language: ${target}`);
@ -787,7 +794,8 @@ export class Parser {
let t0 = tokens.peek(1);
let i = 1;
if (t0.kind === SyntaxKind.BoltPubKeyword) {
t0 = tokens.peek(i++);
i += 1;
t0 = tokens.peek(i);
if (t0.kind !== SyntaxKind.BoltForeignKeyword) {
if (KIND_DECLARATION_KEYWORD.indexOf(t0.kind) === -1) {
throw new ParseError(t0, KIND_DECLARATION_KEYWORD);
@ -823,17 +831,17 @@ export class Parser {
const t0 = tokens.peek();
try {
return this.parseDeclaration(tokens)
} catch (e) {
if (!(e instanceof ParseError)) {
throw e;
} catch (e1) {
if (!(e1 instanceof ParseError)) {
throw e1;
}
try {
return this.parseStatement(tokens);
} catch (e) {
if (!(e instanceof ParseError)) {
throw e;
} catch (e2) {
if (!(e2 instanceof ParseError)) {
throw e2;
}
throw new ParseError(t0, KIND_SOURCEELEMENT_T0)
throw e1;
}
}
}
@ -879,14 +887,17 @@ export class Parser {
// return lhs
//}
public parseCallExpression(tokens: BoltTokenStream): BoltCallExpression {
public parseCallOrPrimitiveExpression(tokens: BoltTokenStream): BoltExpression {
const operator = this.parsePrimitiveExpression(tokens)
const args: BoltExpression[] = []
const t2 = tokens.get();
if (t2.kind === SyntaxKind.BoltEOS) {
return operator;
}
assertToken(t2, SyntaxKind.BoltParenthesized);
const args: BoltExpression[] = []
const innerTokens = createTokenStream(t2);
while (true) {
@ -903,7 +914,9 @@ export class Parser {
}
}
return createBoltCallExpression(operator, args, null)
const node = createBoltCallExpression(operator, args, null)
setOrigNodeRange(node, operator, t2);
return node;
}

View file

@ -1,6 +1,8 @@
import * as path from "path"
import * as fs from "fs-extra"
import { now } from "microtime"
import { EventEmitter } from "events"
import { Parser } from "./parser"
import { TypeChecker } from "./checker"
@ -10,9 +12,10 @@ import { Scanner } from "./scanner"
import { Compiler } from "./compiler"
import { emit } from "./emitter"
import { TextFile } from "./text"
import { BoltSourceFile, Syntax } from "./ast"
import { BoltSourceFile, Syntax, JSSourceFile } from "./ast"
import { upsearchSync, FastStringMap, getFileStem, getLanguage } from "./util"
import { Package } from "./package"
import { verbose, memoize } from "./util"
const targetExtensions: FastStringMap<string> = {
'JS': '.mjs',
@ -20,55 +23,106 @@ const targetExtensions: FastStringMap<string> = {
'C': '.c',
};
export interface TransformationContext {
}
interface TimingInfo {
timestamp: number;
refCount: number;
}
class Timing extends EventEmitter {
private runningTasks: FastStringMap<TimingInfo> = Object.create(null);
public start(name: string) {
if (this.runningTasks[name] !== undefined) {
this.runningTasks[name].refCount++;
return;
}
this.runningTasks[name] = { timestamp: now(), refCount: 1 };
this.emit(`start ${name}`);
}
public end(name: string) {
if (this.runningTasks[name] === undefined) {
throw new Error(`Task '${name}' was never started.`);
}
const info = this.runningTasks[name];
info.refCount--;
if (info.refCount === 0) {
const usecs = now() - info.timestamp;
verbose(`Task '${name}' completed after ${usecs} microseconds.`);
this.emit(`end ${name}`);
}
}
}
export class Program {
public parser: Parser
public evaluator: Evaluator;
public checker: TypeChecker;
public expander: Expander;
public timing: Timing;
private sourceFiles = new Map<string, BoltSourceFile>();
private packages: FastStringMap<Package> = Object.create(null);
constructor(files: TextFile[]) {
constructor(public files: string[]) {
this.checker = new TypeChecker();
this.parser = new Parser();
this.evaluator = new Evaluator(this.checker);
this.expander = new Expander(this.parser, this.evaluator, this.checker);
for (const file of files) {
console.log(`Loading ${file.origPath} ...`);
const contents = fs.readFileSync(file.fullPath, 'utf8');
const scanner = new Scanner(file, contents)
this.sourceFiles.set(file.fullPath, scanner.scan());
}
this.timing = new Timing();
}
@memoize
public getTextFile(filename: string): TextFile {
return new TextFile(filename);
}
@memoize
public getSourceFile(file: TextFile): BoltSourceFile {
this.timing.start('read');
const contents = fs.readFileSync(file.origPath, 'utf8');
this.timing.end('read');
const scanner = new Scanner(file, contents)
this.timing.start('scan');
const sourceFile = scanner.scan();
this.timing.end('scan');
return sourceFile;
}
@memoize
public getFullyExpandedSourceFile(file: TextFile): BoltSourceFile {
const sourceFile = this.getSourceFile(file);
this.timing.start('expand');
const expanded = this.expander.getFullyExpanded(sourceFile) as BoltSourceFile;
this.timing.end('expand');
return expanded;
}
@memoize
public getPackage(filepath: string) {
filepath = path.resolve(filepath);
const projectFile = upsearchSync('Boltfile', path.dirname(filepath));
const file = this.getTextFile(filepath)
const projectFile = upsearchSync('Boltfile', path.dirname(file.fullPath));
if (projectFile === null) {
return null;
}
const projectDir = path.resolve(path.dirname(projectFile));
if (this.packages[projectDir] !== undefined) {
return this.packages[projectDir];
}
return this.packages[projectDir] = new Package(projectDir);
return new Package(projectDir);
}
public compile(target: string) {
const compiler = new Compiler(this, this.checker, { target })
const expanded: SourceFile[] = [];
for (const [filepath, sourceFile] of this.sourceFiles) {
expanded.push(this.expander.getFullyExpanded(sourceFile) as SourceFile);
}
const compiled = compiler.compile(expanded) as AnySourceFile[];
const expanded = this.files.map(filename => this.getFullyExpandedSourceFile(this.getTextFile(filename)));
const compiled = compiler.compile(expanded) as JSSourceFile[];
for (const rootNode of compiled) {
const filepath = rootNode.span!.file.fullPath;
const pkg = this.getPackage(filepath);
if (pkg !== null) {
}
//const filepath = rootNode.span!.file.fullPath;
//const pkg = this.getPackage(filepath);
//if (pkg !== null) {
//
//}
fs.mkdirp('.bolt-work');
fs.writeFileSync(this.mapToTargetFile(rootNode), emit(rootNode), 'utf8');
}
@ -78,13 +132,12 @@ export class Program {
return path.join('.bolt-work', getFileStem(node.span!.file.fullPath) + getDefaultExtension(getLanguage(node)));
}
eval(filename: string) {
const original = this.sourceFiles.get(filename);
if (original === undefined) {
throw new Error(`File ${filename} does not seem to be part of this Program.`)
public eval() {
for (const filename of this.files) {
const file = this.getTextFile(filename);
const expanded = this.getFullyExpandedSourceFile(file);
this.evaluator.eval(expanded)
}
const expanded = this.expander.getFullyExpanded(original) as BoltSourceFile;
return this.evaluator.eval(expanded)
}
}

3
src/renamer.ts Normal file
View file

@ -0,0 +1,3 @@

View file

@ -8,6 +8,7 @@ import {
} from "./text"
import {
setParents,
SyntaxKind,
BoltToken,
BoltSentence,
@ -27,6 +28,18 @@ import {
createBoltEOS,
createBoltDot,
createBoltEqSign,
createBoltPubKeyword,
createBoltMutKeyword,
createBoltStructKeyword,
createBoltEnumKeyword,
createBoltForeignKeyword,
createBoltAssignment,
createBoltYieldKeyword,
createBoltReturnKeyword,
createBoltFnKeyword,
createBoltLArrow,
createBoltDotDot,
createBoltNewTypeKeyword,
} from "./ast"
export enum PunctType {
@ -122,14 +135,13 @@ function isIdentPart(ch: string) {
return ch == '_' || XRegExp('\\p{L}').test(ch)
}
function isOperatorStart(ch: string) {
return /[+\-*\/%$!><]/.test(ch)
function isSymbol(ch: string) {
return /[=+\/-*%$!><&^|]/.test(ch)
}
function isOperatorPart(ch: string) {
return /[=+\-*\/%$!><]/.test(ch)
}
//function isOperatorPart(ch: string) {
//return /[=+\-*\/%$!><]/.test(ch)
//}
const EOF = ''
@ -210,12 +222,6 @@ export class Scanner {
}
switch (c0) {
case '.':
this.getChar();
return createBoltDot(new TextSpan(this.file, startPos, this.currPos.clone()));
case '=':
this.getChar();
return createBoltEqSign(new TextSpan(this.file, startPos, this.currPos.clone()));
case ';':
this.getChar();
return createBoltSemi(new TextSpan(this.file, startPos, this.currPos.clone()));
@ -304,19 +310,43 @@ export class Scanner {
const name = this.takeWhile(isIdentPart);
const endPos = this.currPos.clone();
return createBoltIdentifier(name, new TextSpan(this.file, startPos, endPos))
const span = new TextSpan(this.file, startPos, endPos);
switch (name) {
case 'pub': return createBoltPubKeyword(span);
case 'fn': return createBoltFnKeyword(span);
case 'return': return createBoltReturnKeyword(span);
case 'yield': return createBoltYieldKeyword(span);
case 'foreign': return createBoltForeignKeyword(span);
case 'let': return createBoltPubKeyword(span);
case 'mut': return createBoltMutKeyword(span);
case 'struct': return createBoltStructKeyword(span);
case 'enum': return createBoltEnumKeyword(span);
case 'newtype': return createBoltNewTypeKeyword(span);
default: return createBoltIdentifier(name, span);
}
} else if (isOperatorStart(c0)) {
} else if (isSymbol(c0)) {
const text = this.takeWhile(isOperatorPart)
const text = this.takeWhile(isSymbol)
const endPos = this.currPos.clone()
const span = new TextSpan(this.file, startPos, endPos);
if (text === '->') {
return createBoltRArrow(span);
} else {
if (text.endsWith('=')) {
const operator = text.substring(0, text.length-1);
if (text === '==') {
return createBoltOperator(text, span);
}
return createBoltAssignment(operator.length === 0 ? null : operator, span);
}
switch (text) {
case '->': return createBoltRArrow(span);
case '<-': return createBoltLArrow(span);
case '.': return createBoltDot(span);
case '..': return createBoltDotDot(span);
case '=': return createBoltEqSign(span);
default: return createBoltOperator(text, span);
}
} else {
@ -385,7 +415,9 @@ export class Scanner {
const startPos = this.currPos.clone();
const elements = this.scanTokens();
const endPos = this.currPos.clone();
return createBoltSourceFile(elements, new TextSpan(this.file, startPos, endPos));
const sourceFile = createBoltSourceFile(elements, new TextSpan(this.file, startPos, endPos));
setParents(sourceFile);
return sourceFile;
}
}

View file

@ -13,6 +13,26 @@ function isSyntax(value) {
&& value.__NODE_TYPE !== undefined;
}
function* getChildNodes(node) {
for (const key of Object.keys(node)) {
if (key === 'span' || key === 'parentNode') {
continue
}
const value = node[key];
if (Array.isArray(value)) {
for (const element of value) {
if (isSyntax(element)) {
yield element;
}
}
} else if (isSyntax(value)) {
if (isSyntax(value)) {
yield value;
}
}
}
}
function createNode(nodeType) {
const obj = Object.create(nodeProto);
Object.defineProperty(obj, '__NODE_TYPE', {
@ -66,6 +86,13 @@ for (const nodeName of Object.keys(NODE_TYPES)) {
}
exported.setParents = function setParents(node, parentNode = null) {
node.parentNode = parentNode;
for (const child of getChildNodes(node)) {
setParents(child, node)
}
}
if (typeof module !== 'undefined') {
module.exports = exports;
}

View file

@ -106,6 +106,8 @@ export function generateAST(decls: Declaration[]) {
import { TextSpan } from "./text"
export function setParents(node: Syntax): void;
export type SyntaxRange = [Syntax, Syntax];
interface SyntaxBase {

View file

@ -1,4 +1,10 @@
import * as path from "path"
export function getFileStem(filepath: string): string {
return path.basename(filepath).split('.')[0];
}
function isWhiteSpace(ch: string) {
return /[\r\t ]/.test(ch);
}

View file

@ -1,20 +1,107 @@
import * as path from "path"
import * as fs from "fs"
import moment from "moment"
import chalk from "chalk"
import { TextSpan } from "./text"
import { kindToString, Syntax, BoltToken, BoltQualName, BoltDeclaration, BoltDeclarationModifiers } from "./ast"
import { TextSpan, TextPos } from "./text"
import { Scanner } from "./scanner"
import { kindToString, Syntax, BoltQualName, BoltDeclaration, BoltDeclarationModifiers, createBoltEOS, SyntaxKind, isBoltPunctuated } from "./ast"
export type BoltTokenStream = Stream<BoltToken>;
export function createTokenStream(node: Syntax) {
if (isBoltPunctuated(node)) {
const origPos = node.span!.start;
const startPos = new TextPos(origPos.offset+1, origPos.line, origPos.column+1);
return new Scanner(node.span!.file, node.text, startPos);
} else if (node.kind === SyntaxKind.BoltSentence) {
return new StreamWrapper(
node.tokens,
() => createBoltEOS(new TextSpan(node.span!.file, node.span!.end.clone(), node.span!.end.clone()))
);
} else {
throw new Error(`Could not convert ${kindToString(node.kind)} to a token stream.`);
}
}
export interface JsonArray extends Array<Json> { };
export interface JsonObject { [key: string]: Json }
export type Json = null | string | boolean | number | JsonArray | JsonObject;
export function uniq<T>(elements: T[]): T[] {
const out: T[] = [];
const visited = new Set<T>();
for (const element of elements) {
if (visited.has(element)) {
continue;
}
visited.add(element);
out.push(element);
}
return out;
}
export interface FastStringMap<T> {
[key: string]: T
}
class DeepMap {
private rootMap = new Map<any, any>();
public has(key: any[]) {
let curr = this.rootMap;
for (const element of key) {
if (!curr.has(element)) {
return false;
}
curr = curr.get(element)!;
}
return true;
}
public get(key: any[]) {
let curr = this.rootMap;
for (const element of key) {
if (!curr.has(element)) {
return;
}
curr = curr.get(element)!;
}
return curr;
}
public set(key: any[], value: any) {
let curr = this.rootMap;
for (const element of key.slice(0, -1)) {
if (!curr.has(element)) {
curr.set(element, new Map());
}
curr = curr.get(element)!;
}
curr.set(key[key.length-1], value);
}
}
export function memoize(target: any, key: PropertyKey) {
const origMethod = target[key];
target[key] = function wrapper(...args: any[]) {
if (this.__MEMOIZE_CACHE === undefined) {
this.__MEMOIZE_CACHE = Object.create(null);
}
if (this.__MEMOIZE_CACHE[key] === undefined) {
this.__MEMOIZE_CACHE[key] = new DeepMap();
}
const cache = this.__MEMOIZE_CACHE[key];
if (cache.has(args)) {
return cache.get(args);
}
const result = origMethod.apply(this, args);
cache.set(args, result);
return result;
}
}
const supportedLanguages = ['Bolt', 'JS'];
export function getLanguage(node: Syntax): string {
@ -39,7 +126,7 @@ export function setOrigNodeRange(node: Syntax, startNode: Syntax, endNode: Synta
}
export function hasPublicModifier(node: BoltDeclaration) {
return (node.modifiers & BoltDeclarationModifiers.IsPublic) > 0;
return (node.modifiers & BoltDeclarationModifiers.Public) > 0;
}
export function getFullTextOfQualName(node: BoltQualName) {
@ -80,6 +167,12 @@ export class StreamWrapper<T> {
}
const DATETIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'
export function verbose(message: string) {
console.error(chalk.gray('[') + chalk.magenta('verb') + ' ' + chalk.gray(moment().format(DATETIME_FORMAT) + ']') + ' ' + message);
}
export function upsearchSync(filename: string, startDir = '.') {
let currDir = startDir;
while (true) {