Add a POC test runner for the parser and scanner
This commit is contained in:
parent
99f2944e1c
commit
2a0e24213d
6 changed files with 113 additions and 12 deletions
|
@ -163,7 +163,7 @@ export interface BoltBracketed extends BoltPunctuated {}
|
||||||
|
|
||||||
export interface BoltSourceFile extends BoltSyntax, SourceFile {
|
export interface BoltSourceFile extends BoltSyntax, SourceFile {
|
||||||
elements: BoltSourceElement[],
|
elements: BoltSourceElement[],
|
||||||
pkg: Package,
|
pkg: Package | null,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BoltQualName extends BoltSyntax {
|
export interface BoltQualName extends BoltSyntax {
|
||||||
|
|
|
@ -575,7 +575,7 @@ type BoltBracketedChild = never;
|
||||||
export class BoltSourceFile extends SyntaxBase {
|
export class BoltSourceFile extends SyntaxBase {
|
||||||
parentNode: null | BoltSourceFileParent = null;
|
parentNode: null | BoltSourceFileParent = null;
|
||||||
kind: SyntaxKind.BoltSourceFile = SyntaxKind.BoltSourceFile;
|
kind: SyntaxKind.BoltSourceFile = SyntaxKind.BoltSourceFile;
|
||||||
constructor(public elements: BoltSourceElement[], public pkg: Package, span: TextSpan | null = null) { super(span); }
|
constructor(public elements: BoltSourceElement[], public pkg: Package | null, span: TextSpan | null = null) { super(span); }
|
||||||
*getChildNodes(): IterableIterator<BoltSourceFileChild> { for (let element of this.elements)
|
*getChildNodes(): IterableIterator<BoltSourceFileChild> { for (let element of this.elements)
|
||||||
yield element; }
|
yield element; }
|
||||||
}
|
}
|
||||||
|
@ -1991,7 +1991,7 @@ export function createBoltBraced(text: string, span: TextSpan | null = null): Bo
|
||||||
|
|
||||||
export function createBoltBracketed(text: string, span: TextSpan | null = null): BoltBracketed { return new BoltBracketed(text, span); }
|
export function createBoltBracketed(text: string, span: TextSpan | null = null): BoltBracketed { return new BoltBracketed(text, span); }
|
||||||
|
|
||||||
export function createBoltSourceFile(elements: BoltSourceElement[], pkg: Package, span: TextSpan | null = null): BoltSourceFile { return new BoltSourceFile(elements, pkg, span); }
|
export function createBoltSourceFile(elements: BoltSourceElement[], pkg: Package | null, span: TextSpan | null = null): BoltSourceFile { return new BoltSourceFile(elements, pkg, span); }
|
||||||
|
|
||||||
export function createBoltQualName(isAbsolute: boolean, modulePath: BoltIdentifier[], name: BoltSymbol, span: TextSpan | null = null): BoltQualName { return new BoltQualName(isAbsolute, modulePath, name, span); }
|
export function createBoltQualName(isAbsolute: boolean, modulePath: BoltIdentifier[], name: BoltSymbol, span: TextSpan | null = null): BoltQualName { return new BoltQualName(isAbsolute, modulePath, name, span); }
|
||||||
|
|
||||||
|
|
|
@ -62,14 +62,16 @@ function firstIndexOfNonEmpty(str: string) {
|
||||||
return j
|
return j
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DiagnosticWriter {
|
export class DiagnosticIndex {
|
||||||
|
|
||||||
constructor(private fd: number) {
|
private diagnostics = new Array<Diagnostic>();
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public add(diagnostic: Diagnostic) {
|
public add(diagnostic: Diagnostic) {
|
||||||
fs.writeSync(this.fd, JSON.stringify(diagnostic) + '\n');
|
this.diagnostics.push(diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getAllDiagnostics(): IterableIterator<Diagnostic> {
|
||||||
|
return this.diagnostics[Symbol.iterator]();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
51
src/marktest.ts
Normal file
51
src/marktest.ts
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import { SyntaxKind, Syntax } from "./ast";
|
||||||
|
import { serialize, Json } from "./util";
|
||||||
|
import { DiagnosticIndex } from "./diagnostics";
|
||||||
|
import { Test } from "@marktest/core"
|
||||||
|
|
||||||
|
export async function scanner(test: Test): Promise<Json> {
|
||||||
|
const { DiagnosticIndex } = await import("./diagnostics")
|
||||||
|
const diagnostics = new DiagnosticIndex;
|
||||||
|
const { Scanner } = await import("./scanner");
|
||||||
|
const scanner = new Scanner(test.file, test.text, test.startPos);
|
||||||
|
const tokens = []
|
||||||
|
while (true) {
|
||||||
|
const token = scanner.scan();
|
||||||
|
if (token.kind === SyntaxKind.EndOfFile) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tokens.push(token);
|
||||||
|
}
|
||||||
|
return serialize({
|
||||||
|
diagnostics,
|
||||||
|
tokens,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function parser(test: Test): Promise<Json> {
|
||||||
|
const kind = test.args.kind ?? 'SourceFile';
|
||||||
|
const { Scanner } = await import("./scanner")
|
||||||
|
const { Parser } = await import("./parser");
|
||||||
|
const diagnostics = new DiagnosticIndex;
|
||||||
|
const parser = new Parser();
|
||||||
|
const tokens = new Scanner(test.file, test.text);
|
||||||
|
let results: Syntax[];
|
||||||
|
switch (kind) {
|
||||||
|
case 'SourceFile':
|
||||||
|
results = [ parser.parseSourceFile(tokens) ];
|
||||||
|
break;
|
||||||
|
case 'SourceElements':
|
||||||
|
results = parser.parseSourceElements(tokens);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`I did not know how to parse ${kind}`)
|
||||||
|
}
|
||||||
|
return serialize({
|
||||||
|
diagnostics,
|
||||||
|
results,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function check(test: Test): Json {
|
||||||
|
// TODO
|
||||||
|
}
|
|
@ -1534,7 +1534,7 @@ export class Parser {
|
||||||
// return lhs
|
// return lhs
|
||||||
//}
|
//}
|
||||||
|
|
||||||
public parseSourceFile(tokens: BoltTokenStream, pkg: Package): BoltSourceFile {
|
public parseSourceFile(tokens: BoltTokenStream, pkg: Package | null = null): BoltSourceFile {
|
||||||
const elements = this.parseSourceElements(tokens);
|
const elements = this.parseSourceElements(tokens);
|
||||||
const t1 = tokens.peek();
|
const t1 = tokens.peek();
|
||||||
assertToken(t1, SyntaxKind.EndOfFile);
|
assertToken(t1, SyntaxKind.EndOfFile);
|
||||||
|
|
54
src/util.ts
54
src/util.ts
|
@ -7,6 +7,7 @@ import moment from "moment"
|
||||||
import chalk from "chalk"
|
import chalk from "chalk"
|
||||||
import { LOG_DATETIME_FORMAT } from "./constants"
|
import { LOG_DATETIME_FORMAT } from "./constants"
|
||||||
import { E_CANDIDATE_FUNCTION_REQUIRES_THIS_PARAMETER } from "./diagnostics"
|
import { E_CANDIDATE_FUNCTION_REQUIRES_THIS_PARAMETER } from "./diagnostics"
|
||||||
|
import { isPrimitive } from "util"
|
||||||
export function isPowerOf(x: number, n: number):boolean {
|
export function isPowerOf(x: number, n: number):boolean {
|
||||||
const a = Math.log(x) / Math.log(n);
|
const a = Math.log(x) / Math.log(n);
|
||||||
return Math.pow(a, n) == x;
|
return Math.pow(a, n) == x;
|
||||||
|
@ -152,7 +153,7 @@ function isObjectLike(value: any) {
|
||||||
return typeof value === 'object' && value !== null;
|
return typeof value === 'object' && value !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isPlainObject(value: any): value is object {
|
export function isPlainObject(value: any): value is MapLike<any> {
|
||||||
if (!isObjectLike(value) || getTag(value) != '[object Object]') {
|
if (!isObjectLike(value) || getTag(value) != '[object Object]') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -166,10 +167,16 @@ export function isPlainObject(value: any): value is object {
|
||||||
return Object.getPrototypeOf(value) === proto
|
return Object.getPrototypeOf(value) === proto
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mapValues<T extends object, R extends PropertyKey>(obj: T, func: (value: keyof T) => R): { [K in keyof T]: R } {
|
export function* values<T extends object>(obj: T): IterableIterator<T[keyof T]> {
|
||||||
|
for (const key of Object.keys(obj)) {
|
||||||
|
yield obj[key as keyof T];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mapValues<T extends object, R>(obj: T, func: (value: T[keyof T]) => R): { [K in keyof T]: R } {
|
||||||
const newObj: any = {}
|
const newObj: any = {}
|
||||||
for (const key of Object.keys(obj)) {
|
for (const key of Object.keys(obj)) {
|
||||||
newObj[key] = func((obj as any)[key]);
|
newObj[key as keyof T] = func(obj[key as keyof T]);
|
||||||
}
|
}
|
||||||
return newObj;
|
return newObj;
|
||||||
}
|
}
|
||||||
|
@ -183,6 +190,47 @@ export function prettyPrint(value: any): string {
|
||||||
return value.toString();
|
return value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type Newable<T> = {
|
||||||
|
new(...args: any): T;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const serializeTag = Symbol('serializer tag');
|
||||||
|
|
||||||
|
const serializableClasses = new Map<string, Newable<{ [serializeTag](): Json }>>();
|
||||||
|
|
||||||
|
export function serialize(value: any): Json {
|
||||||
|
if (isPrimitive(value)) {
|
||||||
|
return {
|
||||||
|
type: 'primitive',
|
||||||
|
value,
|
||||||
|
}
|
||||||
|
} else if (isObjectLike(value)) {
|
||||||
|
if (isPlainObject(value)) {
|
||||||
|
return {
|
||||||
|
type: 'object',
|
||||||
|
elements: [...map(values(value), element => serialize(element))]
|
||||||
|
};
|
||||||
|
} else if (value[serializeTag] !== undefined
|
||||||
|
&& typeof(value[serializeTag]) === 'function'
|
||||||
|
&& typeof(value.constructor.name) === 'string') {
|
||||||
|
return {
|
||||||
|
type: 'class',
|
||||||
|
name: value.constructor.name,
|
||||||
|
data: value[serializeTag](),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(`Could not serialize ${value}: it was a non-primitive object and has no serializer tag.`)
|
||||||
|
}
|
||||||
|
} else if (Array.isArray(value)) {
|
||||||
|
return {
|
||||||
|
type: 'array',
|
||||||
|
elements: value.map(serialize)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(`Could not serialize ${value}: is was not recognised as a primitive type, an object, a class instance, or an array.`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export type TransparentProxy<T> = T & { updateHandle(value: T): void }
|
export type TransparentProxy<T> = T & { updateHandle(value: T): void }
|
||||||
|
|
||||||
export function createTransparentProxy<T extends object>(value: T): TransparentProxy<T> {
|
export function createTransparentProxy<T extends object>(value: T): TransparentProxy<T> {
|
||||||
|
|
Loading…
Reference in a new issue