semantifold 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -4
- package/build/index.d.ts +8 -8
- package/build/index.d.ts.map +1 -1
- package/build/index.js +6 -6
- package/build/src/backends/android.js +2 -2
- package/build/src/backends/dart.d.ts +19 -0
- package/build/src/backends/dart.d.ts.map +1 -1
- package/build/src/backends/dart.js +28 -16
- package/build/src/backends/flutter.d.ts +51 -0
- package/build/src/backends/flutter.d.ts.map +1 -0
- package/build/src/backends/flutter.js +1362 -0
- package/build/src/backends/identifiers.js +5 -5
- package/build/src/backends/program.d.ts +4 -4
- package/build/src/backends/program.d.ts.map +1 -1
- package/build/src/backends/program.js +4 -4
- package/build/src/backends/shared.d.ts +7 -0
- package/build/src/backends/shared.d.ts.map +1 -1
- package/build/src/backends/shared.js +11 -6
- package/build/src/language-registry.d.ts.map +1 -1
- package/build/src/language-registry.js +11 -1
- package/build/src/semantic/types.d.ts +152 -2
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +38 -2
- package/docs/android.md +6 -2
- package/docs/architecture.md +2 -0
- package/docs/flutter.md +47 -0
- package/docs/goals.md +1 -1
- package/docs/language-support.md +9 -2
- package/docs/source-maps.md +2 -0
- package/docs/testing.md +22 -1
- package/docs/watch-build-pipeline.md +154 -0
- package/index.js +5 -5
- package/package.json +1 -1
- package/src/backends/android.js +1 -1
- package/src/backends/dart.js +28 -15
- package/src/backends/flutter.js +1465 -0
- package/src/backends/identifiers.js +4 -4
- package/src/backends/program.js +3 -3
- package/src/backends/shared.js +12 -8
- package/src/language-registry.js +10 -0
- package/src/semantic/types.js +40 -1
package/docs/flutter.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Flutter application target
|
|
2
|
+
|
|
3
|
+
Task 030 adds the target-only `flutter` application backend. Flutter is not a source language: callers parse any existing source language into a semantic module or Task 010 program, then request an application artifact set.
|
|
4
|
+
|
|
5
|
+
```js
|
|
6
|
+
const program = parseProgram({entryModule: "main", sources})
|
|
7
|
+
const project = generateProgramArtifactSet({
|
|
8
|
+
language: "flutter",
|
|
9
|
+
program,
|
|
10
|
+
role: "application"
|
|
11
|
+
})
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The current qualified slice is Android-only. Every generated path is below `generated/flutter-app`; no iOS runner is emitted. macOS, Xcode, Apple SDK, CocoaPods, signing, iOS Simulator build/launch, and iOS UI proof remain explicitly deferred by owner direction. The registry and project manifest report that boundary rather than treating an absent Apple toolchain as a skip or claiming dual-platform completion.
|
|
15
|
+
|
|
16
|
+
## Application and package contract
|
|
17
|
+
|
|
18
|
+
The backend accepts exactly the semantic subset already supported by the Dart backend. It emits mapped, namespaced Dart parts plus a synthetic stateless Material shell. Each invocation creates a fresh output sink, executes the semantic entry, joins printed values with LF, and displays the exact result below `Key('semantifold-output')` and the `semantifold-output` semantics label. Widget lifecycle and platform-runner behavior do not enter semantic IR.
|
|
19
|
+
|
|
20
|
+
The emitted template is owned and versioned by Semantifold; generation never invokes `flutter create`. Its Android configuration excludes the inactive profile-installer optimization and removes the SDK embedding's dynamic-receiver permission and startup provider; final-APK acceptance requires zero permissions, providers, receivers, or services. Its package manifest has only direct Flutter-SDK dependencies:
|
|
21
|
+
|
|
22
|
+
- `flutter` from the SDK;
|
|
23
|
+
- `flutter_test` from the SDK as a development dependency.
|
|
24
|
+
|
|
25
|
+
The exact lockfile records the transitive test/runtime packages shipped in the checksum-qualified Flutter archive. Restore is always offline from the archive's preload cache and uses `--enforce-lockfile`; application generation and tests never download a pub package. There are no plugins, platform channels, permissions, external packages, embedded source runtimes, network/storage/background behavior, navigation, or mutable widget state.
|
|
26
|
+
|
|
27
|
+
Configuration is a closed record. Defaults are `semantifold_generated`, organization `dev.semantifold`, application ID `dev.semantifold.generated`, activity `MainActivity`, display name `Semantifold`, version `1.0.0+1`, minimum SDK 23, target/compile SDK 35, and Build Tools 35.0.0. Package and reverse-DNS identities, Java activity name, Unicode display name, version values, the empty permission set, and the exact Android matrix are validated before rendering. A root package name cannot collide with any SDK or hosted package in the owned lockfile, and the launcher class cannot collide with its imported `FlutterActivity` base class.
|
|
28
|
+
|
|
29
|
+
Assets are explicit closed records `{path, content, mediaType, sha256}` below `assets/`. Content is detached before return. Traversal, absolute or unsafe paths, Unicode/case collisions, file-prefix collisions, generated-path conflicts, malformed media types, empty content, and checksum mismatches fail transactionally.
|
|
30
|
+
|
|
31
|
+
## Ownership and provenance
|
|
32
|
+
|
|
33
|
+
`SemantifoldFlutterProject` version 1 records every owned path and SHA-256, excluded cache/build/local-configuration paths, normalized configuration and exact output ranges, asset hashes, semantic source/module/import/entry identities, synthetic scaffolding, platform status, and toolchain identity. Dart semantic tokens—including ordinary source `print` syntax—preserve rich mappings and Source Map v3 projections to all original source files. The Flutter output sink, Material widgets, tests, Android runner, Gradle files, manifests, resources, and template metadata are synthetic or configuration-derived. Generated zero-argument declarations and calls retain the pinned Dart formatter's multiline layout before toolchain validation.
|
|
34
|
+
|
|
35
|
+
The private acceptance materializer writes only to one caller-created empty, real, mode-0700 directory. It uses exclusive creation, reads every file back, verifies checksums, removes partial generated output on failure, and is neither exported nor packed. Generated `android/local.properties`, caches, debug keys, Gradle outputs, and APKs belong only to disposable acceptance state.
|
|
36
|
+
|
|
37
|
+
## Exact Android qualification
|
|
38
|
+
|
|
39
|
+
Flutter is pinned to stable `3.47.4`, framework revision `9584c6713b324636289d067944a46fd6b49df14b`, bundled Dart `3.13.3`, archive size `1576174568`, and SHA-256 `5b45f0ceda99b9bebdc873e7e69f6450aeb4c30f454b505e2e62fc9255a907d3`. The generated Android project reuses Task 028's API-35/Build-Tools-35.0.0, AGP 8.11.1, JDK 21.0.8, emulator 35.6.11, private signing root, and sole TensorBuzz `/dev/kvm` topology. Its engine build pins Kotlin Gradle plugin 2.2.20, NDK 27.0.12077973, and CMake 3.22.1; Kotlin's implicit standard-library dependency remains disabled and generated applications contain no Kotlin source or user native code. The shared Gradle baseline advances to checksum-pinned 8.14 because Flutter 3.47.4 rejects 8.13; dependency validation is not bypassed and no second Gradle/cache topology is introduced.
|
|
40
|
+
|
|
41
|
+
Online bootstrap downloads only the exact Flutter archive, validates it before installation, seeds the isolated pub cache from its 186 included preload archives, and warms the already-shared Gradle cache with a disposable project. Offline acceptance then enforces the lockfile, checks formatter canonicality, runs analyzer with fatal warnings/information, runs real Flutter unit/widget tests, and runs Android lint and debug assembly. It verifies the exact package graph, SDK-owned Flutter embedding, empty declared permission surface, and produced APK.
|
|
42
|
+
|
|
43
|
+
The existing Android emulator script remains the only device lane and still performs exactly two fresh hardware-accelerated API-35 boots. Each boot retains the native Task 028 proof, then installs and launches the Flutter APK, captures UI XML and a screenshot, and requires exactly one `semantifold-output` Android semantics descriptor with exactly one descendant descriptor containing the exact Unicode output. Native same-node text and the previously owned combined descriptor remain accepted exact forms. Failure diagnostics report the observed descriptor/text values without accepting unrelated UI text. Missing KVM, SDK artifacts, cached dependencies, tools, label, output, or clean teardown fails loudly. Local environments without KVM do not substitute software emulation.
|
|
44
|
+
|
|
45
|
+
## Deferred and excluded work
|
|
46
|
+
|
|
47
|
+
No result from this slice establishes iOS support. Dual-platform Task 030 completion remains blocked on separately authorized real Apple toolchain and iOS Simulator proof. Web/desktop Flutter, arbitrary widgets/state/navigation, plugins, platform channels, host APIs, external pub packages, native extensions, physical devices, release signing, store upload, and deployment remain outside the baseline.
|
package/docs/goals.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Semantifold's long-term goal is a language-neutral toolkit for understanding, transforming, generating, and eventually executing programs through a shared high-level semantic representation. The representation should make types, control flow, calls, declarations, diagnostics, and source provenance understandable without exposing a language parser's private tree.
|
|
4
4
|
|
|
5
|
-
The current source tree proves one minimal Tasks 001–004 vertical slice across fifteen registered source languages: PHP, Ruby, JavaScript, TypeScript, Java, Kotlin/JVM, strictly annotated Python, canonical C#, bounded Go, C17, C++20, Rust edition 2021, Swift, Dart, and Zig. Dart and Zig additionally implement the bounded Task 005 required-function/call profile; the original five retain the delivered Task 013 acceptance gate for the combined Tasks 001–008 profile, Task 009 nominal closed records, Task 010 explicit acyclic multi-source semantic programs, Task 011 exact nominal typed unchecked errors, and Task 012 invariant unbounded function/record type parameters with closed applications and argument-only first-order call inference. Task 014 is delivered through PR #41, Task 032 through PR #42, Task 033 through PR #43, and Task 034 through PR #44. Task 035 is delivered through PR #45 and merge commit `a2aad9d4932bfc6087adc4ff8bca43678c92413c`, with versioned canonical standard-library contracts, target host providers and protected native bindings, capability negotiation, and program-mode provider linking. Task 036 is delivered through PR #46 and merge `9aba48f9d48bcef4fd4c9275aec14bfcc4f0904c`; it adds bounded versioned executable qualification facades and exact parser-backed native identity resolution for the original five. Task 037 adds only the first concrete Ruby-source-to-PHP-target blocking TCP/line-output slice on those foundations. The source tree includes versioned source provenance, deterministic semantic node/symbol identity, range-based generated mappings, Source Map v3 interop, composition, and diagnostic remapping across those bounded profiles. The published `semantifold@0.
|
|
5
|
+
The current source tree proves one minimal Tasks 001–004 vertical slice across fifteen registered source languages: PHP, Ruby, JavaScript, TypeScript, Java, Kotlin/JVM, strictly annotated Python, canonical C#, bounded Go, C17, C++20, Rust edition 2021, Swift, Dart, and Zig. Dart and Zig additionally implement the bounded Task 005 required-function/call profile; the original five retain the delivered Task 013 acceptance gate for the combined Tasks 001–008 profile, Task 009 nominal closed records, Task 010 explicit acyclic multi-source semantic programs, Task 011 exact nominal typed unchecked errors, and Task 012 invariant unbounded function/record type parameters with closed applications and argument-only first-order call inference. Task 014 is delivered through PR #41, Task 032 through PR #42, Task 033 through PR #43, and Task 034 through PR #44. Task 035 is delivered through PR #45 and merge commit `a2aad9d4932bfc6087adc4ff8bca43678c92413c`, with versioned canonical standard-library contracts, target host providers and protected native bindings, capability negotiation, and program-mode provider linking. Task 036 is delivered through PR #46 and merge `9aba48f9d48bcef4fd4c9275aec14bfcc4f0904c`; it adds bounded versioned executable qualification facades and exact parser-backed native identity resolution for the original five. Task 037 adds only the first concrete Ruby-source-to-PHP-target blocking TCP/line-output slice on those foundations. The source tree includes versioned source provenance, deterministic semantic node/symbol identity, range-based generated mappings, Source Map v3 interop, composition, and diagnostic remapping across those bounded profiles. The published `semantifold@0.6.0` and annotated `v0.6.0` target exact commit `f76e6d64b388d65959e77f747ae2d60a999d61f5`, including Task 028 from PR #51 and Task 031 from PR #52. Neither the source tree nor that release provides a general analyzer, transformation API, interpreter, optimizer, implicit project/package discovery, debugger integration, coverage remapping, or lossless formatter.
|
|
6
6
|
|
|
7
7
|
The long-term product direction includes bounded standard-library portability through [language compatibility stdlib/facades, canonical Semantifold stdlib contracts/capabilities, and target host providers/native bindings](standard-library-portability.md). That direction is incremental and versioned; it does not promise arbitrary standard-library coverage or arbitrary-application translation. The `0.3.0` release has no imports, classes or methods, loops, optional values, typed errors, resources, host APIs, or stdlib portability.
|
|
8
8
|
|
package/docs/language-support.md
CHANGED
|
@@ -24,8 +24,9 @@ The immutable registry is authoritative for both dispatch and discovery:
|
|
|
24
24
|
| `wasm` | no | no | yes | yes | no | no | no | no | no | no | no | no | no | no | no | no | multiple | no | rich text + v3 + byte ranges | generate, validate, instantiate, execute (`wasm-validate`, `node`, `chromium`) |
|
|
25
25
|
| `ios` | no | no | no | yes | no | no | no | no | no | no | no | no | no | no | no | no | multiple | no | rich text + v3 + byte ranges | generate (Apple acceptance deferred) |
|
|
26
26
|
| `android` | no | no | no | yes | no | no | no | no | no | no | no | no | no | no | no | no | multiple | no | rich text + v3 + byte ranges | generate, compile, validate, instantiate, execute (`android`, TensorBuzz KVM) |
|
|
27
|
+
| `flutter` | no | no | no | yes | no | no | no | no | no | no | no | no | no | no | no | no | multiple | no | rich text + v3 + byte ranges | generate, restore, compile, validate, instantiate, execute (`flutter`, `android`; Android qualified, iOS deferred) |
|
|
27
28
|
|
|
28
|
-
`languageCapabilities` exposes all
|
|
29
|
+
`languageCapabilities` exposes all nineteen records as frozen data, including `features.generalFunctionsAndCalls`, `features.immutableCollections`, `features.optionalValues`, `features.orderedListIteration`, `features.orderedMapIteration`, `features.conditionControlledLoops`, `features.referenceClasses`, `features.effectfulCapabilitiesAndResources`, `features.closedRecords`, `features.typedErrors`, and `features.typeParametersAndGenerics`. A role or semantic feature is not inferred from another role: browser Wasm has binary/application backends without a frontend or text backend, while `ios`, `android`, and `flutter` have only application backends. None has a source round trip or general interoperability role; Android retains the Kotlin/JVM Tasks 001–004 function subset, while Flutter consumes the independent Dart Tasks 001–005 capability subset and both add resolved Task 010 linkage in their application shells. `supportedLanguages` is the derived list of fifteen records with both frontend and text generation. C#, Go, C, and Zig return two text artifacts; Rust and Dart return three; Wasm returns one binary and three text artifacts; iOS, Android, and Flutter return deterministic multi-artifact application projects. Their callers use `generateArtifactSet()`; the legacy single-artifact APIs reject unavailable roles with `UNSUPPORTED_ROLE`.
|
|
29
30
|
|
|
30
31
|
## Task 026 iOS application generation
|
|
31
32
|
|
|
@@ -39,6 +40,12 @@ The `android` registry row is an application-only target over the independent Ko
|
|
|
39
40
|
|
|
40
41
|
The generated build declares only exact non-transitive `testImplementation("junit:junit:4.13.2")` and `testRuntimeOnly("org.hamcrest:hamcrest-core:1.3")`; Kotlin's implicit stdlib dependency is disabled and no library enters the application runtime classpath. Official tool/cache preparation is online, but lint, unit, application APK, instrumentation APK, dependency inspection, and APK leakage checks execute with Gradle offline. TensorBuzz's `/dev/kvm` lane performs two fresh accelerated API-35 emulator boots with exact labelled-text and lifecycle assertions. No local software-emulator fallback, public writer, signing/store route, permission, service, Compose, WebView, NDK/JNI, source runtime, or platform channel exists. See [Android application target](android.md).
|
|
41
42
|
|
|
43
|
+
## Task 030 Flutter application generation
|
|
44
|
+
|
|
45
|
+
The `flutter` registry row is application-only and does not add a sixteenth frontend. It accepts exactly the semantic project subset supported by the Dart backend, emits mapped per-module Dart plus a synthetic stateless Material shell, and owns a versioned Android Flutter template below `generated/flutter-app`. Exact package identity, the SDK-only pubspec/lock, assets, paths, hashes, configuration causality, semantic provenance, and platform/toolchain status are validated before any artifact set is returned.
|
|
46
|
+
|
|
47
|
+
Flutter stable 3.47.4 with bundled Dart 3.13.3 is checksum-pinned. Offline restore uses only its archive preload cache; format, analyzer, Flutter widget tests, Android lint/build, and exact labelled Unicode UI assertion are real. The one Task 028 Gradle cache advances to 8.14 for Flutter compatibility, while its JDK/AGP/API/build-tools/emulator values and exactly two KVM boots remain shared. No plugin, platform channel, permission, external pub package download, source runtime, public writer, or second Android topology exists. iOS runner artifacts are deliberately omitted and every Apple build/launch/UI gate remains deferred rather than silently skipped. See [Flutter application target](flutter.md).
|
|
48
|
+
|
|
42
49
|
## Task 025 core expanded-language acceptance
|
|
43
50
|
|
|
44
51
|
Python, C#, C, C++, Rust, Swift, Kotlin/JVM, and Go share one data-driven Tasks 001–004 acceptance gate. Their five base/scalar/local/operator/statement fixtures normalize to the same location-neutral semantic corpus; the pre-existing `select`/`choose` spelling difference is treated as a reserved-word adaptation only, not different behavior. Each backend generates, reparses, and runs the complete operator profile through its real registered toolchain. Python and Swift emit one source file; C++ emits one source file; C# and C emit two-file projects; Rust emits `Cargo.toml`, `Cargo.lock`, and `src/main.rs`; Kotlin emits mapped `Program.kt` with runnable-JAR metadata; and Go emits `go.mod` plus `main.go`.
|
|
@@ -300,7 +307,7 @@ Binary/application generation returns `program.wasm`, `program.wasm.map`, `seman
|
|
|
300
307
|
|
|
301
308
|
### Bounded standard-library portability
|
|
302
309
|
|
|
303
|
-
The registered language roles cover only the semantic subset documented above. `semantifold@0.
|
|
310
|
+
The registered language roles cover only the semantic subset documented above. The `semantifold@0.6.0` release contains the Task 032 condition-controlled loops, Task 033 bounded reference classes, Task 034 effect/resource foundation, Task 035 versioned canonical contract/provider-linking hub, Task 036 neutral facade qualification layer, Task 037 bounded TCP vertical slice, Task 028 Android application target, and Task 031 Zig target. Task 030 is the subsequent bounded Flutter application candidate described above; it does not broaden standard-library portability.
|
|
304
311
|
|
|
305
312
|
Task 037 adds exactly one concrete lane: Ruby source targeting PHP. The Ruby frontend accepts only literal `require "socket"`, genuine `TCPSocket.new(host, port)`, zero-argument receiver `gets` and `close`, and exactly one string argument to unqualified `puts` or `Kernel.puts`. The output builtin is resolved only inside that same parser-proved socket profile, preserving legacy Ruby program-mode `puts` elsewhere. User-defined/shadowed/reopened/monkey-patched identities, dynamic require/send, blocks, other members, extra arguments, coercive or multi-value output, and timeout or socket options fail at their parser-backed locations.
|
|
306
313
|
|
package/docs/source-maps.md
CHANGED
|
@@ -130,6 +130,8 @@ The iOS project manifest adds an application-specific provenance index without w
|
|
|
130
130
|
|
|
131
131
|
The Android project uses the same separation without sharing platform syntax with iOS. Every per-module semantic Kotlin artifact has authoritative rich/v3 mappings to all Task 010 source identities and filenames. Gradle, manifest, Activity, lifecycle, layout, resources, runtime, and test artifacts are synthetic or configuration-derived. `SemantifoldAndroidProject` version 1 records field-owned output ranges, exact input and output hashes, fixed-root ownership/exclusions, semantic module/dependency/source/entry linkage, and exact toolchain pins. Binary caller inputs retain full byte provenance. See [Android application target](android.md).
|
|
132
132
|
|
|
133
|
+
The Flutter project preserves the same separation through the independent Dart lowering. Each generated semantic Dart part retains rich mappings and a Source Map v3 projection to all originating Task 010 files, while the shared output sink, Material widget shell, SDK/package configuration, Android runner, resources, and tests are synthetic or configuration-derived. `SemantifoldFlutterProject` version 1 records configuration-owned output ranges, exact asset and artifact hashes, fixed-root ownership/exclusions, semantic module/dependency/source/entry linkage, exact Flutter/Android toolchains, and the explicit omission/deferment of iOS artifacts and proof. See [Flutter application target](flutter.md).
|
|
134
|
+
|
|
133
135
|
## Source Map v3 and directives
|
|
134
136
|
|
|
135
137
|
`artifact.sourceMap` exists for all single-text target artifacts and mapped multi-artifact text outputs; browser Wasm additionally has the external binary projection described above. Maps are generated with `@jridgewell/gen-mapping`, while lookup, import, artifact validation, and independent test decoding use `@jridgewell/trace-mapping`. Semantifold does not hand-code VLQ or tracing. Text artifact validation requires dense `sources`, `names`, and optional `sourcesContent` arrays, canonical standard segments, valid indexes, and equality with the authoritative rich mapping projection. The v3 map contains deterministic `file`, `sources`, `sourcesContent`, `names`, and `mappings` fields.
|
package/docs/testing.md
CHANGED
|
@@ -504,7 +504,28 @@ npx velocious-test spec/android-packed-consumer.spec.js
|
|
|
504
504
|
|
|
505
505
|
Generation coverage validates multi-module/Unicode capture, mappings, configuration causality, deterministic fixed-root ownership, manifest/UI/dependency shape, resources/assets, collisions, and preflight diagnostics. Materialization is repository-private acceptance support: it requires an empty mode-0700 non-symlink directory, creates files exclusively, verifies checksum readback, and is neither exported nor packed. The packed proof uses clean public-registry npm install/ci and strict TypeScript consumption.
|
|
506
506
|
|
|
507
|
-
TensorBuzz's Android lane runs `scripts/bootstrap-android.sh` online to checksum and prefetch only the qualified official toolchain/cache, including the exact Kotlin 2.2.10 compiler distribution used as a direct compiler input while implicit Gradle stdlib resolution stays disabled. The same pinned stdlib file is the sole Kotlin runtime packaged into the generated instrumentation APK. `scripts/accept-android.sh` then creates a fresh project and runs lint, JUnit, application/instrumentation assembly, dependency inspection, and APK leakage checks with `--offline`. It requires an empty application runtime graph; the unit-test graph must contain exactly non-transitive JUnit 4.13.2 at implementation scope and Hamcrest Core 1.3 at runtime scope because the JUnit runner loads Hamcrest before test code. Neither unit-test dependency nor Kotlin runtime implementation namespaces may appear in the app APK, while the instrumentation APK must contain `kotlin.jvm.internal.Intrinsics`. `scripts/android-emulator-acceptance.sh` requires usable `/dev/kvm`, forces acceleration, installs/launches/asserts exact base64-encoded UTF-8 output through platform instrumentation,
|
|
507
|
+
TensorBuzz's Android lane runs `scripts/bootstrap-android.sh` online to checksum and prefetch only the qualified official toolchain/cache, including the exact Kotlin 2.2.10 compiler distribution used as a direct compiler input while implicit Gradle stdlib resolution stays disabled. The same pinned stdlib file is the sole Kotlin runtime packaged into the generated instrumentation APK. `scripts/accept-android.sh` then creates a fresh project and runs lint, JUnit, application/instrumentation assembly, dependency inspection, and APK leakage checks with `--offline`. It requires an empty application runtime graph; the unit-test graph must contain exactly non-transitive JUnit 4.13.2 at implementation scope and Hamcrest Core 1.3 at runtime scope because the JUnit runner loads Hamcrest before test code. Neither unit-test dependency nor Kotlin runtime implementation namespaces may appear in the app APK, while the instrumentation APK must contain `kotlin.jvm.internal.Intrinsics`. `scripts/android-emulator-acceptance.sh` requires usable `/dev/kvm`, forces acceleration, installs/launches/asserts exact base64-encoded UTF-8 output through platform instrumentation, then performs the Task 030 Flutter launch/UI proof on the same boot before destroying the emulator and repeating everything on one fresh boot. Missing tools/cache/KVM are infrastructure failures, never skips. Do not run this emulator script locally without KVM or substitute software emulation.
|
|
508
|
+
|
|
509
|
+
## Task030 Flutter application acceptance
|
|
510
|
+
|
|
511
|
+
Run the Flutter files individually and sequentially:
|
|
512
|
+
|
|
513
|
+
```sh
|
|
514
|
+
npx velocious-test spec/flutter-application.spec.js
|
|
515
|
+
npx velocious-test spec/flutter-materialization.spec.js
|
|
516
|
+
npx velocious-test spec/flutter-ui-assertion.spec.js
|
|
517
|
+
npx velocious-test spec/flutter-toolchain-contract.spec.js
|
|
518
|
+
npx velocious-test spec/flutter-packed-consumer.spec.js
|
|
519
|
+
npx velocious-test spec/language-registry.spec.js
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
Generation coverage includes Ruby- and Dart-originating programs, multiple modules, Unicode, exact widget output, registry/public-role separation, package/plugin/permission closure, deterministic ownership and hashes, rich source/configuration provenance, asset snapshots, unsafe identifiers/configuration/paths/collisions, unsupported semantics, and transactional rejection. Private materialization requires a fresh real mode-0700 root and verifies every byte without creating a public or packed writer. The packed proof repeats credential-free ordinary install and clean `npm ci` plus strict TypeScript consumption.
|
|
523
|
+
|
|
524
|
+
Real local SDK qualification uses exact Flutter 3.47.4 revision `9584c6713b324636289d067944a46fd6b49df14b` and its bundled Dart 3.13.3. Seed a fresh isolated `PUB_CACHE` only with the checksum-qualified archive's `.pub-preload-cache/*.tar.gz`; then the generated project must pass `dart pub get --offline --enforce-lockfile`, `dart format --output=none --set-exit-if-changed lib test`, Flutter analyze with `--no-pub --fatal-infos --fatal-warnings`, and Flutter test with `--no-pub`. No command may invoke `flutter create`, download a pub package, or rewrite the owned lockfile.
|
|
525
|
+
|
|
526
|
+
TensorBuzz retains one `android` build and exactly `/dev/kvm:/dev/kvm`. After the Task 028 bootstrap, `scripts/bootstrap-flutter.sh` verifies the official SDK archive's size/SHA/revision, seeds its isolated pub cache, and warms the same Gradle cache with a disposable project. `scripts/accept-flutter.sh` repeats restore, format, analyze, widget tests, lint, assembly, package graph, embedding, and APK-permission checks offline. The existing emulator script then installs and launches the Flutter APK during each of its two Task 028 fresh boots, requires exact `hé😀\nhé😀!\nhé😀!?` output under `semantifold-output`, and captures Flutter UI XML/screenshots. Flutter 3.47.4 requires the single shared Gradle 8.14 baseline; bypassing dependency validation or creating a second cache/emulator topology is forbidden.
|
|
527
|
+
|
|
528
|
+
These checks qualify only Android. No local or TensorBuzz step provisions or invokes macOS, Xcode, Apple SDK, CocoaPods, signing, or an iOS Simulator. No omitted Apple prerequisite is reported as a skip or success; dual-platform Task 030 completion remains deferred.
|
|
508
529
|
|
|
509
530
|
## Task022 Swift acceptance
|
|
510
531
|
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Watch, build, and target-check pipeline
|
|
2
|
+
|
|
3
|
+
## Status and verified baseline
|
|
4
|
+
|
|
5
|
+
This document is a roadmap contract, not an implemented CLI contract.
|
|
6
|
+
|
|
7
|
+
The inspected baseline is `semantifold@0.7.0`, annotated tag `v0.7.0`, commit `8cd70c5a6c7de98df2d4a183d6555ad4f3d5ba5a`. At that revision:
|
|
8
|
+
|
|
9
|
+
- JavaScript/JSDoc is a registered source frontend and Java is a registered text backend.
|
|
10
|
+
- `parse`, `parseProgram`, `generateArtifactSet`, and `generateProgramArtifactSet` are public, deterministic, in-memory APIs.
|
|
11
|
+
- `discoverCanonicalToolchain` finds the configured `javac` and `java` executables.
|
|
12
|
+
- `runAcceptanceStages` materializes an artifact set in a temporary directory and runs caller-supplied exact argument arrays with bounded process execution.
|
|
13
|
+
- A bounded direct proof parsed the 2,648-byte JSDoc fixture `spec/fixtures/compatibility/program.js`, generated `Main.java`, compiled it with `javac 25.0.4`, executed it with Java 25.0.4, and observed the canonical output `compat\nbranch-ok!\npresent\nabsent\n7\n2\n3\n5\n4\n`.
|
|
14
|
+
- `package.json` has no `bin` entry. The public API does not persist generated artifacts, load a project manifest, derive compiler arguments from a target, or watch source files.
|
|
15
|
+
- The only repository file-change watcher is a test helper; there is no product watch coordinator, incremental dependency graph, event coalescing policy, or long-lived compiler owner.
|
|
16
|
+
|
|
17
|
+
The current system therefore already proves **JavaScript/JSDoc → semantic IR → Java → `javac`** as a one-shot composition. The roadmap below turns that composition into a safe project workflow without moving filesystem or process lifecycle concerns into parsers or backends.
|
|
18
|
+
|
|
19
|
+
## Product outcome
|
|
20
|
+
|
|
21
|
+
A project can declare explicit source modules, one or more generated targets, one publisher-owned project publication root, target-relative generated/build projections, and whether each target is checked with its real toolchain. It can then run:
|
|
22
|
+
|
|
23
|
+
- a planned one-shot `semantifold build`; or
|
|
24
|
+
- a planned long-lived `semantifold watch`.
|
|
25
|
+
|
|
26
|
+
For the first end-to-end slice, changing a JSDoc-typed JavaScript source regenerates Java and compiles the staged candidate with `javac`. A successful cycle publishes one coherent generated-source/compiler-output generation through one active-generation pointer switch. A parse, semantic, generation, tool-discovery, compiler, or publication failure reports a located diagnostic, keeps the process alive in watch mode, and leaves the previous successful generation usable. A later valid edit recovers without restarting the watcher.
|
|
27
|
+
|
|
28
|
+
The architecture is target-neutral. Every current text backend eventually supplies one target-owned check plan. Every current source frontend participates through the existing parser/project APIs; the watcher never contains language syntax logic.
|
|
29
|
+
|
|
30
|
+
## Architecture boundaries
|
|
31
|
+
|
|
32
|
+
### Project manifest and loader
|
|
33
|
+
|
|
34
|
+
A versioned, strict manifest declares ordered source modules and target entries. Source language, module identity, entry identity, target language/role, one project publication root, and each target's generated/check subpaths inside an immutable project generation are explicit. These target paths are logical projections, not independently promoted filesystem roots. Version 1 uses explicit file paths rather than introducing a glob language or package-manager resolution.
|
|
35
|
+
|
|
36
|
+
The loader owns path normalization and rejects unknown fields, duplicate module/target identities, ambiguous target roles, source/publication overlap, publication roots outside the project root, colliding or nested target projections, and symlink/path traversal across the owned publication root. It returns an immutable project request and never parses language syntax.
|
|
37
|
+
|
|
38
|
+
### Snapshot builder
|
|
39
|
+
|
|
40
|
+
Each cycle reads a complete content snapshot of every declared source and the project manifest. File events are hints only. Before parsing, the coordinator re-reads and hashes the declared graph in stable order; it never assumes an event filename is complete or authoritative.
|
|
41
|
+
|
|
42
|
+
A snapshot is immutable for the cycle. Edits arriving during parse/generation/check mark the watcher dirty and cause one immediate follow-up cycle after the current owned work closes. Cycles never overlap.
|
|
43
|
+
|
|
44
|
+
### Existing semantic compiler core
|
|
45
|
+
|
|
46
|
+
Frontends, semantic validation, linking, and backends remain pure with respect to the project filesystem. They consume the immutable snapshot and return complete `GeneratedArtifactSet` values. No frontend or backend starts watchers, writes files, spawns a compiler, or reads undeclared project files.
|
|
47
|
+
|
|
48
|
+
### Transactional artifact publication
|
|
49
|
+
|
|
50
|
+
A publisher stages one complete project candidate as a new immutable generation beneath the publisher-owned publication root. Every target's generated artifacts, mappings, and optional compiler outputs live in generation-relative subtrees on the same filesystem. The publisher validates paths, mappings, hashes, collisions, ownership, and manifests before touching committed state.
|
|
51
|
+
|
|
52
|
+
When checking is enabled, target checks run against those staged generation subtrees. Only a fully generated and successfully checked project candidate may be published. The generation manifest is the explicit versioned authority for files the publisher created; it never authorizes mutation outside the publication root.
|
|
53
|
+
|
|
54
|
+
Publication commits exactly one small active-generation pointer by writing and syncing a sibling temporary pointer, atomically replacing the prior pointer on the same filesystem, and syncing the parent directory before reporting success. A host filesystem that cannot provide atomic same-directory regular-file replacement is rejected before publication; copying, truncating, or rewriting the live pointer in place is never a fallback. Readers resolve that pointer once and consume all generated/build paths from the referenced immutable generation. Configured generated-source and compiler-output names are logical projections returned from that resolved generation; they are never independent directories, symlinks, or manifests promoted one after another. The same rule covers every target in a multi-target cycle.
|
|
55
|
+
|
|
56
|
+
An interruption before the pointer replacement leaves the previous generation active; an interruption after it leaves either the old or new complete pointer durably recoverable, never a composite state. A recovery journal may finish cleanup of unpublished candidates and temporary pointer files, but it is not used to claim atomicity across multiple filesystem operations. Publication never removes a committed generation, because a reader may already hold its resolved snapshot; committed-generation retention/garbage collection is a separate explicit policy. A failed candidate is removed without changing the active pointer.
|
|
57
|
+
|
|
58
|
+
### Target check-plan registry
|
|
59
|
+
|
|
60
|
+
The existing language registry remains authoritative for target identity, roles, declared acceptance stages, and required toolchain IDs. A planned target-check capability derives exact stage requests from a validated staged artifact set and its generation-scoped target build subtree.
|
|
61
|
+
|
|
62
|
+
The generic runner owns process lifecycle, locale/timezone normalization, output capture, timeout/cancellation, and close observation. A target check plan owns filenames, exact argv, toolchain IDs, offline/cache policy, and which stages constitute a non-executing developer check. The watcher does not switch on language IDs.
|
|
63
|
+
|
|
64
|
+
The initial check plan is Java/`javac`. Later tasks add current text targets in two cohorts:
|
|
65
|
+
|
|
66
|
+
- interpreted/managed: PHP, Ruby, JavaScript, TypeScript, Kotlin/JVM, Python, and C# (Java is supplied by the initial slice);
|
|
67
|
+
- native/project: Go, C, C++, Rust, Swift, Dart, and Zig.
|
|
68
|
+
|
|
69
|
+
A watch check validates or compiles but does not execute generated application code by default. Restore/download behavior is never an implicit per-edit action; targets with project metadata use pinned, offline, isolated state and rerun prerequisite stages only when their declared inputs change.
|
|
70
|
+
|
|
71
|
+
### Watch coordinator
|
|
72
|
+
|
|
73
|
+
One lifecycle-aware class owns filesystem subscriptions, content snapshots, cycle state, the active checker child, reporting, and shutdown. Its state machine is:
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
starting -> building -> ready
|
|
77
|
+
| |
|
|
78
|
+
v v
|
|
79
|
+
failed <- rebuilding
|
|
80
|
+
| |
|
|
81
|
+
+-- valid edit --+
|
|
82
|
+
|
|
83
|
+
any live state -- SIGINT/SIGTERM --> stopping --> stopped
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`failed` means the last candidate failed; it does not discard the last successful output or terminate watch mode. A watcher event during `building`/`rebuilding` sets one dirty generation marker. The coordinator starts exactly one follow-up after the owned child closes. It never starts a retry while prior compiler work is still live.
|
|
87
|
+
|
|
88
|
+
Native filesystem events require a bounded reconciliation strategy because Node and TypeScript both document platform-dependent watcher behavior. Version 1 watches the explicit manifest/source files and relevant parent directories, re-scans the declared graph after each hint, supports a documented polling fallback, and suppresses no-op cycles by content hash. The publication root and every generated/build projection are rejected as source roots and are never watched.
|
|
89
|
+
|
|
90
|
+
### Reporting contract
|
|
91
|
+
|
|
92
|
+
Human output is concise and stable. A machine-readable mode emits one JSON record per state transition with at least project identity, monotonically increasing cycle number, source snapshot hash, target ID/role, state, timing, changed paths, tool identity, exit status, and a structured diagnostic when present.
|
|
93
|
+
|
|
94
|
+
Exactly one terminal cycle record is emitted per cycle. Process exit is non-zero for failed one-shot builds, but ordinary watch-cycle failures keep the watcher process alive. Startup/configuration failure exits. Signal-driven shutdown forwards cancellation to an active owned child, waits for `close`, removes subscriptions and staging state, and exits once.
|
|
95
|
+
|
|
96
|
+
## Generation and failure semantics
|
|
97
|
+
|
|
98
|
+
1. Initial watch startup performs a real build; it does not wait for the first edit.
|
|
99
|
+
2. Event bursts coalesce into one snapshot rebuild after a bounded quiet period.
|
|
100
|
+
3. Content hashes, not timestamps alone, decide whether a cycle is a no-op.
|
|
101
|
+
4. Parse/generation failure never starts a target checker and never publishes.
|
|
102
|
+
5. Tool discovery/check failure never publishes the staged generated source or build products.
|
|
103
|
+
6. Publication failure before the active-pointer replacement leaves the previous generation active; failure after that replacement reports the new generation as committed and limits recovery to cleanup.
|
|
104
|
+
7. A valid edit after failure performs a fresh complete cycle and may publish normally.
|
|
105
|
+
8. Deleted or renamed declared sources are ordinary invalid snapshots until the manifest is updated; stale owned outputs are not silently treated as current.
|
|
106
|
+
9. Multiple target entries are staged from one source snapshot beneath one immutable project generation. The manifest defines whether all targets must pass before the single active pointer is switched; version 1 defaults to one all-or-nothing project generation.
|
|
107
|
+
10. No shell command strings, compiler-specific branches in the watcher, implicit network access, or arbitrary user hooks are introduced.
|
|
108
|
+
|
|
109
|
+
## Cross-language scope
|
|
110
|
+
|
|
111
|
+
“Works for all languages” means:
|
|
112
|
+
|
|
113
|
+
- every registered frontend can be an explicitly declared watched source when the selected semantic features are supported;
|
|
114
|
+
- every current text backend has an explicit real-toolchain check plan or a fail-loud reason it cannot be checked;
|
|
115
|
+
- the same watcher/materializer/runner code handles every source/target pairing;
|
|
116
|
+
- acceptance uses a bounded matrix: all frontends and all text targets are covered, with the JavaScript/JSDoc-to-Java path plus representative cross-family paths, not a quadratic promise that every semantic feature works for every pair.
|
|
117
|
+
|
|
118
|
+
Binary and application targets use the same generation watcher only after explicit target policy is defined. Browser Wasm can validate/instantiate in its own lane. Android and Flutter checks are heavier platform builds and must be opt-in rather than an automatic keystroke default. iOS remains generation-only until the separately deferred Apple/Xcode acceptance work is authorized; this roadmap does not reopen Tasks 026 or 027.
|
|
119
|
+
|
|
120
|
+
## Test strategy
|
|
121
|
+
|
|
122
|
+
- Unit contracts cover strict manifest validation, snapshot hashing, event coalescing, dirty-during-build behavior, no-op suppression, generation manifests, single-pointer publication, recovery journals, reader snapshot resolution, and exact check-plan construction.
|
|
123
|
+
- Real temporary-directory tests cover create/change/delete/rename behavior and ensure generated roots do not self-trigger.
|
|
124
|
+
- Real-child lifecycle tests cover spawn failure, non-zero compiler close, late output, signal forwarding, shutdown during an active check, and no leaked child/staging directory.
|
|
125
|
+
- Real `javac` acceptance proves the initial JavaScript/JSDoc-to-Java cycle, compiler failure reporting at the check boundary, an unchanged last-good active pointer, recovery after a valid edit, and source/class execution from one resolved committed generation.
|
|
126
|
+
- The terminal text-target matrix invokes every declared real checker/compiler in CI and fails rather than skips when a configured tool is unavailable.
|
|
127
|
+
- Focused local tests remain small; full language/toolchain matrices run only in the existing CI lanes.
|
|
128
|
+
|
|
129
|
+
## Dependency order
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
015 artifact/toolchain foundation
|
|
133
|
+
|
|
|
134
|
+
038 transactional publication
|
|
135
|
+
|
|
|
136
|
+
010 ----+---- 039 project manifest + one-shot build CLI
|
|
137
|
+
|
|
|
138
|
+
040 generic check plans + Java/javac
|
|
139
|
+
| |
|
|
140
|
+
041 managed cohort 043 watcher coordinator
|
|
141
|
+
042 native cohort |
|
|
142
|
+
| 044 JS/JSDoc -> Java watch slice
|
|
143
|
+
+--------------------+
|
|
144
|
+
|
|
|
145
|
+
045 terminal all-text-language matrix
|
|
146
|
+
|
|
|
147
|
+
046 later binary/application policies
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Tasks 038–045 are the selected text-language delivery chain. Task 045 is the single terminal acceptance task for that chain. Task 046 is a non-blocking later extension.
|
|
151
|
+
|
|
152
|
+
## Non-goals
|
|
153
|
+
|
|
154
|
+
This roadmap does not implement language-server/editor protocols, incremental semantic-IR invalidation, daemon IPC, remote/distributed builds, arbitrary compiler flags, shell hooks, package installation, source discovery by glob, runtime execution on every edit, hot reload into a running application, production deployment, signing, store submission, or Apple/Xcode acceptance. It does not change semantic language support or claim all-pairs feature compatibility.
|
package/index.js
CHANGED
|
@@ -97,8 +97,8 @@ export function generateArtifact(input) {
|
|
|
97
97
|
* @param {import("./src/semantic/types.js").BackendLanguage} input.language - Registered program target.
|
|
98
98
|
* @param {import("./src/semantic/types.js").SemanticProgram} input.program - Complete resolved program.
|
|
99
99
|
* @param {"text" | "application"} [input.role] - Explicit role; omitted preserves text generation.
|
|
100
|
-
* @param {import("./src/semantic/types.js").IosApplicationConfigurationInput | import("./src/semantic/types.js").AndroidApplicationConfigurationInput} [input.configuration] - Application configuration.
|
|
101
|
-
* @param {import("./src/semantic/types.js").IosApplicationAssetInput[] | import("./src/semantic/types.js").AndroidApplicationInputFile[]} [input.assets] - Exact caller assets.
|
|
100
|
+
* @param {import("./src/semantic/types.js").IosApplicationConfigurationInput | import("./src/semantic/types.js").AndroidApplicationConfigurationInput | import("./src/semantic/types.js").FlutterApplicationConfigurationInput} [input.configuration] - Application configuration.
|
|
101
|
+
* @param {import("./src/semantic/types.js").IosApplicationAssetInput[] | import("./src/semantic/types.js").AndroidApplicationInputFile[] | import("./src/semantic/types.js").FlutterApplicationAssetInput[]} [input.assets] - Exact caller assets.
|
|
102
102
|
* @param {import("./src/semantic/types.js").AndroidApplicationInputFile[]} [input.resources] - Exact Android caller resources.
|
|
103
103
|
* @returns {import("./src/semantic/types.js").GeneratedArtifactSet} Complete generated set.
|
|
104
104
|
*/
|
|
@@ -116,8 +116,8 @@ export function generateProgramArtifactSet(input) {
|
|
|
116
116
|
* @param {"none" | "external" | "inline"} [input.mapDirective] - JavaScript-family map directive.
|
|
117
117
|
* @param {string} [input.sourceMapFilename] - External map filename.
|
|
118
118
|
* @param {{filename: string, content: string, language?: import("./src/semantic/types.js").SemanticLanguage}[]} [input.sources] - Original sources.
|
|
119
|
-
* @param {import("./src/semantic/types.js").IosApplicationConfigurationInput | import("./src/semantic/types.js").AndroidApplicationConfigurationInput} [input.configuration] - Application configuration.
|
|
120
|
-
* @param {import("./src/semantic/types.js").IosApplicationAssetInput[] | import("./src/semantic/types.js").AndroidApplicationInputFile[]} [input.assets] - Exact caller assets.
|
|
119
|
+
* @param {import("./src/semantic/types.js").IosApplicationConfigurationInput | import("./src/semantic/types.js").AndroidApplicationConfigurationInput | import("./src/semantic/types.js").FlutterApplicationConfigurationInput} [input.configuration] - Application configuration.
|
|
120
|
+
* @param {import("./src/semantic/types.js").IosApplicationAssetInput[] | import("./src/semantic/types.js").AndroidApplicationInputFile[] | import("./src/semantic/types.js").FlutterApplicationAssetInput[]} [input.assets] - Exact caller assets.
|
|
121
121
|
* @param {import("./src/semantic/types.js").AndroidApplicationInputFile[]} [input.resources] - Exact Android caller resources.
|
|
122
122
|
* @returns {import("./src/semantic/types.js").GeneratedArtifactSet} Complete generated set.
|
|
123
123
|
*/
|
|
@@ -221,6 +221,6 @@ export function generateArtifactSet(input) {
|
|
|
221
221
|
const registryRole = role == "binary" ? "binaryBackend" : "applicationBackend"
|
|
222
222
|
const backend = languageRegistry.resolve(language, registryRole, module?.location)
|
|
223
223
|
|
|
224
|
-
return constructArtifactSet(backend(language == "android" ? input :
|
|
224
|
+
return constructArtifactSet(backend(language == "android" || language == "flutter" ? input :
|
|
225
225
|
{assets, configuration, filename, language, mapDirective, module, resources, sourceMapFilename, sources}))
|
|
226
226
|
}
|
package/package.json
CHANGED
package/src/backends/android.js
CHANGED
|
@@ -74,7 +74,7 @@ const toolchain = Object.freeze({
|
|
|
74
74
|
commandLineTools: "11076708",
|
|
75
75
|
compileSdk: 35,
|
|
76
76
|
emulator: "35.6.11",
|
|
77
|
-
gradle: "8.
|
|
77
|
+
gradle: "8.14",
|
|
78
78
|
java: "21.0.8",
|
|
79
79
|
kotlin: "2.2.10",
|
|
80
80
|
kotlinCompilerSha256: "302d1d8e671e5c3207e6ed62ff11fb555462a628e22a1158254dcaaf7e7394bc",
|
package/src/backends/dart.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import {unsupportedCapability} from "../diagnostic.js"
|
|
4
4
|
import {finalizeMapping, toSourceMapV3} from "../mapping.js"
|
|
5
|
-
import {emitExpression, validateBackendModule} from "./shared.js"
|
|
5
|
+
import {emitExpression, flatDartExpression, validateBackendModule} from "./shared.js"
|
|
6
6
|
import {dartRuntime} from "./dart-runtime.js"
|
|
7
7
|
import {emitScalarType} from "./scalars.js"
|
|
8
8
|
import {SourceWriter} from "./writer.js"
|
|
@@ -23,7 +23,7 @@ export function generateDartPackage({filename, mapDirective, module, sourceMapFi
|
|
|
23
23
|
if (mapDirective !== undefined || sourceMapFilename !== undefined) {
|
|
24
24
|
unsupportedCapability("dart", "source-map filename or directive option", module.location)
|
|
25
25
|
}
|
|
26
|
-
const unreadLocals =
|
|
26
|
+
const unreadLocals = findUnreadDartLocals(module)
|
|
27
27
|
const writer = new SourceWriter({filename: "bin/program.dart", language: "dart", module, sources})
|
|
28
28
|
|
|
29
29
|
emitProgram(module, writer, unreadLocals)
|
|
@@ -113,12 +113,12 @@ function emitProgram(module, writer, unreadLocals) {
|
|
|
113
113
|
writer.synthetic(" ", "body spacing", [declaration], [path])
|
|
114
114
|
writer.mapped("{", {mappingKind: "anchor", node: declaration.body, path: `${path}/body`})
|
|
115
115
|
writer.synthetic("\n", "line break", [declaration], [path])
|
|
116
|
-
|
|
116
|
+
emitDartBlock(writer, declaration.body, " ", `${path}/body`, unreadLocals)
|
|
117
117
|
writer.mapped("}", {mappingKind: "anchor", node: declaration.body, path: `${path}/body`})
|
|
118
118
|
writer.synthetic("\n", "line break", [declaration], [path])
|
|
119
119
|
}
|
|
120
120
|
writer.synthetic("\nvoid main() {\n", "Dart generated main scaffold", [module.entryPoint], ["/entryPoint"])
|
|
121
|
-
|
|
121
|
+
emitDartBlock(writer, module.entryPoint.body, " ", "/entryPoint/body", unreadLocals)
|
|
122
122
|
writer.synthetic("}\n", "Dart generated main scaffold", [module.entryPoint], ["/entryPoint"])
|
|
123
123
|
}
|
|
124
124
|
|
|
@@ -129,11 +129,13 @@ function emitProgram(module, writer, unreadLocals) {
|
|
|
129
129
|
* @param {string} indent Current indentation.
|
|
130
130
|
* @param {string} path Exact semantic path.
|
|
131
131
|
* @param {Set<import("../semantic/types.js").LocalDeclaration>} unreadLocals Locals requiring an analyzer-only read.
|
|
132
|
+
* @param {string} [printTarget] Target-owned output function spelling.
|
|
133
|
+
* @param {boolean} [multilinePrint] Whether target plumbing requires trailing-comma multiline calls.
|
|
132
134
|
* @returns {void}
|
|
133
135
|
*/
|
|
134
|
-
function
|
|
136
|
+
export function emitDartBlock(writer, block, indent, path, unreadLocals, printTarget = "print", multilinePrint = false) {
|
|
135
137
|
block.statements.forEach((statement, index) =>
|
|
136
|
-
emitStatement(writer, statement, indent, `${path}/statements/${index}`, unreadLocals))
|
|
138
|
+
emitStatement(writer, statement, indent, `${path}/statements/${index}`, unreadLocals, printTarget, multilinePrint))
|
|
137
139
|
}
|
|
138
140
|
|
|
139
141
|
/**
|
|
@@ -143,9 +145,11 @@ function emitBlock(writer, block, indent, path, unreadLocals) {
|
|
|
143
145
|
* @param {string} indent Current indentation.
|
|
144
146
|
* @param {string} path Exact semantic path.
|
|
145
147
|
* @param {Set<import("../semantic/types.js").LocalDeclaration>} unreadLocals Locals requiring an analyzer-only read.
|
|
148
|
+
* @param {string} printTarget Target-owned output function spelling.
|
|
149
|
+
* @param {boolean} multilinePrint Whether target plumbing requires trailing-comma multiline calls.
|
|
146
150
|
* @returns {void}
|
|
147
151
|
*/
|
|
148
|
-
function emitStatement(writer, statement, indent, path, unreadLocals) {
|
|
152
|
+
function emitStatement(writer, statement, indent, path, unreadLocals, printTarget, multilinePrint) {
|
|
149
153
|
writer.synthetic(indent, "indentation", [statement], [path])
|
|
150
154
|
if (statement.kind == "LocalDeclaration") {
|
|
151
155
|
if (!statement.mutable) writer.mapped("final", {mappingKind: "anchor", node: statement, path})
|
|
@@ -181,7 +185,7 @@ function emitStatement(writer, statement, indent, path, unreadLocals) {
|
|
|
181
185
|
writer.synthetic("\n", "line break", [statement], [path])
|
|
182
186
|
return
|
|
183
187
|
}
|
|
184
|
-
if (statement.kind == "IfStatement") return emitIf(writer, statement, indent, path, unreadLocals)
|
|
188
|
+
if (statement.kind == "IfStatement") return emitIf(writer, statement, indent, path, unreadLocals, printTarget, multilinePrint)
|
|
185
189
|
if (statement.kind == "ReturnStatement") {
|
|
186
190
|
writer.mapped("return", {mappingKind: "anchor", node: statement, path})
|
|
187
191
|
if (statement.expression) {
|
|
@@ -193,10 +197,15 @@ function emitStatement(writer, statement, indent, path, unreadLocals) {
|
|
|
193
197
|
return
|
|
194
198
|
}
|
|
195
199
|
if (statement.kind == "PrintStatement") {
|
|
196
|
-
writer.
|
|
197
|
-
|
|
200
|
+
const expandedPrint = multilinePrint && writer.column - 1 + printTarget.length +
|
|
201
|
+
flatDartExpression(writer, statement.expression).length + 3 > 80
|
|
202
|
+
|
|
203
|
+
if (printTarget == "print") {
|
|
204
|
+
writer.mapped(printTarget, {mappingKind: "anchor", name: "print", node: statement, path, role: "callee"})
|
|
205
|
+
} else writer.synthetic(printTarget, "Dart print plumbing", [statement], [path])
|
|
206
|
+
writer.synthetic(`(${expandedPrint ? `\n${indent} ` : ""}`, "Dart print plumbing", [statement], [path])
|
|
198
207
|
emitExpression(writer, statement.expression, `${path}/expression`, "dart", identity)
|
|
199
|
-
writer.
|
|
208
|
+
writer.synthetic(expandedPrint ? `,\n${indent});` : ");", "Dart print plumbing", [statement], [path])
|
|
200
209
|
writer.synthetic("\n", "line break", [statement], [path])
|
|
201
210
|
return
|
|
202
211
|
}
|
|
@@ -216,23 +225,27 @@ function emitStatement(writer, statement, indent, path, unreadLocals) {
|
|
|
216
225
|
* @param {string} indent Current indentation.
|
|
217
226
|
* @param {string} path Exact semantic path.
|
|
218
227
|
* @param {Set<import("../semantic/types.js").LocalDeclaration>} unreadLocals Locals requiring an analyzer-only read.
|
|
228
|
+
* @param {string} printTarget Target-owned output function spelling.
|
|
229
|
+
* @param {boolean} multilinePrint Whether target plumbing requires trailing-comma multiline calls.
|
|
219
230
|
* @returns {void}
|
|
220
231
|
*/
|
|
221
|
-
function emitIf(writer, statement, indent, path, unreadLocals) {
|
|
232
|
+
function emitIf(writer, statement, indent, path, unreadLocals, printTarget, multilinePrint) {
|
|
222
233
|
writer.mapped("if", {mappingKind: "anchor", node: statement, path})
|
|
223
234
|
writer.synthetic(" (", "Dart conditional scaffold", [statement], [path])
|
|
224
235
|
emitExpression(writer, statement.condition, `${path}/condition`, "dart", identity)
|
|
225
236
|
writer.synthetic(") ", "Dart conditional scaffold", [statement], [path])
|
|
226
237
|
writer.mapped("{", {mappingKind: "anchor", node: statement.consequent, path: `${path}/consequent`})
|
|
227
238
|
writer.synthetic("\n", "line break", [statement], [path])
|
|
228
|
-
|
|
239
|
+
emitDartBlock(writer, statement.consequent, `${indent} `, `${path}/consequent`, unreadLocals, printTarget,
|
|
240
|
+
multilinePrint)
|
|
229
241
|
writer.synthetic(indent, "indentation", [statement], [path])
|
|
230
242
|
writer.mapped("}", {mappingKind: "anchor", node: statement.consequent, path: `${path}/consequent`})
|
|
231
243
|
if (statement.alternate) {
|
|
232
244
|
writer.synthetic(" else ", "Dart alternate scaffold", [statement], [path])
|
|
233
245
|
writer.mapped("{", {mappingKind: "anchor", node: statement.alternate, path: `${path}/alternate`})
|
|
234
246
|
writer.synthetic("\n", "line break", [statement], [path])
|
|
235
|
-
|
|
247
|
+
emitDartBlock(writer, statement.alternate, `${indent} `, `${path}/alternate`, unreadLocals, printTarget,
|
|
248
|
+
multilinePrint)
|
|
236
249
|
writer.synthetic(indent, "indentation", [statement], [path])
|
|
237
250
|
writer.mapped("}", {mappingKind: "anchor", node: statement.alternate, path: `${path}/alternate`})
|
|
238
251
|
}
|
|
@@ -244,7 +257,7 @@ function emitIf(writer, statement, indent, path, unreadLocals) {
|
|
|
244
257
|
* @param {import("../semantic/types.js").SemanticModule} module Validated semantic module.
|
|
245
258
|
* @returns {Set<import("../semantic/types.js").LocalDeclaration>} Unread declaration identities.
|
|
246
259
|
*/
|
|
247
|
-
function
|
|
260
|
+
export function findUnreadDartLocals(module) {
|
|
248
261
|
/** @type {Set<import("../semantic/types.js").LocalDeclaration>} */
|
|
249
262
|
const unread = new Set()
|
|
250
263
|
|