Add line number function to JS document parser.
This commit is contained in:
parent
8dca90037a
commit
70200a4091
1522
packages/js/package-lock.json
generated
Normal file
1522
packages/js/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@terrace-lang/js",
|
"name": "@terrace-lang/js",
|
||||||
"description": "Terrace is a simple structured data syntax for configuration, content authoring, and DSLs.",
|
"description": "Terrace is a simple structured data syntax for configuration, content authoring, and DSLs.",
|
||||||
"version": "0.1.1",
|
"version": "0.1.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
import type { Reader } from './readers/reader.js'
|
import type { Reader } from "./readers/reader.js";
|
||||||
import { createLineData, parseLine } from './parser.js'
|
import { createLineData, parseLine } from "./parser.js";
|
||||||
|
|
||||||
// Container for a handful of convenience functions for parsing documents
|
// Container for a handful of convenience functions for parsing documents
|
||||||
// Obtained from useDocument() below
|
// Obtained from useDocument() below
|
||||||
export type Document = {
|
export type Document = {
|
||||||
next: (levelScope?: number) => Promise<boolean>
|
next: (levelScope?: number) => Promise<boolean>;
|
||||||
level: () => number,
|
level: () => number;
|
||||||
line: (startOffset?: number) => string,
|
lineNumber: () => number;
|
||||||
head: () => string,
|
line: (startOffset?: number) => string;
|
||||||
tail: () => string,
|
head: () => string;
|
||||||
match: (matchValue: string) => boolean
|
tail: () => string;
|
||||||
}
|
match: (matchValue: string) => boolean;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a simple set of convenience functions around parseLine for more ergonomic parsing of Terrace documents
|
* Provides a simple set of convenience functions around parseLine for more ergonomic parsing of Terrace documents
|
||||||
@ -19,15 +20,19 @@ export type Document = {
|
|||||||
* @param {String} indent The character used for indentation in the document. Only a single character is permitted
|
* @param {String} indent The character used for indentation in the document. Only a single character is permitted
|
||||||
* @returns {Document} A set of convenience functions for iterating through and parsing a document line by line
|
* @returns {Document} A set of convenience functions for iterating through and parsing a document line by line
|
||||||
*/
|
*/
|
||||||
export function useDocument (reader: Reader, indent: string = ' '): Document {
|
export function useDocument(reader: Reader, indent: string = " "): Document {
|
||||||
if (indent.length !== 1) throw new Error(`Terrace currently only allows single-character indent strings - you passed "${indent}"`)
|
if (indent.length !== 1)
|
||||||
|
throw new Error(
|
||||||
|
`Terrace currently only allows single-character indent strings - you passed "${indent}"`
|
||||||
|
);
|
||||||
|
|
||||||
const lineData = createLineData(indent)
|
const lineData = createLineData(indent);
|
||||||
let currLine = ''
|
let currLine = "";
|
||||||
|
let currLineNumber = -1;
|
||||||
|
|
||||||
// If `repeatCurrentLine` is `true`, the following call to `next()` will repeat the current line in
|
// If `repeatCurrentLine` is `true`, the following call to `next()` will repeat the current line in
|
||||||
// the document and set `repeatCurrentLine` back to `false`
|
// the document and set `repeatCurrentLine` back to `false`
|
||||||
let repeatCurrentLine = false
|
let repeatCurrentLine = false;
|
||||||
/**
|
/**
|
||||||
* Advances the current position in the terrace document and populates lineData
|
* Advances the current position in the terrace document and populates lineData
|
||||||
* with the parsed information from that line
|
* with the parsed information from that line
|
||||||
@ -54,28 +59,29 @@ export function useDocument (reader: Reader, indent: string = ' '): Document {
|
|||||||
async function next(levelScope: number = -1): Promise<boolean> {
|
async function next(levelScope: number = -1): Promise<boolean> {
|
||||||
// Repeat the current line instead of parsing a new one if the previous call to next()
|
// Repeat the current line instead of parsing a new one if the previous call to next()
|
||||||
// determined the current line to be out of its scope.
|
// determined the current line to be out of its scope.
|
||||||
if (repeatCurrentLine) repeatCurrentLine = false
|
if (repeatCurrentLine) repeatCurrentLine = false;
|
||||||
// Otherwise parse the line normally.
|
// Otherwise parse the line normally.
|
||||||
else {
|
else {
|
||||||
// Load the next line from the line reader.
|
// Load the next line from the line reader.
|
||||||
const line = await reader()
|
const line = await reader();
|
||||||
// If there are no more lines, bail out.
|
// If there are no more lines, bail out.
|
||||||
if (line == null) return false
|
if (line == null) return false;
|
||||||
|
|
||||||
// Populate lineData with parsed information from the current line.
|
// Populate lineData with parsed information from the current line.
|
||||||
currLine = line
|
currLine = line;
|
||||||
parseLine(currLine, lineData)
|
currLineNumber++;
|
||||||
|
parseLine(currLine, lineData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we shouldn't be handling this line, make the following call to next() repeat the current line.
|
// If we shouldn't be handling this line, make the following call to next() repeat the current line.
|
||||||
// Allows a child loop to look forward, determine that the next line will be outside its purview,
|
// Allows a child loop to look forward, determine that the next line will be outside its purview,
|
||||||
// and return control to the calling loop transparently without additional logic.
|
// and return control to the calling loop transparently without additional logic.
|
||||||
if (level() <= levelScope) {
|
if (level() <= levelScope) {
|
||||||
repeatCurrentLine = true
|
repeatCurrentLine = true;
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,7 +97,14 @@ export function useDocument (reader: Reader, indent: string = ' '): Document {
|
|||||||
* ```
|
* ```
|
||||||
* @returns {number} The indent level of the current line
|
* @returns {number} The indent level of the current line
|
||||||
*/
|
*/
|
||||||
const level = (): number => lineData.level
|
const level = (): number => lineData.level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current line number, zero-indexed from first line read.
|
||||||
|
* @returns {number} The current line number, starting from zero.
|
||||||
|
*/
|
||||||
|
const lineNumber = (): number => currLineNumber;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a string with the current line contents. Skips all indent characters by default, but this can be configured with `startOffset`
|
* Get a string with the current line contents. Skips all indent characters by default, but this can be configured with `startOffset`
|
||||||
*
|
*
|
||||||
@ -109,7 +122,9 @@ export function useDocument (reader: Reader, indent: string = ' '): Document {
|
|||||||
* @param {number} startOffset How many indent characters to skip before outputting the line contents. Defaults to the current indent level
|
* @param {number} startOffset How many indent characters to skip before outputting the line contents. Defaults to the current indent level
|
||||||
* @returns {string} The line contents starting from `startOffset`
|
* @returns {string} The line contents starting from `startOffset`
|
||||||
*/
|
*/
|
||||||
const line = (startOffset: number = lineData.level): string => currLine.slice(startOffset)
|
const line = (startOffset: number = lineData.level): string =>
|
||||||
|
currLine.slice(startOffset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the first "word" of a line, starting from the first non-indent character to the first space or end of the line
|
* Get the first "word" of a line, starting from the first non-indent character to the first space or end of the line
|
||||||
* Often used for deciding how to parse a block.
|
* Often used for deciding how to parse a block.
|
||||||
@ -123,7 +138,9 @@ export function useDocument (reader: Reader, indent: string = ' '): Document {
|
|||||||
* ```
|
* ```
|
||||||
* @returns {string} The `head` portion (first word) of a line
|
* @returns {string} The `head` portion (first word) of a line
|
||||||
*/
|
*/
|
||||||
const head = (): string => currLine.slice(lineData.offsetHead, lineData.offsetTail)
|
const head = (): string =>
|
||||||
|
currLine.slice(lineData.offsetHead, lineData.offsetTail);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all text following the first "word" of a line, starting from the first character after the space at the end of `head()`
|
* Get all text following the first "word" of a line, starting from the first character after the space at the end of `head()`
|
||||||
*
|
*
|
||||||
@ -136,7 +153,7 @@ export function useDocument (reader: Reader, indent: string = ' '): Document {
|
|||||||
* ```
|
* ```
|
||||||
* @returns {string} The remainder of the line following the `head()` portion, with no leading space
|
* @returns {string} The remainder of the line following the `head()` portion, with no leading space
|
||||||
*/
|
*/
|
||||||
const tail = (): string => currLine.slice(lineData.offsetTail + 1) // Skip the space
|
const tail = (): string => currLine.slice(lineData.offsetTail + 1); // Skip the space
|
||||||
/**
|
/**
|
||||||
* Quickly check if the current line head matches a specified value
|
* Quickly check if the current line head matches a specified value
|
||||||
*
|
*
|
||||||
@ -154,14 +171,15 @@ export function useDocument (reader: Reader, indent: string = ' '): Document {
|
|||||||
* @param {string} matchValue A string to check against `head()` for equality
|
* @param {string} matchValue A string to check against `head()` for equality
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
const match = (matchValue: string): boolean => matchValue === head()
|
const match = (matchValue: string): boolean => matchValue === head();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
next,
|
next,
|
||||||
level,
|
level,
|
||||||
line,
|
line,
|
||||||
|
lineNumber,
|
||||||
head,
|
head,
|
||||||
tail,
|
tail,
|
||||||
match
|
match,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user