2020-02-25 12:26:21 +01:00
|
|
|
|
2020-02-25 17:55:17 +01:00
|
|
|
import * as acorn from "acorn"
|
|
|
|
|
2020-02-25 12:26:21 +01:00
|
|
|
import {
|
2020-05-10 15:56:34 +02:00
|
|
|
SyntaxKind,
|
|
|
|
BoltToken,
|
|
|
|
BoltIdentifier,
|
|
|
|
createBoltFuncDecl,
|
|
|
|
createBoltIdentifier,
|
|
|
|
createBoltSyntaxKind,
|
|
|
|
createBoltTokenStream,
|
|
|
|
createBoltRetStmt,
|
|
|
|
createBoltVarDecl,
|
|
|
|
createBoltStmt,
|
|
|
|
createBoltPatt,
|
|
|
|
createBoltExpr,
|
|
|
|
createBoltBindPatt,
|
|
|
|
createBoltParam,
|
|
|
|
createBoltRefExpr,
|
|
|
|
createBoltTypeRef,
|
|
|
|
createBoltTypeDecl,
|
|
|
|
createBoltConstExpr,
|
|
|
|
createBoltQualName,
|
|
|
|
createBoltCallExpr,
|
|
|
|
createBoltImportDecl,
|
|
|
|
createBoltSourceElement,
|
|
|
|
createBoltModule,
|
|
|
|
createBoltRecordDecl,
|
|
|
|
createBoltNewTypeDecl,
|
|
|
|
BoltQualName,
|
|
|
|
BoltPattern,
|
|
|
|
createBoltBindPattern,
|
|
|
|
BoltImportDeclaration,
|
|
|
|
BoltTypeNode,
|
|
|
|
createBoltReferenceTypeNode,
|
|
|
|
createJSReferenceExpression,
|
|
|
|
createBoltReferenceExpression,
|
2020-02-25 12:26:21 +01:00
|
|
|
} from "./ast"
|
|
|
|
|
2020-02-26 18:53:28 +01:00
|
|
|
import { stringType, intType } from "./checker"
|
|
|
|
|
|
|
|
import { PrimValue } from "./evaluator"
|
2020-05-10 15:56:34 +02:00
|
|
|
import {BoltTokenStream} from "./util"
|
2020-02-26 18:53:28 +01:00
|
|
|
|
2020-02-25 12:26:21 +01:00
|
|
|
function describeKind(kind: SyntaxKind): string {
|
|
|
|
switch (kind) {
|
2020-05-10 15:56:34 +02:00
|
|
|
case SyntaxKind.BoltIdentifier:
|
2020-02-25 12:26:21 +01:00
|
|
|
return "an identifier"
|
2020-05-10 15:56:34 +02:00
|
|
|
case SyntaxKind.BoltOperator:
|
2020-02-25 12:26:21 +01:00
|
|
|
return "an operator"
|
2020-05-10 15:56:34 +02:00
|
|
|
case SyntaxKind.BoltStringLiteral:
|
2020-02-25 17:55:17 +01:00
|
|
|
return "a string"
|
2020-05-10 15:56:34 +02:00
|
|
|
case SyntaxKind.BoltIntegerLiteral:
|
2020-02-25 17:55:17 +01:00
|
|
|
return "an integer"
|
2020-05-10 15:56:34 +02:00
|
|
|
case SyntaxKind.BoltFnKeyword:
|
2020-02-25 17:55:17 +01:00
|
|
|
return "'fn'"
|
2020-05-10 15:56:34 +02:00
|
|
|
case SyntaxKind.BoltForeignKeyword:
|
2020-02-25 17:55:17 +01:00
|
|
|
return "'foreign'"
|
2020-05-10 15:56:34 +02:00
|
|
|
case SyntaxKind.BoltPubKeyword:
|
2020-02-26 18:53:28 +01:00
|
|
|
return "'pub'"
|
2020-05-10 15:56:34 +02:00
|
|
|
case SyntaxKind.BoltLetKeyword:
|
2020-02-25 17:55:17 +01:00
|
|
|
return "'let'"
|
2020-05-10 15:56:34 +02:00
|
|
|
case SyntaxKind.BoltSemi:
|
2020-02-25 12:26:21 +01:00
|
|
|
return "';'"
|
2020-05-10 15:56:34 +02:00
|
|
|
case SyntaxKind.BoltColon:
|
2020-02-25 12:26:21 +01:00
|
|
|
return "':'"
|
2020-05-10 15:56:34 +02:00
|
|
|
case SyntaxKind.BoltDot:
|
2020-02-25 12:26:21 +01:00
|
|
|
return "'.'"
|
2020-05-10 15:56:34 +02:00
|
|
|
case SyntaxKind.BoltRArrow:
|
2020-03-03 14:53:54 +01:00
|
|
|
return "'->'"
|
2020-05-10 15:56:34 +02:00
|
|
|
case SyntaxKind.BoltComma:
|
2020-02-25 12:26:21 +01:00
|
|
|
return "','"
|
2020-05-10 15:56:34 +02:00
|
|
|
case SyntaxKind.BoltModKeyword:
|
2020-02-26 18:53:28 +01:00
|
|
|
return "'mod'"
|
2020-05-10 15:56:34 +02:00
|
|
|
case SyntaxKind.BoltStructKeyword:
|
2020-02-26 18:53:28 +01:00
|
|
|
return "'struct'"
|
2020-05-10 15:56:34 +02:00
|
|
|
case SyntaxKind.BoltEnumKeyword:
|
2020-02-26 18:53:28 +01:00
|
|
|
return "'enum'"
|
2020-05-10 15:56:34 +02:00
|
|
|
case SyntaxKind.BoltBraced:
|
2020-02-25 12:26:21 +01:00
|
|
|
return "'{' .. '}'"
|
2020-05-10 15:56:34 +02:00
|
|
|
case SyntaxKind.BoltBracketed:
|
2020-02-25 12:26:21 +01:00
|
|
|
return "'[' .. ']'"
|
2020-05-10 15:56:34 +02:00
|
|
|
case SyntaxKind.BoltParenthesized:
|
2020-02-25 12:26:21 +01:00
|
|
|
return "'(' .. ')'"
|
2020-05-10 15:56:34 +02:00
|
|
|
case SyntaxKind.BoltEOS:
|
2020-02-25 17:55:17 +01:00
|
|
|
return "'}', ')', ']' or end-of-file"
|
2020-02-25 12:26:21 +01:00
|
|
|
default:
|
2020-05-10 15:56:34 +02:00
|
|
|
throw new Error(`failed to describe ${kindToString(kind)}`)
|
2020-02-25 12:26:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function enumerate(elements: string[]) {
|
|
|
|
if (elements.length === 1) {
|
|
|
|
return elements[0]
|
|
|
|
} else {
|
2020-02-25 17:55:17 +01:00
|
|
|
return elements.slice(0, elements.length-1).join(',') + ' or ' + elements[elements.length-1]
|
2020-02-25 12:26:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export class ParseError extends Error {
|
|
|
|
constructor(public actual: Token, public expected: SyntaxKind[]) {
|
2020-02-25 17:59:36 +01:00
|
|
|
super(`${actual.span.file.path}:${actual.span.start.line}:${actual.span.start.column}: expected ${enumerate(expected.map(e => describeKind(e)))} but got ${describeKind(actual.kind)}`)
|
2020-02-25 12:26:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-03 14:53:54 +01:00
|
|
|
enum OperatorKind {
|
|
|
|
Prefix,
|
|
|
|
InfixL,
|
|
|
|
InfixR,
|
|
|
|
Suffix,
|
|
|
|
}
|
|
|
|
|
|
|
|
interface OperatorInfo {
|
|
|
|
kind: OperatorKind;
|
|
|
|
arity: number;
|
|
|
|
name: string;
|
|
|
|
precedence: number;
|
|
|
|
}
|
|
|
|
|
2020-02-25 12:26:21 +01:00
|
|
|
export class Parser {
|
|
|
|
|
2020-03-03 14:53:54 +01:00
|
|
|
operatorTable = [
|
|
|
|
[
|
|
|
|
[OperatorKind.InfixL, 2, '&&'],
|
|
|
|
[OperatorKind.InfixL, 2, '||']
|
|
|
|
],
|
|
|
|
[
|
|
|
|
[OperatorKind.InfixL, 2, '<'],
|
|
|
|
[OperatorKind.InfixL, 2, '>'],
|
|
|
|
[OperatorKind.InfixL, 2, '<='],
|
|
|
|
[OperatorKind.InfixL, 2, '>=']
|
|
|
|
],
|
|
|
|
[
|
|
|
|
[OperatorKind.InfixL, 2, '>>'],
|
|
|
|
[OperatorKind.InfixL, 2, '<<']
|
|
|
|
],
|
|
|
|
[
|
|
|
|
[OperatorKind.InfixL, 2, '+'],
|
|
|
|
[OperatorKind.InfixL, 2, '-'],
|
|
|
|
],
|
|
|
|
[
|
|
|
|
[OperatorKind.InfixL, 2, '/'],
|
|
|
|
[OperatorKind.InfixL, 2, '*'],
|
|
|
|
[OperatorKind.InfixL, 2, '%'],
|
|
|
|
],
|
|
|
|
[
|
|
|
|
[OperatorKind.Prefix, '!']
|
|
|
|
],
|
|
|
|
];
|
|
|
|
|
|
|
|
|
2020-05-10 15:56:34 +02:00
|
|
|
parseQualName(tokens: BoltTokenStream): BoltQualName {
|
2020-02-25 12:26:21 +01:00
|
|
|
|
2020-05-10 15:56:34 +02:00
|
|
|
const path: BoltIdentifier[] = [];
|
2020-02-25 12:26:21 +01:00
|
|
|
|
|
|
|
while (true) {
|
|
|
|
const t0 = tokens.peek(2);
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t0.kind !== SyntaxKind.BoltDot) {
|
2020-02-25 12:26:21 +01:00
|
|
|
break;
|
|
|
|
}
|
2020-05-10 15:56:34 +02:00
|
|
|
path.push(tokens.get() as BoltIdentifier)
|
2020-02-25 12:26:21 +01:00
|
|
|
tokens.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
const name = tokens.get();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (name.kind !== SyntaxKind.BoltIdentifier) {
|
|
|
|
throw new ParseError(name, [SyntaxKind.BoltIdentifier]);
|
2020-02-25 12:26:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const startNode = path.length > 0 ? path[0] : name;
|
|
|
|
const endNode = name;
|
2020-05-10 15:56:34 +02:00
|
|
|
return createBoltQualName(path, name, null, [startNode, endNode]);
|
2020-02-25 12:26:21 +01:00
|
|
|
}
|
|
|
|
|
2020-05-10 15:56:34 +02:00
|
|
|
parsePattern(tokens: BoltTokenStream): BoltPattern {
|
2020-02-25 12:26:21 +01:00
|
|
|
const t0 = tokens.peek(1);
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t0.kind === SyntaxKind.BoltIdentifier) {
|
2020-02-25 12:26:21 +01:00
|
|
|
tokens.get();
|
2020-05-10 15:56:34 +02:00
|
|
|
return createBoltBindPattern(t0.text, null, [t0, t0])
|
2020-02-25 12:26:21 +01:00
|
|
|
} else {
|
2020-05-10 15:56:34 +02:00
|
|
|
throw new ParseError(t0, [SyntaxKind.BoltIdentifier])
|
2020-02-25 12:26:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-10 15:56:34 +02:00
|
|
|
parseImportDecl(tokens: BoltTokenStream): BoltImportDeclaration {
|
2020-02-25 18:34:17 +01:00
|
|
|
|
|
|
|
// Assuming first keyword is 'import'
|
|
|
|
tokens.get();
|
|
|
|
|
|
|
|
const t0 = tokens.get();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t0.kind !== SyntaxKind.BoltStringLiteral) {
|
|
|
|
throw new ParseError(t0, [SyntaxKind.BoltStringLiteral])
|
2020-02-25 18:34:17 +01:00
|
|
|
}
|
|
|
|
|
2020-05-10 15:56:34 +02:00
|
|
|
return createBoltImportDecl(t0.value, null, t0);
|
2020-02-25 18:34:17 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-05-10 15:56:34 +02:00
|
|
|
parseTypeDecl(tokens: BoltTokenStream): BoltTypeNode {
|
2020-02-25 12:26:21 +01:00
|
|
|
const t0 = tokens.peek();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t0.kind === SyntaxKind.BoltIdentifier) {
|
2020-02-25 12:26:21 +01:00
|
|
|
const name = this.parseQualName(tokens)
|
2020-05-10 15:56:34 +02:00
|
|
|
return createBoltReferenceTypeNode(name, [], null, name.origNodes)
|
2020-02-25 12:26:21 +01:00
|
|
|
} else {
|
2020-05-10 15:56:34 +02:00
|
|
|
throw new ParseError(t0, [SyntaxKind.BoltIdentifier]);
|
2020-02-25 12:26:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-25 17:55:17 +01:00
|
|
|
parsePrimExpr(tokens: TokenStream): Expr {
|
2020-02-25 12:26:21 +01:00
|
|
|
const t0 = tokens.peek();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t0.kind === SyntaxKind.BoltStringLiteral) {
|
2020-02-25 17:55:17 +01:00
|
|
|
tokens.get();
|
2020-02-26 18:53:28 +01:00
|
|
|
return new ConstExpr(new PrimValue(stringType, t0.value), null, t0);
|
2020-05-10 15:56:34 +02:00
|
|
|
} else if (t0.kind === SyntaxKind.BoltIntegerLiteral) {
|
2020-02-26 18:53:28 +01:00
|
|
|
tokens.get();
|
|
|
|
return new ConstExpr(new PrimValue(intType, t0.value), null, t0);
|
2020-05-10 15:56:34 +02:00
|
|
|
} else if (t0.kind === SyntaxKind.BoltIdentifier) {
|
2020-02-25 12:26:21 +01:00
|
|
|
const name = this.parseQualName(tokens);
|
2020-05-10 15:56:34 +02:00
|
|
|
return createBoltReferenceExpression(name, null, name.origNode);
|
2020-02-25 12:26:21 +01:00
|
|
|
} else {
|
2020-05-10 15:56:34 +02:00
|
|
|
throw new ParseError(t0, [SyntaxKind.BoltStringLiteral, SyntaxKind.BoltIdentifier]);
|
2020-02-25 12:26:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-26 18:53:28 +01:00
|
|
|
parseSyntax(tokens: TokenStream): Syntax {
|
|
|
|
|
|
|
|
// Assuming first token is 'syntax'
|
|
|
|
tokens.get();
|
|
|
|
|
2020-03-03 14:53:54 +01:00
|
|
|
const t1 = tokens.get();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t1.kind !== SyntaxKind.BoltBraced) {
|
|
|
|
throw new ParseError(t1, [SyntaxKind.BoltBraced])
|
2020-03-03 14:53:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const innerTokens = t1.toTokenStream();
|
|
|
|
|
|
|
|
const pattern = this.parsePattern(innerTokens)
|
|
|
|
|
|
|
|
const t2 = innerTokens.get();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t2.kind !== SyntaxKind.BoltRArrow) {
|
|
|
|
throw new ParseError(t2, [SyntaxKind.BoltRArrow]);
|
2020-03-03 14:53:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const body = this.parseBody(innerTokens);
|
|
|
|
|
|
|
|
return new Macro(pattern, body)
|
2020-02-26 18:53:28 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
parseExpr(tokens: TokenStream): Expr {
|
2020-02-25 17:55:17 +01:00
|
|
|
return this.parsePrimExpr(tokens)
|
|
|
|
}
|
|
|
|
|
2020-02-26 18:53:28 +01:00
|
|
|
parseParam(tokens: TokenStream): Param {
|
2020-02-25 12:26:21 +01:00
|
|
|
|
|
|
|
let defaultValue = null;
|
|
|
|
let typeDecl = null;
|
|
|
|
|
|
|
|
const pattern = this.parsePattern(tokens)
|
|
|
|
|
|
|
|
const t0 = tokens.peek(1);
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t0.kind === SyntaxKind.BoltColon) {
|
2020-02-25 12:26:21 +01:00
|
|
|
tokens.get();
|
|
|
|
typeDecl = this.parseTypeDecl(tokens);
|
|
|
|
const t1 = tokens.peek(1);
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t1.kind === SyntaxKind.BoltEqSign) {
|
2020-02-25 12:26:21 +01:00
|
|
|
tokens.get();
|
|
|
|
defaultValue = this.parseExpr(tokens);
|
|
|
|
}
|
2020-02-26 18:53:28 +01:00
|
|
|
}
|
|
|
|
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t0.kind === SyntaxKind.BoltEqSign) {
|
2020-02-25 12:26:21 +01:00
|
|
|
tokens.get();
|
|
|
|
defaultValue = this.parseExpr(tokens);
|
|
|
|
}
|
|
|
|
|
|
|
|
return new Param(pattern, typeDecl, defaultValue)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
parseVarDecl(tokens: TokenStream): VarDecl {
|
|
|
|
|
2020-02-25 18:34:17 +01:00
|
|
|
let isMutable = false;
|
|
|
|
let typeDecl = null;
|
|
|
|
let value = null;
|
|
|
|
|
2020-02-25 17:55:17 +01:00
|
|
|
// Assuming first token is 'let'
|
|
|
|
tokens.get();
|
|
|
|
|
2020-02-25 18:34:17 +01:00
|
|
|
const t0 = tokens.peek();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t0.kind === SyntaxKind.BoltIdentifier && t0.text === 'mut') {
|
2020-02-25 18:34:17 +01:00
|
|
|
tokens.get();
|
|
|
|
isMutable = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const bindings = this.parsePattern(tokens)
|
|
|
|
|
|
|
|
const t1 = tokens.peek();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t1.kind === SyntaxKind.BoltColon) {
|
2020-02-25 18:34:17 +01:00
|
|
|
tokens.get();
|
|
|
|
typeDecl = this.parseTypeDecl(tokens);
|
|
|
|
}
|
|
|
|
|
|
|
|
const t2 = tokens.peek();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t2.kind === SyntaxKind.BoltEqSign) {
|
2020-02-25 18:34:17 +01:00
|
|
|
tokens.get();
|
|
|
|
value = this.parseExpr(tokens);
|
|
|
|
}
|
|
|
|
|
|
|
|
return new VarDecl(isMutable, bindings, typeDecl, value, null)
|
|
|
|
|
2020-02-25 12:26:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
parseRetStmt(tokens: TokenStream): RetStmt {
|
|
|
|
|
|
|
|
// Assuming first token is 'return'
|
|
|
|
const t0 = tokens.get();
|
|
|
|
|
|
|
|
let expr = null;
|
|
|
|
|
|
|
|
const t1 = tokens.peek();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t1.kind !== SyntaxKind.BoltEOS) {
|
2020-02-25 12:26:21 +01:00
|
|
|
expr = this.parseExpr(tokens)
|
|
|
|
}
|
|
|
|
|
|
|
|
return new RetStmt(expr, null, [t0, expr.getEndNode()]);
|
|
|
|
}
|
|
|
|
|
2020-02-26 18:53:28 +01:00
|
|
|
parseStmt(tokens: TokenStream): Stmt {
|
2020-03-03 14:53:54 +01:00
|
|
|
this.parseCallExpr(tokens)
|
2020-02-26 18:53:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
parseRecordDecl(tokens: TokenStream): RecordDecl {
|
|
|
|
|
|
|
|
let isPublic = false;
|
|
|
|
|
|
|
|
let kw = tokens.get();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (kw.kind !== SyntaxKind.BoltIdentifier) {
|
|
|
|
throw new ParseError(kw, [SyntaxKind.BoltPubKeyword, SyntaxKind.BoltStructKeyword]);
|
2020-02-26 18:53:28 +01:00
|
|
|
}
|
|
|
|
if (kw.text === 'pub') {
|
|
|
|
isPublic = true;
|
|
|
|
kw = tokens.get();
|
|
|
|
}
|
|
|
|
|
2020-05-10 15:56:34 +02:00
|
|
|
if (kw.kind !== SyntaxKind.BoltIdentifier || kw.text !== 'struct') {
|
|
|
|
throw new ParseError(kw, [SyntaxKind.BoltStructKeyword])
|
2020-02-26 18:53:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const name = this.parseQualName(tokens);
|
|
|
|
|
|
|
|
const t2 = tokens.get();
|
|
|
|
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t2.kind !== SyntaxKind.BoltBraced) {
|
|
|
|
throw new ParseError(kw, [SyntaxKind.BoltBraced])
|
2020-02-26 18:53:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
let fields = [];
|
|
|
|
|
|
|
|
return new RecordDecl(isPublic, name, fields);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-02-25 12:26:21 +01:00
|
|
|
parseStmts(tokens: TokenStream, origNode: Syntax | null): Stmt[] {
|
|
|
|
// TODO
|
|
|
|
return []
|
|
|
|
}
|
|
|
|
|
2020-02-26 18:53:28 +01:00
|
|
|
parseModDecl(tokens: TokenStream): Module {
|
|
|
|
|
|
|
|
let isPublic = false;
|
|
|
|
|
|
|
|
let kw = tokens.get();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (kw.kind !== SyntaxKind.BoltIdentifier) {
|
|
|
|
throw new ParseError(kw, [SyntaxKind.BoltPubKeyword, SyntaxKind.BoltModKeyword]);
|
2020-02-26 18:53:28 +01:00
|
|
|
}
|
|
|
|
if (kw.text === 'pub') {
|
|
|
|
isPublic = true;
|
|
|
|
kw = tokens.get();
|
|
|
|
}
|
|
|
|
|
2020-05-10 15:56:34 +02:00
|
|
|
if (kw.kind !== SyntaxKind.BoltIdentifier || kw.text !== 'mod') {
|
|
|
|
throw new ParseError(kw, [SyntaxKind.BoltModKeyword])
|
2020-02-26 18:53:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const name = this.parseQualName(tokens);
|
|
|
|
|
|
|
|
const t1 = tokens.get();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t1.kind !== SyntaxKind.BoltBraced) {
|
|
|
|
throw new ParseError(t1, [SyntaxKind.BoltBraced])
|
2020-02-26 18:53:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return new Module(isPublic, name, t1.toSentences());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-02-25 12:26:21 +01:00
|
|
|
protected assertEmpty(tokens: TokenStream) {
|
|
|
|
const t0 = tokens.peek(1);
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t0.kind !== SyntaxKind.BoltEOS) {
|
|
|
|
throw new ParseError(t0, [SyntaxKind.BoltEOS]);
|
2020-02-25 12:26:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-03 14:53:54 +01:00
|
|
|
parseNewType(tokens: TokenSteam): NewTypeDecl {
|
|
|
|
|
|
|
|
let isPublic = false;
|
|
|
|
let t0 = tokens.get();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t0.kind !== SyntaxKind.BoltIdentifier) {
|
|
|
|
throw new ParseError(t0, [SyntaxKind.BoltPubKeyword, SyntaxKind.BoltNewTypeKeyword])
|
2020-03-03 14:53:54 +01:00
|
|
|
}
|
|
|
|
if (t0.text === 'pub') {
|
|
|
|
isPublic = true;
|
|
|
|
t0 = tokens.get();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t0.kind !== SyntaxKind.BoltIdentifier) {
|
|
|
|
throw new ParseError(t0, [SyntaxKind.BoltNewTypeKeyword])
|
2020-03-03 14:53:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t0.text !== 'newtype') {
|
2020-05-10 15:56:34 +02:00
|
|
|
throw new ParseError(t0, [SyntaxKind.BoltNewTypeKeyword])
|
2020-03-03 14:53:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const name = tokens.get();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (name.kind !== SyntaxKind.BoltIdentifier) {
|
|
|
|
throw new ParseError(name, [SyntaxKind.BoltIdentifier])
|
2020-03-03 14:53:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return new NewTypeDecl(isPublic, name)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-02-26 18:53:28 +01:00
|
|
|
parseFuncDecl(tokens: TokenStream, origNode: Syntax | null): FuncDecl {
|
2020-02-25 12:26:21 +01:00
|
|
|
|
2020-02-25 17:55:17 +01:00
|
|
|
let target = "Bolt";
|
2020-02-26 18:53:28 +01:00
|
|
|
let isPublic = false;
|
2020-02-25 17:55:17 +01:00
|
|
|
|
2020-02-25 18:34:17 +01:00
|
|
|
const k0 = tokens.peek();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (k0.kind !== SyntaxKind.BoltIdentifier) {
|
|
|
|
throw new ParseError(k0, [SyntaxKind.BoltPubKeyword, SyntaxKind.BoltForeignKeyword, SyntaxKind.BoltFnKeyword])
|
2020-02-26 18:53:28 +01:00
|
|
|
}
|
|
|
|
if (k0.text === 'pub') {
|
|
|
|
tokens.get();
|
|
|
|
isPublic = true;
|
2020-02-25 17:55:17 +01:00
|
|
|
}
|
2020-02-26 18:53:28 +01:00
|
|
|
|
|
|
|
const k1 = tokens.peek();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (k1.kind !== SyntaxKind.BoltIdentifier) {
|
|
|
|
throw new ParseError(k1, [SyntaxKind.BoltForeignKeyword, SyntaxKind.BoltFnKeyword])
|
2020-02-26 18:53:28 +01:00
|
|
|
}
|
|
|
|
if (k1.text === 'foreign') {
|
|
|
|
tokens.get();
|
2020-02-25 17:55:17 +01:00
|
|
|
const l1 = tokens.get();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (l1.kind !== SyntaxKind.BoltStringLiteral) {
|
|
|
|
throw new ParseError(l1, [SyntaxKind.BoltStringLiteral])
|
2020-02-25 17:55:17 +01:00
|
|
|
}
|
|
|
|
target = l1.value;
|
|
|
|
}
|
2020-02-26 18:53:28 +01:00
|
|
|
const k2 = tokens.get();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (k2.kind !== SyntaxKind.BoltIdentifier || k2.text !== 'fn') {
|
|
|
|
throw new ParseError(k2, [SyntaxKind.BoltFnKeyword])
|
2020-02-25 17:55:17 +01:00
|
|
|
}
|
2020-02-25 12:26:21 +01:00
|
|
|
|
|
|
|
let name: QualName;
|
|
|
|
let returnType = null;
|
|
|
|
let body = null;
|
|
|
|
let params: Param[] = [];
|
|
|
|
|
2020-02-25 17:55:17 +01:00
|
|
|
// Parse parameters
|
|
|
|
|
2020-02-25 12:26:21 +01:00
|
|
|
const t0 = tokens.peek(1);
|
|
|
|
const t1 = tokens.peek(2);
|
|
|
|
|
|
|
|
const isParamLike = (token: Token) =>
|
2020-05-10 15:56:34 +02:00
|
|
|
token.kind === SyntaxKind.BoltIdentifier || token.kind === SyntaxKind.BoltParenthesized;
|
2020-02-25 12:26:21 +01:00
|
|
|
|
|
|
|
const parseParamLike = (tokens: TokenStream) => {
|
|
|
|
const t0 = tokens.peek(1);
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t0.kind === SyntaxKind.BoltIdentifier) {
|
2020-02-25 12:26:21 +01:00
|
|
|
tokens.get();
|
|
|
|
return new Param(new BindPatt(t0, null, t0), null, null, null, t0)
|
2020-05-10 15:56:34 +02:00
|
|
|
} else if (t0.kind === SyntaxKind.BoltParenthesized) {
|
2020-02-25 12:26:21 +01:00
|
|
|
tokens.get();
|
2020-02-25 17:55:17 +01:00
|
|
|
const innerTokens = t0.toTokenStream();
|
2020-02-25 12:26:21 +01:00
|
|
|
const param = this.parseParam(innerTokens)
|
|
|
|
this.assertEmpty(innerTokens);
|
|
|
|
return param
|
|
|
|
} else {
|
2020-05-10 15:56:34 +02:00
|
|
|
throw new ParseError(t0, [SyntaxKind.BoltIdentifier, SyntaxKind.BoltParenthesized])
|
2020-02-25 12:26:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t0.kind === SyntaxKind.BoltOperator) {
|
2020-02-25 12:26:21 +01:00
|
|
|
|
|
|
|
name = new QualName(t0, [], null, t0);
|
|
|
|
tokens.get();
|
|
|
|
params.push(parseParamLike(tokens))
|
|
|
|
|
2020-05-10 15:56:34 +02:00
|
|
|
} else if (isParamLike(t0) && t1.kind == SyntaxKind.BoltOperator) {
|
2020-02-25 12:26:21 +01:00
|
|
|
|
|
|
|
params.push(parseParamLike(tokens));
|
|
|
|
name = new QualName(t1, [], null, t1);
|
|
|
|
while (true) {
|
|
|
|
const t2 = tokens.peek();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t2.kind !== SyntaxKind.BoltOperator) {
|
2020-02-25 12:26:21 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (t2.text !== t1.text) {
|
|
|
|
throw new Error(`Operators have to match when defining or declaring an n-ary operator.`);
|
|
|
|
}
|
|
|
|
tokens.get();
|
|
|
|
params.push(parseParamLike(tokens))
|
|
|
|
}
|
|
|
|
|
2020-05-10 15:56:34 +02:00
|
|
|
} else if (t0.kind === SyntaxKind.BoltIdentifier) {
|
2020-02-25 12:26:21 +01:00
|
|
|
|
|
|
|
name = this.parseQualName(tokens)
|
|
|
|
const t2 = tokens.get();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t2.kind === SyntaxKind.BoltParenthesized) {
|
2020-02-25 17:55:17 +01:00
|
|
|
const innerTokens = t2.toTokenStream();
|
2020-02-25 12:26:21 +01:00
|
|
|
while (true) {
|
|
|
|
const t3 = innerTokens.peek();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t3.kind === SyntaxKind.BoltEOS) {
|
2020-02-25 12:26:21 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
params.push(this.parseParam(innerTokens))
|
|
|
|
const t4 = innerTokens.get();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t4.kind === SyntaxKind.BoltComma) {
|
2020-02-25 12:26:21 +01:00
|
|
|
continue;
|
2020-05-10 15:56:34 +02:00
|
|
|
} else if (t4.kind === SyntaxKind.BoltEOS) {
|
2020-02-25 12:26:21 +01:00
|
|
|
break;
|
|
|
|
} else {
|
2020-05-10 15:56:34 +02:00
|
|
|
throw new ParseError(t4, [SyntaxKind.BoltComma, SyntaxKind.BoltEOS])
|
2020-02-25 12:26:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
2020-05-10 15:56:34 +02:00
|
|
|
throw new ParseError(t0, [SyntaxKind.BoltIdentifier, SyntaxKind.BoltOperator, SyntaxKind.BoltParenthesized])
|
2020-02-25 12:26:21 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse return type
|
|
|
|
|
|
|
|
const t2 = tokens.peek();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t2.kind === SyntaxKind.BoltRArrow) {
|
2020-02-25 12:26:21 +01:00
|
|
|
tokens.get();
|
2020-02-25 17:55:17 +01:00
|
|
|
returnType = this.parseTypeDecl(tokens);
|
2020-02-25 12:26:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Parse function body
|
|
|
|
|
|
|
|
const t3 = tokens.peek();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t3.kind === SyntaxKind.BoltBraced) {
|
2020-02-25 17:55:17 +01:00
|
|
|
tokens.get();
|
|
|
|
switch (target) {
|
|
|
|
case "Bolt":
|
|
|
|
body = this.parseStmts(tokens, t3);
|
|
|
|
break;
|
|
|
|
case "JS":
|
|
|
|
body = acorn.parse(t3.text).body;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new Error(`Unrecognised language: ${target}`);
|
|
|
|
}
|
2020-02-25 12:26:21 +01:00
|
|
|
}
|
|
|
|
|
2020-02-26 18:53:28 +01:00
|
|
|
return new FuncDecl(isPublic, target, name, params, returnType, body, null, origNode)
|
|
|
|
|
|
|
|
}
|
2020-02-25 12:26:21 +01:00
|
|
|
|
2020-02-26 18:53:28 +01:00
|
|
|
parseSourceElement(tokens: TokenStream): SourceElement {
|
|
|
|
const t0 = tokens.peek(1);
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t0.kind === SyntaxKind.BoltIdentifier) {
|
2020-02-26 18:53:28 +01:00
|
|
|
let i = 1;
|
|
|
|
let kw: Token = t0;
|
|
|
|
if (t0.text === 'pub') {
|
|
|
|
i++;
|
|
|
|
kw = tokens.peek(i);
|
2020-05-10 15:56:34 +02:00
|
|
|
if (kw.kind !== SyntaxKind.BoltIdentifier) {
|
|
|
|
throw new ParseError(kw, [SyntaxKind.BoltForeignKeyword, SyntaxKind.BoltModKeyword,
|
|
|
|
SyntaxKind.BoltLetKeyword, SyntaxKind.BoltFnKeyword, SyntaxKind.BoltEnumKeyword, SyntaxKind.BoltStructKeyword])
|
2020-02-26 18:53:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (t0.text === 'foreign') {
|
|
|
|
i += 2;
|
|
|
|
kw = tokens.peek(i);
|
2020-05-10 15:56:34 +02:00
|
|
|
if (kw.kind !== SyntaxKind.BoltIdentifier) {
|
|
|
|
throw new ParseError(kw, [SyntaxKind.BoltModKeyword, SyntaxKind.BoltLetKeyword,
|
|
|
|
SyntaxKind.BoltFnKeyword, SyntaxKind.BoltEnumKeyword, SyntaxKind.BoltStructKeyword])
|
2020-02-26 18:53:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
switch (kw.text) {
|
2020-03-03 14:53:54 +01:00
|
|
|
case 'newtype':
|
|
|
|
return this.parseNewType(tokens);
|
|
|
|
case 'syntax':
|
|
|
|
return this.parseSyntax(tokens);
|
2020-02-26 18:53:28 +01:00
|
|
|
case 'mod':
|
|
|
|
return this.parseModDecl(tokens);
|
|
|
|
case 'fn':
|
|
|
|
return this.parseFuncDecl(tokens, null);
|
|
|
|
case 'let':
|
|
|
|
return this.parseVarDecl(tokens);
|
|
|
|
case 'struct':
|
|
|
|
return this.parseRecordDecl(tokens);
|
|
|
|
case 'enum':
|
|
|
|
return this.parseVariantDecl(tokens);
|
|
|
|
default:
|
2020-03-03 14:53:54 +01:00
|
|
|
try {
|
|
|
|
return this.parseExpr(tokens)
|
|
|
|
} catch (e) {
|
|
|
|
if (e instanceof ParseError) {
|
2020-05-10 15:56:34 +02:00
|
|
|
throw new ParseError(kw, [...e.expected, SyntaxKind.BoltModKeyword, SyntaxKind.BoltLetKeyword,
|
|
|
|
SyntaxKind.BoltFnKeyword, SyntaxKind.BoltEnumKeyword, SyntaxKind.BoltStructKeyword])
|
2020-03-03 14:53:54 +01:00
|
|
|
} else {
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
2020-02-26 18:53:28 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return this.parseStmt(tokens)
|
|
|
|
}
|
2020-02-25 12:26:21 +01:00
|
|
|
}
|
|
|
|
|
2020-03-03 14:53:54 +01:00
|
|
|
getOperatorDesc(seekArity: number, seekName: string): OperatorInfo {
|
|
|
|
for (let i = 0; i < this.operatorTable.length; ++i) {
|
|
|
|
for (const [kind, arity, name] of this.operatorTable[i]) {
|
2020-05-10 15:56:34 +02:00
|
|
|
if (arity == seekArity && name === seekName) {
|
2020-03-03 14:53:54 +01:00
|
|
|
return {
|
|
|
|
kind,
|
|
|
|
name,
|
|
|
|
arity,
|
|
|
|
precedence: i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
parseBinOp(tokens: TokenStream, lhs: Expr , minPrecedence: number) {
|
|
|
|
let lookahead = tokens.peek(1);
|
|
|
|
while (true) {
|
2020-05-10 15:56:34 +02:00
|
|
|
if (lookahead.kind !== SyntaxKind.BoltOperator) {
|
2020-03-03 14:53:54 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
const lookaheadDesc = this.getOperatorDesc(2, lookahead.text);
|
|
|
|
if (lookaheadDesc === null || lookaheadDesc.precedence < minPrecedence) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
const op = lookahead;
|
|
|
|
const opDesc = this.getOperatorDesc(2, op.text);
|
|
|
|
tokens.get();
|
|
|
|
let rhs = this.parsePrimExpr(tokens)
|
|
|
|
lookahead = tokens.peek()
|
|
|
|
while (lookaheadDesc.arity === 2
|
|
|
|
&& ((lookaheadDesc.precedence > opDesc.precedence)
|
|
|
|
|| lookaheadDesc.kind === OperatorKind.InfixR && lookaheadDesc.precedence === opDesc.precedence)) {
|
|
|
|
rhs = this.parseBinOp(tokens, rhs, lookaheadDesc.precedence)
|
|
|
|
}
|
|
|
|
lookahead = tokens.peek();
|
|
|
|
lhs = new CallExpr(new RefExpr(new QualName(op, [])), [lhs, rhs]);
|
|
|
|
}
|
|
|
|
return lhs
|
|
|
|
}
|
|
|
|
|
2020-02-26 18:53:28 +01:00
|
|
|
parseCallExpr(tokens: TokenStream): CallExpr {
|
2020-02-25 17:55:17 +01:00
|
|
|
|
|
|
|
const operator = this.parsePrimExpr(tokens)
|
|
|
|
const args: Expr[] = []
|
|
|
|
|
|
|
|
const t2 = tokens.get();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t2.kind !== SyntaxKind.BoltParenthesized) {
|
|
|
|
throw new ParseError(t2, [SyntaxKind.BoltParenthesized])
|
2020-02-25 12:26:21 +01:00
|
|
|
}
|
2020-02-25 17:55:17 +01:00
|
|
|
|
|
|
|
const innerTokens = t2.toTokenStream();
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
const t3 = innerTokens.peek();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t3.kind === SyntaxKind.BoltEOS) {
|
2020-02-25 17:55:17 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
args.push(this.parseExpr(innerTokens))
|
|
|
|
const t4 = innerTokens.get();
|
2020-05-10 15:56:34 +02:00
|
|
|
if (t4.kind === SyntaxKind.BoltEOS) {
|
2020-02-25 17:55:17 +01:00
|
|
|
break
|
2020-05-10 15:56:34 +02:00
|
|
|
} else if (t4.kind !== SyntaxKind.BoltComma){
|
|
|
|
throw new ParseError(t4, [SyntaxKind.BoltComma])
|
2020-02-25 17:55:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return new CallExpr(operator, args, null)
|
|
|
|
|
2020-02-25 12:26:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|