export interface FunctionSpec { name: string; kind: 'scalar' | 'aggregate'; minArgs: number; maxArgs: number; signature: string; description: string; } /** * The formula surface area. Anything not listed here is rejected at compile * time, so a typo never reaches SQLite as a bare function call. */ export const FUNCTIONS: FunctionSpec[] = [ // aggregates — first argument is `table.Column` (or just `table` for COUNT) { name: 'SUM', kind: 'aggregate', minArgs: 1, maxArgs: 2, signature: 'SUM(table.Column [, "LinkColumn"])', description: 'Total of a column across linked rows.' }, { name: 'COUNT', kind: 'aggregate', minArgs: 1, maxArgs: 2, signature: 'COUNT(table [, "LinkColumn"])', description: 'Number of linked rows.' }, { name: 'AVG', kind: 'aggregate', minArgs: 1, maxArgs: 2, signature: 'AVG(table.Column [, "LinkColumn"])', description: 'Average across linked rows.' }, { name: 'MIN', kind: 'aggregate', minArgs: 1, maxArgs: 2, signature: 'MIN(table.Column) or MIN(a, b, …)', description: 'Smallest linked value, or smallest of the arguments.' }, { name: 'MAX', kind: 'aggregate', minArgs: 1, maxArgs: 2, signature: 'MAX(table.Column) or MAX(a, b, …)', description: 'Largest linked value, or largest of the arguments.' }, // logic { name: 'IF', kind: 'scalar', minArgs: 2, maxArgs: 3, signature: 'IF(test, then [, else])', description: 'Branch on a condition.' }, { name: 'COALESCE', kind: 'scalar', minArgs: 1, maxArgs: 8, signature: 'COALESCE(a, b, …)', description: 'First value that is not empty.' }, { name: 'ISBLANK', kind: 'scalar', minArgs: 1, maxArgs: 1, signature: 'ISBLANK(value)', description: 'True when the value is empty or null.' }, // numbers { name: 'ROUND', kind: 'scalar', minArgs: 1, maxArgs: 2, signature: 'ROUND(number [, digits])', description: 'Round to the given number of digits.' }, { name: 'ABS', kind: 'scalar', minArgs: 1, maxArgs: 1, signature: 'ABS(number)', description: 'Absolute value.' }, // text { name: 'CONCAT', kind: 'scalar', minArgs: 1, maxArgs: 8, signature: 'CONCAT(a, b, …)', description: 'Join values into text.' }, { name: 'UPPER', kind: 'scalar', minArgs: 1, maxArgs: 1, signature: 'UPPER(text)', description: 'Uppercase.' }, { name: 'LOWER', kind: 'scalar', minArgs: 1, maxArgs: 1, signature: 'LOWER(text)', description: 'Lowercase.' }, { name: 'TRIM', kind: 'scalar', minArgs: 1, maxArgs: 1, signature: 'TRIM(text)', description: 'Strip surrounding whitespace.' }, { name: 'LENGTH', kind: 'scalar', minArgs: 1, maxArgs: 1, signature: 'LENGTH(text)', description: 'Number of characters.' }, // dates { name: 'TODAY', kind: 'scalar', minArgs: 0, maxArgs: 0, signature: 'TODAY()', description: "Today's date, as YYYY-MM-DD." }, { name: 'NOW', kind: 'scalar', minArgs: 0, maxArgs: 0, signature: 'NOW()', description: 'Current UTC timestamp.' }, { name: 'DATEDIFF', kind: 'scalar', minArgs: 2, maxArgs: 2, signature: 'DATEDIFF(later, earlier)', description: 'Whole days between two dates.' }, { name: 'YEAR', kind: 'scalar', minArgs: 1, maxArgs: 1, signature: 'YEAR(date)', description: 'Four-digit year.' }, { name: 'MONTH', kind: 'scalar', minArgs: 1, maxArgs: 1, signature: 'MONTH(date)', description: 'Month number, 1–12.' }, { name: 'DAY', kind: 'scalar', minArgs: 1, maxArgs: 1, signature: 'DAY(date)', description: 'Day of month.' }, ]; export const FUNCTIONS_BY_NAME = new Map(FUNCTIONS.map((f) => [f.name, f])); /** Aggregates may be interpreted as a rollup over a linked table. */ export const AGGREGATE_NAMES = new Set( FUNCTIONS.filter((f) => f.kind === 'aggregate').map((f) => f.name), );