2020-05-23 21:15:20 +02:00
|
|
|
import {
|
|
|
|
BoltFunctionBodyElement,
|
|
|
|
BoltReturnStatement,
|
|
|
|
SyntaxKind,
|
2020-05-24 17:47:04 +02:00
|
|
|
BoltExpression,
|
2020-05-24 21:50:29 +02:00
|
|
|
BoltQualName,
|
|
|
|
kindToString,
|
|
|
|
Syntax,
|
|
|
|
Token,
|
2020-05-25 11:29:19 +02:00
|
|
|
isBoltPunctuated,
|
|
|
|
SourceFile,
|
|
|
|
BoltSourceFile,
|
|
|
|
BoltSourceFileModifiers
|
2020-05-23 21:15:20 +02:00
|
|
|
} from "./ast";
|
2020-05-24 21:50:29 +02:00
|
|
|
import { BOLT_SUPPORTED_LANGUAGES } from "./constants"
|
|
|
|
import {emit} from "./emitter";
|
|
|
|
import {FastStringMap, enumerate, escapeChar} from "./util";
|
|
|
|
import {TextSpan, TextPos, TextFile} from "./text";
|
|
|
|
import {Scanner} from "./scanner";
|
2020-05-23 21:15:20 +02:00
|
|
|
|
2020-05-25 11:29:19 +02:00
|
|
|
export class Package {
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
public rootDir: string,
|
|
|
|
public name: string | null,
|
|
|
|
public version: string | null,
|
|
|
|
public sourceFiles: SourceFile[],
|
|
|
|
) {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public addSourceFile(sourceFile: SourceFile) {
|
|
|
|
this.sourceFiles.push(sourceFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
export function isAutoImported(node: BoltSourceFile): boolean {
|
|
|
|
//if (node.kind !== SyntaxKind.BoltSourceFile) {
|
|
|
|
// node = node.getParentOfKind(SyntaxKind.BoltSourceFile)!
|
|
|
|
//}
|
|
|
|
return (node.modifiers & BoltSourceFileModifiers.AutoImport) > 0;
|
|
|
|
}
|
|
|
|
|
2020-05-24 21:50:29 +02:00
|
|
|
export function getLanguage(node: Syntax): string {
|
|
|
|
const kindStr = kindToString(node.kind);
|
|
|
|
for (const prefix of BOLT_SUPPORTED_LANGUAGES) {
|
|
|
|
if (kindStr.startsWith(prefix)) {
|
|
|
|
return prefix;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
throw new Error(`Could not determine the language of ${kindStr}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
|
|
|
throw new Error(`Could not convert ${kindToString(node.kind)} to a token stream.`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export const EOF = ''
|
|
|
|
|
|
|
|
export class ScanError extends Error {
|
|
|
|
constructor(public file: TextFile, public position: TextPos, public char: string) {
|
|
|
|
super(`${file.origPath}:${position.line}:${position.column}: unexpected char '${escapeChar(char)}'`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function cloneSpan(span: TextSpan | null) {
|
|
|
|
if (span === null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return span.clone();
|
|
|
|
}
|
|
|
|
|
|
|
|
export function setOrigNodeRange(node: Syntax, startNode: Syntax, endNode: Syntax): void {
|
|
|
|
node.span = new TextSpan(startNode.span!.file, startNode.span!.start.clone(), endNode.span!.end.clone());
|
|
|
|
}
|
2020-05-24 17:47:04 +02:00
|
|
|
|
2020-05-23 21:15:20 +02:00
|
|
|
export type BoltFunctionBody = BoltFunctionBodyElement[];
|
|
|
|
|
2020-05-24 11:17:56 +02:00
|
|
|
export function getReturnStatementsInFunctionBody(body: BoltFunctionBody): BoltReturnStatement[] {
|
2020-05-23 21:15:20 +02:00
|
|
|
|
|
|
|
const results: BoltReturnStatement[] = [];
|
|
|
|
|
|
|
|
for (const element of body) {
|
|
|
|
visit(element);
|
|
|
|
}
|
|
|
|
|
|
|
|
return results;
|
|
|
|
|
|
|
|
function visit(node: BoltFunctionBodyElement) {
|
|
|
|
switch (node.kind) {
|
|
|
|
case SyntaxKind.BoltReturnStatement:
|
|
|
|
results.push(node);
|
|
|
|
break;
|
|
|
|
case SyntaxKind.BoltExpressionStatement:
|
|
|
|
visitExpression(node.expression);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function visitExpression(node: BoltExpression) {
|
|
|
|
switch (node.kind) {
|
|
|
|
case SyntaxKind.BoltBlockExpression:
|
2020-05-24 21:50:29 +02:00
|
|
|
for (const element of node.elements) {
|
2020-05-23 21:15:20 +02:00
|
|
|
visit(element);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SyntaxKind.BoltMatchExpression:
|
|
|
|
for (const arm of node.arms) {
|
|
|
|
visitExpression(arm.body);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SyntaxKind.BoltCallExpression:
|
|
|
|
visitExpression(node.operator);
|
|
|
|
for (const operand of node.operands) {
|
|
|
|
visitExpression(operand);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-05-24 21:50:29 +02:00
|
|
|
export function hasPublicModifier(node: BoltDeclaration) {
|
|
|
|
return (node.modifiers & BoltDeclarationModifiers.Public) > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
export enum OperatorKind {
|
|
|
|
Prefix,
|
|
|
|
InfixL,
|
|
|
|
InfixR,
|
|
|
|
Suffix,
|
|
|
|
}
|
|
|
|
|
|
|
|
export function isRightAssoc(kind: OperatorKind) {
|
|
|
|
return kind === OperatorKind.InfixR;
|
|
|
|
}
|
|
|
|
|
|
|
|
export class ParseError extends Error {
|
|
|
|
constructor(public actual: Syntax, public expected: SyntaxKind[]) {
|
|
|
|
super(`${actual.span!.file.origPath}:${actual.span!.start.line}:${actual.span!.start.column}: expected ${enumerate(expected.map(e => describeKind(e)))} but got ${describeKind(actual.kind)}`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface OperatorInfo {
|
|
|
|
kind: OperatorKind;
|
|
|
|
arity: number;
|
|
|
|
name: string;
|
|
|
|
precedence: number;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function assertToken(node: Token, kind: SyntaxKind) {
|
|
|
|
if (node.kind !== kind) {
|
|
|
|
throw new ParseError(node, [kind]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type OperatorTableList = [OperatorKind, number, string][][];
|
|
|
|
|
|
|
|
export class OperatorTable {
|
|
|
|
|
|
|
|
private operatorsByName = new FastStringMap<string, OperatorInfo>();
|
|
|
|
//private operatorsByPrecedence = FastStringMap<number, OperatorInfo>();
|
|
|
|
|
|
|
|
constructor(definitions: OperatorTableList) {
|
|
|
|
let i = 0;
|
|
|
|
for (const group of definitions) {
|
|
|
|
for (const [kind, arity, name] of group) {
|
|
|
|
const info = { kind, arity, name, precedence: i }
|
|
|
|
this.operatorsByName.set(name, info);
|
|
|
|
//this.operatorsByPrecedence[i] = info;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public lookup(name: string): OperatorInfo | null {
|
|
|
|
if (!this.operatorsByName.has(name)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return this.operatorsByName.get(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
export function describeKind(kind: SyntaxKind): string {
|
|
|
|
switch (kind) {
|
2020-05-25 11:29:19 +02:00
|
|
|
case SyntaxKind.BoltImportKeyword:
|
|
|
|
return "'import'";
|
|
|
|
case SyntaxKind.BoltExportKeyword:
|
|
|
|
return "'export'";
|
2020-05-24 21:50:29 +02:00
|
|
|
case SyntaxKind.BoltExMark:
|
|
|
|
return "'!'";
|
|
|
|
case SyntaxKind.JSIdentifier:
|
|
|
|
return "a JavaScript identifier"
|
|
|
|
case SyntaxKind.BoltIdentifier:
|
|
|
|
return "an identifier"
|
|
|
|
case SyntaxKind.BoltOperator:
|
|
|
|
return "an operator"
|
|
|
|
case SyntaxKind.BoltStringLiteral:
|
|
|
|
return "a string"
|
|
|
|
case SyntaxKind.BoltIntegerLiteral:
|
|
|
|
return "an integer"
|
|
|
|
case SyntaxKind.BoltFnKeyword:
|
|
|
|
return "'fn'"
|
2020-05-24 22:23:18 +02:00
|
|
|
case SyntaxKind.BoltWhereKeyword:
|
|
|
|
return "'where'";
|
2020-05-24 21:50:29 +02:00
|
|
|
case SyntaxKind.BoltQuoteKeyword:
|
|
|
|
return "'quote'";
|
|
|
|
case SyntaxKind.BoltModKeyword:
|
|
|
|
return "'mod'";
|
|
|
|
case SyntaxKind.BoltForeignKeyword:
|
|
|
|
return "'foreign'"
|
|
|
|
case SyntaxKind.BoltMatchKeyword:
|
|
|
|
return "'match'";
|
|
|
|
case SyntaxKind.BoltYieldKeyword:
|
|
|
|
return "'yield'";
|
|
|
|
case SyntaxKind.BoltReturnKeyword:
|
|
|
|
return "'return'";
|
|
|
|
case SyntaxKind.BoltPubKeyword:
|
|
|
|
return "'pub'"
|
|
|
|
case SyntaxKind.BoltLetKeyword:
|
|
|
|
return "'let'"
|
|
|
|
case SyntaxKind.BoltSemi:
|
|
|
|
return "';'"
|
|
|
|
case SyntaxKind.BoltColon:
|
|
|
|
return "':'"
|
|
|
|
case SyntaxKind.BoltColonColon:
|
|
|
|
return "'::'";
|
|
|
|
case SyntaxKind.BoltDot:
|
|
|
|
return "'.'"
|
|
|
|
case SyntaxKind.JSDot:
|
|
|
|
return "'.'"
|
|
|
|
case SyntaxKind.JSDotDotDot:
|
|
|
|
return "'...'"
|
|
|
|
case SyntaxKind.BoltRArrow:
|
|
|
|
return "'->'"
|
|
|
|
case SyntaxKind.BoltVBar:
|
|
|
|
return "'|'";
|
|
|
|
case SyntaxKind.BoltComma:
|
|
|
|
return "','"
|
|
|
|
case SyntaxKind.BoltModKeyword:
|
|
|
|
return "'mod'"
|
|
|
|
case SyntaxKind.BoltStructKeyword:
|
|
|
|
return "'struct'"
|
|
|
|
case SyntaxKind.BoltEnumKeyword:
|
|
|
|
return "'enum'"
|
|
|
|
case SyntaxKind.BoltTypeKeyword:
|
|
|
|
return "'type'";
|
|
|
|
case SyntaxKind.BoltBraced:
|
|
|
|
return "'{' .. '}'"
|
|
|
|
case SyntaxKind.BoltBracketed:
|
|
|
|
return "'[' .. ']'"
|
|
|
|
case SyntaxKind.BoltParenthesized:
|
|
|
|
return "'(' .. ')'"
|
|
|
|
case SyntaxKind.EndOfFile:
|
|
|
|
return "'}', ')', ']' or end-of-file"
|
|
|
|
case SyntaxKind.BoltLtSign:
|
|
|
|
return "'<'";
|
|
|
|
case SyntaxKind.BoltGtSign:
|
|
|
|
return "'<'";
|
|
|
|
case SyntaxKind.BoltEqSign:
|
|
|
|
return "'='";
|
|
|
|
case SyntaxKind.JSOpenBrace:
|
|
|
|
return "'{'";
|
|
|
|
case SyntaxKind.JSCloseBrace:
|
|
|
|
return "'}'";
|
|
|
|
case SyntaxKind.JSOpenBracket:
|
|
|
|
return "'['";
|
|
|
|
case SyntaxKind.JSCloseBracket:
|
|
|
|
return "']'";
|
|
|
|
case SyntaxKind.JSOpenParen:
|
|
|
|
return "'('";
|
|
|
|
case SyntaxKind.JSCloseParen:
|
|
|
|
return "')'";
|
|
|
|
case SyntaxKind.JSSemi:
|
|
|
|
return "';'";
|
|
|
|
case SyntaxKind.JSComma:
|
|
|
|
return "','";
|
|
|
|
case SyntaxKind.BoltTraitKeyword:
|
|
|
|
return "'trait'";
|
|
|
|
case SyntaxKind.BoltTraitKeyword:
|
|
|
|
return "'impl'";
|
|
|
|
case SyntaxKind.BoltImplKeyword:
|
2020-05-25 11:29:19 +02:00
|
|
|
return "'impl'";
|
2020-05-24 21:50:29 +02:00
|
|
|
case SyntaxKind.BoltForKeyword:
|
|
|
|
return "'for'";
|
|
|
|
case SyntaxKind.JSMulOp:
|
|
|
|
return "'*'";
|
|
|
|
case SyntaxKind.JSAddOp:
|
|
|
|
return "'+'";
|
|
|
|
case SyntaxKind.JSDivOp:
|
|
|
|
return "'/'";
|
|
|
|
case SyntaxKind.JSSubOp:
|
|
|
|
return "'-'";
|
|
|
|
case SyntaxKind.JSLtOp:
|
|
|
|
return "'<'";
|
|
|
|
case SyntaxKind.JSGtOp:
|
|
|
|
return "'>'";
|
|
|
|
case SyntaxKind.JSBOrOp:
|
|
|
|
return "'|'";
|
|
|
|
case SyntaxKind.JSBXorOp:
|
|
|
|
return "'^'";
|
|
|
|
case SyntaxKind.JSBAndOp:
|
|
|
|
return "'&'";
|
|
|
|
case SyntaxKind.JSBNotOp:
|
|
|
|
return "'~'";
|
|
|
|
case SyntaxKind.JSNotOp:
|
|
|
|
return "'~'";
|
|
|
|
case SyntaxKind.JSString:
|
|
|
|
return "a JavaScript string"
|
|
|
|
case SyntaxKind.JSReturnKeyword:
|
|
|
|
return "'return'";
|
|
|
|
case SyntaxKind.JSForKeyword:
|
|
|
|
return "'for'";
|
|
|
|
case SyntaxKind.JSTryKeyword:
|
|
|
|
return "'try'";
|
|
|
|
case SyntaxKind.BoltRArrowAlt:
|
|
|
|
return "'=>'";
|
|
|
|
default:
|
|
|
|
throw new Error(`failed to describe ${kindToString(kind)}`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-25 11:29:19 +02:00
|
|
|
export type DeclarationPath = unknown;
|
|
|
|
|
|
|
|
type DeclarationPathInfo = {
|
|
|
|
modulePath: string[],
|
|
|
|
isAbsolute: boolean,
|
|
|
|
name: string
|
|
|
|
};
|
|
|
|
|
|
|
|
export function getModulePath(path: DeclarationPath): string[] {
|
|
|
|
return (path as DeclarationPathInfo).modulePath;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function hasAbsoluteModulePath(path: DeclarationPath): boolean {
|
|
|
|
return (path as DeclarationPathInfo).modulePath.length > 0
|
|
|
|
&& (path as DeclarationPathInfo).isAbsolute;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function hasRelativeModulePath(path: DeclarationPath): boolean {
|
|
|
|
return (path as DeclarationPathInfo).modulePath.length > 0
|
|
|
|
&& !(path as DeclarationPathInfo).isAbsolute;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getSymbolNameOfDeclarationPath(path: DeclarationPath): string {
|
|
|
|
return (path as DeclarationPathInfo).name;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function createDeclarationPath(node: BoltQualName): DeclarationPath {
|
|
|
|
const name = emit(node.name);
|
2020-05-24 21:50:29 +02:00
|
|
|
if (node.modulePath === null) {
|
2020-05-25 11:29:19 +02:00
|
|
|
return { modulePath: [], isAbsolute: false, name };
|
2020-05-24 21:50:29 +02:00
|
|
|
}
|
2020-05-25 11:29:19 +02:00
|
|
|
return { modulePath: node.modulePath.map(id => id.text), isAbsolute: false, name };
|
2020-05-24 21:50:29 +02:00
|
|
|
}
|
|
|
|
|