2021-01-18 22:11:53 +00:00
|
|
|
import { Pos } from './position.js';
|
2021-01-14 12:09:53 +00:00
|
|
|
|
|
|
|
export enum TokenType {
|
|
|
|
SPACE,
|
|
|
|
NEWLINE,
|
|
|
|
ATOM,
|
|
|
|
STRING,
|
|
|
|
OPEN,
|
|
|
|
CLOSE,
|
|
|
|
}
|
|
|
|
|
2021-01-18 22:11:53 +00:00
|
|
|
export interface TokenBase {
|
2021-01-14 12:09:53 +00:00
|
|
|
start: Pos;
|
|
|
|
end: Pos;
|
2021-01-22 23:12:11 +00:00
|
|
|
synthetic?: boolean; // default: false
|
2021-01-18 22:11:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface Token extends TokenBase {
|
|
|
|
type: TokenType;
|
2021-01-14 12:09:53 +00:00
|
|
|
text: string;
|
|
|
|
}
|
|
|
|
|
2021-01-18 22:11:53 +00:00
|
|
|
export interface Group extends TokenBase {
|
|
|
|
open: Token;
|
|
|
|
close: Token | null;
|
2021-01-14 12:09:53 +00:00
|
|
|
items: Items;
|
|
|
|
}
|
|
|
|
|
|
|
|
export type Item = Token | Group;
|
|
|
|
export type Items = Array<Item>;
|
|
|
|
|
2021-01-18 22:11:53 +00:00
|
|
|
export type GroupInProgress = Omit<Group, 'end'>;
|
|
|
|
|
|
|
|
export function finishGroup(g: GroupInProgress, end: Pos): Group {
|
|
|
|
return { ... g, end };
|
2021-01-14 12:09:53 +00:00
|
|
|
}
|
|
|
|
|
2021-01-18 22:11:53 +00:00
|
|
|
export function makeGroup(open: Token, items: Array<Item>, close: Token): Group {
|
|
|
|
return { start: open.start, open, end: close.end, close, items };
|
2021-01-14 12:09:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export function isSpace(i: Item): i is Token {
|
|
|
|
return isTokenType(i, TokenType.SPACE) || isTokenType(i, TokenType.NEWLINE);
|
|
|
|
}
|
|
|
|
|
|
|
|
export function isGroup(i: Item): i is Group {
|
|
|
|
return i && ('items' in i);
|
|
|
|
}
|
|
|
|
|
|
|
|
export function isToken(i: Item): i is Token {
|
|
|
|
return i && ('type' in i);
|
|
|
|
}
|
|
|
|
|
|
|
|
export function isTokenType(i: Item, t: TokenType): i is Token {
|
|
|
|
return isToken(i) && i.type === t;
|
|
|
|
}
|
|
|
|
|
|
|
|
export type ItemTextOptions = {
|
|
|
|
missing?: string,
|
|
|
|
color?: boolean,
|
|
|
|
};
|
|
|
|
|
2021-01-16 16:46:18 +00:00
|
|
|
export function foldItems<T>(i: Items,
|
|
|
|
fToken: (t: Token) => T,
|
2021-01-18 22:11:53 +00:00
|
|
|
fGroup: (g: Group, t: T, k: (t: Token) => T) => T,
|
2021-01-16 16:46:18 +00:00
|
|
|
fItems: (ts: T[]) => T): T
|
|
|
|
{
|
|
|
|
const walk = (i: Item): T => {
|
2021-01-14 12:09:53 +00:00
|
|
|
if (isGroup(i)) {
|
2021-01-18 22:11:53 +00:00
|
|
|
return fGroup(i, fItems(i.items.map(walk)), walk);
|
2021-01-14 12:09:53 +00:00
|
|
|
} else {
|
2021-01-16 16:46:18 +00:00
|
|
|
return fToken(i);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return fItems(i.map(walk));
|
|
|
|
}
|
|
|
|
|
|
|
|
export function itemText(items: Items, options: ItemTextOptions = {}): string {
|
|
|
|
return foldItems(
|
|
|
|
items,
|
|
|
|
i => {
|
2021-01-14 12:09:53 +00:00
|
|
|
if (options.color ?? false) {
|
|
|
|
switch (i.type) {
|
|
|
|
case TokenType.SPACE:
|
|
|
|
case TokenType.NEWLINE:
|
|
|
|
return '\x1b[31m' + i.text + '\x1b[0m';
|
|
|
|
case TokenType.STRING:
|
|
|
|
return '\x1b[34m' + i.text + '\x1b[0m';
|
|
|
|
default:
|
|
|
|
return i.text;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return i.text;
|
|
|
|
}
|
2021-01-16 16:46:18 +00:00
|
|
|
},
|
2021-01-18 22:11:53 +00:00
|
|
|
(g, inner, k) => k(g.open) + inner + (g.close ? k(g.close) : options.missing ?? ''),
|
2021-01-16 16:46:18 +00:00
|
|
|
strs => strs.join(''));
|
2021-01-14 12:09:53 +00:00
|
|
|
}
|