Improve preprocessor error reporting
This commit is contained in:
parent
8888ac3fe9
commit
3edb680c19
|
@ -6,7 +6,7 @@ import {
|
||||||
laxRead, itemText,
|
laxRead, itemText,
|
||||||
|
|
||||||
Items, Pattern, Templates, Substitution, TokenType,
|
Items, Pattern, Templates, Substitution, TokenType,
|
||||||
SourceMap, CodeWriter, TemplateFunction, Token, SpanIndex,
|
SourceMap, CodeWriter, TemplateFunction, Token, SpanIndex, match, TokenBase, getRange, Pos,
|
||||||
} from '../syntax/index.js';
|
} from '../syntax/index.js';
|
||||||
import {
|
import {
|
||||||
SyndicateParser, SyndicateTypedParser,
|
SyndicateParser, SyndicateTypedParser,
|
||||||
|
@ -29,7 +29,7 @@ export function stripShebang(items: Items): Items {
|
||||||
|
|
||||||
export type ModuleType ='es6' | 'require' | 'global';
|
export type ModuleType ='es6' | 'require' | 'global';
|
||||||
|
|
||||||
export type ErrorSink = (message: string) => void;
|
export type ErrorSink = (message: string, start: Pos | undefined, end: Pos | undefined) => void;
|
||||||
|
|
||||||
export interface CompileOptions {
|
export interface CompileOptions {
|
||||||
source: string,
|
source: string,
|
||||||
|
@ -52,17 +52,17 @@ export class ExpansionContext {
|
||||||
readonly parser: SyndicateParser;
|
readonly parser: SyndicateParser;
|
||||||
readonly moduleType: ModuleType;
|
readonly moduleType: ModuleType;
|
||||||
readonly typescript: boolean;
|
readonly typescript: boolean;
|
||||||
readonly emitError: (message: string) => void;
|
readonly errorEmitter: ErrorSink;
|
||||||
nextIdNumber = 0;
|
nextIdNumber = 0;
|
||||||
|
|
||||||
constructor(moduleType: ModuleType,
|
constructor(moduleType: ModuleType,
|
||||||
typescript: boolean,
|
typescript: boolean,
|
||||||
emitError: ErrorSink)
|
errorEmitter: ErrorSink)
|
||||||
{
|
{
|
||||||
this.parser = typescript ? new SyndicateTypedParser : new SyndicateParser();
|
this.parser = typescript ? new SyndicateTypedParser : new SyndicateParser();
|
||||||
this.moduleType = moduleType;
|
this.moduleType = moduleType;
|
||||||
this.typescript = typescript;
|
this.typescript = typescript;
|
||||||
this.emitError = emitError;
|
this.errorEmitter = errorEmitter;
|
||||||
}
|
}
|
||||||
|
|
||||||
quasiRandomId(): string {
|
quasiRandomId(): string {
|
||||||
|
@ -72,6 +72,10 @@ export class ExpansionContext {
|
||||||
argDecl(t: TemplateFunction, name: Substitution, type: Substitution): Items {
|
argDecl(t: TemplateFunction, name: Substitution, type: Substitution): Items {
|
||||||
return (this.typescript) ? t`${name}: ${type}` : t`${name}`;
|
return (this.typescript) ? t`${name}: ${type}` : t`${name}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emitError(m: string, loc: TokenBase) {
|
||||||
|
this.errorEmitter(m, loc.start, loc.end);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function stringifyId(i: Identifier): Items {
|
function stringifyId(i: Identifier): Items {
|
||||||
|
@ -99,7 +103,8 @@ function binderTypeGuard(ctx: ExpansionContext, t: TemplateFunction): (binder: B
|
||||||
case 'any':
|
case 'any':
|
||||||
return bind;
|
return bind;
|
||||||
default:
|
default:
|
||||||
ctx.emitError(`Cannot emit guard for binding of type: ${JSON.stringify(typeText)}`);
|
ctx.emitError(`Cannot emit guard for binding of type: ${JSON.stringify(typeText)}`,
|
||||||
|
getRange(binder.type));
|
||||||
return bind; /* act as if "any", for now */
|
return bind; /* act as if "any", for now */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
/// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
/// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Token, Item, Items,
|
Token, Items,
|
||||||
Pattern,
|
Pattern,
|
||||||
foldItems, match, anonymousTemplate as template, commaJoin,
|
foldItems, match, anonymousTemplate as template, commaJoin,
|
||||||
|
|
||||||
scope, bind, seq, alt, upTo, atom, atomString, group,
|
scope, bind, seq, alt, upTo, atom, atomString, group,
|
||||||
repeat, option, withoutSpace, map, mapm, rest, discard,
|
repeat, option, withoutSpace, map, mapm, rest, discard,
|
||||||
value, succeed, fail, separatedOrTerminatedBy, anything, not
|
value, succeed, fail, separatedOrTerminatedBy, not, TokenBase
|
||||||
} from '../syntax/index.js';
|
} from '../syntax/index.js';
|
||||||
import * as Matcher from '../syntax/matcher.js';
|
import * as Matcher from '../syntax/matcher.js';
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,10 @@ export interface Pos {
|
||||||
fixed?: boolean;
|
fixed?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function formatPos(p?: Pos): string {
|
||||||
|
return p ? `${p.name ?? '?'}:${p.line}:${p.column}` : '?';
|
||||||
|
}
|
||||||
|
|
||||||
export function startPos(name: string | null): Pos {
|
export function startPos(name: string | null): Pos {
|
||||||
return { line: 1, column: 0, pos: 0, name };
|
return { line: 1, column: 0, pos: 0, name };
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,14 @@ export type Items = Array<Item>;
|
||||||
|
|
||||||
export type GroupInProgress = Omit<Group, 'end'>;
|
export type GroupInProgress = Omit<Group, 'end'>;
|
||||||
|
|
||||||
|
export function getRange(t: Item | Items): TokenBase {
|
||||||
|
if (Array.isArray(t)) {
|
||||||
|
return { start: t[0].start, end: t[t.length - 1].end, synthetic: true };
|
||||||
|
} else {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function finishGroup(g: GroupInProgress, end: Pos): Group {
|
export function finishGroup(g: GroupInProgress, end: Pos): Group {
|
||||||
return { ... g, end };
|
return { ... g, end };
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { glob } from 'glob';
|
import { glob } from 'glob';
|
||||||
|
|
||||||
import { compile } from '@syndicate-lang/compiler';
|
import { compile, Syntax } from '@syndicate-lang/compiler';
|
||||||
import { dataURL, sourceMappingComment} from './util.js';
|
import { dataURL, sourceMappingComment} from './util.js';
|
||||||
|
|
||||||
export type ModuleChoice = 'es6' | 'require' | 'global';
|
export type ModuleChoice = 'es6' | 'require' | 'global';
|
||||||
|
@ -117,7 +117,7 @@ export function main(argv: string[]) {
|
||||||
argv => argv)
|
argv => argv)
|
||||||
.argv);
|
.argv);
|
||||||
|
|
||||||
const collectedErrors: Array<string> = [];
|
let errorCount = 0;
|
||||||
|
|
||||||
const rename = makeRenamer(options.outputDirectory ?? '',
|
const rename = makeRenamer(options.outputDirectory ?? '',
|
||||||
options.rootDirectory ?? '.',
|
options.rootDirectory ?? '.',
|
||||||
|
@ -146,9 +146,9 @@ export function main(argv: string[]) {
|
||||||
runtime: options.runtime,
|
runtime: options.runtime,
|
||||||
module: options.module,
|
module: options.module,
|
||||||
typescript: options.typed,
|
typescript: options.typed,
|
||||||
emitError: m => {
|
emitError: (m, start, end) => {
|
||||||
console.error(m);
|
console.error(`${Syntax.formatPos(start)}-${Syntax.formatPos(end)}: ${m}`);
|
||||||
collectedErrors.push(m);
|
errorCount++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
map.sourcesContent = [source];
|
map.sourcesContent = [source];
|
||||||
|
@ -168,7 +168,7 @@ export function main(argv: string[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (collectedErrors.length > 0) {
|
if (errorCount > 0) {
|
||||||
throw new Error(`Compilation failed with ${collectedErrors.length} error(s).`);
|
throw new Error(`Compilation failed with ${errorCount} error(s).`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ const boot: tslib.server.PluginModuleFactory = ({ typescript: ts }) => {
|
||||||
interface SyndicateInfo {
|
interface SyndicateInfo {
|
||||||
sourceFile: ts.SourceFile;
|
sourceFile: ts.SourceFile;
|
||||||
originalSource: string;
|
originalSource: string;
|
||||||
|
diagnostics: ts.DiagnosticWithLocation[];
|
||||||
targetToSourceMap: Syntax.SpanIndex<Syntax.Token>;
|
targetToSourceMap: Syntax.SpanIndex<Syntax.Token>;
|
||||||
sourceToTargetMap: Syntax.SpanIndex<number>;
|
sourceToTargetMap: Syntax.SpanIndex<number>;
|
||||||
}
|
}
|
||||||
|
@ -159,20 +160,30 @@ const boot: tslib.server.PluginModuleFactory = ({ typescript: ts }) => {
|
||||||
onError?.(`Could not read input file ${fileName}`);
|
onError?.(`Could not read input file ${fileName}`);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
const diagnostics:
|
||||||
|
Array<{ message: string, start?: Syntax.Pos, end?: Syntax.Pos }> = [];
|
||||||
console.log('Syndicate compiling', fileName);
|
console.log('Syndicate compiling', fileName);
|
||||||
const { text: expandedText, targetToSourceMap, sourceToTargetMap } = compile({
|
const { text: expandedText, targetToSourceMap, sourceToTargetMap } = compile({
|
||||||
source: inputText,
|
source: inputText,
|
||||||
name: fileName,
|
name: fileName,
|
||||||
typescript: true,
|
typescript: true,
|
||||||
emitError: m => {
|
emitError: (message, start, end) => {
|
||||||
console.error(m);
|
console.error(`${Syntax.formatPos(start)}-${Syntax.formatPos(end)}: ${message}`);
|
||||||
onError?.(m);
|
diagnostics.push({ message, start, end });
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const sf = ts.createSourceFile(fileName, expandedText, languageVersion, true);
|
const sf = ts.createSourceFile(fileName, expandedText, languageVersion, true);
|
||||||
syndicateInfo.set(fileName, {
|
syndicateInfo.set(fileName, {
|
||||||
sourceFile: sf,
|
sourceFile: sf,
|
||||||
originalSource: inputText,
|
originalSource: inputText,
|
||||||
|
diagnostics: diagnostics.map(({ message, start, end }) => ({
|
||||||
|
category: ts.DiagnosticCategory.Error,
|
||||||
|
code: 99999,
|
||||||
|
file: sf,
|
||||||
|
start: start?.pos ?? 0,
|
||||||
|
length: Math.max(0, (end?.pos ?? 0) - (start?.pos ?? 0)),
|
||||||
|
messageText: message,
|
||||||
|
})),
|
||||||
targetToSourceMap,
|
targetToSourceMap,
|
||||||
sourceToTargetMap,
|
sourceToTargetMap,
|
||||||
});
|
});
|
||||||
|
@ -243,7 +254,11 @@ const boot: tslib.server.PluginModuleFactory = ({ typescript: ts }) => {
|
||||||
|
|
||||||
getSyntacticDiagnostics(fileName: string): ts.DiagnosticWithLocation[] {
|
getSyntacticDiagnostics(fileName: string): ts.DiagnosticWithLocation[] {
|
||||||
const ds = this.inner.getSyntacticDiagnostics(fileName);
|
const ds = this.inner.getSyntacticDiagnostics(fileName);
|
||||||
return withFileName(fileName, () => ds, (fixup) => fixup.diagnostics(ds));
|
return withFileName(fileName, () => ds, (fixup) => {
|
||||||
|
const fixedUp = fixup.diagnostics(ds);
|
||||||
|
fixedUp.push(... fixup.info.diagnostics);
|
||||||
|
return fixedUp;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getSemanticDiagnostics(fileName: string): ts.Diagnostic[] {
|
getSemanticDiagnostics(fileName: string): ts.Diagnostic[] {
|
||||||
|
|
|
@ -8,7 +8,7 @@ import crypto from 'crypto';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
import { compile } from '@syndicate-lang/compiler';
|
import { compile, Syntax } from '@syndicate-lang/compiler';
|
||||||
import { SpanIndex, Token } from '@syndicate-lang/compiler/lib/syntax';
|
import { SpanIndex, Token } from '@syndicate-lang/compiler/lib/syntax';
|
||||||
|
|
||||||
export type CommandLineArguments = {
|
export type CommandLineArguments = {
|
||||||
|
@ -150,8 +150,8 @@ function runBuildOnce(options: CommandLineArguments, toWatch = new ToWatch()) {
|
||||||
source: inputText,
|
source: inputText,
|
||||||
name: fileName,
|
name: fileName,
|
||||||
typescript: true,
|
typescript: true,
|
||||||
emitError: m => {
|
emitError: (m, start, end) => {
|
||||||
console.error(m);
|
console.error(`${Syntax.formatPos(start)}-${Syntax.formatPos(end)}: ${m}`);
|
||||||
onError?.(m);
|
onError?.(m);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue