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 {
|
||||
elements: BoltSourceElement[],
|
||||
pkg: Package,
|
||||
pkg: Package | null,
|
||||
}
|
||||
|
||||
export interface BoltQualName extends BoltSyntax {
|
||||
|
|
|
@ -575,7 +575,7 @@ type BoltBracketedChild = never;
|
|||
export class BoltSourceFile extends SyntaxBase {
|
||||
parentNode: null | BoltSourceFileParent = null;
|
||||
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)
|
||||
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 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); }
|
||||
|
||||
|
|
|
@ -62,14 +62,16 @@ function firstIndexOfNonEmpty(str: string) {
|
|||
return j
|
||||
}
|
||||
|
||||
export class DiagnosticWriter {
|
||||
export class DiagnosticIndex {
|
||||
|
||||
constructor(private fd: number) {
|
||||
|
||||
}
|
||||
private diagnostics = new Array<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
|
||||
//}
|
||||
|
||||
public parseSourceFile(tokens: BoltTokenStream, pkg: Package): BoltSourceFile {
|
||||
public parseSourceFile(tokens: BoltTokenStream, pkg: Package | null = null): BoltSourceFile {
|
||||
const elements = this.parseSourceElements(tokens);
|
||||
const t1 = tokens.peek();
|
||||
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 { LOG_DATETIME_FORMAT } from "./constants"
|
||||
import { E_CANDIDATE_FUNCTION_REQUIRES_THIS_PARAMETER } from "./diagnostics"
|
||||
import { isPrimitive } from "util"
|
||||
export function isPowerOf(x: number, n: number):boolean {
|
||||
const a = Math.log(x) / Math.log(n);
|
||||
return Math.pow(a, n) == x;
|
||||
|
@ -152,7 +153,7 @@ function isObjectLike(value: any) {
|
|||
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]') {
|
||||
return false
|
||||
}
|
||||
|
@ -166,10 +167,16 @@ export function isPlainObject(value: any): value is object {
|
|||
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 = {}
|
||||
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;
|
||||
}
|
||||
|
@ -183,6 +190,47 @@ export function prettyPrint(value: any): string {
|
|||
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 function createTransparentProxy<T extends object>(value: T): TransparentProxy<T> {
|
||||
|
|
Loading…
Reference in a new issue