[sheet] make query a primitive value
This commit is contained in:
parent
fd3629851d
commit
6e1a57c105
|
@ -754,8 +754,7 @@ nav.level.grid-2 {
|
|||
}
|
||||
|
||||
.sheet-result {
|
||||
background-color: $white-ter;
|
||||
color: $grey;
|
||||
border-left: 1px solid $grey-lighter;
|
||||
}
|
||||
|
||||
.sheet-editor .cm-editor {
|
||||
|
|
|
@ -15,22 +15,47 @@ import { functions } from "./sheet/functions";
|
|||
import { Environment, buildAST } from "./sheet/interpreter";
|
||||
import type { Posting } from "./utils";
|
||||
|
||||
function lint(editor: EditorView): Diagnostic[] {
|
||||
const diagnostics: Diagnostic[] = [];
|
||||
const tree = syntaxTree(editor.state);
|
||||
function lint(env: Environment) {
|
||||
return function (editor: EditorView): Diagnostic[] {
|
||||
const diagnostics: Diagnostic[] = [];
|
||||
const tree = syntaxTree(editor.state);
|
||||
|
||||
tree.cursor().iterate((node) => {
|
||||
if (node.type.isError) {
|
||||
diagnostics.push({
|
||||
from: node.from,
|
||||
to: node.to,
|
||||
severity: "error",
|
||||
message: "Invalid syntax"
|
||||
tree.cursor().iterate((node) => {
|
||||
if (node.type.isError) {
|
||||
diagnostics.push({
|
||||
from: node.from,
|
||||
to: node.to,
|
||||
severity: "error",
|
||||
message: "Invalid syntax"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
sheetEditorState.update((current) => {
|
||||
if (!current.pendingEval) {
|
||||
return current;
|
||||
}
|
||||
|
||||
const startTime = performance.now();
|
||||
let results = current.results;
|
||||
try {
|
||||
const ast = buildAST(tree.topNode, editor.state);
|
||||
results = ast.evaluate(env);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
// ignore
|
||||
}
|
||||
const endTime = performance.now();
|
||||
|
||||
return _.assign({}, current, {
|
||||
pendingEval: false,
|
||||
evalDuration: endTime - startTime,
|
||||
results
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return diagnostics;
|
||||
return diagnostics;
|
||||
};
|
||||
}
|
||||
|
||||
export function createEditor(
|
||||
|
@ -45,6 +70,8 @@ export function createEditor(
|
|||
env.scope = functions;
|
||||
env.postings = postings;
|
||||
|
||||
let firstLoad = true;
|
||||
|
||||
return new EditorView({
|
||||
extensions: [
|
||||
keymap.of(opts.keybindings || []),
|
||||
|
@ -53,28 +80,31 @@ export function createEditor(
|
|||
closeBrackets(),
|
||||
EditorView.contentAttributes.of({ "data-enable-grammarly": "false" }),
|
||||
sheetExtension(),
|
||||
linter(lint),
|
||||
linter(lint(env), {
|
||||
delay: 300,
|
||||
needsRefresh: () => {
|
||||
if (firstLoad) {
|
||||
firstLoad = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
lintGutter(),
|
||||
history(),
|
||||
EditorView.updateListener.of((viewUpdate) => {
|
||||
const doc = viewUpdate.state.doc.toString();
|
||||
const currentLine = viewUpdate.state.doc.lineAt(viewUpdate.state.selection.main.head);
|
||||
sheetEditorState.update((current) => {
|
||||
let results = current.results;
|
||||
let pendingEval = current.pendingEval;
|
||||
if (current.doc !== doc) {
|
||||
const tree = syntaxTree(viewUpdate.state);
|
||||
try {
|
||||
const ast = buildAST(tree.topNode, viewUpdate.state);
|
||||
results = ast.evaluate(env);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
// ignore
|
||||
}
|
||||
pendingEval = true;
|
||||
}
|
||||
|
||||
return _.assign({}, current, {
|
||||
results: results,
|
||||
doc: doc,
|
||||
pendingEval,
|
||||
doc,
|
||||
currentLine: currentLine.number,
|
||||
hasUnsavedChanges: current.hasUnsavedChanges || viewUpdate.docChanged,
|
||||
undoDepth: undoDepth(viewUpdate.state),
|
||||
|
|
|
@ -66,7 +66,7 @@ Sheet(Line(Expression(FunctionCall(Identifier))))
|
|||
|
||||
# Postings Search Query
|
||||
|
||||
postings(`amount > 0`)
|
||||
{amount > 0}
|
||||
|
||||
===>
|
||||
|
||||
|
@ -83,8 +83,24 @@ Sheet(Line(FunctionDefinition(Identifier,Parameters(Identifier),Expression(Binar
|
|||
|
||||
# Empty Postings Search Query
|
||||
|
||||
postings(``)
|
||||
{}
|
||||
|
||||
===>
|
||||
|
||||
Sheet(Line(Expression(Postings(SearchQueryString(Query)))))
|
||||
|
||||
# Query composition AND
|
||||
|
||||
{amount > 0} AND {date > [2024]}
|
||||
|
||||
===>
|
||||
|
||||
Sheet(Line(Expression(BinaryExpression(Expression(Postings(SearchQueryString("{",Query(Clause(Condition(Property(Amount),Operator(">"),Value(Number)))),"}"))),BinaryOperator,Expression(Postings(SearchQueryString("{",Query(Clause(Condition(Property(Date),Operator(">"),Value(DateValue)))),"}")))))))
|
||||
|
||||
# Query composition OR
|
||||
|
||||
{amount > 0} OR {date > [2024]}
|
||||
|
||||
===>
|
||||
|
||||
Sheet(Line(Expression(BinaryExpression(Expression(Postings(SearchQueryString("{",Query(Clause(Condition(Property(Amount),Operator(">"),Value(Number)))),"}"))),BinaryOperator,Expression(Postings(SearchQueryString("{",Query(Clause(Condition(Property(Date),Operator(">"),Value(DateValue)))),"}")))))))
|
||||
|
|
|
@ -1,9 +1,52 @@
|
|||
import type { Posting } from "$lib/utils";
|
||||
import type { Environment } from "./interpreter";
|
||||
import _ from "lodash";
|
||||
import type { Environment, Query } from "./interpreter";
|
||||
import { BigNumber } from "bignumber.js";
|
||||
|
||||
function cost(env: Environment, ps: Posting[]) {
|
||||
type PostingsOrQuery = Posting[] | Query;
|
||||
|
||||
function cost(env: Environment, q: PostingsOrQuery): BigNumber {
|
||||
const ps = toPostings(env, q);
|
||||
return ps.reduce((acc, p) => acc.plus(new BigNumber(p.amount)), new BigNumber(0));
|
||||
}
|
||||
|
||||
export const functions = { cost };
|
||||
function fifo(env: Environment, q: PostingsOrQuery): Posting[] {
|
||||
const ps = toPostings(env, q);
|
||||
return _.chain(ps)
|
||||
.groupBy((p) => [p.account, p.commodity].join(":"))
|
||||
.map((ps) => {
|
||||
ps = _.sortBy(ps, (p) => p.date);
|
||||
const available: Posting[] = [];
|
||||
while (ps.length > 0) {
|
||||
const p = ps.shift();
|
||||
if (p.quantity >= 0) {
|
||||
available.push(p);
|
||||
} else {
|
||||
let quantity = -p.quantity;
|
||||
while (quantity > 0 && available.length > 0) {
|
||||
const a = available.shift();
|
||||
if (a.quantity > quantity) {
|
||||
const diff = a.quantity - quantity;
|
||||
const price = a.amount / a.quantity;
|
||||
available.unshift({ ...a, quantity: diff, amount: diff * price });
|
||||
quantity = 0;
|
||||
} else {
|
||||
quantity -= a.quantity;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return available;
|
||||
})
|
||||
.flatten()
|
||||
.value();
|
||||
}
|
||||
|
||||
function toPostings(env: Environment, q: PostingsOrQuery) {
|
||||
if (Array.isArray(q)) {
|
||||
return q;
|
||||
}
|
||||
return q.resolve(env);
|
||||
}
|
||||
|
||||
export const functions = { cost, fifo };
|
||||
|
|
|
@ -7,7 +7,7 @@ export const sheetHighlighting = styleTags({
|
|||
"FunctionDefinition/Identifier": t.function(t.variableName),
|
||||
"FunctionCall/Identifier": t.function(t.variableName),
|
||||
Postings: t.special(t.variableName),
|
||||
"`": t.heading,
|
||||
"{ }": t.operator,
|
||||
UnaryOperator: t.operator,
|
||||
BinaryOperator: t.operator,
|
||||
AssignmentOperator: t.operator
|
||||
|
|
|
@ -2,7 +2,7 @@ import type { SyntaxNode } from "@lezer/common";
|
|||
import * as Terms from "./parser.terms";
|
||||
import type { EditorState } from "@codemirror/state";
|
||||
import { BigNumber } from "bignumber.js";
|
||||
import { asTransaction, type Posting, type SheetLineResult } from "$lib/utils";
|
||||
import { asTransaction, formatCurrency, type Posting, type SheetLineResult } from "$lib/utils";
|
||||
import {
|
||||
buildFilter,
|
||||
buildAST as buildSearchAST,
|
||||
|
@ -33,6 +33,38 @@ export class Environment {
|
|||
}
|
||||
}
|
||||
|
||||
export class Query {
|
||||
predicate: TransactionPredicate;
|
||||
result: Posting[] | null;
|
||||
|
||||
constructor(predicate: TransactionPredicate) {
|
||||
this.predicate = predicate;
|
||||
this.result = null;
|
||||
}
|
||||
|
||||
resolve(env: Environment): Posting[] {
|
||||
if (this.result === null) {
|
||||
this.result = env.postings
|
||||
.map(asTransaction)
|
||||
.filter(this.predicate)
|
||||
.map((t) => t.postings[0]);
|
||||
}
|
||||
return this.result;
|
||||
}
|
||||
|
||||
and(query: Query): Query {
|
||||
return new Query((t) => this.predicate(t) && query.predicate(t));
|
||||
}
|
||||
|
||||
or(query: Query): Query {
|
||||
return new Query((t) => this.predicate(t) || query.predicate(t));
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AST {
|
||||
readonly id: number;
|
||||
constructor(readonly node: SyntaxNode) {
|
||||
|
@ -82,6 +114,8 @@ class UnaryExpressionAST extends AST {
|
|||
switch (this.operator) {
|
||||
case "-":
|
||||
return (this.value.evaluate(env) as BigNumber).negated();
|
||||
case "+":
|
||||
return this.value.evaluate(env);
|
||||
default:
|
||||
throw new Error("Unexpected operator");
|
||||
}
|
||||
|
@ -114,6 +148,10 @@ class BinaryExpressionAST extends AST {
|
|||
return (this.left.evaluate(env) as BigNumber).div(this.right.evaluate(env));
|
||||
case "^":
|
||||
return (this.left.evaluate(env) as BigNumber).exponentiatedBy(this.right.evaluate(env));
|
||||
case "AND":
|
||||
return this.left.evaluate(env).and(this.right.evaluate(env));
|
||||
case "OR":
|
||||
return this.left.evaluate(env).or(this.right.evaluate(env));
|
||||
default:
|
||||
throw new Error("Unexpected operator");
|
||||
}
|
||||
|
@ -147,11 +185,8 @@ class PostingsAST extends AST {
|
|||
this.predicate = buildFilter(buildSearchAST(state, node.lastChild.firstChild.nextSibling));
|
||||
}
|
||||
|
||||
evaluate(env: Environment): any {
|
||||
return env.postings
|
||||
.map(asTransaction)
|
||||
.filter(this.predicate)
|
||||
.map((t) => t.postings[0]);
|
||||
evaluate(): Query {
|
||||
return new Query(this.predicate);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -291,7 +326,7 @@ class LineAST extends AST {
|
|||
evaluate(env: Environment): Record<string, any> {
|
||||
let value = this.value.evaluate(env);
|
||||
if (value instanceof BigNumber) {
|
||||
value = value.toFixed(2);
|
||||
value = formatCurrency(value.toNumber());
|
||||
}
|
||||
switch (this.valueId) {
|
||||
case Terms.Assignment:
|
||||
|
@ -300,7 +335,7 @@ class LineAST extends AST {
|
|||
case Terms.FunctionDefinition:
|
||||
return { result: "" };
|
||||
case Terms.Header:
|
||||
return { result: value?.toString() || "", align: "left", bold: true, underline: true };
|
||||
return { result: value?.toString() || "", align: "left", bold: true };
|
||||
default:
|
||||
throw new Error("Unexpected node type");
|
||||
}
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
unary @right,
|
||||
factor @left,
|
||||
term @left,
|
||||
comparison @left
|
||||
equality @left
|
||||
comparison @left,
|
||||
equality @left,
|
||||
and @left,
|
||||
or @left
|
||||
}
|
||||
|
||||
lines { Line (newline+ Line)* newline* }
|
||||
|
@ -21,14 +23,16 @@ UnaryExpression { !unary UnaryOperator ~unary Expression }
|
|||
BinaryExpression {
|
||||
Expression !factor BinaryOperator<"*" | "/"> Expression |
|
||||
Expression !term BinaryOperator<"+" | "-"> ~unary Expression |
|
||||
Expression !comparison BinaryOperator<"<" | "<=" | ">" | ">="> Expression
|
||||
Expression !equality BinaryOperator<"==" | "!="> Expression
|
||||
Expression !comparison BinaryOperator<"<" | "<=" | ">" | ">="> Expression |
|
||||
Expression !equality BinaryOperator<"==" | "!="> Expression |
|
||||
Expression !and BinaryOperator<@specialize<Identifier, "AND">> Expression |
|
||||
Expression !or BinaryOperator<@specialize<Identifier, "OR">> Expression
|
||||
}
|
||||
Assignment { Identifier AssignmentOperator Expression }
|
||||
FunctionCall { Identifier !call "(" Arguments? ")" }
|
||||
Arguments { Expression ~call ("," Expression)* }
|
||||
|
||||
Postings { @specialize<Identifier, "postings"> "(" SearchQueryString ")" }
|
||||
Postings { SearchQueryString }
|
||||
|
||||
FunctionDefinition { Identifier !call "(" Parameters? ")" "=" Expression }
|
||||
Parameters { Identifier ~call ("," Identifier)* }
|
||||
|
@ -54,13 +58,13 @@ BinaryOperator<expr> { expr }
|
|||
}
|
||||
|
||||
@local tokens {
|
||||
stringEnd[@name='`'] { '`' }
|
||||
stringEnd[@name='}'] { '}' }
|
||||
stringEscape { "\\" _ }
|
||||
@else stringContent
|
||||
}
|
||||
|
||||
@skip {} {
|
||||
stringStart[@name='`'] { '`' }
|
||||
stringStart[@name='{'] { '{' }
|
||||
SearchQueryString { stringStart SearchQuery stringEnd }
|
||||
SearchQuery { (stringContent | stringEscape)* }
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ export const sheetLanguage = LRLanguage.define({
|
|||
})
|
||||
}),
|
||||
languageData: {
|
||||
closeBrackets: { brackets: ["[", "(", "/", '"', "`"] }
|
||||
closeBrackets: { brackets: ["[", "(", "/", '"', "`", "{"] }
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||
import {LRParser, LocalTokenGroup} from "@lezer/lr"
|
||||
import {sheetHighlighting} from "./highlight"
|
||||
const spec_Identifier = {__proto__:null,postings:102}
|
||||
const spec_Identifier = {__proto__:null,AND:100, OR:102}
|
||||
export const parser = LRParser.deserialize({
|
||||
version: 14,
|
||||
states: "+|QVQPOOOOQO'#Cw'#CwQVQPOOOOQO'#C`'#C`OOQO'#Cc'#CcOtQPO'#CbO!wQPO'#C^OtQPO'#CiO#sQPO'#C_O#}QPO'#CmOOQO'#C_'#C_OOQO'#C^'#C^O$SQPO'#DQQOQPOOOOQO-E6u-E6uOOQO,58|,58|O$[QPO'#C_OOQO'#Ce'#CeOOQO'#Cf'#CfOOQO'#Cg'#CgOtQPO,59OOtQPO,59OOtQPO,59OO$uQPO,59TO%bQPO,59VOOQO'#Cs'#CsOtQPO,59^O%lQPO,59XO%qQPO,59lO%xQPO,59lO&QQPO,59VOOQO1G.j1G.jO&sQPO1G.jO'dQPO1G.jOOQO1G.o1G.oO(_QPO'#ClO)WQPO'#C_O)hQPO1G.qO*]QPO1G.qO*bQPO1G.zO*gQPO1G.xOOOO'#Co'#CoO*qOSO'#CnO*|QPO1G.sOOQO,59g,59gO+RQPO1G/WOOQO-E6y-E6yOOQO1G.q1G.qO+YQPO'#CcO,WQQO7+$UO,bQQO'#C_OtQPO'#CxO,lQPO,59WO,tQPO'#CzO,yQPO,59aOtQPO7+$fOOQO7+$]7+$]O-RQPO7+$fOOOO'#Cy'#CyO-WOSO'#CpO-cOSO,59YOOQO7+$_7+$_PVQPO'#CwOOQO'#Ch'#ChOtQPO<<GpO-hQPO,59dOOQO-E6v-E6vOOQO,59f,59fOOQO-E6x-E6xO-rQPO<<HQOtQPO<<HQOOOO-E6w-E6wOOQO1G.t1G.tO-|QQO1G.jO.rQPOAN=[O/cQPOAN=lO/mQQOAN=[O/wQPO'#CbO/wQPO,59OO/wQPO,59OO/wQPO<<GpO,WQQO7+$UO'dQPO1G.jOtQPO,59O",
|
||||
stateData: "0O~OrOS~OTRO^WOjZOsPOuSOvSOwSO!RVO!TXO~OTRO^`OuSOvSOwSO!RVO!TXO~OubOvbOxaOyaOzcO{cO|cO}cO~OpQXsQX~P!]OpRXsRXuRXvRXxRXyRXzRX{RX|RX}RX~O!RhO!XiO~P#RO!RkO~OsPOptX~O!RnO!QRXTRX^RXwRX!TRX!SRX~P#RO!QrO~P!]OTROuSOvSOwSO!RVO!TXO~O^tO!QuO~P$|O!UyO~Opta~PVOsPOpta~O!Q!PO~PtOxaOyaOuWivWizWi{Wi|Wi}Wi~OpWisWi!QWiTWi^WiwWi!RWi!TWi!SWi~P&XOTRO^!SOu!QOv!QOwSOxaOyaOzcO{cO|cO}cO!RVO!TXO~O!S!TO!Q`X~P!]O!RnOuRXvRXxRXyRXzRX{RX|RX}RX~O!S!VO!QRX!QiX!SRX~P(iO!X!XOp_is_iu_iv_ix_iy_iz_i{_i|_i}_i~O!Q!YO~O!Q!ZO~Opfisfi~P!]O!V![O!W![OedP~O!Q!_O~Opti~PVOTVXTYX^VX^YXuVXuYXvVXvYXwVXwYX!RVX!RYX!TVX!TYX~O!O!aO!P!aO~P!]O!ORX!PRX~P(iO!S!TO!Q`a~O^!eO~O!S!VO!Qia~O!X!hO~O!V![O!W![OedX~Oe!jO~O!Qla!Sla~P!]Ophyshy~P!]O!OWi!PWi~P&XOubOvbOxaOyaOzW!R{W!R|W!R}W!R~OpW!RsW!R!QW!RTW!R^W!RwW!R!RW!R!TW!R!SW!R~P.WOph!Rsh!R~P!]O!OW!R!PW!R~P.WO^!SO~P$|O",
|
||||
goto: "'XuPPv!P#]P#]#w#]$e$x%Z%j#]P#]%p#]%t%w%zP%}&U%}&XP&[&k&q&w&}PPPP'TS[OQV|l}!`YUOQl}!`S_T!oQgVSod!pQpeQqfSshnQxjQ!RqQ!c!TQ!g!XQ!k!qQ!l!bQ!m!hQ!n!rQ!s!tR!t!u!RYOQTVdefhjlnq}!T!X!`!b!h!o!p!q!r!t!utTOQTVdefhjln}!T!X!`!b!h!u]!oq!o!p!q!r!tfdUgpqsx!c!g!l!m!tX!p!R!k!n!sdeUgqsx!c!g!l!m!tV!q!R!n!sbfUgqsx!c!g!m!tT!u!R!sQ!b!RR!r!sTvhnR{kRzkR!^zZZOQl}!`RjWRwhQQOW^Ql}!`Ql[R}mQ!UsR!d!UQ!]zR!i!]Q!WtR!f!WQm[R!OmT]OQ",
|
||||
nodeNames: "⚠ Sheet Line Expression Literal Number UnaryExpression UnaryOperator BinaryExpression BinaryOperator BinaryOperator BinaryOperator BinaryOperator Grouping Identifier FunctionCall Arguments Postings SearchQueryString ` SearchQuery ` Assignment AssignmentOperator FunctionDefinition Parameters Header",
|
||||
maxTerm: 55,
|
||||
states: "*tQVQPOOOOQO'#Cy'#CyQVQPOOOOQO'#C`'#C`OOQO'#Cc'#CcOtQPO'#CbO#TQQO'#C^OtQPO'#ClO$]QQO'#C_OOOO'#Cq'#CqO$gOSO'#CpOOQO'#Co'#CoOOQO'#C_'#C_OOQO'#C^'#C^O$rQPO'#DSQOQPOOOOQO-E6w-E6wOOQO,58|,58|O$zQQO'#C_OOQO'#Ce'#CeOOQO'#Cf'#CfOOQO'#Cg'#CgOOQO'#Ch'#ChOOQO'#Ci'#CiOOQO'#Ck'#CkOtQPO,59OOtQPO,59OOtQPO,59OOtQPO,59OOtQPO,59OOtQPO,59OO%XQQO,59WO%tQPO,59XOOQO'#Cu'#CuOtQPO,59`OOOO'#C{'#C{O&OOSO'#CrO&ZOSO,59[O&`QPO,59nO&gQPO,59nO&oQPO,59XOOQO1G.j1G.jO'tQQO1G.jO(OQQO1G.jO)WQQO1G.jO)bQQO1G.jO*aQQO1G.jOOQO1G.r1G.rO*tQQO'#CnO+OQQO'#C_O,VQQO1G.sO-WQPO1G.sO-]QPO1G.|O-bQQO1G.zOOOO-E6y-E6yOOQO1G.v1G.vOOQO,59i,59iO-lQPO1G/YOOQO-E6{-E6{OOQO1G.s1G.sOtQPO'#CzO-sQPO,59YO-{QPO'#C|O.QQPO,59cOtQPO7+$hOOQO7+$_7+$_O.YQPO7+$hPVQPO'#CyO._QQO,59fOOQO-E6x-E6xOOQO,59h,59hOOQO-E6z-E6zO.iQQO<<HSOtQPO<<HSO.sQQOAN=n",
|
||||
stateData: ".}~OtOS~OTRO^WOl]OuPOwSOxSOySO!VVO!XXO~OTRO^bOwSOxSOySO!VVO!XXO~OwdOxdOzcO{cO|eO}eO!OeO!PeO!QfO!RfO!SgO!ThO~OrQXuQX~P!]OrRXuRXwRXxRXzRX{RX|RX}RX!ORX!PRX!QRX!RRX!SRX!TRX~O!VpO![qO~P#_O!YsO!ZsOgfP~OuPOrvX~O!VxO!URX!WRX~P#_O!U!PO~P!]OTROwSOxSOySO!VVO!XXO~O^!RO!U!SO~P%`O!YsO!ZsOgfX~Og!XO~Orva~PVOuPOrva~O!U!]O~PtOzcO{cOrWiuWi|Wi}Wi!OWi!PWi!QWi!RWi!SWi!TWi!UWi!WWi~OwWixWi~P&vOwdOxdO~P&vOwdOxdOzcO{cO|eO}eO!OeO!PeOrWiuWi!SWi!TWi!UWi!WWi~O!QWi!RWi~P(YO!QfO!RfO~P(YOwdOxdOzcO{cO|eO}eO!OeO!PeO!QfO!RfO!SgO~OrWiuWi!TWi!UWi!WWi~P)lO!W!^O!UbX~P!]O!VxO!W!`OwRXxRXzRX{RX|RX}RX!ORX!PRX!QRX!RRX!SRX!TRX!URX!UkX!WRX~O![!bOraiuaiwaixaizai{ai|ai}ai!Oai!Pai!Qai!Rai!Sai!Tai~O!U!cO~O!U!dO~Orhiuhi~P!]Orvi~PVO!W!^O!Uba~O^!hO~O!W!`O!Uka~O![!kO~O!Una!Wna~P!]Orjyujy~P!]Orj!Ruj!R~P!]O",
|
||||
goto: "'twPPx!R#SP#S#i#S$O$^$k$w%SP%^#S#S%g#S%k&Q&gP&j&q&j&tP&w'W'^'d'jPPPP'pS^OQV!Yv!Z!eYUOQv!Z!eQaTQoVQyiQzjQ{kQ|lQ}mQ!OnS!QpxQ!VrQ!f!^Q!j!bR!l!kw[OQTVijklmnprvx!Z!^!b!e!kwTOQTVijklmnprvx!Z!^!b!e!kiiUoz{|}!O!Q!V!f!j!lgjUo{|}!O!Q!V!f!j!lekUo|}!O!Q!V!f!j!lclUo}!O!Q!V!f!j!lamUo!O!Q!V!f!j!l_nUo!Q!V!f!j!lT!TpxwZOQTVijklmnprvx!Z!^!b!e!kwYOQTVijklmnprvx!Z!^!b!e!kRuYZ]OQv!Z!eRrWR!UpQQOW`Qv!Z!eQv^R!ZwQ!_!QR!g!_QtYR!WtQ!a!RR!i!aQw^R![wT_OQ",
|
||||
nodeNames: "⚠ Sheet Line Expression Literal Number UnaryExpression UnaryOperator BinaryExpression BinaryOperator BinaryOperator BinaryOperator BinaryOperator BinaryOperator Identifier BinaryOperator Grouping FunctionCall Arguments Postings SearchQueryString { SearchQuery } Assignment AssignmentOperator FunctionDefinition Parameters Header",
|
||||
maxTerm: 58,
|
||||
propSources: [sheetHighlighting],
|
||||
skippedNodes: [0],
|
||||
repeatNodeCount: 5,
|
||||
tokenData: "'e~RfXY!gYZ!l]^!lpq!gqr!qst#Oxy#gyz#lz{#q{|#v|}#{}!O$Q!P!Q$V!Q!R$[!R![%j!^!_%{!_!`&Y!`!a&g!c!}&t#R#S&t#S#T'`#T#o&t~!lOr~~!qOs~U!vPwQ!_!`!yS#OO!PS~#TSj~OY#OZ;'S#O;'S;=`#a<%lO#O~#dP;=`<%l#O~#lO!R~~#qO!Q~~#vOx~~#{Ou~~$QO!S~~$VOv~~$[Oy~~$aRT~!O!P$j!g!h%O#X#Y%O~$mP!Q![$p~$uRT~!Q![$p!g!h%O#X#Y%O~%RR{|%[}!O%[!Q![%b~%_P!Q![%b~%gPT~!Q![%b~%oST~!O!P$j!Q![%j!g!h%O#X#Y%O~&QPz~!_!`&T~&YO{~U&_P!XQ!_!`&bS&gO!OS~&lP|~!_!`&o~&tO}~~&yV^~!O!P&t!P!Q&t!Q![&t![!]&t!c!}&t#R#S&t#T#o&t~'eO!U~",
|
||||
tokenizers: [1, 2, new LocalTokenGroup("x~RQ#O#PX#S#Tr~[RO;'Se;'S;=`j;=`Oe~jO!W~~oP!W~;=`<%le~wOe~~", 39, 53)],
|
||||
tokenData: "'e~RfXY!gYZ!l]^!lpq!gqr!qst#Oxy#gyz#lz{#q{|#v|}#{}!O$Q!P!Q$V!Q!R$[!R![%j!^!_%{!_!`&Y!`!a&g!c!}&t#R#S&t#T#o&t#o#p'`~!lOt~~!qOu~U!vPyQ!_!`!yS#OO!RS~#TSl~OY#OZ;'S#O;'S;=`#a<%lO#O~#dP;=`<%l#O~#lO!V~~#qO!U~~#vOz~~#{Ow~~$QO!W~~$VOx~~$[O{~~$aRT~!O!P$j!g!h%O#X#Y%O~$mP!Q![$p~$uRT~!Q![$p!g!h%O#X#Y%O~%RR{|%[}!O%[!Q![%b~%_P!Q![%b~%gPT~!Q![%b~%oST~!O!P$j!Q![%j!g!h%O#X#Y%O~&QP|~!_!`&T~&YO}~~&_P![~!_!`&b~&gO!Q~~&lP!O~!_!`&o~&tO!P~~&yV^~!O!P&t!P!Q&t!Q![&t![!]&t!c!}&t#R#S&t#T#o&t~'eO!X~",
|
||||
tokenizers: [1, 2, new LocalTokenGroup("x~RQ#O#PX#q#rr~[RO;'Se;'S;=`j;=`Oe~jO!Z~~oP!Z~;=`<%le~wOg~~", 39, 56)],
|
||||
topRules: {"Sheet":[0,1]},
|
||||
specialized: [{term: 14, get: (value) => spec_Identifier[value] || -1}],
|
||||
tokenPrec: 0,
|
||||
termNames: {"0":"⚠","1":"@top","2":"Line","3":"Expression","4":"Literal","5":"Number","6":"UnaryExpression","7":"UnaryOperator","8":"BinaryExpression","9":"BinaryOperator<\"*\" | \"/\">","10":"BinaryOperator<\"+\" | \"-\">","11":"BinaryOperator<\"<\" | \"<=\" | \">\" | \">=\">","12":"BinaryOperator<\"==\" | \"!=\">","13":"Grouping","14":"Identifier","15":"FunctionCall","16":"Arguments","17":"Postings","18":"SearchQueryString","19":"stringStart","20":"SearchQuery","21":"stringEnd","22":"Assignment","23":"AssignmentOperator","24":"FunctionDefinition","25":"Parameters","26":"Header","27":"newline+","28":"(\",\" Expression)+","29":"(stringContent | stringEscape)+","30":"(\",\" Identifier)+","31":"(newline+ Line)+","32":"␄","33":"%mainskip","34":"whitespace","35":"newline","36":"lines","37":"\"+\"","38":"\"-\"","39":"\"!\"","40":"\"*\"","41":"\"/\"","42":"\"<\"","43":"\"<=\"","44":"\">\"","45":"\">=\"","46":"\"==\"","47":"\"!=\"","48":"\")\"","49":"\"(\"","50":"\",\"","51":"Identifier/\"postings\"","52":"\"`\"","53":"stringContent","54":"stringEscape","55":"\"=\""}
|
||||
termNames: {"0":"⚠","1":"@top","2":"Line","3":"Expression","4":"Literal","5":"Number","6":"UnaryExpression","7":"UnaryOperator","8":"BinaryExpression","9":"BinaryOperator<\"*\" | \"/\">","10":"BinaryOperator<\"+\" | \"-\">","11":"BinaryOperator<\"<\" | \"<=\" | \">\" | \">=\">","12":"BinaryOperator<\"==\" | \"!=\">","13":"BinaryOperator<@specialize[]<Identifier, \"AND\">>","14":"Identifier","15":"BinaryOperator<@specialize[]<Identifier, \"OR\">>","16":"Grouping","17":"FunctionCall","18":"Arguments","19":"Postings","20":"SearchQueryString","21":"stringStart","22":"SearchQuery","23":"stringEnd","24":"Assignment","25":"AssignmentOperator","26":"FunctionDefinition","27":"Parameters","28":"Header","29":"newline+","30":"(\",\" Expression)+","31":"(stringContent | stringEscape)+","32":"(\",\" Identifier)+","33":"(newline+ Line)+","34":"␄","35":"%mainskip","36":"whitespace","37":"newline","38":"lines","39":"\"+\"","40":"\"-\"","41":"\"!\"","42":"\"*\"","43":"\"/\"","44":"\"<\"","45":"\"<=\"","46":"\">\"","47":"\">=\"","48":"\"==\"","49":"\"!=\"","50":"Identifier/\"AND\"","51":"Identifier/\"OR\"","52":"\")\"","53":"\"(\"","54":"\",\"","55":"\"{\"","56":"stringContent","57":"stringEscape","58":"\"=\""}
|
||||
})
|
||||
|
|
|
@ -8,17 +8,17 @@ export const
|
|||
UnaryExpression = 6,
|
||||
UnaryOperator = 7,
|
||||
BinaryExpression = 8,
|
||||
Grouping = 13,
|
||||
Identifier = 14,
|
||||
FunctionCall = 15,
|
||||
Arguments = 16,
|
||||
Postings = 17,
|
||||
SearchQueryString = 18,
|
||||
stringStart = 19,
|
||||
SearchQuery = 20,
|
||||
stringEnd = 21,
|
||||
Assignment = 22,
|
||||
AssignmentOperator = 23,
|
||||
FunctionDefinition = 24,
|
||||
Parameters = 25,
|
||||
Header = 26
|
||||
Grouping = 16,
|
||||
FunctionCall = 17,
|
||||
Arguments = 18,
|
||||
Postings = 19,
|
||||
SearchQueryString = 20,
|
||||
stringStart = 21,
|
||||
SearchQuery = 22,
|
||||
stringEnd = 23,
|
||||
Assignment = 24,
|
||||
AssignmentOperator = 25,
|
||||
FunctionDefinition = 26,
|
||||
Parameters = 27,
|
||||
Header = 28
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
<script lang="ts">
|
||||
import { createEditor, sheetEditorState } from "$lib/sheet";
|
||||
import { moveToEnd, moveToLine, updateContent } from "$lib/editor";
|
||||
import { ajax, buildDirectoryTree, type Posting, type SheetFile } from "$lib/utils";
|
||||
import { moveToLine, updateContent } from "$lib/editor";
|
||||
import {
|
||||
ajax,
|
||||
buildDirectoryTree,
|
||||
formatFloatUptoPrecision,
|
||||
type Posting,
|
||||
type SheetFile
|
||||
} from "$lib/utils";
|
||||
import { redo, undo } from "@codemirror/commands";
|
||||
import type { KeyBinding } from "@codemirror/view";
|
||||
import * as toast from "bulma-toast";
|
||||
|
@ -140,8 +146,6 @@
|
|||
}
|
||||
moveToLine(editor, lineNumber, true);
|
||||
lineNumber = 0;
|
||||
} else {
|
||||
moveToEnd(editor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -292,6 +296,16 @@
|
|||
>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="control ml-5">
|
||||
<button
|
||||
class:is-loading={$sheetEditorState.pendingEval}
|
||||
class="is-loading button is-small pointer-events-none px-0"
|
||||
style="border: none"
|
||||
>
|
||||
{formatFloatUptoPrecision($sheetEditorState.evalDuration, 2)}ms
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -311,7 +325,7 @@
|
|||
</div>
|
||||
<div class="column is-9-widescreen is-10-fullhd is-8">
|
||||
<div class="flex">
|
||||
<div class="box box-r-none py-0 pr-1 mb-0 basis-[36rem] max-w-[36rem]">
|
||||
<div class="box box-r-none py-0 pr-1 mb-0 basis-[36rem] max-w-[48rem]">
|
||||
<div class="sheet-editor" bind:this={editorDom} />
|
||||
</div>
|
||||
<div
|
||||
|
|
|
@ -33,6 +33,8 @@ interface SheetEditorState {
|
|||
undoDepth: number;
|
||||
redoDepth: number;
|
||||
doc: string;
|
||||
pendingEval: boolean;
|
||||
evalDuration: number;
|
||||
currentLine: number;
|
||||
errors: SheetFileError[];
|
||||
results: SheetLineResult[];
|
||||
|
@ -44,6 +46,8 @@ export const initialSheetEditorState: SheetEditorState = {
|
|||
redoDepth: 0,
|
||||
currentLine: 0,
|
||||
doc: "",
|
||||
pendingEval: false,
|
||||
evalDuration: 0,
|
||||
errors: [],
|
||||
results: []
|
||||
};
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"compilerOptions": {
|
||||
"types": ["bun-types"],
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"checkJs": false,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
|
|
Loading…
Reference in New Issue