mirror of
https://github.com/Horizontal42/summarum.git
synced 2026-11-10 15:42:01 +03:00
split lexLine's number/date matching into named helpers
lexLine had grown into one long function trying dates, hex/bin/oct, grouped numbers, decimals and xrefs inline. Extracted each into a tryLex* helper with the same fallback order, so the driving loop reads as a priority list instead of nested ifs.
This commit is contained in:
parent
f634b1858e
commit
f2deaa1b94
1 changed files with 104 additions and 93 deletions
|
|
@ -34,6 +34,99 @@ function validDateMs(y: number, mo: number, d: number): number | null {
|
|||
: null;
|
||||
}
|
||||
|
||||
function tryLexDate(rest: string, i: number, dateOrder: DateOrder): Lex | null {
|
||||
const iso = /^(\d{4})-(\d{2})-(\d{2})(?!\d)/.exec(rest);
|
||||
if (iso) {
|
||||
const ms = validDateMs(Number(iso[1]), Number(iso[2]), Number(iso[3]));
|
||||
if (ms !== null) {
|
||||
return { type: "date", raw: iso[0], start: i, end: i + iso[0].length, dateMs: ms };
|
||||
}
|
||||
}
|
||||
const dm = SLASH_DOT_DATE_RE.exec(rest);
|
||||
if (dm) {
|
||||
const a = Number(dm[1]);
|
||||
const b = Number(dm[3]);
|
||||
// a>12 must be the day, b>12 must be the month; both ≤12 → configured order
|
||||
const dayFirst = a > 12 ? true : b > 12 ? false : dateOrder === "dmy";
|
||||
const y = Number(dm[4]);
|
||||
const ms = dayFirst ? validDateMs(y, b, a) : validDateMs(y, a, b);
|
||||
if (ms !== null) {
|
||||
return { type: "date", raw: dm[0], start: i, end: i + dm[0].length, dateMs: ms };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function tryLexBaseNumber(rest: string, i: number): Lex | null {
|
||||
const hex = /^0[xX][0-9a-fA-F]+/.exec(rest);
|
||||
if (hex) {
|
||||
return { type: "num", raw: hex[0], start: i, end: i + hex[0].length, value: new Decimal(parseInt(hex[0], 16)), repr: "hex" };
|
||||
}
|
||||
const bin = /^0[bB][01]+/.exec(rest);
|
||||
if (bin) {
|
||||
return { type: "num", raw: bin[0], start: i, end: i + bin[0].length, value: new Decimal(parseInt(bin[0].slice(2), 2)), repr: "binary" };
|
||||
}
|
||||
const oct = /^0[oO][0-7]+/.exec(rest);
|
||||
if (oct) {
|
||||
return { type: "num", raw: oct[0], start: i, end: i + oct[0].length, value: new Decimal(parseInt(oct[0].slice(2), 8)), repr: "octal" };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function tryLexDecimalNumber(rest: string, i: number): Lex | null {
|
||||
let raw: string | null = null;
|
||||
let cleaned = "";
|
||||
const sp = NUM_SPACE_GROUPED_RE.exec(rest);
|
||||
const cg = sp ? null : NUM_COMMA_GROUPED_RE.exec(rest);
|
||||
const dc = sp || cg ? null : NUM_DECIMAL_COMMA_RE.exec(rest);
|
||||
const pl = sp || cg || dc ? null : NUM_PLAIN_RE.exec(rest);
|
||||
if (sp) {
|
||||
raw = sp[0];
|
||||
cleaned = raw.replace(/[ ]/g, "").replace(",", ".");
|
||||
} else if (cg) {
|
||||
raw = cg[0];
|
||||
cleaned = raw.replace(/,/g, "");
|
||||
} else if (dc) {
|
||||
raw = dc[0];
|
||||
cleaned = raw.replace(",", ".");
|
||||
} else if (pl) {
|
||||
raw = pl[0];
|
||||
cleaned = raw;
|
||||
}
|
||||
if (raw) {
|
||||
return { type: "num", raw, start: i, end: i + raw.length, value: new Decimal(cleaned), repr: "decimal" };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function tryLexDotNumber(rest: string, i: number): Lex | null {
|
||||
if (/^\.\d/.test(rest)) {
|
||||
const m = /^\.\d+/.exec(rest)!;
|
||||
return { type: "num", raw: m[0], start: i, end: i + m[0].length, value: new Decimal(m[0]), repr: "decimal" };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function tryLexXRef(rest: string, i: number): Lex | null {
|
||||
const bracket = /^@\[([^\]]+)\]\.([\p{L}_][\p{L}\d_]*)/u.exec(rest);
|
||||
if (bracket) {
|
||||
return { type: "xref", raw: bracket[0], start: i, end: i + bracket[0].length, sheet: bracket[1].trim(), key: bracket[2] };
|
||||
}
|
||||
const bare = /^@([\p{L}_][\p{L}\d_]*)\.([\p{L}_][\p{L}\d_]*)/u.exec(rest);
|
||||
if (bare) {
|
||||
return { type: "xref", raw: bare[0], start: i, end: i + bare[0].length, sheet: bare[1], key: bare[2] };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function tryLexWord(rest: string, i: number): Lex | null {
|
||||
const word = WORD_RE.exec(rest);
|
||||
if (word) {
|
||||
return { type: "word", raw: word[0], start: i, end: i + word[0].length };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function lexLine(text: string, dateOrder: DateOrder = "dmy"): Lex[] {
|
||||
const out: Lex[] = [];
|
||||
let i = 0;
|
||||
|
|
@ -45,103 +138,21 @@ export function lexLine(text: string, dateOrder: DateOrder = "dmy"): Lex[] {
|
|||
}
|
||||
const rest = text.slice(i);
|
||||
|
||||
let lex: Lex | null = null;
|
||||
if (ch >= "0" && ch <= "9") {
|
||||
const iso = /^(\d{4})-(\d{2})-(\d{2})(?!\d)/.exec(rest);
|
||||
if (iso) {
|
||||
const ms = validDateMs(Number(iso[1]), Number(iso[2]), Number(iso[3]));
|
||||
if (ms !== null) {
|
||||
out.push({ type: "date", raw: iso[0], start: i, end: i + iso[0].length, dateMs: ms });
|
||||
i += iso[0].length;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const dm = SLASH_DOT_DATE_RE.exec(rest);
|
||||
if (dm) {
|
||||
const a = Number(dm[1]);
|
||||
const b = Number(dm[3]);
|
||||
// a>12 must be the day, b>12 must be the month; both ≤12 → configured order
|
||||
const dayFirst = a > 12 ? true : b > 12 ? false : dateOrder === "dmy";
|
||||
const y = Number(dm[4]);
|
||||
const ms = dayFirst ? validDateMs(y, b, a) : validDateMs(y, a, b);
|
||||
if (ms !== null) {
|
||||
out.push({ type: "date", raw: dm[0], start: i, end: i + dm[0].length, dateMs: ms });
|
||||
i += dm[0].length;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
lex = tryLexDate(rest, i, dateOrder) || tryLexBaseNumber(rest, i) || tryLexDecimalNumber(rest, i);
|
||||
} else if (ch === ".") {
|
||||
lex = tryLexDotNumber(rest, i);
|
||||
} else if (ch === "@") {
|
||||
lex = tryLexXRef(rest, i);
|
||||
}
|
||||
if (ch >= "0" && ch <= "9") {
|
||||
const hex = /^0[xX][0-9a-fA-F]+/.exec(rest);
|
||||
if (hex) {
|
||||
out.push({ type: "num", raw: hex[0], start: i, end: i + hex[0].length, value: new Decimal(parseInt(hex[0], 16)), repr: "hex" });
|
||||
i += hex[0].length;
|
||||
continue;
|
||||
}
|
||||
const bin = /^0[bB][01]+/.exec(rest);
|
||||
if (bin) {
|
||||
out.push({ type: "num", raw: bin[0], start: i, end: i + bin[0].length, value: new Decimal(parseInt(bin[0].slice(2), 2)), repr: "binary" });
|
||||
i += bin[0].length;
|
||||
continue;
|
||||
}
|
||||
const oct = /^0[oO][0-7]+/.exec(rest);
|
||||
if (oct) {
|
||||
out.push({ type: "num", raw: oct[0], start: i, end: i + oct[0].length, value: new Decimal(parseInt(oct[0].slice(2), 8)), repr: "octal" });
|
||||
i += oct[0].length;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (ch >= "0" && ch <= "9") {
|
||||
let raw: string | null = null;
|
||||
let cleaned = "";
|
||||
const sp = NUM_SPACE_GROUPED_RE.exec(rest);
|
||||
const cg = sp ? null : NUM_COMMA_GROUPED_RE.exec(rest);
|
||||
const dc = sp || cg ? null : NUM_DECIMAL_COMMA_RE.exec(rest);
|
||||
const pl = sp || cg || dc ? null : NUM_PLAIN_RE.exec(rest);
|
||||
if (sp) {
|
||||
raw = sp[0];
|
||||
cleaned = raw.replace(/[ ]/g, "").replace(",", ".");
|
||||
} else if (cg) {
|
||||
raw = cg[0];
|
||||
cleaned = raw.replace(/,/g, "");
|
||||
} else if (dc) {
|
||||
raw = dc[0];
|
||||
cleaned = raw.replace(",", ".");
|
||||
} else if (pl) {
|
||||
raw = pl[0];
|
||||
cleaned = raw;
|
||||
}
|
||||
if (raw) {
|
||||
out.push({ type: "num", raw, start: i, end: i + raw.length, value: new Decimal(cleaned), repr: "decimal" });
|
||||
i += raw.length;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (ch === "." && /^\.\d/.test(rest)) {
|
||||
const m = /^\.\d+/.exec(rest)!;
|
||||
out.push({ type: "num", raw: m[0], start: i, end: i + m[0].length, value: new Decimal(m[0]), repr: "decimal" });
|
||||
i += m[0].length;
|
||||
continue;
|
||||
if (!lex) {
|
||||
lex = tryLexWord(rest, i);
|
||||
}
|
||||
|
||||
if (ch === "@") {
|
||||
const bracket = /^@\[([^\]]+)\]\.([\p{L}_][\p{L}\d_]*)/u.exec(rest);
|
||||
if (bracket) {
|
||||
out.push({ type: "xref", raw: bracket[0], start: i, end: i + bracket[0].length, sheet: bracket[1].trim(), key: bracket[2] });
|
||||
i += bracket[0].length;
|
||||
continue;
|
||||
}
|
||||
const bare = /^@([\p{L}_][\p{L}\d_]*)\.([\p{L}_][\p{L}\d_]*)/u.exec(rest);
|
||||
if (bare) {
|
||||
out.push({ type: "xref", raw: bare[0], start: i, end: i + bare[0].length, sheet: bare[1], key: bare[2] });
|
||||
i += bare[0].length;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const word = WORD_RE.exec(rest);
|
||||
if (word) {
|
||||
out.push({ type: "word", raw: word[0], start: i, end: i + word[0].length });
|
||||
i += word[0].length;
|
||||
if (lex) {
|
||||
out.push(lex);
|
||||
i += lex.raw.length;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue