Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ jobs:
GEN_DIR="$PREFIX/lib/fpc/${FPC_VERSION}/units/${TARGET}/rtl-generics"
FCL_DIR="$PREFIX/lib/fpc/${FPC_VERSION}/units/${TARGET}/fcl-process"
FCL_BASE_SRC="$PREFIX/share/fpcsrc/packages/fcl-base/src"
REGEXPR_SRC="$PREFIX/share/fpcsrc/packages/regexpr/src"
FCL_NET_SRC="$PREFIX/share/fpcsrc/packages/fcl-net/src"
OPENSSL_SRC="$PREFIX/share/fpcsrc/packages/openssl/src"

Expand All @@ -144,11 +143,9 @@ jobs:
echo "rtl-generics units: $(ls "$GEN_DIR"/*.ppu 2>/dev/null | wc -l) .ppu files"
echo "fcl-process units: $(ls "$FCL_DIR"/*.ppu 2>/dev/null | wc -l) .ppu files"
echo "fcl-base source path: $FCL_BASE_SRC"
echo "regexpr source path: $REGEXPR_SRC"
echo "fcl-net source path: $FCL_NET_SRC"
echo "openssl source path: $OPENSSL_SRC"
test -d "$FCL_BASE_SRC"
test -d "$REGEXPR_SRC"
test -d "$FCL_NET_SRC"
test -d "$OPENSSL_SRC"

Expand All @@ -168,7 +165,7 @@ jobs:
"$CROSS_FPC" -T"${OS}" -O4 -dPRODUCTION -Xs -CX -XX -B \
-Fu./source/units -Fu./source/generated -Fu./source/shared -Fu./source/app -Fu./souffle \
-Fi./source/units -Fi./source/shared -Fi./souffle \
-Fu"$RTL_DIR" -Fu"$OBJPAS_DIR" -Fu"$GEN_DIR" -Fu"$FCL_DIR" -Fu"$FCL_BASE_SRC" -Fu"$REGEXPR_SRC" -Fu"$FCL_NET_SRC" -Fu"$OPENSSL_SRC" \
-Fu"$RTL_DIR" -Fu"$OBJPAS_DIR" -Fu"$GEN_DIR" -Fu"$FCL_DIR" -Fu"$FCL_BASE_SRC" -Fu"$FCL_NET_SRC" -Fu"$OPENSSL_SRC" \
-FU"build/compiled" -FE"build" \
$EXTRA_FLAGS \
-dFPC_SOFT_FPUX80 \
Expand All @@ -184,7 +181,7 @@ jobs:
"$CROSS_FPC" -T"${OS}" -O4 -dPRODUCTION -Xs -CX -XX -B \
-Fu./source/units -Fu./source/generated -Fu./source/shared -Fu./source/app -Fu./souffle \
-Fi./source/units -Fi./source/shared -Fi./souffle \
-Fu"$RTL_DIR" -Fu"$OBJPAS_DIR" -Fu"$GEN_DIR" -Fu"$FCL_DIR" -Fu"$FCL_BASE_SRC" -Fu"$REGEXPR_SRC" -Fu"$FCL_NET_SRC" -Fu"$OPENSSL_SRC" \
-Fu"$RTL_DIR" -Fu"$OBJPAS_DIR" -Fu"$GEN_DIR" -Fu"$FCL_DIR" -Fu"$FCL_BASE_SRC" -Fu"$FCL_NET_SRC" -Fu"$OPENSSL_SRC" \
-FU"build/compiled" -FE"build" \
$EXTRA_FLAGS \
-dFPC_SOFT_FPUX80 \
Expand All @@ -197,7 +194,7 @@ jobs:
"$CROSS_FPC" -T"${OS}" -O4 -dPRODUCTION -Xs -CX -XX -B \
-Fu./source/units -Fu./source/generated -Fu./source/shared -Fu./source/app -Fu./souffle \
-Fi./source/units -Fi./source/shared -Fi./souffle \
-Fu"$RTL_DIR" -Fu"$OBJPAS_DIR" -Fu"$GEN_DIR" -Fu"$FCL_DIR" -Fu"$FCL_BASE_SRC" -Fu"$REGEXPR_SRC" -Fu"$FCL_NET_SRC" -Fu"$OPENSSL_SRC" \
-Fu"$RTL_DIR" -Fu"$OBJPAS_DIR" -Fu"$GEN_DIR" -Fu"$FCL_DIR" -Fu"$FCL_BASE_SRC" -Fu"$FCL_NET_SRC" -Fu"$OPENSSL_SRC" \
-FU"build/compiled" -FE"build" \
$EXTRA_FLAGS \
-dFPC_SOFT_FPUX80 \
Expand All @@ -209,7 +206,7 @@ jobs:
"$CROSS_FPC" -T"${OS}" -O4 -dPRODUCTION -Xs -CX -XX -B \
-Fu./source/units -Fu./source/generated -Fu./source/shared -Fu./source/app -Fu./souffle \
-Fi./source/units -Fi./source/shared -Fi./souffle \
-Fu"$RTL_DIR" -Fu"$OBJPAS_DIR" -Fu"$GEN_DIR" -Fu"$FCL_DIR" -Fu"$FCL_BASE_SRC" -Fu"$REGEXPR_SRC" -Fu"$FCL_NET_SRC" -Fu"$OPENSSL_SRC" \
-Fu"$RTL_DIR" -Fu"$OBJPAS_DIR" -Fu"$GEN_DIR" -Fu"$FCL_DIR" -Fu"$FCL_BASE_SRC" -Fu"$FCL_NET_SRC" -Fu"$OPENSSL_SRC" \
-FU"build/compiled" -FE"build" \
$EXTRA_FLAGS \
-dFPC_SOFT_FPUX80 \
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/toolchain.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,8 @@ jobs:

# Keep official package sources available for cross builds. The
# cached cross toolchain only prebuilds a minimal package subset, so
# source-based lookup is needed for units like Base64 and RegExpr.
# source-based lookup is needed for units like Base64.
cp -R "$GITHUB_WORKSPACE/fpc-source/packages/fcl-base" "$PREFIX/share/fpcsrc/packages/"
cp -R "$GITHUB_WORKSPACE/fpc-source/packages/regexpr" "$PREFIX/share/fpcsrc/packages/"
cp -R "$GITHUB_WORKSPACE/fpc-source/packages/fcl-net" "$PREFIX/share/fpcsrc/packages/"
cp -R "$GITHUB_WORKSPACE/fpc-source/packages/openssl" "$PREFIX/share/fpcsrc/packages/"

Expand Down
2 changes: 1 addition & 1 deletion docs/build-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ It:
6. If the `FPC_TARGET_CPU` environment variable is set, prepends `-P<arch>` to the compiler arguments (used by CI to target x86_64 on Windows where the FPC package defaults to i386).
7. For the `tests` target, auto-discovers all `*.Test.pas` files in `source/units/` and `source/shared/`.

The GitHub Actions cross-compilation workflow uses a reduced cached FPC toolchain rather than a full target-side FCL install. It prebuilds the RTL, `rtl-objpas`, `rtl-generics`, and `fcl-process`, and also caches the official `fcl-base` and `regexpr` sources so cross builds can resolve units such as `Base64` and `RegExpr` on demand from the shipped FPC packages.
The GitHub Actions cross-compilation workflow uses a reduced cached FPC toolchain rather than a full target-side FCL install. It prebuilds the RTL, `rtl-objpas`, `rtl-generics`, and `fcl-process`, and also caches the official `fcl-base` sources so cross builds can resolve units such as `Base64` on demand from the shipped FPC packages.

## Project Structure for Compilation

Expand Down
2 changes: 1 addition & 1 deletion docs/built-ins.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ RegExp is available as both `RegExp()` and `new RegExp()`. Regex literals (`/pat
- When the replacer is a function and named groups are present, the `groups` object is passed as the last argument after `input`.
- `String.prototype.match`, `matchAll`, `replace`, `replaceAll`, `search`, and `split` dispatch through the corresponding well-known symbol hooks, so custom protocol objects work as expected.
- `matchAll()` returns a lazy iterator that advances matches on demand per the specification.
- The `u` flag enables Unicode-aware pattern matching. Unicode property escapes (`\p{Letter}`, `\P{ASCII}`, etc.) are expanded to equivalent character classes. Unicode code point escapes (`\u{41}`, `\u{1F600}`) are converted to UTF-8 byte sequences. Supported properties: `L`/`Letter`, `Lu`/`Uppercase_Letter`, `Ll`/`Lowercase_Letter`, `N`/`Number`, `Nd`/`Decimal_Number`, `P`/`Punctuation`, `S`/`Symbol`, `Z`/`Separator`, `Cc`/`Control`, `ASCII`, `ASCII_Hex_Digit`, `White_Space`. Unsupported properties throw `SyntaxError`. The `u` flag also disables TRegExpr's Russian charset extensions and enables correct `AdvanceStringIndex` for multi-byte UTF-8 sequences.
- The `u` flag enables Unicode-aware pattern matching. Unicode property escapes (`\p{Letter}`, `\P{ASCII}`, etc.) are matched against Unicode code point range tables. Unicode code point escapes (`\u{41}`, `\u{1F600}`) are converted to UTF-8 byte sequences. Supported properties: `L`/`Letter`, `Lu`/`Uppercase_Letter`, `Ll`/`Lowercase_Letter`, `N`/`Number`, `Nd`/`Decimal_Number`, `P`/`Punctuation`, `S`/`Symbol`, `Z`/`Separator`, `Cc`/`Control`, `ASCII`, `ASCII_Hex_Digit`, `White_Space`. Unsupported properties throw `SyntaxError`. The `u` flag enables correct `AdvanceStringIndex` for multi-byte UTF-8 sequences.
- The `v` flag (Unicode sets) is accepted and exposed through `.flags` and `.unicodeSets`. The `u` and `v` flags are mutually exclusive. Full Unicode set notation and properties of strings in character classes are not yet implemented beyond basic `u` flag behavior.
- The `d` flag (indices) is accepted and exposed through `.flags` and `.hasIndices`. Match indices are not yet populated.

Expand Down
2 changes: 2 additions & 0 deletions docs/decision-log.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Chronological record of key architectural and implementation decisions, newest f

---

**2026-05-08** · `engine` — Replace TRegExpr with a purpose-built backtracking bytecode VM regex engine. TRegExpr used native call recursion for backtracking, causing SIGSEGV on inputs ~42K+ chars when combined with the evaluator's stack depth (#515). Three preprocessing passes papered over feature gaps: `(?s)` modifier scope leak, no named groups (two-pass rewrite), and inadequate Unicode (`\p{...}` expanded to ASCII approximations). New architecture: `Goccia.RegExp.Compiler.pas` (recursive-descent parser + bytecode emitter) and `Goccia.RegExp.VM.pas` (iterative dispatch loop with heap-allocated backtrack stack and always-on failure memoization). The compiler parses ES2026 regex grammar directly, handling named groups, Unicode property escapes, inline modifier groups, and backreferences natively — no preprocessing passes. The memoization cache records `(PC, InputPos)` failure states to prune exponential backtracking (e.g., `(a+)+b`). Configurable step limit (default 10M) throws `Error` instead of crashing. Removes the FPC `regexpr` package from the cross-compilation toolchain. Reuses `TextSemantics.pas` UTF-8 functions (`TryReadUTF8CodePoint`, `AdvanceUTF8StringIndex`, `CodePointToUTF8`, etc.) rather than reimplementing. Public API (`ExecuteRegExp` signature, `TGocciaRegExpMatchResult` record) unchanged; `Goccia.RegExp.Runtime.pas` and `Goccia.Builtins.GlobalRegExp.pas` unmodified.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

**2026-05-05** · `parser` — Opt-in traditional `for(init; test; update)` loops (`--compat-traditional-for-loop`). Added behind a new compatibility flag for ECMAScript compatibility when porting legacy code, mirroring the existing `--compat-var` and `--compat-function` posture. The flag is off by default and ORed in by `--compat-all` so test262 (which always passes `--compat-all`) executes traditional `for(;;)` bodies that previously parser-warn-and-skipped — surfacing real engine gaps in unrelated areas (Atomics #541, Intl #542, BigInt postfix increment #540, etc.). `let`/`const` declarations in for-init create a per-iteration lexical environment per ES2026 §14.7.4.4, so closures captured during iteration N pin to that iteration's binding (the textbook `fns.push(() => i)` case yields `[0, 1, 2]`, not `[3, 3, 3]`). `var` declarations require both `--compat-var` and the new flag and share a single hoisted binding visible after the loop. The bytecode compiler reuses the counted-loop pattern from `CompileCountedForOf` for `for(let i = N; i <op> M; i++ | i--)` shapes (rejecting var/const, bodies that mutate the loop var, type annotations, and non-integer-literal cond RHS). `while` and `do...while` remain excluded — they have the same stub status but were intentionally split into a separate iteration. [language-tables.md](language-tables.md).

**2026-05-04** · `testing` · [#513](https://github.com/frostney/GocciaScript/pull/513) — test262 conformance harness reframed around the standard tc39 convention. Previously the wrapper ran inside `GocciaTestRunner` and had to selectively hide / capture / restore the test-library globals (`expect`, `describe`, `test`, `runTests`, etc.) it registered, with failure capture leaning on an `undefined` sentinel that collided with thrown `undefined` and chunked-runner crashes that masked thousands of conformance failures as wrapper failures (#491 history). Replaced with: per-test `GocciaScriptLoaderBare` subprocess, stock tc39/test262 harness files read directly from the pinned checkout's `harness/` directory (with a small set of bundled adaptations under `scripts/test262_harness/` for stock files that depend on language features Goccia excludes by design or that work around specific engine bugs — see [test262.md § Bundled harness adaptations](test262.md#bundled-harness-adaptations)), exit-code + stdout-marker wire protocol identical to `test262-harness`/`eshost`/test262.fyi, and a thin TypeScript orchestrator (`scripts/run_test262_suite.ts`). No eligibility filter — every discovered test runs; per-test subprocess + `--timeout` + `--max-memory` bound the blast radius. Wrapper-template drift is now structurally impossible because the "template" is `harness + body` string concatenation. Wrapper-infra failures are classified separately and gated to zero in CI. Surfaced eleven engine bugs (all milestoned 0.8.0, all labeled `engine` per the architecture split where `engine` covers `TGocciaEngine` — language semantics + ECMAScript built-ins — and `runtime` is reserved for `TGocciaRuntime` host extensions like console/fetch/JSON5): [#514](https://github.com/frostney/GocciaScript/issues/514) (Iterator.concat SIGSEGV), [#515](https://github.com/frostney/GocciaScript/issues/515) (RegExp.test SIGSEGV), [#516](https://github.com/frostney/GocciaScript/issues/516) (`Reflect.construct` rejects function decls/exprs), [#517](https://github.com/frostney/GocciaScript/issues/517) (script-mode unattached call `this`), [#518](https://github.com/frostney/GocciaScript/issues/518) (bytecode VM Range-check on top-level `Promise.then` drain), [#519](https://github.com/frostney/GocciaScript/issues/519) (`Error.prototype.constructor` missing), [#520](https://github.com/frostney/GocciaScript/issues/520) (module arrow `this` lexical inheritance), [#521](https://github.com/frostney/GocciaScript/issues/521) (`var`/`function` shadowing built-in globals), [#522](https://github.com/frostney/GocciaScript/issues/522) (`String(obj)` doesn't invoke `toString`), [#523](https://github.com/frostney/GocciaScript/issues/523) (`yield*` accesses `.next` on null), [#524](https://github.com/frostney/GocciaScript/issues/524) (for-of re-fetches `iterator.next` each iteration). Each bundled-harness adaptation under `scripts/test262_harness/` references its tracking issue and is to be removed when the underlying engine bug is fixed. [test262.md](test262.md).
Expand Down
2 changes: 0 additions & 2 deletions scripts/run_test262_suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,6 @@ const DEFAULT_JOBS = 4;
// bug. Per docs/test262.md "Updating the contract", this list is the
// only allowed form of test-skipping; no generic eligibility filter.
const KNOWN_ENGINE_CRASHES = new Set<string>([
// SIGSEGV: RegExp.prototype.test trailing-input edge case. https://github.com/frostney/GocciaScript/issues/515
"staging/sm/RegExp/test-trailing.js",
]);

// ---------------------------------------------------------------------------
Expand Down
Loading
Loading