Skip to content

Commit

Permalink
fix: count line and column in syntax error
Browse files Browse the repository at this point in the history
  • Loading branch information
imrim12 committed Feb 4, 2023
1 parent 8981652 commit c0976d5
Show file tree
Hide file tree
Showing 12 changed files with 82 additions and 33 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"type": "module",
"version": "1.0.0-rc.5",
"version": "0.1.0",
"description": "Form marKup Language",
"packageManager": "[email protected]",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/adapters/vue/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@formkl/vue",
"version": "1.0.0-rc.5",
"version": "0.1.0",
"description": "A Vue adapter to generate usable Vue from component from Formkl syntax/schema",
"type": "module",
"exports": {
Expand Down
2 changes: 1 addition & 1 deletion packages/editor/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@formkl/editor",
"version": "1.0.0-rc.5",
"version": "0.1.0",
"description": "A universal Editor for Formkl using web component API",
"type": "module",
"exports": {
Expand Down
10 changes: 5 additions & 5 deletions packages/editor/src/extensions/lint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ function lintSyntax(view: EditorView): readonly Diagnostic[] {
try {
FormklParser.parse(content);
} catch (err: any) {
const errorIndex: Array<number> | undefined = err.message
.match(/at\s.*\./g)?.[0]
.replace(/at\s|\./g, "")
.split(":")
.map((n: string) => +n);
const errorIndex: Array<number> | undefined = /at \d+\:\d+/
.exec(err.message)[0] // Get "at lineNumber:columnNumber"
.replace(/at /g, "") // Remove "at "
.split(":") // Get ["lineNumber", "columnNumber"]
.map(Number);

diagnostics.push({
from: errorIndex ? view.state.doc.line(errorIndex[0]).from : view.state.doc.length,
Expand Down
2 changes: 1 addition & 1 deletion packages/elemento/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@formkl/elemento",
"version": "1.0.0-rc.5",
"version": "0.1.0",
"type": "module",
"exports": {
".": {
Expand Down
2 changes: 1 addition & 1 deletion packages/language/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "formkl",
"version": "1.0.0-rc.5",
"version": "0.1.0",
"description": "Form marKup Language",
"type": "module",
"exports": {
Expand Down
28 changes: 27 additions & 1 deletion packages/language/src/__tests__/capitalized-syntax.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import parser, { defineForm } from "../";
describe("Used with Capitalized syntax", () => {
it("should parse the form syntax correctly", () => {
const result = parser.parse(`Formkl {
Includes {
Has {
Text;
"Another" Text;
}
Expand Down Expand Up @@ -31,4 +31,30 @@ describe("Used with Capitalized syntax", () => {
}),
);
});

it("should show syntax error correctly", () => {
expect(() => {
parser.parse(`Formkl {
Includes {
Text;
"Another" Text;
}
}`);
}).toThrowError(/Unexpected token: "I" at 2:7/);
});

it("should show syntax error correctly", () => {
expect(() => {
parser.parse(`formkl "Your Formkl example" "This form is generated by the formkl adapter" {
multiple "Work Experience" has {
require text;
}
"Personal Information" as {
text;
multiple paragraph;
multiple text as "something";
}
}`);
}).toThrowError(/Unexpected token: "as" at 5:30, expected: "HAS"/);
});
});
24 changes: 14 additions & 10 deletions packages/language/src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,36 @@ import { capitalize } from "./utils/capitalize";
import { kebabCase } from "./utils/kebabCase";

export class Parser {
private _string: string;
private _tokenizer: Tokenizer;
public syntax: string;
public tokenizer: Tokenizer;

private _lookahead: Token | null;

/**
* Initializes the parser.
*/
constructor() {
this._string = "";
this._tokenizer = new Tokenizer("");
this.syntax = "";
this.tokenizer = new Tokenizer("");

this._lookahead = null;
}

/**
* Parse a Formkl syntax string into Formkl object
*/
parse(string: string): Formkl {
this._string = "";
this.syntax = "";
this._lookahead = null;

this._string = string;
this._tokenizer = new Tokenizer(this._string);
this.syntax = string;
this.tokenizer = new Tokenizer(this.syntax);

// Prime the tokenizer to obtain the first
// token which is our lookahead. The lookahead is
// used for predective parsing.

this._lookahead = this._tokenizer.getNextToken();
this._lookahead = this.tokenizer.getNextToken();

// Parse recursively starting from the main
// entry point, the Program:
Expand Down Expand Up @@ -646,11 +648,13 @@ export class Parser {
}

if (token.type !== tokenType) {
throw new SyntaxError(`Unexpected token: "${token.value}", expected: "${tokenType}"`);
throw new SyntaxError(
`Unexpected token: "${token.value}" at ${this.tokenizer.currentLine}:${this.tokenizer.currentColumn}, expected: "${tokenType}"`,
);
}

// Advance to next token.
this._lookahead = this._tokenizer.getNextToken();
this._lookahead = this.tokenizer.getNextToken();

return token;
}
Expand Down
37 changes: 28 additions & 9 deletions packages/language/src/tokenizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { createKeywordRegex } from "./utils/createKeywordRegex";
const Specs: Array<Spec> = [
// --------------------------------------
// Whitespace:
[/^\n/, null],

[/^\s+/, null],

// --------------------------------------
Expand Down Expand Up @@ -109,27 +111,32 @@ const Specs: Array<Spec> = [
* Lazily pulls a token from a stream.
*/
export class Tokenizer {
private _string: string;
private _cursor: number;
public syntax: string;
public cursor: number;

public currentLine: number;
public currentColumn: number;

/**
* Initializes the string.
*/
constructor(string: string) {
this._string = string;
this._cursor = 0; // track the position of each character
this.syntax = string;
this.cursor = 0; // track the position of each character
this.currentLine = 1;
this.currentColumn = 0;
}
/**
* Whether the tokenizer reached EOF.
*/
isEOF() {
return this._cursor === this._string.length;
return this.cursor === this.syntax.length;
}
/**
* Whether we still have more tokens.
*/
hasMoreTokens() {
return this._cursor < this._string.length;
return this.cursor < this.syntax.length;
}
/**
* Obtains next token.
Expand All @@ -138,7 +145,7 @@ export class Tokenizer {
if (!this.hasMoreTokens()) {
return null;
}
const string = this._string.slice(this._cursor);
const string = this.syntax.slice(this.cursor);

for (const [regexp, tokenType] of Specs) {
const tokenValue = this._match(regexp, string);
Expand All @@ -160,18 +167,30 @@ export class Tokenizer {
};
}

throw new SyntaxError(`Unexpected token: "${string[0]}"`);
throw new SyntaxError(
`Unexpected token: "${string[0]}" at ${this.currentLine}:${this.currentColumn}`,
);
}

/**
* Matches a token for a regular expression.
*/
_match(regexp: RegExp, string: string) {
const matched = regexp.exec(string);

if (matched === null) {
return null;
}
this._cursor += matched[0].length;

this.cursor += matched[0].length;

if (regexp.source === "^\\n" && matched !== null) {
this.currentLine++;
this.currentColumn = 0;
}

this.currentColumn += matched[0].length;

return matched[0];
}
}
2 changes: 1 addition & 1 deletion packages/plugins/vite/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@formkl/plugin-vite",
"description": "A Formkl Vite plugin to load .form files.",
"version": "1.0.0-rc.5",
"version": "0.1.0",
"type": "module",
"exports": {
".": {
Expand Down
2 changes: 1 addition & 1 deletion packages/plugins/webpack/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@formkl/plugin-webpack",
"version": "1.0.0-rc.5",
"version": "0.1.0",
"description": "A Formkl plugin to include .form file into Webpack build",
"type": "module",
"exports": {
Expand Down
2 changes: 1 addition & 1 deletion packages/shared/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@formkl/shared",
"version": "1.0.0-rc.5",
"version": "0.1.0",
"type": "module",
"main": "./src/index.ts",
"scripts": {
Expand Down

0 comments on commit c0976d5

Please sign in to comment.