Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ It's based on the thought "What if we implement ECMAScript today, but without th

## Features

GocciaScript implements a modern subset of ECMAScript: `let`/`const`, arrow functions, classes with private fields, `for...of`, async/await, ES modules, decorators, and TypeScript-style type annotations. Features that are error-prone, redundant, or security risks (`var`, `function` keyword, `==`/`!=`, `eval`, labels, traditional loops) are excluded by default; selected legacy forms are available through explicit compatibility flags.
GocciaScript implements a modern subset of ECMAScript: `let`/`const`, arrow functions, classes with private fields, `for...of`, async/await, ES modules, decorators, and TypeScript-style type annotations. Features that are error-prone, redundant, or security risks (`var`, `function` keyword, `==`/`!=`, `eval`, labels, traditional loops, `for...in`) are excluded by default; selected legacy forms are available through explicit compatibility flags.

See [Language](docs/language.md) for the complete specification of supported features, TC39 proposals, and exclusions.

Expand Down
1 change: 1 addition & 0 deletions docs/build-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ Relative paths are resolved against the current working directory. A missing fil
"compat-loose-equality": true,
"compat-label": true,
"compat-traditional-for-loop": true,
"compat-for-in-loop": true,
"compat-while-loops": true,
"strict-types": true,
"unsafe-ffi": true,
Expand Down
4 changes: 3 additions & 1 deletion 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-06-02** · `engine` — Added opt-in `for...in` JavaScript compatibility behind `--compat-for-in-loop` / `"compat-for-in-loop"` / `cfForIn`. The flag remains separate from `--compat-traditional-for-loop` because `for...in` uses property-name enumeration rather than counted-loop control flow. It stays off by default with the existing parser warning/no-op posture and supports enumerable string own and inherited keys in interpreter and bytecode modes. [language.md](language.md#forin-loop).

**2026-05-29** · `engine` — Labeled `break` and `continue` targets are available behind `--compat-label` / `"compat-label"` for JavaScript compatibility and test262 coverage. Labels stay disabled by default, but when enabled the parser preserves label targets and both interpreter and bytecode paths resolve named breaks and iteration-only continues. [language.md § Labeled Statements](language.md#labeled-statements).

**2026-05-24** · `parser` — Parser policy now lives behind the source pipeline instead of being repeated at host call sites. `TGocciaSourcePipeline` owns compatibility/source-type mapping and exposes narrow parse entry points for full source, module source, dynamic `Function` validation/wrapping, and expression fragments; module loading consumes a module-specific result rather than the full host-facing parse report. [architecture.md § Overview](architecture.md#overview). [embedding.md § Engine API](embedding.md#engine-api).
Expand All @@ -43,7 +45,7 @@ Chronological record of key architectural and implementation decisions, newest f

**2026-05-11** · `engine` — Added opt-in loose equality compatibility (`--compat-loose-equality` / `"compat-loose-equality"`). `==` and `!=` remain warning-producing no-ops by default, but when enabled they use the shared ES2026 `IsLooselyEqual` implementation in interpreter and bytecode modes, including string/number/boolean coercion, BigInt mixed comparisons, and object `ToPrimitive`; the bytecode format is bumped to v27 for `OP_LOOSE_EQ` and `OP_LOOSE_NEQ`. [language.md](language.md#loose-equality--and-).

**2026-05-10** · `engine` — Removed the `--compat-all` meta-flag. It was originally added to give test262 a single switch that turns on every `--compat-*` option, but in practice it (a) hides which compatibility surface a given run actually depends on, (b) silently widens its own meaning whenever a new `--compat-*` flag lands, and (c) needs the same fan-out logic kept in sync in three frontends (`GocciaScriptLoaderBare.dpr`, `Goccia.CLI.Application.pas`, `GocciaBundler.dpr`). Consumers now enumerate the specific flags they need; `scripts/run_test262_suite.ts` lists `--compat-var --compat-function --compat-traditional-for-loop` explicitly. The individual compat flags themselves (`--compat-var`, `--compat-function`, `--compat-traditional-for-loop`) and their `goccia.json` keys are unchanged.
**2026-05-10** · `engine` — Removed the `--compat-all` meta-flag. It was originally added to give test262 a single switch that turns on every `--compat-*` option, but in practice it (a) hides which compatibility surface a given run actually depends on, (b) silently widens its own meaning whenever a new `--compat-*` flag lands, and (c) needs the same fan-out logic kept in sync in three frontends (`GocciaScriptLoaderBare.dpr`, `Goccia.CLI.Application.pas`, `GocciaBundler.dpr`). Consumers now enumerate the specific flags they need; `scripts/run_test262_suite.ts` lists the required `--compat-*` flags explicitly for the conformance corpus. The individual compat flags themselves and their `goccia.json` keys are unchanged.

**2026-05-08** · `runtime` — ECMA-402 Intl namespace. Full implementation of the Internationalization API: Intl.Locale, Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat, Intl.PluralRules, Intl.RelativeTimeFormat, Intl.ListFormat, Intl.DisplayNames, Intl.Segmenter, Intl.DurationFormat, plus Intl.getCanonicalLocales and Intl.supportedValuesOf. Data strategy mirrors Temporal timezone: system ICU library (macOS libicucore, Linux libicui18n, Windows icu.dll shared with Temporal) as primary, embedded CLDR resource as fallback. Shared units in `source/shared/` (ICU.pas, BCP47.pas, IntlTypes.pas, IntlICU.pas, IntlCLDRData.pas, IntlLocaleResolver.pas) provide platform-independent Intl infrastructure. Existing locale stubs on String/Number/Date prototypes wired to Intl formatters. [built-ins-intl.md](built-ins-intl.md). [build-system.md § Generated Intl Data](build-system.md#generated-intl-data).

Expand Down
1 change: 1 addition & 0 deletions docs/embedding.md
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,7 @@ try
cfNonStrictMode,
cfLabel,
cfTraditionalFor,
cfForIn,
cfWhileLoops
]; // Enable selected compatibility semantics
Engine.SourceType := stModule; // Run entry as module source (top-level this is undefined; import.meta resolves)
Expand Down
1 change: 1 addition & 0 deletions docs/errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Other features that produce suggestions include:
- `function` declarations and expressions -- suggests arrow functions
- `==` / `!=` (loose equality) -- suggests `===` / `!==`
- Traditional `for(init; test; update)` loops when `--compat-traditional-for-loop` is off -- suggests `for...of`, array methods, or the flag
- `for...in` loops when `--compat-for-in-loop` is off -- suggests `Object.keys()` / `Object.entries()` with `for...of`, or the flag
- `while` / `do...while` loops when `--compat-while-loops` is off -- suggests `for...of`, array methods, or the flag

See [Language](language.md) for the full list of excluded features and their rationale.
Expand Down
2 changes: 1 addition & 1 deletion docs/goals.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Desktop applications built with FreePascal (Lazarus, command-line tools, game en
## What GocciaScript is Not

- **Not a Node.js replacement** — No `require()`, no `node:` built-in modules, no event loop with I/O callbacks
- **Not aiming for 100% ECMAScript conformance** — Features excluded by design, such as `eval`, will not be added. `var`, the `function` keyword, `arguments`/`with`/`delete`/`this` non-strict semantics, loose equality, labels, the traditional `for(init; test; update)` loop, and `while`/`do...while` loops are disabled by default and exposed through targeted JavaScript compatibility toggles (`--compat-var`, `--compat-function`, `--compat-non-strict-mode`, `--compat-loose-equality`, `--compat-label`, `--compat-traditional-for-loop`, `--compat-while-loops`) for cases such as conformance tests or legacy semantic requirements. Runtime enforcement of type annotations is also opt-in (`--strict-types`). See [Language](language.md) for the full list.
- **Not aiming for 100% ECMAScript conformance** — Features excluded by design, such as `eval`, will not be added. `var`, the `function` keyword, `arguments`/`with`/`delete`/`this` non-strict semantics, loose equality, labels, the traditional `for(init; test; update)` loop, `for...in`, and `while`/`do...while` loops are disabled by default and exposed through targeted JavaScript compatibility toggles (`--compat-var`, `--compat-function`, `--compat-non-strict-mode`, `--compat-loose-equality`, `--compat-label`, `--compat-traditional-for-loop`, `--compat-for-in-loop`, `--compat-while-loops`) for cases such as conformance tests or legacy semantic requirements. Runtime enforcement of type annotations is also opt-in (`--strict-types`). See [Language](language.md) for the full list.
- **Not a formally verified sandbox** — The sandbox reduces attack surface but has not been independently audited
- **Not performance-competitive with V8/SpiderMonkey** — GocciaScript prioritizes correctness, embeddability, and reduced attack surface over raw throughput

Expand Down
1 change: 1 addition & 0 deletions docs/language-tables.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
| Non-strict assignment failures | ES1 | Strict by default; script source `--compat-non-strict-mode` silently ignores failed ordinary object/global writes while assignment expressions return the assigned value |
| Labeled statements | ES1 | Opt-in for JavaScript compatibility (`--compat-label`); disabled by default |
| Traditional `for(init; test; update)` loop | ES1 | Opt-in for JavaScript compatibility (`--compat-traditional-for-loop`); disabled by default |
| `for...in` | ES1 | Opt-in for JavaScript compatibility (`--compat-for-in-loop`); disabled by default |
| `while` / `do...while` | ES1 | Opt-in for JavaScript compatibility (`--compat-while-loops`); disabled by default |
| `with` statement | ES1 | Opt-in for script source (`--compat-non-strict-mode`) for compatibility with object-environment lookup, `Symbol.unscopables`, closure capture, method-call receivers, and non-strict write failures — prefer explicit property access |
| `delete` non-strict return values | ES1 | Strict by default; script source `--compat-non-strict-mode` makes `delete identifier` handle declared bindings, configurable global object properties, and unresolvable names with legacy booleans; non-configurable property deletion returns `false` |
Expand Down
33 changes: 27 additions & 6 deletions docs/language.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
- **Modern subset** — `let`/`const`, arrow functions, classes with private fields, `for...of`, async/await, ES modules
- **TC39 proposals** — Decorators, decorator metadata, pattern matching, types as comments, enums, `Math.clamp`
- **Excluded by design** — `eval`, wildcard re-exports
- **Graceful handling** — Parser-recognized excluded or disabled syntax (`==`/`!=` when `--compat-loose-equality` is off, labels when `--compat-label` is off, `while`/`do...while` when `--compat-while-loops` is off, `with` when `--compat-non-strict-mode` is off, traditional `for(;;)` when `--compat-traditional-for-loop` is off) parses successfully but executes as a no-op with a warning and suggestion
- **Opt-in toggles** — ASI (`--compat-asi`), `var` declarations (`--compat-var`), `function` keyword (`--compat-function`), non-strict Script compatibility (`--compat-non-strict-mode` for `arguments`, `with`, silent assignment failures, legacy `delete` returns, and sloppy `this`), loose equality (`--compat-loose-equality`), labels (`--compat-label`), traditional `for(init; test; update)` loops (`--compat-traditional-for-loop`), `while`/`do...while` loops (`--compat-while-loops`), runtime type enforcement (`--strict-types`)
- **Graceful handling** — Parser-recognized excluded or disabled syntax (`==`/`!=` when `--compat-loose-equality` is off, labels when `--compat-label` is off, `while`/`do...while` when `--compat-while-loops` is off, `with` when `--compat-non-strict-mode` is off, traditional `for(;;)` when `--compat-traditional-for-loop` is off, `for...in` when `--compat-for-in-loop` is off) parses successfully but executes as a no-op with a warning and suggestion
- **Opt-in toggles** — ASI (`--compat-asi`), `var` declarations (`--compat-var`), `function` keyword (`--compat-function`), non-strict Script compatibility (`--compat-non-strict-mode` for `arguments`, `with`, silent assignment failures, legacy `delete` returns, and sloppy `this`), loose equality (`--compat-loose-equality`), labels (`--compat-label`), traditional `for(init; test; update)` loops (`--compat-traditional-for-loop`), `for...in` loops (`--compat-for-in-loop`), `while`/`do...while` loops (`--compat-while-loops`), runtime type enforcement (`--strict-types`)
- **Default preprocessors** — JSX (enabled by default via `DefaultPreprocessors`)

GocciaScript implements a curated subset of ECMAScript. This document details what's supported, what's excluded, and the rationale for each decision. For quick-reference tables of every feature and TC39 proposal, see [Language Tables](language-tables.md).
Expand Down Expand Up @@ -133,6 +133,7 @@ class Counter {
- `return`
- Block statements
- `for...of` and `for await...of` (see [Supported Iteration](#supported-iteration))
- `for...in` via `--compat-for-in-loop` (see [`for...in`](#forin-loop))
- `while` and `do...while` via `--compat-while-loops` (see [`while` and `do...while`](#while-and-dowhile))
- `import`/`export` (ES module system)

Expand Down Expand Up @@ -665,7 +666,7 @@ console.log(x); // 5

With `let`/`const`, accessing before declaration is a `ReferenceError` (Temporal Dead Zone), which catches bugs early.

When enabled (CLI: `--compat-var`, engine API: include `cfVar` in `Engine.Compatibility`, config: `{"compat-var": true}`), `var` declarations follow ES2026 §14.3.2 semantics: function-scoped (escapes blocks), hoisted to function top as `undefined`, redeclaration allowed, no TDZ, with destructuring and for-of support. Var bindings are stored in a separate binding map (`FVarBindings`) on function/module/global scopes, distinct from lexical bindings. See [interpreter.md § Scope Chain Design](interpreter.md#scope-chain-design).
When enabled (CLI: `--compat-var`, engine API: include `cfVar` in `Engine.Compatibility`, config: `{"compat-var": true}`), `var` declarations follow ES2026 §14.3.2 semantics: function-scoped (escapes blocks), hoisted to function top as `undefined`, redeclaration allowed, no TDZ, with destructuring, for-of, and enabled for-in support. Var bindings are stored in a separate binding map (`FVarBindings`) on function/module/global scopes, distinct from lexical bindings. See [interpreter.md § Scope Chain Design](interpreter.md#scope-chain-design).

### `function` Keyword

Expand Down Expand Up @@ -796,7 +797,27 @@ items.filter((item) => item.isValid);
items.reduce((acc, item) => acc + item, 0);
```

`break` exits the nearest enclosing `switch`, `for...of`, `for await...of`, enabled traditional `for` loop, or enabled `while`/`do...while` loop. `continue` only applies to iteration constructs — `for...of`, `for await...of`, enabled traditional `for`, and enabled `while`/`do...while` — never to `switch`.
`break` exits the nearest enclosing `switch`, `for...of`, `for await...of`, enabled traditional `for` loop, enabled `for...in` loop, or enabled `while`/`do...while` loop. `continue` only applies to iteration constructs — `for...of`, `for await...of`, enabled traditional `for`, enabled `for...in`, and enabled `while`/`do...while` — never to `switch`.

### `for...in` Loop

**Opt-in for JavaScript compatibility.** Excluded by default. Available via `--compat-for-in-loop` (CLI flag, `cfForIn` in `Engine.Compatibility`, or `{"compat-for-in-loop": true}` in config) when a program or conformance suite needs ECMAScript property enumeration semantics.

When disabled (default), the parser accepts `for...in` declaration and assignment-target forms but treats the loop as a no-op and emits a warning. When enabled, declaration-head loops such as `for (const key in object) body` / `for (let key in object) body` and assignment-target loops such as `for (key in object) body` are supported in interpreter and bytecode modes:

- The right-hand side follows ECMAScript `ForIn/OfHeadEvaluation`: `null` and `undefined` produce an empty loop; other primitives are boxed with `ToObject`.
- The loop enumerates enumerable string property names, including inherited enumerable properties, and never returns symbol keys.
- A property name appears at most once. An own property shadows a prototype property with the same name even when the own property is non-enumerable.
- `var` declaration heads, including destructuring heads, require both `--compat-var` and `--compat-for-in-loop`; the bindings hoist out of the loop and are shared across iterations.
- `break`, `continue`, and `return` unwind as they do in other supported loops.

New GocciaScript code should usually prefer `Object.keys(obj)`, `Object.entries(obj)`, or explicit `for...of` over property names:

```javascript
for (const key of Object.keys(obj)) {
// ...
}
```

### `while` and `do...while`

Expand Down Expand Up @@ -850,8 +871,8 @@ The labeled statement itself (the statement after the `:`) is still parsed and e

When enabled, labels can target `break` and `continue` statements in interpreter and bytecode modes:

- `break label;` exits the matching enclosing labeled statement, including labeled blocks, `switch`, `for...of`, `for await...of`, traditional `for(;;)` with `--compat-traditional-for-loop`, and `while`/`do...while` with `--compat-while-loops`.
- `continue label;` targets matching enclosing labeled iteration statements only: `for...of`, `for await...of`, traditional `for(;;)` with `--compat-traditional-for-loop`, and `while`/`do...while` with `--compat-while-loops`.
- `break label;` exits the matching enclosing labeled statement, including labeled blocks, `switch`, `for...of`, `for await...of`, traditional `for(;;)` with `--compat-traditional-for-loop`, `for...in` with `--compat-for-in-loop`, and `while`/`do...while` with `--compat-while-loops`.
- `continue label;` targets matching enclosing iteration statements only: `for...of`, `for await...of`, traditional `for(;;)` with `--compat-traditional-for-loop`, `for...in` with `--compat-for-in-loop`, and `while`/`do...while` with `--compat-while-loops`.

### Generators and Iterators

Expand Down
13 changes: 9 additions & 4 deletions docs/test262.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,10 +256,15 @@ Script tests also receive the flag, but the injected directive keeps
`arguments`, `with`, non-strict assignment failures, legacy `delete`
return values, and regular-function nullish `this` coercion on the strict
path. Remaining `noStrict` tests rely on sloppy-only behaviors that
GocciaScript still does not provide and fail naturally. They are
documented in `scripts/test262_compatibility_roadmap.json` as
`excluded-by-language-design` and counted as expected failures, not as
wrapper-infra failures.
GocciaScript still does not provide and fail naturally as ordinary
conformance failures, not as wrapper-infra failures.

The runner passes syntax compatibility flags such as
`--compat-traditional-for-loop`, `--compat-for-in-loop`,
`--compat-while-loops`, and `--compat-label` unconditionally because
test262 uses those forms across both harness helpers and test bodies; the
test's source type and strictness still decide runtime strict-mode
semantics.

## Path normalization

Expand Down
1 change: 1 addition & 0 deletions docs/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ Here's a quick reference of GocciaScript's key restrictions:
| `==` / `!=` | Off by default | `===` / `!==` or `--compat-loose-equality` |
| labels / `break label` / `continue label` | Off by default | `return` from a helper, an early-exit flag, or `--compat-label` for JavaScript compatibility |
| `for (init; test; update)` | Off by default | `for...of`, `.map()`, `.forEach()`, `.reduce()`, or `--compat-traditional-for-loop` for JavaScript compatibility |
| `for (key in object)` | Off by default | `Object.keys()` / `Object.entries()` with `for...of`, or `--compat-for-in-loop` for JavaScript compatibility |
| `while (...)` / `do ... while (...)` | Off by default | `for...of`, `.map()`, `.forEach()`, `.reduce()`, or `--compat-while-loops` for JavaScript compatibility |
| `eval("code")` | Not supported | No alternative (by design) |
| `arguments` | Off by default | Prefer rest parameters (`...args`) or script source `--compat-non-strict-mode` |
Expand Down
Loading
Loading