diff --git a/src/bin/bolt.ts b/src/bin/bolt.ts index 275ac8dc4..3b9c02e7f 100644 --- a/src/bin/bolt.ts +++ b/src/bin/bolt.ts @@ -8,7 +8,7 @@ import fs from "fs" import yargs from "yargs" import { Diagnostics, UnexpectedCharDiagnostic, UnexpectedTokenDiagnostic } from "../diagnostics" -import { Punctuator, Scanner } from "../scanner" +import { Punctuator, ScanError, Scanner } from "../scanner" import { ParseError, Parser } from "../parser" import { Checker } from "../checker" import { TextFile } from "../cst" @@ -44,11 +44,15 @@ yargs try { sourceFile = parser.parseSourceFile(); } catch (error) { - if (!(error instanceof ParseError)) { - throw error; + if (error instanceof ParseError) { + diagnostics.add(new UnexpectedTokenDiagnostic(error.file, error.actual, error.expected)); + return; } - diagnostics.add(new UnexpectedTokenDiagnostic(error.file, error.actual, error.expected)); - return; + if (error instanceof ScanError) { + diagnostics.add(new UnexpectedCharDiagnostic(error.file, error.position, error.actual)); + return; + } + throw error; } sourceFile.setParents(); //debug(sourceFile.toJSON()); diff --git a/src/diagnostics.ts b/src/diagnostics.ts index 3ed721a24..20ad3a8c7 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -40,7 +40,7 @@ export class UnexpectedCharDiagnostic { const endPos = this.position.clone(); endPos.advance(this.actual); return ANSI_FG_RED + ANSI_BOLD + 'error: ' + ANSI_RESET - + `unexpeced character '${this.actual}'.\n\n` + + `unexpeced character sequence '${this.actual}'.\n\n` + printExcerpt(this.file, new TextRange(this.position, endPos)) + '\n'; } diff --git a/src/scanner.ts b/src/scanner.ts index 6e020d41f..03b2c0e1c 100644 --- a/src/scanner.ts +++ b/src/scanner.ts @@ -28,6 +28,8 @@ import { Constructor, Integer, TextFile, + Dot, + DotDot, } from "./cst" import { Diagnostics, UnexpectedCharDiagnostic } from "./diagnostics" import { Stream, BufferedStream, assert } from "./util"; @@ -64,7 +66,17 @@ function isOperatorPart(ch: string): boolean { return /\+-*\/%^&|$<>!?=/.test(ch); } -class ScanError extends Error {} +export class ScanError extends Error { + + public constructor( + public file: TextFile, + public position: TextPosition, + public actual: string, + ) { + super(`Uncaught scanner error`); + } + +} export class Scanner extends BufferedStream { @@ -174,8 +186,7 @@ export class Scanner extends BufferedStream { case '\'': contents += '\''; break; case '\"': contents += '\"'; break; default: - this.diagnostics.add(new UnexpectedCharDiagnostic(this.file, startPos, c1)); - throw new ScanError(); + throw new ScanError(this.file, startPos, c1); } escaping = false; } else { @@ -203,7 +214,16 @@ export class Scanner extends BufferedStream { case '}': return new RBrace(startPos); case ',': return new Comma(startPos); case ':': return new Colon(startPos); - case '.': return new Dot(startPos); + case '.': { + const dots = c0 + this.takeWhile(ch => ch === '.'); + if (dots === '.') { + return new Dot(startPos); + } else if (dots === '..') { + return new DotDot(startPos); + } else { + throw new ScanError(this.file, startPos, dots); + } + } case '+': case '-': @@ -336,8 +356,7 @@ export class Scanner extends BufferedStream { default: // Nothing matched, so the current character is unrecognisable - this.diagnostics.add(new UnexpectedCharDiagnostic(this.file, startPos, c0)); - throw new ScanError(); + throw new ScanError(this.file, startPos, c0); } }