@typecad/cuttlefish 1.0.0-alpha.13 → 1.0.0-alpha.15
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 +82 -79
- package/assets/editor-extensions/typecad-debug/README.md +141 -0
- package/assets/editor-extensions/typecad-debug/out/extension.js +347 -0
- package/assets/editor-extensions/typecad-debug/out/extension.js.map +1 -0
- package/assets/editor-extensions/typecad-debug/package.json +76 -0
- package/assets/editor-extensions/typecad-ui/LICENSE +27 -0
- package/assets/editor-extensions/typecad-ui/README.md +52 -0
- package/assets/editor-extensions/typecad-ui/file-icon-dark.png +0 -0
- package/assets/editor-extensions/typecad-ui/file-icon-light.png +0 -0
- package/assets/editor-extensions/typecad-ui/icon.png +0 -0
- package/assets/editor-extensions/typecad-ui/language-configuration.json +43 -0
- package/assets/editor-extensions/typecad-ui/package.json +54 -0
- package/assets/editor-extensions/typecad-ui/snippets/typecad-ui.json +80 -0
- package/assets/editor-extensions/typecad-ui/syntaxes/markdown-ui.json +45 -0
- package/assets/editor-extensions/typecad-ui/syntaxes/typecad-ui.tmLanguage.json +1269 -0
- package/dist/api/config.d.ts +30 -27
- package/dist/api/schema/types.d.ts +20 -3
- package/dist/api/shared/async-runtime-static.d.ts +1 -1
- package/dist/api/shared/async-runtime-static.js +5 -5
- package/dist/api/shared/async-types.d.ts +3 -2
- package/dist/api/shared/board-catalog.d.ts +2 -0
- package/dist/api/shared/board-catalog.js +6 -0
- package/dist/api/shared/board-resolver.d.ts +4 -7
- package/dist/api/shared/board-resolver.js +2 -1
- package/dist/api/shared/coop-scheduler.d.ts +1 -1
- package/dist/api/shared/coop-scheduler.js +1 -1
- package/dist/api/shared/cpp-type-ir.d.ts +1 -1
- package/dist/api/shared/cpp-type-ir.js +1 -1
- package/dist/api/shared/display-profile.d.ts +1 -1
- package/dist/api/shared/display-profile.js +3 -5
- package/dist/api/shared/framework-manifest-registry.d.ts +1 -1
- package/dist/api/shared/framework-manifest-registry.js +0 -1
- package/dist/api/shared/framework-manifest.d.ts +65 -507
- package/dist/api/shared/framework-manifest.js +6 -11
- package/dist/api/shared/hal-op-ir.d.ts +294 -685
- package/dist/api/shared/hal-op-ir.js +31 -65
- package/dist/api/shared/index.d.ts +0 -4
- package/dist/api/shared/index.js +1 -7
- package/dist/api/shared/ir-core.d.ts +6 -0
- package/dist/api/shared/ir-declarations.d.ts +4 -0
- package/dist/api/shared/ir.d.ts +1 -3
- package/dist/api/shared/platform-strategy.d.ts +14 -37
- package/dist/api/shared/polyfill-helper-registry.js +0 -5
- package/dist/api/shared/polyfill-types.js +0 -27
- package/dist/api/shared/promise-runtime.js +3 -3
- package/dist/api/shared/spdx-licenses.d.ts +12 -10
- package/dist/api/shared/spdx-licenses.js +1 -1
- package/dist/api/shared/toolchain-types.d.ts +1 -1
- package/dist/api/shared/toolchain-types.js +3 -3
- package/dist/api/shared/types.d.ts +0 -3
- package/dist/api/shared/validate-framework-manifest.js +13 -33
- package/dist/board-catalog/dts-reader.d.ts +220 -0
- package/dist/board-catalog/dts-reader.js +1052 -0
- package/dist/board-catalog/index.d.ts +11 -0
- package/dist/board-catalog/index.js +12 -0
- package/dist/board-catalog/pinconfig.d.ts +48 -0
- package/dist/board-catalog/pinconfig.js +263 -0
- package/dist/board-catalog/sdk.d.ts +82 -0
- package/dist/board-catalog/sdk.js +180 -0
- package/dist/board-catalog/store.d.ts +196 -0
- package/dist/board-catalog/store.js +363 -0
- package/dist/board-catalog/types.d.ts +233 -0
- package/dist/board-catalog/types.js +7 -0
- package/dist/board-catalog/walker.d.ts +112 -0
- package/dist/board-catalog/walker.js +1101 -0
- package/dist/cli-utils.d.ts +2 -2
- package/dist/cli-utils.js +5 -5
- package/dist/cli.js +227 -244
- package/dist/config-loader.d.ts +14 -19
- package/dist/config-loader.js +242 -78
- package/dist/config-schema.d.ts +19 -227
- package/dist/config-schema.js +9 -8
- package/dist/contract/board-generator.d.ts +4 -4
- package/dist/contract/board-generator.js +16 -5
- package/dist/contract/contract-parser.d.ts +36 -127
- package/dist/contract/contract-parser.js +76 -0
- package/dist/contract/index.d.ts +1 -1
- package/dist/contract/index.js +70 -21
- package/dist/create/active-board-catalog.d.ts +9 -0
- package/dist/create/active-board-catalog.js +94 -0
- package/dist/create/board-catalog.generated.d.ts +96 -0
- package/dist/create/board-catalog.generated.js +128737 -0
- package/dist/create/board-search.d.ts +23 -0
- package/dist/create/board-search.js +88 -0
- package/dist/create/editor-integration.d.ts +32 -0
- package/dist/create/editor-integration.js +257 -0
- package/dist/create/eslint-rules-template.d.ts +2 -2
- package/dist/create/framework-catalog.d.ts +48 -8
- package/dist/create/framework-catalog.js +58 -44
- package/dist/create/index.d.ts +8 -13
- package/dist/create/index.js +5 -9
- package/dist/create/mcu-target.d.ts +39 -0
- package/dist/create/mcu-target.js +80 -0
- package/dist/create/pack-targets.d.ts +19 -0
- package/dist/create/pack-targets.js +56 -0
- package/dist/create/{init-scaffold.d.ts → scaffold.d.ts} +8 -11
- package/dist/create/{init-scaffold.js → scaffold.js} +28 -87
- package/dist/create/templates.d.ts +51 -0
- package/dist/create/{init-templates.js → templates.js} +135 -116
- package/dist/create/wizard.d.ts +19 -0
- package/dist/create/wizard.js +314 -0
- package/dist/debug/preprocessor.d.ts +5 -0
- package/dist/debug/preprocessor.js +31 -7
- package/dist/diagnostics/diagnostics-report.d.ts +1 -1
- package/dist/diagnostics/diagnostics-report.js +3 -11
- package/dist/diagnostics/json-schema.d.ts +1 -1
- package/dist/diagnostics/md-writer.js +2 -2
- package/dist/diagnostics/mermaid-builder.d.ts +1 -1
- package/dist/diagnostics/mermaid-builder.js +1 -1
- package/dist/emit/cpp-emitter.js +4 -1
- package/dist/emit/emitters/class-emitter.js +32 -4
- package/dist/emit/emitters/emitter-context.d.ts +11 -0
- package/dist/emit/emitters/entrypoint-synthesizer.js +1 -1
- package/dist/emit/emitters/function-emitter-impl.js +33 -17
- package/dist/emit/emitters/output-finalizer.d.ts +7 -0
- package/dist/emit/emitters/output-finalizer.js +31 -23
- package/dist/emit/emitters/setup.js +147 -63
- package/dist/emit/emitters/top-level-prep.js +1 -1
- package/dist/emit/emitters/type-decl-emitter.js +13 -8
- package/dist/emit/emitters/ui-emitter.js +1 -1
- package/dist/emit/expression-renderer.d.ts +3 -3
- package/dist/emit/expression-renderer.js +17 -7
- package/dist/emit/route-hal-op.js +17 -48
- package/dist/emit/snprintf-helpers.d.ts +1 -3
- package/dist/emit/snprintf-helpers.js +1 -39
- package/dist/emit/statement-renderer.d.ts +0 -4
- package/dist/emit/statement-renderer.js +54 -45
- package/dist/emit/utils/async-state-machine.d.ts +16 -9
- package/dist/emit/utils/async-state-machine.js +56 -54
- package/dist/emit/utils/comment-helpers.d.ts +0 -8
- package/dist/emit/utils/comment-helpers.js +1 -1
- package/dist/emit/utils/cpp-helpers.d.ts +2 -2
- package/dist/emit/utils/cpp-helpers.js +2 -2
- package/dist/emit/utils/hal-op-cpp-type.js +4 -6
- package/dist/emit/utils/index.d.ts +3 -3
- package/dist/emit/utils/index.js +3 -3
- package/dist/emit/utils/type-inference.d.ts +1 -29
- package/dist/emit/utils/type-inference.js +0 -75
- package/dist/framework-package.d.ts +3 -0
- package/dist/framework-package.js +23 -6
- package/dist/framework-registry.d.ts +5 -5
- package/dist/frameworks/native/graphics/terminal-preview.d.ts +5 -0
- package/dist/frameworks/native/graphics/terminal-preview.js +33 -0
- package/dist/frameworks/native/index.d.ts +3 -0
- package/dist/frameworks/native/index.js +15 -0
- package/dist/frameworks/native/native-compile.d.ts +17 -0
- package/dist/frameworks/native/native-compile.js +170 -0
- package/dist/frameworks/native/native-config.d.ts +41 -0
- package/dist/frameworks/native/native-config.js +7 -0
- package/dist/frameworks/native/strategy.d.ts +97 -0
- package/dist/frameworks/native/strategy.js +664 -0
- package/dist/ir/board-resolver.d.ts +18 -23
- package/dist/ir/board-resolver.js +43 -401
- package/dist/ir/build-ir-state.d.ts +3 -0
- package/dist/ir/build-ir-state.js +42 -0
- package/dist/ir/build-ir.d.ts +1 -1
- package/dist/ir/build-ir.js +64 -45
- package/dist/ir/declaration-builders.js +1 -0
- package/dist/ir/expression-to-ir.js +17 -10
- package/dist/ir/hal/hal-emitter.d.ts +0 -2
- package/dist/ir/hal/hal-emitter.js +92 -41
- package/dist/ir/hal/hal-parser.d.ts +5 -5
- package/dist/ir/hal/hal-parser.js +128 -36
- package/dist/ir/hal/hal-plugins.d.ts +0 -1
- package/dist/ir/hal/hal-plugins.js +425 -716
- package/dist/ir/heap-analysis.js +2 -41
- package/dist/ir/identifier-collector.js +32 -20
- package/dist/ir/network-validation.js +7 -11
- package/dist/ir/ownership-analysis.js +75 -13
- package/dist/ir/peripheral-usage.d.ts +8 -2
- package/dist/ir/peripheral-usage.js +78 -149
- package/dist/ir/peripheral-validation.js +23 -3
- package/dist/ir/pin-capability-validation.d.ts +12 -0
- package/dist/ir/pin-capability-validation.js +61 -97
- package/dist/ir/pin-mode-validation.js +5 -5
- package/dist/ir/program-analysis.d.ts +8 -15
- package/dist/ir/program-analysis.js +140 -323
- package/dist/ir/pulldown-validation.js +11 -4
- package/dist/ir/resource-analysis.js +15 -3
- package/dist/ir/statement-to-ir.js +0 -6
- package/dist/ir/timing-validation.js +1 -1
- package/dist/ir/transformers/array-methods.js +11 -5
- package/dist/ir/transformers/call-statement.js +20 -87
- package/dist/ir/transformers/callback-context-registry.d.ts +1 -1
- package/dist/ir/transformers/callback-context-registry.js +8 -16
- package/dist/ir/transformers/control-flow.js +0 -13
- package/dist/ir/transformers/expressions.js +10 -9
- package/dist/ir/transformers/hal-call-resolver.d.ts +2 -2
- package/dist/ir/transformers/hal-call-resolver.js +24 -10
- package/dist/ir/transformers/hal-emit-helpers.d.ts +2 -2
- package/dist/ir/transformers/hal-emit-helpers.js +2 -2
- package/dist/ir/transformers/namespace-methods.js +1 -1
- package/dist/ir/transformers/ui-callback-lowering.d.ts +0 -2
- package/dist/ir/transformers/ui-callback-lowering.js +0 -24
- package/dist/ir/transformers/variables.js +338 -42
- package/dist/ir/try-catch-validation.js +4 -3
- package/dist/ir/type-resolution.js +15 -4
- package/dist/ir/validation-orchestrator.js +0 -9
- package/dist/libdef/cpp-to-decl.d.ts +0 -9
- package/dist/libdef/cpp-to-decl.js +0 -72
- package/dist/libdef/registry.d.ts +0 -1
- package/dist/libdef/registry.js +1 -24
- package/dist/library/catalog.d.ts +35 -0
- package/dist/library/catalog.js +67 -0
- package/dist/library/cli.d.ts +2 -0
- package/dist/library/cli.js +168 -0
- package/dist/library/init.d.ts +25 -0
- package/dist/library/init.js +397 -0
- package/dist/library/install.d.ts +9 -0
- package/dist/library/install.js +80 -0
- package/dist/library/registry-search.d.ts +34 -0
- package/dist/library/registry-search.js +50 -0
- package/dist/library/validate.d.ts +17 -0
- package/dist/library/validate.js +175 -0
- package/dist/library-packages.d.ts +95 -0
- package/dist/library-packages.js +275 -0
- package/dist/mapping/peripheral-names.js +9 -4
- package/dist/mapping/source-map.d.ts +3 -4
- package/dist/mapping/source-map.js +12 -18
- package/dist/orchestrator/graph-builder.d.ts +3 -3
- package/dist/orchestrator/graph-builder.js +29 -12
- package/dist/orchestrator/type-checker.d.ts +3 -3
- package/dist/orchestrator/type-checker.js +3 -3
- package/dist/platform/async-runtime.js +1 -1
- package/dist/platform/coop-scheduler-runtime.js +2 -2
- package/dist/platform/generic-debug-codegen.d.ts +36 -0
- package/dist/platform/generic-debug-codegen.js +185 -0
- package/dist/platform/generic-strategy.d.ts +3 -4
- package/dist/platform/generic-strategy.js +5 -80
- package/dist/platform/index.js +1 -1
- package/dist/platform/registry.d.ts +2 -2
- package/dist/platform/registry.js +2 -2
- package/dist/preview/client.js +17 -3
- package/dist/safety/asil-decorators.d.ts +32 -0
- package/dist/safety/asil-decorators.js +32 -0
- package/dist/safety/authoring.d.ts +85 -0
- package/dist/safety/authoring.js +61 -0
- package/dist/safety/engine.d.ts +3 -0
- package/dist/safety/engine.js +83 -0
- package/dist/safety/hal/ops.d.ts +48 -0
- package/dist/safety/hal/ops.js +46 -0
- package/dist/safety/iso26262/analyze.d.ts +13 -0
- package/dist/safety/iso26262/analyze.js +29 -0
- package/dist/safety/iso26262/asil.d.ts +10 -0
- package/dist/safety/iso26262/asil.js +22 -0
- package/dist/safety/iso26262/collect.d.ts +14 -0
- package/dist/safety/iso26262/collect.js +137 -0
- package/dist/safety/iso26262/goto-checker.d.ts +10 -0
- package/dist/safety/iso26262/goto-checker.js +72 -0
- package/dist/safety/iso26262/heap-checker.d.ts +7 -0
- package/dist/safety/iso26262/heap-checker.js +57 -0
- package/dist/safety/iso26262/init-checker.d.ts +13 -0
- package/dist/safety/iso26262/init-checker.js +127 -0
- package/dist/safety/iso26262/loop-checker.d.ts +7 -0
- package/dist/safety/iso26262/loop-checker.js +86 -0
- package/dist/safety/iso26262/recursion-checker.d.ts +9 -0
- package/dist/safety/iso26262/recursion-checker.js +134 -0
- package/dist/safety/iso26262/sidecar-writer.d.ts +25 -0
- package/dist/safety/iso26262/sidecar-writer.js +13 -0
- package/dist/safety/passes/pinMode-intercept.d.ts +3 -0
- package/dist/safety/passes/pinMode-intercept.js +60 -0
- package/dist/safety/runtime/mode-table.d.ts +5 -0
- package/dist/safety/runtime/mode-table.js +55 -0
- package/dist/safety/runtime/polyfills.d.ts +6 -0
- package/dist/safety/runtime/polyfills.js +12 -0
- package/dist/safety/runtime/result-struct.d.ts +11 -0
- package/dist/safety/runtime/result-struct.js +98 -0
- package/dist/safety/runtime/safe-int.d.ts +33 -0
- package/dist/safety/runtime/safe-int.js +170 -0
- package/dist/safety/runtime/safe-traits.d.ts +4 -0
- package/dist/safety/runtime/safe-traits.js +95 -0
- package/dist/safety/runtime/safe-variable.d.ts +39 -0
- package/dist/safety/runtime/safe-variable.js +226 -0
- package/dist/safety/runtime/vote.d.ts +8 -0
- package/dist/safety/runtime/vote.js +92 -0
- package/dist/safety/runtime/write-verify.d.ts +8 -0
- package/dist/safety/runtime/write-verify.js +71 -0
- package/dist/safety/safe-int-types.d.ts +45 -0
- package/dist/safety/safe-int-types.js +25 -0
- package/dist/safety/safe-variable-types.d.ts +29 -0
- package/dist/safety/safe-variable-types.js +19 -0
- package/dist/safety/safety-bridge.d.ts +5 -6
- package/dist/safety/safety-bridge.js +15 -28
- package/dist/safety/sidecar-bridge.d.ts +1 -3
- package/dist/safety/sidecar-bridge.js +3 -20
- package/dist/safety/specifiers.d.ts +6 -0
- package/dist/safety/specifiers.js +14 -0
- package/dist/safety-hook.d.ts +8 -7
- package/dist/safety-hook.js +12 -13
- package/dist/testing.d.ts +5 -6
- package/dist/testing.js +5 -5
- package/dist/transpile/resolution.d.ts +1 -1
- package/dist/transpile/resolution.js +18 -9
- package/dist/transpile/test-pins.d.ts +18 -0
- package/dist/transpile/test-pins.js +142 -0
- package/dist/transpile.d.ts +1 -2
- package/dist/transpile.js +90 -29
- package/dist/types.d.ts +45 -26
- package/dist/ui-hook.d.ts +1 -1
- package/dist/utils/cli.d.ts +2 -2
- package/dist/utils/cli.js +160 -157
- package/dist/utils/fs.d.ts +3 -4
- package/dist/utils/fs.js +4 -5
- package/dist/utils/strings.d.ts +0 -1
- package/dist/utils/strings.js +0 -8
- package/dist/utils/ui.d.ts +5 -2
- package/dist/utils/ui.js +13 -3
- package/package.json +158 -149
- package/dist/add-preset.d.ts +0 -4
- package/dist/add-preset.js +0 -74
- package/dist/api/shared/async-symbol-detector.d.ts +0 -11
- package/dist/api/shared/async-symbol-detector.js +0 -140
- package/dist/api/shared/native-display-op-resolver.d.ts +0 -10
- package/dist/api/shared/native-display-op-resolver.js +0 -64
- package/dist/create/board-checklist.d.ts +0 -2
- package/dist/create/board-checklist.js +0 -52
- package/dist/create/board-codegen.d.ts +0 -9
- package/dist/create/board-codegen.js +0 -238
- package/dist/create/board-generators.d.ts +0 -12
- package/dist/create/board-generators.js +0 -651
- package/dist/create/board-spec.d.ts +0 -637
- package/dist/create/board-spec.js +0 -214
- package/dist/create/init-templates.d.ts +0 -28
- package/dist/create/init-wizard.d.ts +0 -8
- package/dist/create/init-wizard.js +0 -182
- package/dist/install/framework-catalog.d.ts +0 -53
- package/dist/install/framework-catalog.js +0 -107
- package/dist/install/handle-install.d.ts +0 -35
- package/dist/install/handle-install.js +0 -177
- package/dist/install/index.d.ts +0 -4
- package/dist/install/index.js +0 -3
- package/dist/ir/pin-state-tracking.d.ts +0 -58
- package/dist/ir/pin-state-tracking.js +0 -182
- package/dist/ir/pwm-timer-sharing.d.ts +0 -4
- package/dist/ir/pwm-timer-sharing.js +0 -94
- package/dist/ir/timer0-pwm-timing-conflict.d.ts +0 -4
- package/dist/ir/timer0-pwm-timing-conflict.js +0 -72
- package/dist/libdef/component-decls.d.ts +0 -2
- package/dist/libdef/component-decls.js +0 -6
- package/dist/libdef/component-discovery.d.ts +0 -43
- package/dist/libdef/component-discovery.js +0 -83
- package/dist/theme-tokens.d.ts +0 -22
- package/dist/theme-tokens.js +0 -172
|
@@ -0,0 +1,664 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// NativeStrategy — standard C++ target for portable Windows/Linux executables
|
|
3
|
+
//
|
|
4
|
+
// Outputs standard C++ with main(), std::string, and std::thread-
|
|
5
|
+
// based async. No hardware or Arduino dependencies.
|
|
6
|
+
//
|
|
7
|
+
// EMIT BOUNDARY: This file is a canonical entry point of the framework strategy
|
|
8
|
+
// surface (B) — its main()/stdout scaffold bytes land in user executables.
|
|
9
|
+
// The emitted bytes are covered by the TypeCAD Runtime Exception (see
|
|
10
|
+
// RUNTIME_EXCEPTION.md at the repository root) and are not subject to the
|
|
11
|
+
// license of this tool source.
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
import { DEFAULT_STDLIB_SUPPORT, generateStaticAsyncRuntime } from '../../api/shared/index.js';
|
|
14
|
+
import { programUsesSafety } from '../../api/index.js';
|
|
15
|
+
import { resolveTerminalPreviewOp } from './graphics/terminal-preview.js';
|
|
16
|
+
export class NativeStrategy {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.id = 'native';
|
|
19
|
+
this._largeEnumNames = new Set();
|
|
20
|
+
}
|
|
21
|
+
// ── Profile ─────────────────────────────────────────────────────────────
|
|
22
|
+
forcedIncludes(program, ctx) {
|
|
23
|
+
// <cstdint>, <cctype>, and <chrono> are universal: type-resolution passes
|
|
24
|
+
// int32_t/uint8_t/etc. through verbatim, char classification is broadly
|
|
25
|
+
// used, and the shim unconditionally emits Date.now()/millis() definitions
|
|
26
|
+
// that reference std::chrono (see shimLines). The remaining headers are
|
|
27
|
+
// gated on usage analysis so a native program that doesn't touch
|
|
28
|
+
// std::vector / etc. doesn't pull them in. When ctx.analysis is absent
|
|
29
|
+
// (capability queries, manifest validation), the gates default open to
|
|
30
|
+
// preserve existing behavior in those paths.
|
|
31
|
+
const inc = ['<cctype>', '<cstdint>', '<chrono>'];
|
|
32
|
+
const a = ctx?.analysis;
|
|
33
|
+
const uses = (f) => a ? !!a[f] : true;
|
|
34
|
+
if (uses('usesVectorTypes'))
|
|
35
|
+
inc.push('<vector>');
|
|
36
|
+
if (uses('usesStdMap'))
|
|
37
|
+
inc.push('<map>');
|
|
38
|
+
if (uses('usesSet'))
|
|
39
|
+
inc.push('<set>');
|
|
40
|
+
if (uses('usesAlgorithm'))
|
|
41
|
+
inc.push('<algorithm>');
|
|
42
|
+
if (uses('usesCstdio'))
|
|
43
|
+
inc.push('<cstdio>');
|
|
44
|
+
return inc;
|
|
45
|
+
}
|
|
46
|
+
symbolAliases() {
|
|
47
|
+
return {};
|
|
48
|
+
}
|
|
49
|
+
shimLines(program, ctx) {
|
|
50
|
+
const a = ctx?.analysis;
|
|
51
|
+
const uses = (f) => a ? !!a[f] : true;
|
|
52
|
+
const baseLines = [
|
|
53
|
+
'// cuttlefish runtime shim. Wrapped in a single include guard so the',
|
|
54
|
+
'// block is safe to emit into multiple headers and .cpp files within',
|
|
55
|
+
'// one translation unit (a .cpp may #include several headers that each',
|
|
56
|
+
'// carry the shim, e.g. when a class method body uses `??` which lowers',
|
|
57
|
+
'// to cuttlefish_nullish(...)). The guard ensures the definitions are',
|
|
58
|
+
'// seen exactly once per TU.',
|
|
59
|
+
'#ifndef CUTTLEFISH_SHIM_DEFINED',
|
|
60
|
+
'#define CUTTLEFISH_SHIM_DEFINED',
|
|
61
|
+
'#ifndef CUTTLEFISH_UNDEFINED',
|
|
62
|
+
'#define CUTTLEFISH_UNDEFINED 0',
|
|
63
|
+
'#endif',
|
|
64
|
+
'template<typename T> inline bool cuttlefish_is_nullish(const T& v) { return false; }',
|
|
65
|
+
'inline bool cuttlefish_is_nullish(long long v) { return v == CUTTLEFISH_UNDEFINED; }',
|
|
66
|
+
'inline bool cuttlefish_is_nullish(int v) { return v == CUTTLEFISH_UNDEFINED; }',
|
|
67
|
+
'inline bool cuttlefish_is_nullish(double v) { return v == static_cast<double>(CUTTLEFISH_UNDEFINED); }',
|
|
68
|
+
'inline bool cuttlefish_is_nullish(bool v) { return v == false; }',
|
|
69
|
+
'template<typename T> inline bool cuttlefish_is_nullish(T* v) { return v == nullptr; }',
|
|
70
|
+
'template<typename T> inline bool cuttlefish_exists(const T& v) { return !cuttlefish_is_nullish(v); }',
|
|
71
|
+
'template<typename T, typename U> inline T cuttlefish_nullish(const T& a, U b) { return !cuttlefish_is_nullish(a) ? a : (T)b; }',
|
|
72
|
+
'namespace Date { inline long now() { auto t = std::chrono::system_clock::now(); return static_cast<long>(std::chrono::duration_cast<std::chrono::milliseconds>(t.time_since_epoch()).count()); } }',
|
|
73
|
+
'inline uint32_t __tc_now_ms(void) { return static_cast<uint32_t>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count()); }',
|
|
74
|
+
];
|
|
75
|
+
// Arduino-compat polyfills (pin reads, constrain, map) — emit only when the
|
|
76
|
+
// program (or a mounted UI) actually references them. The UI runtime references
|
|
77
|
+
// digitalRead/HIGH/LOW, so emit them when GPIO is in use too. Default open
|
|
78
|
+
// when ctx.analysis is absent (capability queries).
|
|
79
|
+
if (uses('usesDigitalRead') || uses('usesGPIO')) {
|
|
80
|
+
baseLines.push('#ifndef HIGH', '#define HIGH 1', '#endif', '#ifndef LOW', '#define LOW 0', '#endif', '#ifndef PROGMEM', '#define PROGMEM', '#endif', 'inline int digitalRead(int) { return LOW; }');
|
|
81
|
+
}
|
|
82
|
+
if (uses('usesMap')) {
|
|
83
|
+
baseLines.push('inline long map(long x, long in_min, long in_max, long out_min, long out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; }');
|
|
84
|
+
}
|
|
85
|
+
if (uses('usesConstrain')) {
|
|
86
|
+
baseLines.push('inline long constrain(long x, long a, long b) { return x < a ? a : (x > b ? b : x); }');
|
|
87
|
+
}
|
|
88
|
+
baseLines.push('#endif // CUTTLEFISH_SHIM_DEFINED');
|
|
89
|
+
// Safety: emit the __tc_gpio_read / __tc_delay_us shims when the program
|
|
90
|
+
// uses @typecad/safety. Native target stubs GPIO read (the SDL simulator
|
|
91
|
+
// doesn't model real digital input levels) and the microsecond delay
|
|
92
|
+
// (no real timing source); returns 0 (LOW) / no-op. Real native demos
|
|
93
|
+
// that exercise safe.read would need their own input source wired in here.
|
|
94
|
+
if (program && programUsesSafety(program)) {
|
|
95
|
+
baseLines.push('inline int __tc_gpio_read(uint32_t pin) { return 0; }', 'inline void __tc_gpio_write(uint32_t pin, uint32_t value) { (void)pin; (void)value; }', '#ifndef __TC_DELAY_US_DEFINED', '#define __TC_DELAY_US_DEFINED', 'inline void __tc_delay_us(uint32_t us) { (void)us; }', '#endif');
|
|
96
|
+
}
|
|
97
|
+
return baseLines;
|
|
98
|
+
}
|
|
99
|
+
profileDiagnostics() {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
// ── File shape ──────────────────────────────────────────────────────────
|
|
103
|
+
sourceExtension() {
|
|
104
|
+
return 'cpp';
|
|
105
|
+
}
|
|
106
|
+
entrypointFunctionName() {
|
|
107
|
+
return 'main';
|
|
108
|
+
}
|
|
109
|
+
requiresLoopFunction() {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
overrideBaseName(originalBaseName) {
|
|
113
|
+
return originalBaseName;
|
|
114
|
+
}
|
|
115
|
+
effectiveEmitMode(requestedMode) {
|
|
116
|
+
return requestedMode;
|
|
117
|
+
}
|
|
118
|
+
// ── Type normalisation ──────────────────────────────────────────────────
|
|
119
|
+
normalizeCppType(typeName) {
|
|
120
|
+
// Preserve auto — C++ compiler deduces the correct type (critical for template returns)
|
|
121
|
+
if (typeName === 'auto')
|
|
122
|
+
return 'auto';
|
|
123
|
+
// JavaScript number is 64-bit — map to long long to avoid overflow
|
|
124
|
+
if (typeName === 'int')
|
|
125
|
+
return 'long long';
|
|
126
|
+
// JavaScript number fractional precision needs double, not float
|
|
127
|
+
if (typeName === 'float')
|
|
128
|
+
return 'double';
|
|
129
|
+
// Map StaticArray back to its alias name (handled by pipeline typedef)
|
|
130
|
+
if (typeName.startsWith("__tc_StaticArray")) {
|
|
131
|
+
const match = typeName.match(/^__tc_StaticArray<(.+),\s*\d+>$/);
|
|
132
|
+
if (match)
|
|
133
|
+
return `std::vector<${match[1]}>`;
|
|
134
|
+
return typeName.replace("__tc_StaticArray", "StaticArray");
|
|
135
|
+
}
|
|
136
|
+
return typeName;
|
|
137
|
+
}
|
|
138
|
+
defaultNumericType(compliance) {
|
|
139
|
+
return compliance?.isBanned("A3-9-1") ? 'int64_t' : 'long long';
|
|
140
|
+
}
|
|
141
|
+
mapReturnType(functionName, returnType) {
|
|
142
|
+
if (functionName === 'main')
|
|
143
|
+
return 'int';
|
|
144
|
+
return this.normalizeCppType(returnType);
|
|
145
|
+
}
|
|
146
|
+
isStringLikeType(cppType) {
|
|
147
|
+
return cppType === "std::string" || cppType === "const char*" || cppType === "char*";
|
|
148
|
+
}
|
|
149
|
+
isPointerType(cppType) {
|
|
150
|
+
return cppType.endsWith("*");
|
|
151
|
+
}
|
|
152
|
+
mapFunctionName(originalName) {
|
|
153
|
+
if (originalName === '__cuttlefish_entrypoint__')
|
|
154
|
+
return 'main';
|
|
155
|
+
return originalName;
|
|
156
|
+
}
|
|
157
|
+
// ── Expression rendering ────────────────────────────────────────────────
|
|
158
|
+
normalizeRawExpression(value) {
|
|
159
|
+
let prev = '';
|
|
160
|
+
let v = value;
|
|
161
|
+
// NOTE: array/collection method lowering (`.push`/`.pop`/`.map`/`.filter`/
|
|
162
|
+
// `.splice`/... → `__tc_*` helpers) and `.length` → `.size()` previously
|
|
163
|
+
// lived here as RECV-regex rewrites over rendered C++ text. They have been
|
|
164
|
+
// MOVED to structural IR-build-time lowering: array mutators in
|
|
165
|
+
// `ir/transformers/array-methods.ts` `tryLowerArrayAndStringMethods`
|
|
166
|
+
// (gated to hosted targets via `requiresLoopFunction()`), and `.length` in
|
|
167
|
+
// `ir/expression-to-ir.ts` `resolveLengthProperty`. Both render the
|
|
168
|
+
// receiver via `expressionToIR` so pointer/value access is already correct,
|
|
169
|
+
// eliminating the RECV receiver-capture class of bug (demo #22 Finding A,
|
|
170
|
+
// where `(\w+)` stopped at `>` and emitted `this->__tc_pop(ops)`).
|
|
171
|
+
//
|
|
172
|
+
// What remains here is genuinely text-level: literal→sentinel and
|
|
173
|
+
// well-known free-function rewrites that don't depend on symbol resolution.
|
|
174
|
+
while (prev !== v) {
|
|
175
|
+
prev = v;
|
|
176
|
+
v = v.replace(/\bundefined\b/g, 'CUTTLEFISH_UNDEFINED');
|
|
177
|
+
v = v.replace(/\bnull\b/g, 'CUTTLEFISH_UNDEFINED');
|
|
178
|
+
v = v.replace(/Date\.now\(\)/g, 'Date::now()');
|
|
179
|
+
// String-method lowering now happens at IR-build time
|
|
180
|
+
// (tryLowerArrayAndStringMethods in array-methods.ts, demo #27 Findings
|
|
181
|
+
// D/E), so it handles every receiver shape (bare id / this.field /
|
|
182
|
+
// obj.field / X[i]) structurally. The old text-level
|
|
183
|
+
// `applyStringMethodRewrites` call is removed; its RECEIVER_PATTERN only
|
|
184
|
+
// matched bare identifiers and `.member` chains, which left
|
|
185
|
+
// `ALPHABET[i].toLowerCase()` verbatim and failed to register the
|
|
186
|
+
// `__tc_toLowerCase` helper. The native `startsWith` → `rfind` special
|
|
187
|
+
// case is preserved inside the structural lowering (lowerStringMethod).
|
|
188
|
+
v = v.replace(/JSON\.stringify\(([^)]+)\)/g, '__tc_jsonStringify($1)');
|
|
189
|
+
v = v.replace(/JSON\.parse\(([^)]+)\)/g, '__tc_jsonParse($1)');
|
|
190
|
+
}
|
|
191
|
+
return v;
|
|
192
|
+
}
|
|
193
|
+
nullValue() {
|
|
194
|
+
return 'CUTTLEFISH_UNDEFINED';
|
|
195
|
+
}
|
|
196
|
+
wrapStringConcat() {
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
199
|
+
wrapStringObject(value) {
|
|
200
|
+
// With useSnprintfForStrings() = true, concat flows through the snprintf
|
|
201
|
+
// path and this fallback is rarely used. Keep std::to_string for safety,
|
|
202
|
+
// but it must never receive an enum/class instance in practice.
|
|
203
|
+
return `std::to_string(${value})`;
|
|
204
|
+
}
|
|
205
|
+
useSnprintfForStrings() {
|
|
206
|
+
// Unify native with the Arduino/AVR snprintf path so string concatenation
|
|
207
|
+
// handles enums/floats/objects correctly and never emits std::to_string
|
|
208
|
+
// on non-arithmetic types. See inferFormatSpecifier for type handling.
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
promoteDivisionToDouble() {
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
renameEnumMember(_enumName, memberName) {
|
|
215
|
+
return memberName;
|
|
216
|
+
}
|
|
217
|
+
setLargeEnumNames(names) {
|
|
218
|
+
this._largeEnumNames = new Set(names);
|
|
219
|
+
}
|
|
220
|
+
enumCastType(enumName) {
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
renderBoardDefinitionAccess() {
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
// ── Statement rendering ─────────────────────────────────────────────────
|
|
227
|
+
/**
|
|
228
|
+
* Native lowers array literals to std::vector (not __tc_StaticArray), so the
|
|
229
|
+
* structural array-method lowering must use the std::vector/__tc_* helper form
|
|
230
|
+
* (.push_back, __tc_pop) rather than the StaticArray wrapper's .push/.pop.
|
|
231
|
+
* Embedded/generic targets promote literals to StaticArray and default to true.
|
|
232
|
+
*/
|
|
233
|
+
promotesArrayLiteralsToStaticArray() {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
renderThrow(valueExpr) {
|
|
237
|
+
return `throw ${valueExpr};`;
|
|
238
|
+
}
|
|
239
|
+
objectFieldInitializer() {
|
|
240
|
+
return undefined;
|
|
241
|
+
}
|
|
242
|
+
overrideClassFieldType(_fieldName, normalizedType) {
|
|
243
|
+
return normalizedType;
|
|
244
|
+
}
|
|
245
|
+
// ── Name guards ─────────────────────────────────────────────────────────
|
|
246
|
+
reservedNames() {
|
|
247
|
+
return new Set();
|
|
248
|
+
}
|
|
249
|
+
passthroughMacroNames() {
|
|
250
|
+
return new Set();
|
|
251
|
+
}
|
|
252
|
+
apiReservedEnumNames() {
|
|
253
|
+
return new Set();
|
|
254
|
+
}
|
|
255
|
+
apiReservedEnumGuard() {
|
|
256
|
+
return '';
|
|
257
|
+
}
|
|
258
|
+
ambientTypeDeclarations() {
|
|
259
|
+
return [
|
|
260
|
+
"",
|
|
261
|
+
" // Host timers — real OS threads back these on the native (host) target",
|
|
262
|
+
" // only. Embedded targets have no JS-named timers: periodic work is a",
|
|
263
|
+
" // Thread.",
|
|
264
|
+
" declare function setInterval(handler: () => void, timeout?: number): number;",
|
|
265
|
+
" declare function setTimeout(handler: () => void, timeout?: number): number;",
|
|
266
|
+
" declare function clearInterval(id: number): void;",
|
|
267
|
+
" declare function clearTimeout(id: number): void;",
|
|
268
|
+
];
|
|
269
|
+
}
|
|
270
|
+
// ── Includes ────────────────────────────────────────────────────────────
|
|
271
|
+
needsIostream() { return true; }
|
|
272
|
+
needsStdString() { return true; }
|
|
273
|
+
needsStdVector() { return true; }
|
|
274
|
+
needsStdExcept() { return true; }
|
|
275
|
+
needsStdFunction() { return true; }
|
|
276
|
+
mathHeader() { return '<cmath>'; }
|
|
277
|
+
cstringHeader() { return '<cstring>'; }
|
|
278
|
+
needsLargeEnumUnderlying() { return false; }
|
|
279
|
+
// ── Struct field handling ───────────────────────────────────────────────
|
|
280
|
+
renameStructField(fieldName) {
|
|
281
|
+
return fieldName;
|
|
282
|
+
}
|
|
283
|
+
structFieldInitializer() {
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
286
|
+
// ── Async — std::async background pump ──────────────────────────────────
|
|
287
|
+
getAsyncRuntimeConfig() {
|
|
288
|
+
return {
|
|
289
|
+
queueCapacity: 256,
|
|
290
|
+
scheduler: "thread",
|
|
291
|
+
waitForPinEdge: "stub",
|
|
292
|
+
hasPromiseRuntime: true,
|
|
293
|
+
hasTimers: true,
|
|
294
|
+
requiredIncludes: ["<functional>", "<vector>", "<utility>", "<string>", "<thread>", "<chrono>", "<future>"],
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
asyncLoopInjection(taskVarNames, configOrBool, hasTimers) {
|
|
298
|
+
let hasPromiseRuntime;
|
|
299
|
+
let hasTimersVal;
|
|
300
|
+
if (typeof configOrBool === 'boolean') {
|
|
301
|
+
hasPromiseRuntime = configOrBool;
|
|
302
|
+
hasTimersVal = hasTimers ?? false;
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
hasPromiseRuntime = configOrBool.hasPromiseRuntime;
|
|
306
|
+
hasTimersVal = configOrBool.hasTimers;
|
|
307
|
+
}
|
|
308
|
+
const lines = [];
|
|
309
|
+
if (taskVarNames.length > 0 || hasPromiseRuntime) {
|
|
310
|
+
lines.push('std::async(std::launch::async, [&]() {');
|
|
311
|
+
lines.push(' while (true) {');
|
|
312
|
+
for (const n of taskVarNames) {
|
|
313
|
+
lines.push(` ${n}.run();`);
|
|
314
|
+
}
|
|
315
|
+
if (hasPromiseRuntime) {
|
|
316
|
+
lines.push(' cuttlefish_pump_microtasks();');
|
|
317
|
+
}
|
|
318
|
+
lines.push(' std::this_thread::sleep_for(std::chrono::milliseconds(1));');
|
|
319
|
+
lines.push(' }');
|
|
320
|
+
lines.push('});');
|
|
321
|
+
}
|
|
322
|
+
return lines;
|
|
323
|
+
}
|
|
324
|
+
asyncDriverFunctionName() {
|
|
325
|
+
return 'main';
|
|
326
|
+
}
|
|
327
|
+
// ── Type aliases ────────────────────────────────────────────────────────
|
|
328
|
+
shouldSkipTypeAlias() {
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
// ── Diagnostics ─────────────────────────────────────────────────────────
|
|
332
|
+
emitDiagnostics() {
|
|
333
|
+
return [];
|
|
334
|
+
}
|
|
335
|
+
currentTimeMillis() {
|
|
336
|
+
return '__tc_now_ms()';
|
|
337
|
+
}
|
|
338
|
+
// ── Build configuration ──────────────────────────────────────────────────
|
|
339
|
+
asyncQueueCapacity() { return 256; }
|
|
340
|
+
outputSubdirectory(_baseName) { return ".build"; }
|
|
341
|
+
generateHeaderFile() { return true; }
|
|
342
|
+
enumApiGuard(_enumName) { return undefined; }
|
|
343
|
+
getStdLibSupport(_architecture) { return DEFAULT_STDLIB_SUPPORT; }
|
|
344
|
+
// ── Native polyfills ────────────────────────────────────────────────────
|
|
345
|
+
nativePolyfills() {
|
|
346
|
+
return new Set(['console', 'string_methods', 'timer_methods', 'array_methods', 'math_methods']);
|
|
347
|
+
}
|
|
348
|
+
generateNativePolyfills(program) {
|
|
349
|
+
const polyfills = [
|
|
350
|
+
{
|
|
351
|
+
kind: 'polyfill',
|
|
352
|
+
id: 'string_methods',
|
|
353
|
+
domain: 'standard',
|
|
354
|
+
requiredIncludes: ['<sstream>'],
|
|
355
|
+
forwardDeclarations: [],
|
|
356
|
+
helperStructs: [],
|
|
357
|
+
helperFunctions: [
|
|
358
|
+
// ── Core string methods (existing) ─────────────────────────────
|
|
359
|
+
'inline std::string __tc_toUpperCase(const std::string& s) { std::string r = s; for (auto& c : r) c = static_cast<char>(toupper(static_cast<unsigned char>(c))); return r; }',
|
|
360
|
+
'inline std::string __tc_toLowerCase(const std::string& s) { std::string r = s; for (auto& c : r) c = static_cast<char>(tolower(static_cast<unsigned char>(c))); return r; }',
|
|
361
|
+
'inline std::string __tc_trim(const std::string& s) { size_t start = s.find_first_not_of(" \\t\\n\\r"); if (start == std::string::npos) return ""; size_t end = s.find_last_not_of(" \\t\\n\\r"); return s.substr(start, end - start + 1); }',
|
|
362
|
+
'inline std::string __tc_substring2(const std::string& s, int start, int end) { return s.substr(start, end - start); }',
|
|
363
|
+
'inline std::string __tc_substring1(const std::string& s, int start) { return s.substr(start); }',
|
|
364
|
+
'inline std::string __tc_replace(const std::string& s, const std::string& old, const std::string& repl) { std::string r = s; size_t pos = 0; while ((pos = r.find(old, pos)) != std::string::npos) { r.replace(pos, old.length(), repl); pos += repl.length(); } return r; }',
|
|
365
|
+
'inline std::string __tc_charAt(const std::string& s, int idx) { return std::string(1, s[idx]); }',
|
|
366
|
+
'inline int __tc_charCodeAt(const std::string& s, int idx) { return static_cast<int>(static_cast<unsigned char>(s[idx])); }',
|
|
367
|
+
'inline std::vector<std::string> __tc_split(const std::string& s, const std::string& delim) { std::vector<std::string> parts; if (delim.empty()) { for (char c : s) parts.push_back(std::string(1, c)); return parts; } size_t start = 0, end; while ((end = s.find(delim, start)) != std::string::npos) { parts.push_back(s.substr(start, end - start)); start = end + delim.length(); } parts.push_back(s.substr(start)); return parts; }',
|
|
368
|
+
// ── Extended string methods (Phase 1) ──────────────────────────
|
|
369
|
+
'inline bool __tc_endsWith(const std::string& s, const std::string& suffix) { if (suffix.size() > s.size()) return false; return s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0; }',
|
|
370
|
+
'inline int __tc_lastIndexOf(const std::string& s, const std::string& search) { size_t pos = s.rfind(search); return pos != std::string::npos ? static_cast<int>(pos) : -1; }',
|
|
371
|
+
'inline std::string __tc_padStart(const std::string& s, int len, const std::string& fill) { if (static_cast<int>(s.size()) >= len) return s; std::string result; int padLen = len - static_cast<int>(s.size()); for (int i = 0; i < padLen; i++) result += fill[i % static_cast<int>(fill.size())]; return result + s; }',
|
|
372
|
+
'inline std::string __tc_padStart_default(const std::string& s, int len) { return __tc_padStart(s, len, " "); }',
|
|
373
|
+
'inline std::string __tc_padEnd(const std::string& s, int len, const std::string& fill) { if (static_cast<int>(s.size()) >= len) return s; std::string result = s; int padLen = len - static_cast<int>(s.size()); for (int i = 0; i < padLen; i++) result += fill[i % static_cast<int>(fill.size())]; return result; }',
|
|
374
|
+
'inline std::string __tc_padEnd_default(const std::string& s, int len) { return __tc_padEnd(s, len, " "); }',
|
|
375
|
+
'inline std::string __tc_repeat(const std::string& s, int count) { std::string result; for (int i = 0; i < count; i++) result += s; return result; }',
|
|
376
|
+
// ── Overloaded includes/indexOf for std::string ────────────────
|
|
377
|
+
'inline bool __tc_includes(const std::string& s, const std::string& search) { return s.find(search) != std::string::npos; }',
|
|
378
|
+
'inline int __tc_indexOf(const std::string& s, const std::string& search) { size_t pos = s.find(search); return pos != std::string::npos ? static_cast<int>(pos) : -1; }',
|
|
379
|
+
// ── String slice overloads ──────────────────────────────────────
|
|
380
|
+
'inline std::string __tc_slice2(const std::string& s, int start, int end) { return s.substr(start, end - start); }',
|
|
381
|
+
'inline std::string __tc_slice1(const std::string& s, int start) { return s.substr(start); }',
|
|
382
|
+
],
|
|
383
|
+
shimMacros: [],
|
|
384
|
+
dependencies: [],
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
kind: 'polyfill',
|
|
388
|
+
id: 'timer_methods',
|
|
389
|
+
domain: 'standard',
|
|
390
|
+
// <atomic> for the cancel flag, <map>+<memory> for the handle store.
|
|
391
|
+
// <future> kept for any caller that still threads a detached policy.
|
|
392
|
+
requiredIncludes: ['<thread>', '<chrono>', '<future>', '<atomic>', '<map>', '<memory>'],
|
|
393
|
+
forwardDeclarations: [],
|
|
394
|
+
helperStructs: [],
|
|
395
|
+
helperFunctions: [
|
|
396
|
+
// Cancellable timer handles. The old shims launched a detached
|
|
397
|
+
// std::async (`(void)f;`) whose `while(true)` ignored clear* —
|
|
398
|
+
// cancelled timers kept firing and the futures leaked. Timers are a
|
|
399
|
+
// language feature on native (not hardware), so they must actually
|
|
400
|
+
// cancel: each handle owns an atomic cancel flag its worker thread
|
|
401
|
+
// checks each iteration; clear* sets the flag. Leaked handles at
|
|
402
|
+
// process exit are fine (the OS reclaims threads); the fix is about
|
|
403
|
+
// correctness (a cleared timer stops firing) and bounded growth.
|
|
404
|
+
'struct __tc_timer_handle { std::atomic<bool> cancelled{false}; std::thread worker; };',
|
|
405
|
+
'static std::map<int, std::shared_ptr<__tc_timer_handle>>& __tc_timers() { static std::map<int, std::shared_ptr<__tc_timer_handle>> m; return m; }',
|
|
406
|
+
'static int __tc_next_timer_id = 1;',
|
|
407
|
+
'static int __tc_start_timer(std::function<void()> cb, long long ms, bool repeat) { int id = __tc_next_timer_id++; auto h = std::make_shared<__tc_timer_handle>(); h->worker = std::thread([h, cb, ms, repeat]() { if (repeat) { while (!h->cancelled.load()) { std::this_thread::sleep_for(std::chrono::milliseconds(ms)); if (h->cancelled.load()) break; cb(); } } else { std::this_thread::sleep_for(std::chrono::milliseconds(ms)); if (!h->cancelled.load()) cb(); } }); h->worker.detach(); __tc_timers()[id] = h; return id; }',
|
|
408
|
+
'int __tc_setTimeout(std::function<void()> cb, long long ms) { return __tc_start_timer(cb, ms, false); }',
|
|
409
|
+
'int __tc_setInterval(std::function<void()> cb, long long ms) { return __tc_start_timer(cb, ms, true); }',
|
|
410
|
+
'void __tc_clearInterval(int id) { auto it = __tc_timers().find(id); if (it != __tc_timers().end()) { it->second->cancelled.store(true); __tc_timers().erase(it); } }',
|
|
411
|
+
'void __tc_clearTimeout(int id) { __tc_clearInterval(id); }',
|
|
412
|
+
],
|
|
413
|
+
shimMacros: [],
|
|
414
|
+
dependencies: [],
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
kind: 'polyfill',
|
|
418
|
+
id: 'math_methods',
|
|
419
|
+
domain: 'standard',
|
|
420
|
+
requiredIncludes: ['<cstdlib>', '<random>', '<iomanip>', '<sstream>'],
|
|
421
|
+
forwardDeclarations: [],
|
|
422
|
+
helperStructs: [],
|
|
423
|
+
helperFunctions: [
|
|
424
|
+
'inline double __tc_random() { static std::mt19937 gen(std::random_device{}()); static std::uniform_real_distribution<double> dist(0.0, 1.0); return dist(gen); }',
|
|
425
|
+
'inline std::string __tc_toFixed(double val, int digits) { std::ostringstream oss; oss << std::fixed << std::setprecision(digits) << val; return oss.str(); }',
|
|
426
|
+
],
|
|
427
|
+
shimMacros: [],
|
|
428
|
+
dependencies: [],
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
kind: 'polyfill',
|
|
432
|
+
id: 'array_methods',
|
|
433
|
+
domain: 'standard',
|
|
434
|
+
// `<sstream>` is required by `__tc_join` below (it builds the joined
|
|
435
|
+
// string through a `std::ostringstream`). A project that uses `.join`
|
|
436
|
+
// WITHOUT also pulling in `__tc_toFixed`/`__tc_random` (whose
|
|
437
|
+
// `math_methods` block is what previously transitively included
|
|
438
|
+
// `<sstream>`) emitted the `__tc_join` template with no `<sstream>`
|
|
439
|
+
// → g++ "std::ostringstream has incomplete type". Declaring the include
|
|
440
|
+
// on THIS block makes `.join` self-contained. Demo #32 Finding B.
|
|
441
|
+
requiredIncludes: ['<algorithm>', '<map>', '<sstream>'],
|
|
442
|
+
forwardDeclarations: [],
|
|
443
|
+
helperStructs: [],
|
|
444
|
+
helperFunctions: [
|
|
445
|
+
// ── Core array methods (existing) ───────────────────────────────
|
|
446
|
+
'template<typename T> std::string __tc_join(const std::vector<T>& v, const std::string& delim) { std::ostringstream oss; for (size_t i = 0; i < v.size(); i++) { if (i > 0) oss << delim; oss << v[i]; } return oss.str(); }',
|
|
447
|
+
'template<typename T> std::vector<T> __tc_slice2(const std::vector<T>& v, int start, int end) { if (end > static_cast<int>(v.size())) end = static_cast<int>(v.size()); return std::vector<T>(v.begin() + start, v.begin() + end); }',
|
|
448
|
+
'template<typename T> std::vector<T> __tc_slice1(const std::vector<T>& v, int start) { return std::vector<T>(v.begin() + start, v.end()); }',
|
|
449
|
+
'template<typename T> std::vector<T> __tc_reverse(std::vector<T> v) { std::reverse(v.begin(), v.end()); return v; }',
|
|
450
|
+
// ── Overloaded includes/indexOf for std::vector ─────────────────
|
|
451
|
+
'template<typename T> bool __tc_includes(const std::vector<T>& v, const T& val) { return std::find(v.begin(), v.end(), val) != v.end(); }',
|
|
452
|
+
'template<typename T> int __tc_indexOf(const std::vector<T>& v, const T& val) { auto it = std::find(v.begin(), v.end(), val); return it != v.end() ? static_cast<int>(it - v.begin()) : -1; }',
|
|
453
|
+
// ── Array mutation methods (Phase 1) ────────────────────────────
|
|
454
|
+
'template<typename T> T __tc_shift(std::vector<T>& v) { T val = v.front(); v.erase(v.begin()); return val; }',
|
|
455
|
+
'template<typename T> T __tc_pop(std::vector<T>& v) { T val = v.back(); v.pop_back(); return val; }',
|
|
456
|
+
'template<typename T> void __tc_unshift(std::vector<T>& v, const T& val) { v.insert(v.begin(), val); }',
|
|
457
|
+
'template<typename T> void __tc_sort(std::vector<T>& v) { std::sort(v.begin(), v.end()); }',
|
|
458
|
+
'template<typename T, typename F> void __tc_sort_fn(std::vector<T>& v, F comp) { std::sort(v.begin(), v.end(), [&v, comp](const typename std::vector<T>::value_type& a, const typename std::vector<T>::value_type& b) { return comp(a, b) < 0; }); }',
|
|
459
|
+
'template<typename T> void __tc_fill(std::vector<T>& v, const T& val) { std::fill(v.begin(), v.end(), val); }',
|
|
460
|
+
'template<typename T> void __tc_fill3(std::vector<T>& v, const T& val, int start, int end) { if (end > static_cast<int>(v.size())) end = static_cast<int>(v.size()); std::fill(v.begin() + start, v.begin() + end, val); }',
|
|
461
|
+
'template<typename T> std::vector<T> __tc_concat(const std::vector<T>& a, const std::vector<T>& b) { std::vector<T> result = a; result.insert(result.end(), b.begin(), b.end()); return result; }',
|
|
462
|
+
'template<typename T> std::vector<T> __tc_splice1(std::vector<T>& v, int start) { std::vector<T> removed(v.begin() + start, v.end()); v.erase(v.begin() + start, v.end()); return removed; }',
|
|
463
|
+
'template<typename T> std::vector<T> __tc_splice2(std::vector<T>& v, int start, int deleteCount) { int end = start + deleteCount; if (end > static_cast<int>(v.size())) end = static_cast<int>(v.size()); std::vector<T> removed(v.begin() + start, v.begin() + end); v.erase(v.begin() + start, v.begin() + end); return removed; }',
|
|
464
|
+
// ── Array functional methods (Phase 1) ──────────────────────────
|
|
465
|
+
'template<typename T, typename F> std::vector<T> __tc_filter(const std::vector<T>& v, F pred) { std::vector<T> result; for (const auto& x : v) if (pred(x)) result.push_back(x); return result; }',
|
|
466
|
+
'template<typename T, typename F> auto __tc_map(const std::vector<T>& v, F fn) -> std::vector<decltype(fn(v[0]))> { using R = decltype(fn(v[0])); std::vector<R> result; result.reserve(v.size()); for (const auto& x : v) result.push_back(fn(x)); return result; }',
|
|
467
|
+
'template<typename T, typename U, typename F> auto __tc_reduce(const std::vector<T>& v, F fn, U init) -> U { U acc = init; for (const auto& x : v) acc = fn(acc, x); return acc; }',
|
|
468
|
+
'template<typename T, typename F> T __tc_reduce_no_init(std::vector<T>& v, F fn) { T acc = v[0]; for (size_t i = 1; i < v.size(); i++) acc = fn(acc, v[i]); return acc; }',
|
|
469
|
+
'template<typename T, typename F> T __tc_find(const std::vector<T>& v, F pred) { for (const auto& x : v) if (pred(x)) return x; return T(); }',
|
|
470
|
+
'template<typename T, typename F> int __tc_findIndex(const std::vector<T>& v, F pred) { for (int i = 0; i < static_cast<int>(v.size()); i++) if (pred(v[i])) return i; return -1; }',
|
|
471
|
+
'template<typename T, typename F> bool __tc_every(const std::vector<T>& v, F pred) { for (const auto& x : v) if (!pred(x)) return false; return true; }',
|
|
472
|
+
'template<typename T, typename F> bool __tc_some(const std::vector<T>& v, F pred) { for (const auto& x : v) if (pred(x)) return true; return false; }',
|
|
473
|
+
// ── Map helper methods (Object.keys/values/entries) ──────────────
|
|
474
|
+
'template<typename K, typename V> std::vector<K> __tc_mapKeys(const std::map<K, V>& m) { std::vector<K> keys; for (const auto& p : m) keys.push_back(p.first); return keys; }',
|
|
475
|
+
'template<typename K, typename V> std::vector<V> __tc_mapValues(const std::map<K, V>& m) { std::vector<V> vals; for (const auto& p : m) vals.push_back(p.second); return vals; }',
|
|
476
|
+
'template<typename K, typename V> std::vector<std::pair<K, V>> __tc_mapEntries(const std::map<K, V>& m) { std::vector<std::pair<K, V>> entries; for (const auto& p : m) entries.push_back(p); return entries; }',
|
|
477
|
+
// ── Set helper methods (Set.values()/keys()/entries()) ───────────
|
|
478
|
+
// Set.values()/keys() both yield the elements; entries() yields
|
|
479
|
+
// pair<elem,elem>. (demo #15 fix A)
|
|
480
|
+
'template<typename T> std::vector<T> __tc_setValues(const std::set<T>& s) { std::vector<T> vals; for (const auto& x : s) vals.push_back(x); return vals; }',
|
|
481
|
+
'template<typename T> std::vector<std::pair<T, T>> __tc_setEntries(const std::set<T>& s) { std::vector<std::pair<T, T>> entries; for (const auto& x : s) entries.push_back({x, x}); return entries; }',
|
|
482
|
+
// ── Object.fromEntries helper ───────────────────────────────
|
|
483
|
+
'template<typename K, typename V> std::map<K, V> __tc_fromEntries(const std::vector<std::pair<K, V>>& entries) { std::map<K, V> result; for (const auto& p : entries) result[p.first] = p.second; return result; }',
|
|
484
|
+
// ── JSON helpers ─────────────────────────────────────────────
|
|
485
|
+
'inline std::string __tc_jsonStringify(const std::string& s) { return s; }',
|
|
486
|
+
'template<typename T> std::string __tc_jsonStringify(const T& v) { return std::to_string(v); }',
|
|
487
|
+
'inline std::string __tc_jsonParse(const std::string& s) { return s; }',
|
|
488
|
+
],
|
|
489
|
+
shimMacros: [],
|
|
490
|
+
dependencies: [],
|
|
491
|
+
},
|
|
492
|
+
];
|
|
493
|
+
// Heap-free async runtime, mirroring the Zephyr strategy: the static
|
|
494
|
+
// runtime needs no STL headers and polls the millis() shim the native
|
|
495
|
+
// base shim defines unconditionally. Polyfill definitions emit before
|
|
496
|
+
// shimLines, so forward-declare millis() for the runtime's timer bodies.
|
|
497
|
+
if (program && program.functions.some((fn) => fn && fn.isAsync)) {
|
|
498
|
+
polyfills.push({
|
|
499
|
+
kind: 'polyfill',
|
|
500
|
+
id: 'async_runtime',
|
|
501
|
+
domain: 'embedded',
|
|
502
|
+
requiredIncludes: [],
|
|
503
|
+
forwardDeclarations: ['uint32_t __tc_now_ms(void);'],
|
|
504
|
+
helperStructs: [generateStaticAsyncRuntime(8, this.getAsyncRuntimeConfig().waitForPinEdge)],
|
|
505
|
+
helperFunctions: [],
|
|
506
|
+
shimMacros: [],
|
|
507
|
+
dependencies: [],
|
|
508
|
+
hasPromiseRuntime: true,
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
return polyfills;
|
|
512
|
+
}
|
|
513
|
+
// ── Graphics ───────────────────────────────────────────────────────────
|
|
514
|
+
resolveDisplayOp(op) {
|
|
515
|
+
// The sdl driver has a real display adapter (SdlGfxTarget) that defines
|
|
516
|
+
// display_init() etc., so route its display.init op to a real call instead
|
|
517
|
+
// of the terminal-preview comment. Other ops (fill_rect/draw_text/flush)
|
|
518
|
+
// are drawn by the reactive runtime through the HAL, not via DisplayHALOp,
|
|
519
|
+
// so they stay as comments (harmless — the runtime drives the real draws).
|
|
520
|
+
if (op.operation === "display.init" && op.driver === "sdl") {
|
|
521
|
+
return { code: "display_init();" };
|
|
522
|
+
}
|
|
523
|
+
return resolveTerminalPreviewOp(op);
|
|
524
|
+
}
|
|
525
|
+
// SDL event loop: pump SDL events, tick the UI, present the framebuffer each
|
|
526
|
+
// frame. Only active when a UI is mounted (the emitter's entryHasUI() gate),
|
|
527
|
+
// so non-UI native programs stay single-shot. The preIteration body is emitted
|
|
528
|
+
// into the same .cpp as the runtime header, so it can call ui_kb_* /
|
|
529
|
+
// ui_apply_scroll_delta / ui_hit_test / __ui_kb_visible directly.
|
|
530
|
+
hostEventLoop() {
|
|
531
|
+
return {
|
|
532
|
+
flagName: "sdl_running",
|
|
533
|
+
continueCondition: "sdl_running",
|
|
534
|
+
preIteration: [
|
|
535
|
+
// Feature 3 — event-driven mouse: SDL_MOUSEBUTTONDOWN/MOTION/BUTTONUP
|
|
536
|
+
// write to file-scope __sdl_mouse_* state that the SDL touch shim reads
|
|
537
|
+
// (instead of polling SDL_GetMouseState every frame). The flags are
|
|
538
|
+
// declared alongside the other __sdl_* globals in the SDL adapter.
|
|
539
|
+
`SDL_Event __e; while (SDL_PollEvent(&__e)) {`,
|
|
540
|
+
` if (__e.type == SDL_QUIT) { sdl_running = false; }`,
|
|
541
|
+
` else if (__e.type == SDL_MOUSEBUTTONDOWN || __e.type == SDL_MOUSEBUTTONUP) {`,
|
|
542
|
+
` __sdl_mouse_down = (__e.type == SDL_MOUSEBUTTONDOWN && __e.button.button == SDL_BUTTON_LEFT) ? 1 : 0;`,
|
|
543
|
+
` __sdl_mouse_x = static_cast<int16_t>(__e.button.x);`,
|
|
544
|
+
` __sdl_mouse_y = static_cast<int16_t>(__e.button.y);`,
|
|
545
|
+
` } else if (__e.type == SDL_MOUSEMOTION) {`,
|
|
546
|
+
` __sdl_mouse_x = static_cast<int16_t>(__e.motion.x);`,
|
|
547
|
+
` __sdl_mouse_y = static_cast<int16_t>(__e.motion.y);`,
|
|
548
|
+
` }`,
|
|
549
|
+
// Feature 1 — real keyboard text input: route keystrokes into the
|
|
550
|
+
// on-screen keyboard buffer while it's visible, so a desktop user types
|
|
551
|
+
// on their real keyboard instead of clicking the 6×4 grid. Start/stop
|
|
552
|
+
// SDL text input to track visibility (also enables IME composition).
|
|
553
|
+
` else if (__e.type == SDL_TEXTINPUT) {`,
|
|
554
|
+
` if (__ui_kb_visible) { for (int __i = 0; __e.text.text[__i] != 0 && __i < 4; __i++) ui_kb_insert(__e.text.text[__i]); }`,
|
|
555
|
+
` } else if (__e.type == SDL_KEYDOWN && __ui_kb_visible) {`,
|
|
556
|
+
` if (__e.key.keysym.sym == SDLK_BACKSPACE) ui_kb_delete();`,
|
|
557
|
+
` else if (__e.key.keysym.sym == SDLK_RETURN || __e.key.keysym.sym == SDLK_KP_ENTER || __e.key.keysym.sym == SDLK_ESCAPE) ui_kb_close();`,
|
|
558
|
+
` }`,
|
|
559
|
+
// Feature 2 — mouse-wheel scrolling: SDL_MOUSEWHEEL scrolls the
|
|
560
|
+
// scrollable container under the cursor. Query the live mouse position
|
|
561
|
+
// here (NOT __sdl_mouse_x/y — those only update on MOTION, so they go
|
|
562
|
+
// stale when the user stops moving the mouse and just spins the wheel).
|
|
563
|
+
// Use ui_scroll_node_at (the same scan the touch path uses) — the
|
|
564
|
+
// hit-test + ancestor-walk approach misses when the cursor is over a
|
|
565
|
+
// non-child node (text/sibling/padding), giving "works on some screens,
|
|
566
|
+
// needs two attempts" behavior. Pass event.wheel.y through UNNEGATED:
|
|
567
|
+
// ui_apply_scroll_delta does nextY = sy - dy, so wheel-down (-1) yields
|
|
568
|
+
// dy=-40 → scrollY increases → scrolls toward bottom (the desktop
|
|
569
|
+
// expectation). SDL already delivers the OS's natural-scroll direction,
|
|
570
|
+
// so this respects the system preference with no extra setting needed.
|
|
571
|
+
` else if (__e.type == SDL_MOUSEWHEEL && !__ui_kb_visible) {`,
|
|
572
|
+
` int __wx, __wy; SDL_GetMouseState(&__wx, &__wy);`,
|
|
573
|
+
// Scale window/logical coords to framebuffer coords (same as
|
|
574
|
+
// touch_readRaw) so the hit-test lands on the right node in fullscreen,
|
|
575
|
+
// where the window is larger than the fixed w_×h_ framebuffer.
|
|
576
|
+
` int __ww = 0, __wh = 0; SDL_GetWindowSize(__tc_display.win, &__ww, &__wh);`,
|
|
577
|
+
` if (__ww <= 0) __ww = display_width();`,
|
|
578
|
+
` if (__wh <= 0) __wh = display_height();`,
|
|
579
|
+
` int16_t __fx = static_cast<int16_t>(static_cast<int32_t>(__wx) * display_width() / __ww);`,
|
|
580
|
+
` int16_t __fy = static_cast<int16_t>(static_cast<int32_t>(__wy) * display_height() / __wh);`,
|
|
581
|
+
` int16_t __owner = ui_scroll_node_at(__fx, __fy);`,
|
|
582
|
+
` if (__owner >= 0) ui_apply_scroll_delta(__owner, static_cast<int16_t>(__e.wheel.y * 40));`,
|
|
583
|
+
` }`,
|
|
584
|
+
`}`,
|
|
585
|
+
// Track keyboard visibility with SDL text input so IME composition works
|
|
586
|
+
// and the OS shows an on-screen cursor while typing. Self-corrects each
|
|
587
|
+
// frame rather than hooking ui_kb_open/close (no cross-layer wiring).
|
|
588
|
+
`if (__ui_kb_visible && !SDL_IsTextInputActive()) SDL_StartTextInput();`,
|
|
589
|
+
`else if (!__ui_kb_visible && SDL_IsTextInputActive()) SDL_StopTextInput();`,
|
|
590
|
+
].join(" "),
|
|
591
|
+
postIteration: "display_present();",
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
supportedDisplayDrivers() {
|
|
595
|
+
// Only `sdl` has a registered display adapter (display-adapter.ts registers
|
|
596
|
+
// st7796/ssd1680/ssd1309/sdl/ili9341). `native-preview` was a terminal-stub
|
|
597
|
+
// concept that was never wired up as a real adapter — advertising it here
|
|
598
|
+
// let mount validation pass and then crashed the transpile with
|
|
599
|
+
// "No display adapter registered for driver native-preview". Drop it so the
|
|
600
|
+
// standard "Unsupported display driver" error fires up front instead.
|
|
601
|
+
return new Set(["sdl"]);
|
|
602
|
+
}
|
|
603
|
+
modelsGpio() {
|
|
604
|
+
// The SDL desktop target has no GPIO pins. ui.watchPin / ui.press({pin})
|
|
605
|
+
// are GPIO-hardware APIs with no native equivalent — the shim's digitalRead
|
|
606
|
+
// returns constant LOW (so watchers never fire) and pinMode/attachInterrupt
|
|
607
|
+
// are undefined (link failure). Returning false makes the entrypoint
|
|
608
|
+
// synthesizer emit a clear transpile-time diagnostic instead of those.
|
|
609
|
+
return false;
|
|
610
|
+
}
|
|
611
|
+
// ── Atomic HAL primitives (no GPIO on the native target) ──────────────────
|
|
612
|
+
// These return documented no-op stubs so cuttlefish never emits a Wiring
|
|
613
|
+
// token by name. The native target has no GPIO/timing hardware.
|
|
614
|
+
readDigitalPin(_pin) {
|
|
615
|
+
return "/* gpio unavailable on native target */ 0";
|
|
616
|
+
}
|
|
617
|
+
readAnalogPin(_pin) {
|
|
618
|
+
return "/* adc unavailable on native target */ 0";
|
|
619
|
+
}
|
|
620
|
+
writeDigitalPin(_pin, _val) {
|
|
621
|
+
return "/* gpio unavailable on native target */";
|
|
622
|
+
}
|
|
623
|
+
setPinMode(_pin, _mode) {
|
|
624
|
+
return "/* gpio unavailable on native target */";
|
|
625
|
+
}
|
|
626
|
+
delayMs(_ms) {
|
|
627
|
+
return "/* delay unavailable on native target */";
|
|
628
|
+
}
|
|
629
|
+
delayMicroseconds(_us) {
|
|
630
|
+
return "/* delay unavailable on native target */";
|
|
631
|
+
}
|
|
632
|
+
halCallNames() {
|
|
633
|
+
return new Set();
|
|
634
|
+
}
|
|
635
|
+
isHalCall(_name) {
|
|
636
|
+
return false;
|
|
637
|
+
}
|
|
638
|
+
analogReadCallNames() {
|
|
639
|
+
return new Set();
|
|
640
|
+
}
|
|
641
|
+
colorFormat() {
|
|
642
|
+
// Honor the resolved display profile's colorFormat so an rgb888 SDL target
|
|
643
|
+
// lowers colors at full 888 precision (and emits UI_COLOR_DEPTH 888).
|
|
644
|
+
// Defaults to rgb565 for non-display native programs (byte-identical).
|
|
645
|
+
try {
|
|
646
|
+
const { getDisplayProfile } = require('../../api/shared/index.js');
|
|
647
|
+
const profile = getDisplayProfile?.();
|
|
648
|
+
if (profile?.colorFormat)
|
|
649
|
+
return profile.colorFormat;
|
|
650
|
+
}
|
|
651
|
+
catch {
|
|
652
|
+
// getDisplayProfile not available (e.g. capability query before a build) → default.
|
|
653
|
+
}
|
|
654
|
+
return "rgb565";
|
|
655
|
+
}
|
|
656
|
+
graphicsCapacity() {
|
|
657
|
+
return {
|
|
658
|
+
maxNodes: Number.MAX_SAFE_INTEGER,
|
|
659
|
+
maxBindings: Number.MAX_SAFE_INTEGER,
|
|
660
|
+
maxActiveTransitions: Number.MAX_SAFE_INTEGER,
|
|
661
|
+
nodeStorage: "flash",
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
}
|