Commit Graph
146 Commits
Author SHA1 Message Date
Zef Hemel 02ee8c7942 lint fixes 2026-07-28 14:30:10 +02:00
Zef Hemel 5f5f244c46 Changed Lua documentation rendering slightly 2026-07-16 12:28:37 +02:00
Zef Hemel 75ca3b9c39 Migrate Lua code complete and code navigation to typescript 2026-07-16 12:06:59 +02:00
Zef Hemel 0d99f471e9 Document built-in syscalls 2026-07-16 09:17:04 +02:00
Zef Hemel 2020fbb16d refactor: flatten documented callback metadata 2026-07-16 09:12:29 +02:00
Zef Hemel 37d6c59555 Docs in code: unify documented function definitions 2026-07-16 09:00:18 +02:00
Zef Hemel 599798c831 Docs for stdlib 2026-07-15 17:55:18 +02:00
Zef Hemel 23f338dc14 Runtime-expose API documentation (base) 2026-07-15 17:52:35 +02:00
Zef Hemel 9c59c2827c Move Lua comment parsing into actual parser, preserve comments for docs purposes 2026-07-15 17:51:31 +02:00
Zef Hemel 28f1f8af16 Long overdue "make fmt" sorry about this one 2026-07-14 15:58:15 +02:00
Zef Hemel 50d8f1f09f Feature: baked sections 2026-06-23 17:14:41 +02:00
Zef Hemel c1f2bb595f Lua: js.importFromSpace to cleanly import a .js file from your space 2026-06-22 09:36:51 +02:00
Zef Hemel dfcf3035eb Lua: fix parenthes evaluation 2026-06-21 19:13:11 +02:00
Zef Hemel 2aa89202b3 Support Lua-based codeWidget support 2026-06-21 19:11:59 +02:00
Zef Hemel a4f738815f Lua: add AST pretty-printer and align parse/print naming 2026-05-31 13:50:31 +02:00
Zef Hemel 9134944896 Introduce "relation" indexer, demote "link" to virtual view 2026-05-21 16:49:34 +02:00
Zef Hemel 09a318a5e9 Revert "Fix ref links in tables regression"
This reverts commit 15ccabc361.
2026-05-07 08:13:07 +02:00
Zef Hemel 15ccabc361 Fix ref links in tables regression 2026-05-06 16:46:05 +02:00
Zef Hemel 8b241f9448 Rebranding LIQ to SLIQ! 2026-04-22 20:54:43 +02:00
Zef Hemel 64898923e1 Restore separate copy path for Lua widgets with cleaner markdown
Specifically for tables using (less rich) markdown syntax when possible
2026-04-09 14:38:10 +02:00
Zef Hemel 1946df3cb5 Generate <br/> instead of <br> for line breaks 2026-04-07 18:15:43 +02:00
Matouš Jan FialkaandGitHub 83bf71a907 [LIQ] Fix: Coerce values to plain numbers in numeric aggregate functions (#1925)
* [LIQ] Fix: Coerce values to plain numbers in numeric aggregate functions

All numeric aggregate iterate functions (`sum`, `product`, `avg`,
`stddev`, variance, covariance, correlation, bitwise, quantile, ...)
used TypeScript as number type assertions which have no effect at
runtime.  When a value was a string (e.g., YAML `rs: '3000.0'`) or
a `LuaTaggedFloat` object JS arithmetic operators like `+=` would
silently perform string concatenation or produce `[object Object]`
nonsense instead of numeric addition.

The fix introduces a small helper wrapping the `coerceToNumber` that
correctly unwraps plain numbers, `LuaTaggedFloat` objects and numeric
strings to plain JS numbers, and applies it at every numeric aggregate
intake point.

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* [LIQ] Fix: Unwrap `LuaTaggedFloat` before passing values iterate function

`executeAggregate` now unwraps `LuaTaggedFloat` boxing before passing
values to any iterate function ensuring user-defined aggregates also
receive plain JS numbers instead of opaque internal objects.

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Remove forgotten trivial wrapper `aggNum`

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

---------

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
2026-04-07 11:31:28 +02:00
Zef Hemel 909e6e208b Trim single values when rendering Lua results as markdown 2026-04-06 21:51:35 +02:00
Zef Hemel 15317ce821 Refactored Lua expression result rendering
Now produce "markdown" (or <table> tags for tables) instead of pure
HTML. Hopefully doesn't introduce regressions. Added a test suite for
future iterations.
2026-04-06 20:13:25 +02:00
Zef Hemel 42635590c2 Fix tests 2026-04-06 15:20:46 +02:00
Zef Hemel b158dc6697 Unit test for render_lua_html test to make future changes not regress 2026-04-06 14:53:19 +02:00
Zef Hemel 75d992c10e Support JS objects for spacelua.interpolate 2026-04-02 13:47:13 +02:00
Zef Hemel dc4e701ddf syntax.define refinements 2026-03-31 15:01:16 +02:00
Zef Hemel 52b96f7093 Fixes #1915 2026-03-29 15:41:56 +02:00
Zef Hemel 6c94a8fea7 Performance: performance tuned LuaTable 2026-03-26 17:22:40 +01:00
5996e51c6f [LIQ] Unify from, select, and group by clauses to use field lists, implement multi-source cross-join (#1909)
* Website: Make manual build ref configurable

* [LIQ] Unify `from`, `select`, and `group by` clauses to use field lists

Query clauses `from`, `select`, and `group by` now accept the same field
syntax as Lua table constructors (`name = expr`, bare expr, or `[expr]
= expr`), giving them a consistent grammar and enabling named bindings
everywhere. #feature

What changed:

* New `FieldList` rule in `lua.grammar`. All three clauses now parse
  their arguments through it instead of using expression lists or the
  special `Name "=" exp` pattern.

* AST types `LuaFromClause`, `LuaSelectClause`, and `LuaGroupByClause`
  now carry fields: `LuaTableField[]` instead of a single expression or
  an expression array. The parser, static analysis helpers and the
  evaluator are all updated to work with this new shape.

Named bindings in `select`
--------------------------
`select name, age` now derives column names from bare variable or
property access expressions so the result table has string keys (`name`,
`age`) rather than integer indices. Explicit naming still works.

Named bindings in `group by`
----------------------------
`group b`y fields can carry explicit aliases that propagate into the
post-grouping scope. For example:

```sql
group by
  n = name
select {
  label = n
}
```

The alias `n` is now visible in `select`, `having`, and `order by`.
Previously only the raw expression form (`name`) was bound and an
explicit alias was silently lost. #bugfix

A new `LuaGroupByEntry` type pairs each `group by` expression with its
optional alias. The environment builder binds every
declared alias to the corresponding key value, regardless of whether the
group key is a scalar or a multi-key table.

Named bindings in `from`
------------------------
`from p = page` continues to work as before. The clause now also accepts
the full field-list syntax which lays the groundwork for multi-source
from (cross join) in a future change. #todo #feature

Complete LIQ syntax now
-----------------------
Complete LIQ syntax (in Postgres documentation style) could be now
roughly expressed as follows: #todo #documentation

```text
query[[ clause [...] ]]

where clause is one of:

    FROM field [, ...]
    WHERE condition
    GROUP BY field [, ...]
    HAVING condition
    SELECT field [, ...]
    ORDER BY sort_key [, ...]
    LIMIT count [, offset]
    OFFSET start

where field is one of:

    expression
    name = expression
    [expression] = expression

where sort_key is:

    expression [ ASC | DESC ]
               [ NULLS { FIRST | LAST } ]
               [ USING name ]
               [ USING FUNCTION function_body ]

where expression is any valid Lua expression, including:

    aggregate_call
    aggregate_call FILTER ( WHERE condition )
```

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Fix type error caused by earlier merge conflict resolution

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* [LIQ] Cross-join support for multi-source `from`

Examples of what works now:

```
${query [[
  from
    x = {   1,   2 },
    y = {  10,  20 },
    z = { 100, 200 }
  select {
    sum = x + y + z
  }
]]}
```

Or (more complicated three-way cross-join) to illustrate:

```
${query [[
  from
    s = {
      { id = 1, name = 'Eva',  },
      { id = 2, name = 'Adam', },
      { id = 3, name = 'John', },
      { id = 4, name = 'Zef',  },
    },
    c = {
      { id = 101, title = 'Mathematics',      },
      { id = 102, title = 'Arts',             },
      { id = 103, title = 'Physics',          },
      { id = 104, title = 'Computer Science', },
      { id = 105, title = 'Literature',       },
    },
    e = {
      { sid = 1, cid = 101, },
      { sid = 1, cid = 103, },
      { sid = 2, cid = 101, },
      { sid = 2, cid = 102, },
      { sid = 2, cid = 103, },
      { sid = 3, cid = 102, },
      { sid = 3, cid = 105, },
      { sid = 4, cid = 101, },
      { sid = 4, cid = 103, },
      { sid = 4, cid = 104, },
    }
  where
    s.id  == e.sid and
    e.cid == c.id
  order by
    s.name
  group by
    s.name
  having
    s.name:match('^A') or
    s.name:match('f$')
  select
    student = s.name:upper(),
    courses = string_agg(c.title, ', '
      order by c.title desc
    )
]]}
```

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

---------

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
Co-authored-by: Zef Hemel <zef@zef.me>
2026-03-25 14:48:57 +01:00
Matouš Jan FialkaandGitHub 80cc5605da [LIQ] Implement implicit single group for aggregates without group by (#1907)
* Allow aggregate functions in `select`/`having` without an explicit
  `group by` clause. The entire result set is treated as one group.

  Note: Uses a `Symbol` as the map key for the implicit single group to
  avoid any collision with string keys from `generateKey`.

* Extend documentation.

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
2026-03-24 12:12:52 +01:00
Zef Hemel ee2a3edb47 Lua interpreter performance optimization (hot paths)
* The interpreter wasted cycles on unnecessary async/Promise wrapping
  and redundant allocations even for synchronous operations.
* Now: more "sync-first" evaluation — try the fast synchronous path
  first, only fall back to Promises when actually needed.
* In addition: fast paths for table access, variable lookups, string
  concat, arithmetic, and function calls; loops can re-enter sync mode
  after an async iteration resolves.
* Added benchmarks

Results: 1.3x–1.7x speedup across typical workloads (API calls, table
traversal, string ops), up to 1.92x in some cases.
2026-03-20 14:44:00 +01:00
Matouš Jan FialkaandGitHub 9170627d27 Add tests for aggregates and few useful aggregates (#1897)
* [LIQ] Add more tests for builtin aggregates

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* [LIQ] Add `first`, `last`, `mode` and `median` aggregates and `group_concat` alias

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

---------

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
2026-03-19 12:34:58 +01:00
Matouš Jan FialkaandGitHub 6cbb61c7db [LIQ] Add new aggregate functions, aliases, and queryable aggregate registry (#1891)
* [LIQ] Add new aggregate functions, aliases, and queryable aggregate registry

* Extend with 13 new built-in aggregates: `product`, `string_agg`,
  `yaml_agg`, `json_agg`, `bit_and`, `bit_or`, `bit_xor`, `bool_and`,
  `bool_or`, `stddev_pop`, `stddev_samp`, `var_pop` and `var_samp`.

* Introduce `aggregate.alias` API allowing users to define custom
  aliases for any aggregate. Standard aliases (`every`, `std`, `stddev`
  and `variance`) are now defined via this API rather than hardcoded.

* Add `index.aggregates` queryable collection so users can discover
  all available aggregates directly from LIQ queries.

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Fix config pass through query path so custom aggregates work

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Fix: Preserve `LuaTable`/`LuaFunction` values in aggregate config storage

`config.set` uses `LuaNativeJSFunction` which calls `luaValueToJS` on
all arguments. This converted the aggregate `LuaTable` to a plain JS
object and wrapped `LuaFunction` callbacks in JS functions that also
converted their returned values via `luaValueToJS`. The result was that
state returned by initialize (a `LuaTable`) got converted to a plain JS
object before being passed to `iterate`. Therefor Lua operations like
`table.insert` on that were failing because they expected a `LuaTable`
and not a plain JS array.

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Fix formatting

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Improve aggregate functions descriptions, fix `sum` divergence

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Align `product` with `sum`

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Fix: extract `alias` from `LuaTable` via `rawGet` in `aggregates()` registry

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Rename `alias` in `aggregates()` to `target` for clarity

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Fix: Add a null guard at the top of `jsToLuaValue`

This preserves `null`/`undefined` as-is (both map to Lua nil) and
prevents them from falling through to the `typeof` "object" branch.

For this PR it means that null `target` in our `aggregates` entries will
correctly show as empty/`nil` in query results rather than `{}`.

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Fix: Documentation reflects recent changes

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Fix: make `sum`/`product` return null on empty input; stop `LIQ_NULL` leaks

* `sum(`) and` product(`) now return null when no rows match (matching
  Postgres semantics) instead of returning 0 and 1 respectively.

* Query result columns that hold null are internally preserved using
  a `LIQ_NULL` sentinel so that column keys survive in `LuaTable`
  storage.  This sentinel was leaking into Lua code as "userdata"
  through three read paths:

  * `luaIndexValue`: `rawGet` returned the sentinel directly to Lua when
    accessing table fields,

  * `rawget` (stdlib): the builtin `rawget` function exposed the
    sentinel without converting it back to `nil`,

  * `createAugmentedEnv`: string interpolation unpacked table values via
    `rawGet` into local variables, making the sentinel visible in
    template expressions like `${var}`.

  All three now convert `LIQ_NULL` to `nil` at the read boundary,
  keeping the sentinel internal to table storage where it belongs.

* Update affected test expectations accordingly.

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Fix: Remove duplicated LIQ_NULL hazard, add guard for all builtin aggregate `iterate`s

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Fix: `array_agg` preserves NULL positions

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Fix: Add symbol guard to `json_agg`

`JSON.stringify(Symbol(...))` in an array produces null by accident.
That is a JS implementation detail we **MUST NOT** rely on. Explicit
null push makes intent clear and avoids surprises if the `Symbol`
representation ever changes.

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Fix: Add symbol guard to `yaml_agg` (ditto)

`js-yaml` has no knowledge of the `LIQ_NULL` symbol. Passing null makes
it emit YAML null (or `~`), which is the correct YAML representation of
a missing value and matches standard `json_agg`/`yaml_agg`
NULL-inclusion semantics.

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Fix: Add intra-aggregate ordering null guards

Without this, `LIQ_NULL` sort keys would fall through to `valA < valB`
which is always false for `Symbol`s which is breaking the `nulls
first`/`nulls last` contract...

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Fix: Ditto, but for `order by` null comparisons

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Fix: Guard `luaTypeName`, `luaTypeOf` and `luaToString` against `LIQ_NULL` sentinel

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Fix: Guard presentation layer against `LIQ_NULL` sentinel leaking as visible text

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Fix: Evaluate extra args per-item in `executeAggregate`; add new aggregates

Extra arguments (2nd, 3rd, etc.) to aggregate functions were evaluated
against the outer query environment where the object variable is not
bound. This caused multi-argument aggregates like `covar_samp(data.y,
data.x)` to fail with nil reference errors. This commit addresses this
by evaluating extra args per-item inside the iterate loop using the item
environment so all arguments resolve correctly.

We also add few common aggregates:

- `covar_pop`, `covar_samp`, `corr`: population/sample covariance and
  correlation coefficient using online co-moment algorithm.

- `quantile(value, q, method)`: general quantile with interpolation
  methods: lower, higher, nearest, midpoint and default linear.

- `percentile_cont(value, q)`: continuous percentile (linear)

- `percentile_disc(value, q)`: discrete percentile (lower)

Note: `percentile_cont` and `percentile_disc` share the `quantile`
implementation through `ctx.name` at initialize time.

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Update docs

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Make the ordering for quantile aggregates explicit

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Update docs

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

* Improve docs

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>

---------

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
2026-03-19 09:37:09 +01:00
Brett AnthoineandGitHub 4045e92887 Fix: unwrap multi-return values in PropertyAccess and method calls (#1895) 2026-03-18 21:05:03 +01:00
Matouš Jan FialkaandGitHub fea7e5a508 Merge branch 'main' into liq-implement-offset 2026-03-11 18:49:29 +01:00
Matouš Jan Fialka 5205ab5154 [LIQ] Implement offset (4: add tests)
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
2026-03-11 11:47:11 +01:00
Matouš Jan Fialka 6970c41a4e [LIQ] Implement offset (1: make generate)
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
2026-03-11 11:36:46 +01:00
Matouš Jan Fialka 76fe46e590 [LIQ] Implement offset (2: update AST, parser and eval)
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
2026-03-11 11:35:19 +01:00
Matouš Jan Fialka a8c0538627 [LIQ] Implement offset (1: grammar)
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
2026-03-11 11:34:26 +01:00
Matouš Jan Fialka a1b3c7f083 Reformat
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
2026-03-11 09:43:08 +01:00
Matouš Jan Fialka 9be3490575 Resolve merge conflicts with main branch
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
2026-03-11 09:37:37 +01:00
Matouš Jan Fialka 7eca1562e0 [LIQ] Implement intra-aggregate order by (4: add Space Lua tests)
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
2026-03-11 08:43:16 +01:00
Matouš Jan Fialka 10f7d5d1b4 [LIQ] Implement intra-aggregate order by (3: add TS tests)
Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
2026-03-11 08:42:23 +01:00
Matouš Jan Fialka 8fdc61aa7a [LIQ] Implement intra-aggregate order by (2: make generate-ed files)
* Add files generated by `make generate` from `lua.grammar`.

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
2026-03-11 08:41:14 +01:00
Matouš Jan Fialka af6c0e338b [LIQ] Implement intra-aggregate order by (1)
* Add support for `order by ...` inside aggregate function calls, e.g.,
  `array_agg(p.name order by p.name asc)` matching Postgres
  per-aggregate ordering semantics.

* Add support for extra arguments for aggregates.

Signed-off-by: Matouš Jan Fialka <mjf@mjf.cz>
2026-03-11 08:35:48 +01:00
Zef Hemel d9f4b93795 Eliminate (most) uses of the client global
Also an opportunity to refactor the transclusion code, which had become
very messy
2026-03-10 14:02:05 +01:00
Zef Hemel 799e27a9b4 Enable biome based formatter and reformat 2026-03-10 09:38:59 +01:00
Zef Hemel cce5b669f6 Merge branch 'main' into to-node 2026-03-10 08:57:58 +01:00