Move some declarations from src/util.ts to src/common.ts

This commit is contained in:
Sam Vervaeck 2020-05-24 21:50:29 +02:00
parent 1068dae5d6
commit 79f150bb4b
10 changed files with 275 additions and 287 deletions

View file

@ -202,6 +202,13 @@ export class TypeChecker {
switch (node.kind) { switch (node.kind) {
case SyntaxKind.BoltModule:
{
for (const element of node.elements) {
visitSourceElement(element);
}
}
case SyntaxKind.BoltRecordDeclaration: case SyntaxKind.BoltRecordDeclaration:
{ {
if (node.members !== null) { if (node.members !== null) {

View file

@ -3,13 +3,56 @@ import {
BoltReturnStatement, BoltReturnStatement,
SyntaxKind, SyntaxKind,
BoltExpression, BoltExpression,
BoltSourceFile, BoltQualName,
JSSourceFile kindToString,
Syntax,
Token,
isBoltPunctuated
} from "./ast"; } from "./ast";
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";
export type SourceFile export function getLanguage(node: Syntax): string {
= BoltSourceFile const kindStr = kindToString(node.kind);
| JSSourceFile 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());
}
export type BoltFunctionBody = BoltFunctionBodyElement[]; export type BoltFunctionBody = BoltFunctionBodyElement[];
@ -37,7 +80,7 @@ export function getReturnStatementsInFunctionBody(body: BoltFunctionBody): BoltR
function visitExpression(node: BoltExpression) { function visitExpression(node: BoltExpression) {
switch (node.kind) { switch (node.kind) {
case SyntaxKind.BoltBlockExpression: case SyntaxKind.BoltBlockExpression:
for (const element of node.statements) { for (const element of node.elements) {
visit(element); visit(element);
} }
break; break;
@ -57,3 +100,206 @@ export function getReturnStatementsInFunctionBody(body: BoltFunctionBody): BoltR
} }
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) {
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'"
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:
return "'trait'";
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)}`)
}
}
export function toDeclarationPath(node: BoltQualName): string[] {
const lastElement = emit(node.name);
if (node.modulePath === null) {
return [ lastElement ];
}
return [...node.modulePath.map(id => id.text), lastElement];
}

View file

@ -1 +1,3 @@
export const BOLT_SUPPORTED_LANGUAGES = ['Bolt', 'JS'];

View file

@ -29,7 +29,6 @@ import {
BoltDeclarationModifiers, BoltDeclarationModifiers,
BoltStringLiteral, BoltStringLiteral,
BoltImportSymbol, BoltImportSymbol,
BoltCallExpression,
BoltExpressionStatement, BoltExpressionStatement,
createBoltExpressionStatement, createBoltExpressionStatement,
BoltVariableDeclaration, BoltVariableDeclaration,
@ -37,7 +36,6 @@ import {
createBoltVariableDeclaration, createBoltVariableDeclaration,
BoltReturnStatement, BoltReturnStatement,
createBoltReturnStatement, createBoltReturnStatement,
BoltRecordMember,
BoltModule, BoltModule,
createBoltModule, createBoltModule,
BoltTypeAliasDeclaration, BoltTypeAliasDeclaration,
@ -47,17 +45,14 @@ import {
createBoltCallExpression, createBoltCallExpression,
BoltSymbol, BoltSymbol,
BoltTypeParameter, BoltTypeParameter,
createBoltTypePattern,
createBoltTypeParameter, createBoltTypeParameter,
BoltTraitDeclaration, BoltTraitDeclaration,
createBoltTraitKeyword,
createBoltTraitDeclaration, createBoltTraitDeclaration,
createBoltImplDeclaration, createBoltImplDeclaration,
BoltImplDeclaration, BoltImplDeclaration,
BoltSourceFile, BoltSourceFile,
BoltFunctionBodyElement, BoltFunctionBodyElement,
createBoltSourceFile, createBoltSourceFile,
BoltRecordField,
setParents, setParents,
BoltMatchExpression, BoltMatchExpression,
createBoltMatchArm, createBoltMatchArm,
@ -70,7 +65,6 @@ import {
BoltRecordPattern, BoltRecordPattern,
createBoltRecordPattern, createBoltRecordPattern,
createBoltRecordFieldPattern, createBoltRecordFieldPattern,
BoltQuoteKeyword,
isBoltPunctuated, isBoltPunctuated,
Token, Token,
createBoltQuoteExpression, createBoltQuoteExpression,
@ -82,23 +76,20 @@ import {
createBoltFunctionExpression, createBoltFunctionExpression,
BoltMacroCall, BoltMacroCall,
createBoltMacroCall, createBoltMacroCall,
BoltMemberExpression,
createBoltMemberExpression, createBoltMemberExpression,
} from "./ast" } from "./ast"
import { parseForeignLanguage } from "./foreign" import { parseForeignLanguage } from "./foreign"
import { import {
Stream,
OperatorKind, OperatorKind,
OperatorTable, OperatorTable,
assertToken, assertToken,
ParseError, ParseError,
setOrigNodeRange, setOrigNodeRange,
createTokenStream, createTokenStream,
uniq, } from "./common"
assert, import { Stream, uniq } from "./util"
} from "./util"
export type BoltTokenStream = Stream<BoltToken>; export type BoltTokenStream = Stream<BoltToken>;

View file

@ -1,6 +1,5 @@
import { SourceFile } from "./common" import { SourceFile } from "./ast"
import { BoltSourceFile } from "./ast"
import { FastStringMap } from "./util"; import { FastStringMap } from "./util";
export class Program { export class Program {
@ -8,7 +7,7 @@ export class Program {
private transformed = new FastStringMap<string, SourceFile>(); private transformed = new FastStringMap<string, SourceFile>();
constructor( constructor(
sourceFiles: BoltSourceFile[] sourceFiles: SourceFile[]
) { ) {
for (const sourceFile of sourceFiles) { for (const sourceFile of sourceFiles) {
this.transformed.set(sourceFile.span!.file.fullPath, sourceFile); this.transformed.set(sourceFile.span!.file.fullPath, sourceFile);

View file

@ -1,5 +1,5 @@
import { EOF, ScanError } from "./util" import { EOF, ScanError } from "./common"
import { import {
TextFile, TextFile,
@ -8,8 +8,6 @@ import {
} from "./text" } from "./text"
import { import {
setParents,
SyntaxKind,
BoltToken, BoltToken,
createBoltRArrowAlt, createBoltRArrowAlt,
createEndOfFile, createEndOfFile,
@ -19,7 +17,6 @@ import {
createBoltParenthesized, createBoltParenthesized,
createBoltBraced, createBoltBraced,
createBoltBracketed, createBoltBracketed,
createBoltSourceFile,
createBoltSemi, createBoltSemi,
createBoltComma, createBoltComma,
createBoltStringLiteral, createBoltStringLiteral,

View file

@ -10,12 +10,12 @@ import {
BoltMacroCall, BoltMacroCall,
} from "../ast" } from "../ast"
import { TypeChecker, Scope } from "../checker" import { TypeChecker } from "../checker"
import { BoltTokenStream, Parser, isModifierKeyword } from "../parser" import { BoltTokenStream, Parser, isModifierKeyword } from "../parser"
import { Evaluator, TRUE, FALSE } from "../evaluator" import { Evaluator, TRUE, FALSE } from "../evaluator"
import { Transformer, TransformManager } from "./index" import { Transformer, TransformManager } from "./index"
import { inject } from "../di" import { inject } from "../di"
import {SourceFile} from "../program" import { SourceFile } from "../ast"
interface SyntaxTransformer { interface SyntaxTransformer {
pattern: BoltPattern; pattern: BoltPattern;

View file

@ -1,8 +1,7 @@
import { SourceFile, Program } from "../program" import { Program } from "../program"
import { Container, Newable } from "../di" import { Container, Newable } from "../di"
import {Evaluator} from "../evaluator"; import {SourceFile} from "../ast";
import {TypeChecker} from "../checker";
export interface Transformer { export interface Transformer {
isApplicable(node: SourceFile): boolean; isApplicable(node: SourceFile): boolean;

View file

@ -5,7 +5,7 @@ import * as path from "path"
const PACKAGE_ROOT = path.resolve(__dirname, '..', '..'); const PACKAGE_ROOT = path.resolve(__dirname, '..', '..');
import { Syntax, Declaration, NodeDeclaration, TypeDeclaration, EnumDeclaration, TypeNode, NodeField } from "./ast" import { Syntax, Declaration, NodeDeclaration, TypeDeclaration, EnumDeclaration, TypeNode, NodeField } from "./ast"
import { FastStringMap } from "../util" import { MapLike } from "../util"
import { FileWriter } from "./util" import { FileWriter } from "./util"
export function generateAST(decls: Declaration[]) { export function generateAST(decls: Declaration[]) {
@ -21,7 +21,7 @@ export function generateAST(decls: Declaration[]) {
const enumDecls: EnumDeclaration[] = decls.filter(decl => decl.type === 'EnumDeclaration') as EnumDeclaration[]; const enumDecls: EnumDeclaration[] = decls.filter(decl => decl.type === 'EnumDeclaration') as EnumDeclaration[];
const langNames: string[] = decls.filter(decl => decl.type === 'LanguageDeclaration').map(decl => decl.name); const langNames: string[] = decls.filter(decl => decl.type === 'LanguageDeclaration').map(decl => decl.name);
const declByName: FastStringMap<Declaration> = Object.create(null); const declByName: MapLike<Declaration> = Object.create(null);
i = 0; i = 0;
for (const decl of decls) { for (const decl of decls) {
decl.index = i++; decl.index = i++;
@ -31,7 +31,7 @@ export function generateAST(decls: Declaration[]) {
// Generate a mapping from parent node to child node // Generate a mapping from parent node to child node
// This makes it easy to generate union types for the intermediate nodes. // This makes it easy to generate union types for the intermediate nodes.
const childrenOf: FastStringMap<string[]> = Object.create(null); const childrenOf: MapLike<string[]> = Object.create(null);
for (const nodeDecl of nodeDecls) { for (const nodeDecl of nodeDecls) {
for (const parentName of nodeDecl.parents) { for (const parentName of nodeDecl.parents) {
if (childrenOf[parentName] === undefined) { if (childrenOf[parentName] === undefined) {

View file

@ -4,31 +4,12 @@ import * as fs from "fs"
import moment from "moment" import moment from "moment"
import chalk from "chalk" import chalk from "chalk"
import { TextFile, TextSpan, TextPos } from "./text"
import { Scanner } from "./scanner"
import { kindToString, Syntax, BoltQualName, BoltDeclaration, BoltDeclarationModifiers, createEndOfFile, SyntaxKind, isBoltPunctuated } from "./ast"
export function assert(test: boolean): void { export function assert(test: boolean): void {
if (!test) { if (!test) {
throw new Error(`Invariant violation: an internal sanity check failed.`); throw new Error(`Invariant violation: an internal sanity check failed.`);
} }
} }
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,
() => createEndOfFile(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 JsonArray extends Array<Json> { };
export interface JsonObject { [key: string]: Json } export interface JsonObject { [key: string]: Json }
export type Json = null | string | boolean | number | JsonArray | JsonObject; export type Json = null | string | boolean | number | JsonArray | JsonObject;
@ -146,40 +127,6 @@ export function memoize(hasher: (...args: any[]) => string) {
} }
} }
const supportedLanguages = ['Bolt', 'JS'];
export function getLanguage(node: Syntax): string {
const kindStr = kindToString(node.kind);
for (const prefix of supportedLanguages) {
if (kindStr.startsWith(prefix)) {
return prefix;
}
}
throw new Error(`Could not determine the language of ${kindStr}`);
}
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());
}
export function hasPublicModifier(node: BoltDeclaration) {
return (node.modifiers & BoltDeclarationModifiers.Public) > 0;
}
export function toDeclarationPath(node: BoltQualName): string[] {
if (node.modulePath === null) {
return [ node.name.text ];
}
return [...node.modulePath.map(id => id.text), node.name.text];
}
export interface Stream<T> { export interface Stream<T> {
get(): T; get(): T;
peek(count?: number): T; peek(count?: number): T;
@ -235,140 +182,7 @@ export function getFileStem(filepath: string): string {
return path.basename(filepath).split('.')[0]; return path.basename(filepath).split('.')[0];
} }
export function describeKind(kind: SyntaxKind): string { export function enumerate(elements: string[]) {
switch (kind) {
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'"
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:
return "'trait'";
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)}`)
}
}
function enumerate(elements: string[]) {
if (elements.length === 1) { if (elements.length === 1) {
return elements[0] return elements[0]
} else { } else {
@ -376,68 +190,7 @@ function enumerate(elements: string[]) {
} }
} }
export class ParseError extends Error { export function escapeChar(ch: string) {
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 enum OperatorKind {
Prefix,
InfixL,
InfixR,
Suffix,
}
export function isRightAssoc(kind: OperatorKind) {
return kind === OperatorKind.InfixR;
}
export interface OperatorInfo {
kind: OperatorKind;
arity: number;
name: string;
precedence: number;
}
export function assertToken(node: Syntax, 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 const EOF = ''
function escapeChar(ch: string) {
switch (ch) { switch (ch) {
case '\a': return '\\a'; case '\a': return '\\a';
case '\b': return '\\b'; case '\b': return '\\b';
@ -460,12 +213,6 @@ function escapeChar(ch: string) {
} }
} }
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 interface MapLike<T> { export interface MapLike<T> {
[key: string]: T; [key: string]: T;
} }