mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
73 lines
2.4 KiB
TypeScript
73 lines
2.4 KiB
TypeScript
|
/**
|
||
|
*
|
||
|
* This represents a formula supported under SQL for on-demand tables. This is currently
|
||
|
* a very small subset of the formulas supported by the data engine for regular tables.
|
||
|
*
|
||
|
* The following kinds of formula are supported:
|
||
|
* $refColId.colId [where colId is not itself a formula]
|
||
|
* $colId [where colId is not itself a formula]
|
||
|
* NNN [a non-negative integer]
|
||
|
* TODO: support a broader range of formula, by adding a parser or reusing Python parser.
|
||
|
* An argument for reusing Python parser: wwe already do substantial parsing of the formula code.
|
||
|
* E.g. Python does such amazing things as handle updating the formula when any of the columns
|
||
|
* referred to in Foo.lookup(bar=$baz).blah get updated.
|
||
|
*
|
||
|
*/
|
||
|
export type Formula = LiteralNumberFormula | ColumnFormula | ForeignColumnFormula | FormulaError;
|
||
|
|
||
|
// A simple copy of another column. E.g. "$Person"
|
||
|
export interface ColumnFormula {
|
||
|
kind: 'column';
|
||
|
colId: string;
|
||
|
}
|
||
|
|
||
|
// A copy of a column in another table (via a reference column). E.g. "$Person.FirstName"
|
||
|
export interface ForeignColumnFormula {
|
||
|
kind: 'foreignColumn';
|
||
|
colId: string;
|
||
|
refColId: string;
|
||
|
}
|
||
|
|
||
|
export interface LiteralNumberFormula {
|
||
|
kind: 'literalNumber';
|
||
|
value: number;
|
||
|
}
|
||
|
|
||
|
// A formula that couldn't be parsed.
|
||
|
export interface FormulaError {
|
||
|
kind: 'error';
|
||
|
msg: string;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Convert a string to a parsed formula. Regexes are adequate for the very few
|
||
|
* supported formulas, but once the syntax is at all flexible a proper parser will
|
||
|
* be needed. In principle, it might make sense to support python syntax, for
|
||
|
* compatibility with the data engine, but compatibility in corner cases will be
|
||
|
* fiddly given underlying differences between sqlite and python.
|
||
|
*/
|
||
|
export function parseFormula(txt: string): Formula {
|
||
|
// Formula of form: $x.y
|
||
|
let m = txt.match(/^\$([a-z]\w*)\.([a-z]\w*)$/i);
|
||
|
if (m) {
|
||
|
return {kind: 'foreignColumn', refColId: m[1], colId: m[2]};
|
||
|
}
|
||
|
|
||
|
// Formula of form: $x
|
||
|
m = txt.match(/^\$([a-z][a-z_0-9]*)$/i);
|
||
|
if (m) {
|
||
|
return {kind: 'column', colId: m[1]};
|
||
|
}
|
||
|
|
||
|
// Formula of form: NNN
|
||
|
m = txt.match(/^[0-9]+$/);
|
||
|
if (m) {
|
||
|
const value = parseInt(txt, 10);
|
||
|
if (isNaN(value)) { return {kind: 'error', msg: 'Cannot parse integer'}; }
|
||
|
return {kind: 'literalNumber', value};
|
||
|
}
|
||
|
|
||
|
// Everything else is an error.
|
||
|
return {kind: 'error', msg: 'Formula not supported'};
|
||
|
}
|