@typecad/cuttlefish 0.1.0-alpha.1
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/LICENSE +21 -0
- package/README.md +79 -0
- package/dist/api/board-types.d.ts +1 -0
- package/dist/api/board-types.js +4 -0
- package/dist/api/capabilities.d.ts +12 -0
- package/dist/api/capabilities.js +1 -0
- package/dist/api/config.d.ts +120 -0
- package/dist/api/config.js +7 -0
- package/dist/api/index.d.ts +5 -0
- package/dist/api/index.js +2 -0
- package/dist/api/schema/builder.d.ts +14 -0
- package/dist/api/schema/builder.js +15 -0
- package/dist/api/schema/index.d.ts +2 -0
- package/dist/api/schema/index.js +1 -0
- package/dist/api/schema/types.d.ts +257 -0
- package/dist/api/schema/types.js +4 -0
- package/dist/api/shared/async-runtime-static.d.ts +8 -0
- package/dist/api/shared/async-runtime-static.js +201 -0
- package/dist/api/shared/async-types.d.ts +42 -0
- package/dist/api/shared/async-types.js +8 -0
- package/dist/api/shared/board-resolver.d.ts +12 -0
- package/dist/api/shared/board-resolver.js +7 -0
- package/dist/api/shared/cpp-type-ir.d.ts +229 -0
- package/dist/api/shared/cpp-type-ir.js +672 -0
- package/dist/api/shared/display-adapter.d.ts +12 -0
- package/dist/api/shared/display-adapter.js +145 -0
- package/dist/api/shared/display-adapters/eink-mono.d.ts +2 -0
- package/dist/api/shared/display-adapters/eink-mono.js +53 -0
- package/dist/api/shared/display-adapters/sdl.d.ts +2 -0
- package/dist/api/shared/display-adapters/sdl.js +452 -0
- package/dist/api/shared/display-adapters/ssd1309.d.ts +2 -0
- package/dist/api/shared/display-adapters/ssd1309.js +136 -0
- package/dist/api/shared/display-adapters/st7796.d.ts +2 -0
- package/dist/api/shared/display-adapters/st7796.js +132 -0
- package/dist/api/shared/display-capabilities.d.ts +45 -0
- package/dist/api/shared/display-capabilities.js +54 -0
- package/dist/api/shared/display-op-ir.d.ts +62 -0
- package/dist/api/shared/display-op-ir.js +19 -0
- package/dist/api/shared/display-profile.d.ts +265 -0
- package/dist/api/shared/display-profile.js +338 -0
- package/dist/api/shared/graphics-strategy.d.ts +29 -0
- package/dist/api/shared/graphics-strategy.js +11 -0
- package/dist/api/shared/hal-op-ir.d.ts +393 -0
- package/dist/api/shared/hal-op-ir.js +18 -0
- package/dist/api/shared/index.d.ts +26 -0
- package/dist/api/shared/index.js +21 -0
- package/dist/api/shared/ir-core.d.ts +363 -0
- package/dist/api/shared/ir-core.js +8 -0
- package/dist/api/shared/ir-declarations.d.ts +201 -0
- package/dist/api/shared/ir-declarations.js +26 -0
- package/dist/api/shared/ir.d.ts +85 -0
- package/dist/api/shared/ir.js +13 -0
- package/dist/api/shared/platform-strategy.d.ts +402 -0
- package/dist/api/shared/platform-strategy.js +14 -0
- package/dist/api/shared/polyfill-helper-registry.d.ts +11 -0
- package/dist/api/shared/polyfill-helper-registry.js +86 -0
- package/dist/api/shared/polyfill-types.d.ts +33 -0
- package/dist/api/shared/polyfill-types.js +76 -0
- package/dist/api/shared/promise-runtime.d.ts +10 -0
- package/dist/api/shared/promise-runtime.js +244 -0
- package/dist/api/shared/snprintf-types.d.ts +29 -0
- package/dist/api/shared/snprintf-types.js +8 -0
- package/dist/api/shared/string-method-registry.d.ts +53 -0
- package/dist/api/shared/string-method-registry.js +168 -0
- package/dist/api/shared/toolchain-types.d.ts +63 -0
- package/dist/api/shared/toolchain-types.js +71 -0
- package/dist/api/shared/types.d.ts +50 -0
- package/dist/api/shared/types.js +6 -0
- package/dist/ast/parse.d.ts +2 -0
- package/dist/ast/parse.js +4 -0
- package/dist/cache.d.ts +26 -0
- package/dist/cache.js +95 -0
- package/dist/cli-utils.d.ts +37 -0
- package/dist/cli-utils.js +250 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +635 -0
- package/dist/config-loader.d.ts +78 -0
- package/dist/config-loader.js +508 -0
- package/dist/config-schema.d.ts +233 -0
- package/dist/config-schema.js +83 -0
- package/dist/create/board-checklist.d.ts +2 -0
- package/dist/create/board-checklist.js +52 -0
- package/dist/create/board-codegen.d.ts +9 -0
- package/dist/create/board-codegen.js +238 -0
- package/dist/create/board-generators.d.ts +12 -0
- package/dist/create/board-generators.js +652 -0
- package/dist/create/board-spec.d.ts +640 -0
- package/dist/create/board-spec.js +215 -0
- package/dist/create/eslint-rules-template.d.ts +2 -0
- package/dist/create/eslint-rules-template.js +672 -0
- package/dist/create/index.d.ts +11 -0
- package/dist/create/index.js +8 -0
- package/dist/create/init-scaffold.d.ts +27 -0
- package/dist/create/init-scaffold.js +150 -0
- package/dist/create/init-templates.d.ts +24 -0
- package/dist/create/init-templates.js +379 -0
- package/dist/create/init-wizard.d.ts +8 -0
- package/dist/create/init-wizard.js +168 -0
- package/dist/debug/breakpoint-loader.d.ts +24 -0
- package/dist/debug/breakpoint-loader.js +137 -0
- package/dist/debug/index.d.ts +3 -0
- package/dist/debug/index.js +7 -0
- package/dist/debug/preprocessor.d.ts +9 -0
- package/dist/debug/preprocessor.js +214 -0
- package/dist/debug/types.d.ts +32 -0
- package/dist/debug/types.js +6 -0
- package/dist/diagnostics/diagnostics-report.d.ts +51 -0
- package/dist/diagnostics/diagnostics-report.js +333 -0
- package/dist/diagnostics/json-schema.d.ts +138 -0
- package/dist/diagnostics/json-schema.js +27 -0
- package/dist/diagnostics/md-writer.d.ts +10 -0
- package/dist/diagnostics/md-writer.js +336 -0
- package/dist/diagnostics/mermaid-builder.d.ts +65 -0
- package/dist/diagnostics/mermaid-builder.js +681 -0
- package/dist/emit/cpp-emitter.d.ts +6 -0
- package/dist/emit/cpp-emitter.js +48 -0
- package/dist/emit/emitters/class-emitter.d.ts +2 -0
- package/dist/emit/emitters/class-emitter.js +523 -0
- package/dist/emit/emitters/emitter-context.d.ts +174 -0
- package/dist/emit/emitters/emitter-context.js +1 -0
- package/dist/emit/emitters/entrypoint-synthesizer.d.ts +2 -0
- package/dist/emit/emitters/entrypoint-synthesizer.js +175 -0
- package/dist/emit/emitters/function-emitter-impl.d.ts +19 -0
- package/dist/emit/emitters/function-emitter-impl.js +428 -0
- package/dist/emit/emitters/line-appender.d.ts +15 -0
- package/dist/emit/emitters/line-appender.js +344 -0
- package/dist/emit/emitters/namespace-emitter.d.ts +2 -0
- package/dist/emit/emitters/namespace-emitter.js +324 -0
- package/dist/emit/emitters/output-finalizer.d.ts +4 -0
- package/dist/emit/emitters/output-finalizer.js +267 -0
- package/dist/emit/emitters/setup.d.ts +3 -0
- package/dist/emit/emitters/setup.js +851 -0
- package/dist/emit/emitters/top-level-prep.d.ts +2 -0
- package/dist/emit/emitters/top-level-prep.js +706 -0
- package/dist/emit/emitters/type-decl-emitter.d.ts +2 -0
- package/dist/emit/emitters/type-decl-emitter.js +251 -0
- package/dist/emit/emitters/ui-emitter.d.ts +21 -0
- package/dist/emit/emitters/ui-emitter.js +564 -0
- package/dist/emit/enum-emitter.d.ts +1 -0
- package/dist/emit/enum-emitter.js +1 -0
- package/dist/emit/expression-renderer.d.ts +283 -0
- package/dist/emit/expression-renderer.js +1477 -0
- package/dist/emit/native-helpers-emitter.d.ts +11 -0
- package/dist/emit/native-helpers-emitter.js +34 -0
- package/dist/emit/route-hal-op.d.ts +5 -0
- package/dist/emit/route-hal-op.js +13 -0
- package/dist/emit/snprintf-helpers.d.ts +17 -0
- package/dist/emit/snprintf-helpers.js +462 -0
- package/dist/emit/statement-renderer.d.ts +189 -0
- package/dist/emit/statement-renderer.js +948 -0
- package/dist/emit/utils/async-state-machine.d.ts +42 -0
- package/dist/emit/utils/async-state-machine.js +343 -0
- package/dist/emit/utils/comment-helpers.d.ts +20 -0
- package/dist/emit/utils/comment-helpers.js +31 -0
- package/dist/emit/utils/cpp-helpers.d.ts +47 -0
- package/dist/emit/utils/cpp-helpers.js +85 -0
- package/dist/emit/utils/include-resolver.d.ts +58 -0
- package/dist/emit/utils/include-resolver.js +155 -0
- package/dist/emit/utils/index.d.ts +8 -0
- package/dist/emit/utils/index.js +12 -0
- package/dist/emit/utils/type-inference.d.ts +159 -0
- package/dist/emit/utils/type-inference.js +559 -0
- package/dist/eslint-check.d.ts +10 -0
- package/dist/eslint-check.js +102 -0
- package/dist/framework-package.d.ts +10 -0
- package/dist/framework-package.js +58 -0
- package/dist/framework-registry.d.ts +39 -0
- package/dist/framework-registry.js +30 -0
- package/dist/incremental-cache.d.ts +116 -0
- package/dist/incremental-cache.js +335 -0
- package/dist/ir/adc-range-validation.d.ts +11 -0
- package/dist/ir/adc-range-validation.js +250 -0
- package/dist/ir/ast-node-utils.d.ts +8 -0
- package/dist/ir/ast-node-utils.js +32 -0
- package/dist/ir/board-pin-utils.d.ts +13 -0
- package/dist/ir/board-pin-utils.js +65 -0
- package/dist/ir/board-resolver.d.ts +36 -0
- package/dist/ir/board-resolver.js +437 -0
- package/dist/ir/build-ir-state.d.ts +167 -0
- package/dist/ir/build-ir-state.js +306 -0
- package/dist/ir/build-ir.d.ts +2 -0
- package/dist/ir/build-ir.js +808 -0
- package/dist/ir/call-graph.d.ts +35 -0
- package/dist/ir/call-graph.js +194 -0
- package/dist/ir/compile-time-only.d.ts +2 -0
- package/dist/ir/compile-time-only.js +16 -0
- package/dist/ir/declaration-builders.d.ts +9 -0
- package/dist/ir/declaration-builders.js +608 -0
- package/dist/ir/entry-points.d.ts +30 -0
- package/dist/ir/entry-points.js +191 -0
- package/dist/ir/expression-to-ir.d.ts +5 -0
- package/dist/ir/expression-to-ir.js +2249 -0
- package/dist/ir/extract-property-chain.d.ts +10 -0
- package/dist/ir/extract-property-chain.js +30 -0
- package/dist/ir/feature-prescan.d.ts +3 -0
- package/dist/ir/feature-prescan.js +43 -0
- package/dist/ir/feature-registry.d.ts +30 -0
- package/dist/ir/feature-registry.js +816 -0
- package/dist/ir/filter.d.ts +7 -0
- package/dist/ir/filter.js +85 -0
- package/dist/ir/function-builder.d.ts +19 -0
- package/dist/ir/function-builder.js +670 -0
- package/dist/ir/hal/hal-emitter.d.ts +62 -0
- package/dist/ir/hal/hal-emitter.js +735 -0
- package/dist/ir/hal/hal-parser.d.ts +61 -0
- package/dist/ir/hal/hal-parser.js +473 -0
- package/dist/ir/hal/hal-plugins.d.ts +34 -0
- package/dist/ir/hal/hal-plugins.js +706 -0
- package/dist/ir/hal-resolver.d.ts +5 -0
- package/dist/ir/hal-resolver.js +5 -0
- package/dist/ir/heap-analysis.d.ts +8 -0
- package/dist/ir/heap-analysis.js +350 -0
- package/dist/ir/heap-array-validation.d.ts +24 -0
- package/dist/ir/heap-array-validation.js +29 -0
- package/dist/ir/identifier-collector.d.ts +18 -0
- package/dist/ir/identifier-collector.js +439 -0
- package/dist/ir/interrupt-analysis.d.ts +39 -0
- package/dist/ir/interrupt-analysis.js +467 -0
- package/dist/ir/memory-budget-validation.d.ts +10 -0
- package/dist/ir/memory-budget-validation.js +86 -0
- package/dist/ir/namespace-builder.d.ts +7 -0
- package/dist/ir/namespace-builder.js +152 -0
- package/dist/ir/ownership-analysis.d.ts +9 -0
- package/dist/ir/ownership-analysis.js +1207 -0
- package/dist/ir/peripheral-ownership.d.ts +3 -0
- package/dist/ir/peripheral-ownership.js +152 -0
- package/dist/ir/peripheral-symbols.d.ts +7 -0
- package/dist/ir/peripheral-symbols.js +57 -0
- package/dist/ir/peripheral-usage.d.ts +54 -0
- package/dist/ir/peripheral-usage.js +691 -0
- package/dist/ir/peripheral-validation.d.ts +8 -0
- package/dist/ir/peripheral-validation.js +77 -0
- package/dist/ir/pin-alias-conflict.d.ts +4 -0
- package/dist/ir/pin-alias-conflict.js +34 -0
- package/dist/ir/pin-capability-validation.d.ts +6 -0
- package/dist/ir/pin-capability-validation.js +413 -0
- package/dist/ir/pin-mode-validation.d.ts +10 -0
- package/dist/ir/pin-mode-validation.js +301 -0
- package/dist/ir/pin-safety.d.ts +11 -0
- package/dist/ir/pin-safety.js +58 -0
- package/dist/ir/program-analysis.d.ts +44 -0
- package/dist/ir/program-analysis.js +589 -0
- package/dist/ir/pulldown-validation.d.ts +7 -0
- package/dist/ir/pulldown-validation.js +49 -0
- package/dist/ir/pwm-timer-sharing.d.ts +4 -0
- package/dist/ir/pwm-timer-sharing.js +93 -0
- package/dist/ir/reachability.d.ts +68 -0
- package/dist/ir/reachability.js +224 -0
- package/dist/ir/register-decorators.d.ts +6 -0
- package/dist/ir/register-decorators.js +49 -0
- package/dist/ir/render-expr.d.ts +4 -0
- package/dist/ir/render-expr.js +127 -0
- package/dist/ir/resource-analysis.d.ts +19 -0
- package/dist/ir/resource-analysis.js +153 -0
- package/dist/ir/statement-to-ir.d.ts +23 -0
- package/dist/ir/statement-to-ir.js +349 -0
- package/dist/ir/symbol-table.d.ts +68 -0
- package/dist/ir/symbol-table.js +179 -0
- package/dist/ir/symbol-types.d.ts +56 -0
- package/dist/ir/symbol-types.js +107 -0
- package/dist/ir/timer0-pwm-timing-conflict.d.ts +4 -0
- package/dist/ir/timer0-pwm-timing-conflict.js +71 -0
- package/dist/ir/timing-validation.d.ts +7 -0
- package/dist/ir/timing-validation.js +95 -0
- package/dist/ir/transformers/array-methods.d.ts +8 -0
- package/dist/ir/transformers/array-methods.js +493 -0
- package/dist/ir/transformers/call-statement.d.ts +5 -0
- package/dist/ir/transformers/call-statement.js +758 -0
- package/dist/ir/transformers/callback-context-registry.d.ts +20 -0
- package/dist/ir/transformers/callback-context-registry.js +55 -0
- package/dist/ir/transformers/canvas-lowering.d.ts +42 -0
- package/dist/ir/transformers/canvas-lowering.js +249 -0
- package/dist/ir/transformers/control-flow.d.ts +8 -0
- package/dist/ir/transformers/control-flow.js +508 -0
- package/dist/ir/transformers/expressions.d.ts +7 -0
- package/dist/ir/transformers/expressions.js +406 -0
- package/dist/ir/transformers/hal-call-resolver.d.ts +29 -0
- package/dist/ir/transformers/hal-call-resolver.js +274 -0
- package/dist/ir/transformers/hal-emit-helpers.d.ts +14 -0
- package/dist/ir/transformers/hal-emit-helpers.js +63 -0
- package/dist/ir/transformers/namespace-methods.d.ts +20 -0
- package/dist/ir/transformers/namespace-methods.js +110 -0
- package/dist/ir/transformers/register-assignment.d.ts +5 -0
- package/dist/ir/transformers/register-assignment.js +56 -0
- package/dist/ir/transformers/ui-call-resolver.d.ts +149 -0
- package/dist/ir/transformers/ui-call-resolver.js +1136 -0
- package/dist/ir/transformers/ui-callback-lowering.d.ts +62 -0
- package/dist/ir/transformers/ui-callback-lowering.js +623 -0
- package/dist/ir/transformers/ui-mount.d.ts +20 -0
- package/dist/ir/transformers/ui-mount.js +35 -0
- package/dist/ir/transformers/ui-reactive.d.ts +68 -0
- package/dist/ir/transformers/ui-reactive.js +129 -0
- package/dist/ir/transformers/variables.d.ts +14 -0
- package/dist/ir/transformers/variables.js +1045 -0
- package/dist/ir/try-catch-validation.d.ts +26 -0
- package/dist/ir/try-catch-validation.js +128 -0
- package/dist/ir/type-resolution.d.ts +31 -0
- package/dist/ir/type-resolution.js +977 -0
- package/dist/ir/ui-element-auto-wire.d.ts +50 -0
- package/dist/ir/ui-element-auto-wire.js +218 -0
- package/dist/ir/unit-suspicion-validation.d.ts +12 -0
- package/dist/ir/unit-suspicion-validation.js +296 -0
- package/dist/ir/utils/walk-ir.d.ts +6 -0
- package/dist/ir/utils/walk-ir.js +162 -0
- package/dist/ir/validation-orchestrator.d.ts +4 -0
- package/dist/ir/validation-orchestrator.js +45 -0
- package/dist/libdef/base-class-resolver.d.ts +23 -0
- package/dist/libdef/base-class-resolver.js +50 -0
- package/dist/libdef/cpp-to-decl.d.ts +34 -0
- package/dist/libdef/cpp-to-decl.js +584 -0
- package/dist/libdef/header-parser.d.ts +53 -0
- package/dist/libdef/header-parser.js +269 -0
- package/dist/libdef/registry.d.ts +10 -0
- package/dist/libdef/registry.js +142 -0
- package/dist/mapping/peripheral-names.d.ts +23 -0
- package/dist/mapping/peripheral-names.js +63 -0
- package/dist/mapping/source-map.d.ts +46 -0
- package/dist/mapping/source-map.js +207 -0
- package/dist/orchestrator/dts-generator.d.ts +17 -0
- package/dist/orchestrator/dts-generator.js +83 -0
- package/dist/orchestrator/graph-builder.d.ts +18 -0
- package/dist/orchestrator/graph-builder.js +218 -0
- package/dist/orchestrator/semantic-facts-verifier.d.ts +38 -0
- package/dist/orchestrator/semantic-facts-verifier.js +121 -0
- package/dist/orchestrator/semantic-facts.d.ts +141 -0
- package/dist/orchestrator/semantic-facts.js +652 -0
- package/dist/orchestrator/type-checker.d.ts +39 -0
- package/dist/orchestrator/type-checker.js +760 -0
- package/dist/platform/async-runtime.d.ts +8 -0
- package/dist/platform/async-runtime.js +26 -0
- package/dist/platform/generic-strategy.d.ts +101 -0
- package/dist/platform/generic-strategy.js +289 -0
- package/dist/platform/index.d.ts +4 -0
- package/dist/platform/index.js +9 -0
- package/dist/platform/registry.d.ts +21 -0
- package/dist/platform/registry.js +42 -0
- package/dist/platform/toolchain.d.ts +4 -0
- package/dist/platform/toolchain.js +43 -0
- package/dist/preview/client.d.ts +1 -0
- package/dist/preview/client.js +137 -0
- package/dist/preview/server.d.ts +5 -0
- package/dist/preview/server.js +199 -0
- package/dist/profiler/index.d.ts +28 -0
- package/dist/profiler/index.js +28 -0
- package/dist/profiler/profiler.d.ts +95 -0
- package/dist/profiler/profiler.js +338 -0
- package/dist/profiler/types.d.ts +87 -0
- package/dist/profiler/types.js +12 -0
- package/dist/stores/display-profile-store.d.ts +16 -0
- package/dist/stores/display-profile-store.js +48 -0
- package/dist/stores/theme-store.d.ts +13 -0
- package/dist/stores/theme-store.js +30 -0
- package/dist/testing.d.ts +45 -0
- package/dist/testing.js +47 -0
- package/dist/transpile/resolution.d.ts +29 -0
- package/dist/transpile/resolution.js +282 -0
- package/dist/transpile.d.ts +3 -0
- package/dist/transpile.js +722 -0
- package/dist/types.d.ts +217 -0
- package/dist/types.js +1 -0
- package/dist/ui/ui-bridge.d.ts +4 -0
- package/dist/ui/ui-bridge.js +34 -0
- package/dist/ui-hook.d.ts +84 -0
- package/dist/ui-hook.js +45 -0
- package/dist/utils/cli.d.ts +3 -0
- package/dist/utils/cli.js +463 -0
- package/dist/utils/fs.d.ts +4 -0
- package/dist/utils/fs.js +23 -0
- package/dist/utils/logger.d.ts +2 -0
- package/dist/utils/logger.js +8 -0
- package/dist/utils/strings.d.ts +35 -0
- package/dist/utils/strings.js +142 -0
- package/dist/utils/ui.d.ts +66 -0
- package/dist/utils/ui.js +149 -0
- package/dist/watch.d.ts +35 -0
- package/dist/watch.js +111 -0
- package/package.json +127 -0
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import { arrayLiteralSizes, mutableArrayVars, activeCArrayVars, getContext, activeEnumNames, activeStringEnumNames } from "../build-ir-state.js";
|
|
3
|
+
import { getCurrentIrTypeScope } from "../symbol-types.js";
|
|
4
|
+
import { expressionToIR } from "../expression-to-ir.js";
|
|
5
|
+
import { renderExprAsText } from "../render-expr.js";
|
|
6
|
+
import { assignmentOperatorToString } from "./variables.js";
|
|
7
|
+
import { STRING_METHODS, STRING_METHOD_NAMES } from "../../api/shared/string-method-registry.js";
|
|
8
|
+
import { parsedElementString } from "../../api/shared/cpp-type-ir.js";
|
|
9
|
+
import { INTEGRAL_CPP_TYPE_RE } from "../../emit/utils/cpp-helpers.js";
|
|
10
|
+
// Methods that require StaticArray promotion (not all are mutating — indexOf is read-only
|
|
11
|
+
// but needs StaticArray since C arrays don't have an indexOf method).
|
|
12
|
+
export const ARRAY_METHODS_REQUIRING_STATIC_ARRAY = new Set(["push", "pop", "indexOf"]);
|
|
13
|
+
export function prescanArrayUsage(statement) {
|
|
14
|
+
if (ts.isVariableStatement(statement)) {
|
|
15
|
+
for (const decl of statement.declarationList.declarations) {
|
|
16
|
+
if (ts.isIdentifier(decl.name) && decl.initializer) {
|
|
17
|
+
if (ts.isArrayLiteralExpression(decl.initializer)) {
|
|
18
|
+
arrayLiteralSizes.set(decl.name.text, decl.initializer.elements.length);
|
|
19
|
+
}
|
|
20
|
+
prescanExprForArrayMethods(decl.initializer);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
else if (ts.isExpressionStatement(statement)) {
|
|
25
|
+
prescanExprForArrayMethods(statement.expression);
|
|
26
|
+
}
|
|
27
|
+
else if (ts.isReturnStatement(statement) && statement.expression) {
|
|
28
|
+
prescanExprForArrayMethods(statement.expression);
|
|
29
|
+
}
|
|
30
|
+
else if (ts.isIfStatement(statement)) {
|
|
31
|
+
prescanArrayUsageBlock(statement.thenStatement);
|
|
32
|
+
if (statement.elseStatement)
|
|
33
|
+
prescanArrayUsageBlock(statement.elseStatement);
|
|
34
|
+
}
|
|
35
|
+
else if (ts.isForStatement(statement) || ts.isWhileStatement(statement) || ts.isDoStatement(statement)) {
|
|
36
|
+
prescanArrayUsageBlock(statement.statement);
|
|
37
|
+
}
|
|
38
|
+
else if (ts.isForOfStatement(statement) || ts.isForInStatement(statement)) {
|
|
39
|
+
prescanArrayUsageBlock(statement.statement);
|
|
40
|
+
}
|
|
41
|
+
else if (ts.isBlock(statement)) {
|
|
42
|
+
for (const s of statement.statements)
|
|
43
|
+
prescanArrayUsage(s);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function prescanArrayUsageBlock(stmt) {
|
|
47
|
+
if (ts.isBlock(stmt)) {
|
|
48
|
+
for (const s of stmt.statements)
|
|
49
|
+
prescanArrayUsage(s);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
prescanArrayUsage(stmt);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export function prescanExprForArrayMethods(expr) {
|
|
56
|
+
if (ts.isCallExpression(expr) && ts.isPropertyAccessExpression(expr.expression)) {
|
|
57
|
+
const methodName = expr.expression.name.text;
|
|
58
|
+
if (ARRAY_METHODS_REQUIRING_STATIC_ARRAY.has(methodName) && ts.isIdentifier(expr.expression.expression)) {
|
|
59
|
+
const varName = expr.expression.expression.text;
|
|
60
|
+
mutableArrayVars.add(varName);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Detect indexed assignment (arr[i] = val and compounds like arr[i] += val).
|
|
64
|
+
// TypeScript const only locks the binding, not the array contents, so
|
|
65
|
+
// assigning to an element forces non-const C++ storage (matching the
|
|
66
|
+
// existing behaviour for .push() / .pop()).
|
|
67
|
+
if (ts.isBinaryExpression(expr)
|
|
68
|
+
&& ts.isElementAccessExpression(expr.left)
|
|
69
|
+
&& ts.isIdentifier(expr.left.expression)
|
|
70
|
+
&& assignmentOperatorToString(expr.operatorToken.kind) !== undefined) {
|
|
71
|
+
mutableArrayVars.add(expr.left.expression.text);
|
|
72
|
+
}
|
|
73
|
+
// Detect element increment / decrement (arr[i]++ / arr[i]-- / ++arr[i] / --arr[i]).
|
|
74
|
+
if ((ts.isPostfixUnaryExpression(expr) || ts.isPrefixUnaryExpression(expr))
|
|
75
|
+
&& ts.isElementAccessExpression(expr.operand)
|
|
76
|
+
&& ts.isIdentifier(expr.operand.expression)
|
|
77
|
+
&& (expr.operator === ts.SyntaxKind.PlusPlusToken || expr.operator === ts.SyntaxKind.MinusMinusToken)) {
|
|
78
|
+
mutableArrayVars.add(expr.operand.expression.text);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export function buildInlineForLoop(span, srcSize, srcName, paramName, bodyExpr, sourceText, diagnostics, assignTarget, _isFilter) {
|
|
82
|
+
const bodyIR = expressionToIR(bodyExpr, sourceText, diagnostics);
|
|
83
|
+
return {
|
|
84
|
+
kind: "for",
|
|
85
|
+
sourceSpan: span,
|
|
86
|
+
initializer: { kind: "var_decl", sourceSpan: span, name: "__tc_i", storage: "let", cppType: "int", initializer: { kind: "number", value: 0 } },
|
|
87
|
+
condition: { kind: "binary", left: { kind: "identifier", value: "__tc_i" }, operator: "<", right: { kind: "number", value: srcSize } },
|
|
88
|
+
increment: { kind: "update", sourceSpan: span, target: "__tc_i", operator: "++", prefix: false },
|
|
89
|
+
body: [
|
|
90
|
+
{ kind: "var_decl", sourceSpan: span, name: paramName, storage: "const", cppType: "auto",
|
|
91
|
+
initializer: { kind: "raw", value: `${srcName}[__tc_i]` } },
|
|
92
|
+
{ kind: "assign", sourceSpan: span, target: assignTarget, operator: "=", value: bodyIR },
|
|
93
|
+
],
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const VECTOR_VALUE_METHOD_LOWERINGS = {
|
|
97
|
+
// Mutators that the native runtime exposes as free-function helpers:
|
|
98
|
+
pop: (r) => `__tc_pop(${r})`,
|
|
99
|
+
shift: (r) => `__tc_shift(${r})`,
|
|
100
|
+
reverse: (r) => `__tc_reverse(${r})`,
|
|
101
|
+
unshift: (r, [a]) => `__tc_unshift(${r}, ${a})`,
|
|
102
|
+
// .fill(v) vs .fill(v, start, end)
|
|
103
|
+
fill: (r, args, n) => n >= 3 ? `__tc_fill3(${r}, ${args[0]}, ${args[1]}, ${args[2]})` : `__tc_fill(${r}, ${args[0] ?? ""})`,
|
|
104
|
+
concat: (r, [a]) => `__tc_concat(${r}, ${a})`,
|
|
105
|
+
// .splice(i) vs .splice(i, n)
|
|
106
|
+
splice: (r, args, n) => n >= 2 ? `__tc_splice2(${r}, ${args[0]}, ${args[1]})` : `__tc_splice1(${r}, ${args[0]})`,
|
|
107
|
+
// .slice() with no args copies the whole vector. (Sliced sub-ranges are not
|
|
108
|
+
// handled here — they didn't have a regex either.)
|
|
109
|
+
slice: (r, _args, n) => n === 0
|
|
110
|
+
? `std::vector<typename std::decay<decltype(${r})>::type>(${r}.begin(), ${r}.end())`
|
|
111
|
+
: null,
|
|
112
|
+
// .join(delim) — folds a std::vector<T> into a std::string with `delim`
|
|
113
|
+
// between elements (the __tc_join template helper). Demo #31 Finding B —
|
|
114
|
+
// `join` was previously misclassified as a STRING method (it was listed in
|
|
115
|
+
// STRING_METHODS even though it operates on a vector), so on a known
|
|
116
|
+
// `mutableArrayVars` receiver (a `string[]` built via .push) BOTH lowering
|
|
117
|
+
// paths declined it: the string path rejects known arrays, and this vector
|
|
118
|
+
// table had no `join` entry. The call was then emitted verbatim and g++
|
|
119
|
+
// rejected it. `join` is a vector→string transformation; it lives here.
|
|
120
|
+
join: (r, [a]) => `__tc_join(${r}, ${a ?? '""'})`,
|
|
121
|
+
};
|
|
122
|
+
// Callback-arg methods: map method name → helper callee. The receiver and all
|
|
123
|
+
// args (including the callback) become structured method-call args so the
|
|
124
|
+
// callback hoister can find and name them. `.sort()` with NO arg is a value
|
|
125
|
+
// method (`__tc_sort(recv)`), handled separately below.
|
|
126
|
+
const VECTOR_CALLBACK_METHOD_HELPERS = {
|
|
127
|
+
filter: "__tc_filter",
|
|
128
|
+
map: "__tc_map",
|
|
129
|
+
find: "__tc_find",
|
|
130
|
+
findIndex: "__tc_findIndex",
|
|
131
|
+
every: "__tc_every",
|
|
132
|
+
some: "__tc_some",
|
|
133
|
+
reduce: "__tc_reduce", // 2-arg form; 1-arg → __tc_reduce_no_init (below)
|
|
134
|
+
sort: "__tc_sort_fn", // .sort(fn); bare .sort() → __tc_sort (below)
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* Render an array/string-method RECEIVER for use as a `__tc_*` helper argument.
|
|
138
|
+
*
|
|
139
|
+
* The only difference from a plain `renderExprAsText(expressionToIR(receiver))`
|
|
140
|
+
* is the INLINE ARRAY LITERAL case: `[...].join(sep)`, `[...].concat(x)`, ....
|
|
141
|
+
* The `__tc_*` helpers are templates, and a bare brace-init-list (`{ ... }`)
|
|
142
|
+
* CANNOT drive template argument deduction (g++: "couldn't deduce template
|
|
143
|
+
* parameter 'T'"). A typed `std::vector<ElemType>{ ... }` temporary CAN. So
|
|
144
|
+
* when the receiver is an inline array literal whose element type we can infer
|
|
145
|
+
* (from a literal element), we wrap it in `std::vector<ElemType>{ ... }`. A
|
|
146
|
+
* NAMED receiver (`x.join(...)` where `x: string[]`) already has a concrete
|
|
147
|
+
* `std::vector<...>` type and is rendered verbatim.
|
|
148
|
+
*
|
|
149
|
+
* Demo #29 Finding D. This is intentionally scoped to the method-receiver
|
|
150
|
+
* position: a bare `{ ... }` is correct in direct-initialization
|
|
151
|
+
* (`const T x = {...}`) and HAL argument (`Wire.write({...})`) contexts, so the
|
|
152
|
+
* type-qualification happens ONLY here, not in the generic array renderer.
|
|
153
|
+
*/
|
|
154
|
+
function renderArrayMethodReceiver(receiverNode, sourceText, diagnostics, pointerVars) {
|
|
155
|
+
const ir = expressionToIR(receiverNode, sourceText, diagnostics, pointerVars);
|
|
156
|
+
const text = renderExprAsText(ir);
|
|
157
|
+
// Only an inline array literal needs type-qualification for deduction.
|
|
158
|
+
if (ir.kind === "array" && ir.elementType && ir.elementType !== "auto") {
|
|
159
|
+
return `std::vector<${ir.elementType}>${text}`;
|
|
160
|
+
}
|
|
161
|
+
return text;
|
|
162
|
+
}
|
|
163
|
+
export function tryLowerArrayAndStringMethods(expr, sourceText, diagnostics, pointerVars) {
|
|
164
|
+
if (ts.isPropertyAccessExpression(expr.expression) &&
|
|
165
|
+
ts.isIdentifier(expr.expression.expression)) {
|
|
166
|
+
const receiverName = expr.expression.expression.text;
|
|
167
|
+
const methodName = expr.expression.name.text;
|
|
168
|
+
// ---- Array method translation for mutable arrays (StaticArray) -----------
|
|
169
|
+
// Translate push → push_back, pop → pop_back, indexOf → indexOf at the expression level.
|
|
170
|
+
// IMPORTANT: this branch emits the StaticArray wrapper's own methods
|
|
171
|
+
// (`__tc_StaticArray` exposes .push/.pop/.indexOf). It must NOT fire for a
|
|
172
|
+
// plain std::vector — even one in mutableArrayVars — because std::vector has
|
|
173
|
+
// no `.push` member (it's `push_back`). The `mutableArrayVars` set is
|
|
174
|
+
// populated by prescan whenever .push/.pop/.indexOf is *called* on a var,
|
|
175
|
+
// regardless of whether that var lowered to StaticArray or std::vector; so
|
|
176
|
+
// gate on the resolved C++ type actually being a StaticArray. A std::vector
|
|
177
|
+
// falls through to the vector-method lowering below (push_back / __tc_pop).
|
|
178
|
+
if (mutableArrayVars.has(receiverName)) {
|
|
179
|
+
const resolvedType = getCurrentIrTypeScope()?.locals.get(receiverName) ?? getCurrentIrTypeScope()?.globals.get(receiverName);
|
|
180
|
+
// Use the StaticArray wrapper's own methods (.push/.pop/.indexOf) ONLY when
|
|
181
|
+
// the receiver genuinely lowered to a __tc_StaticArray. The resolved C++
|
|
182
|
+
// type is the primary signal, BUT it can be stale: the StaticArray
|
|
183
|
+
// promotion (variables.ts) runs during var_decl processing, AFTER call
|
|
184
|
+
// statements are lowered — so a literal-declared array's type is still
|
|
185
|
+
// "std::vector<...>" (pre-promotion) when its .push statement is lowered.
|
|
186
|
+
// Predict the post-promotion type instead: if the var is a mutable
|
|
187
|
+
// array-literal on a target that promotes literals to StaticArray, it WILL
|
|
188
|
+
// become a __tc_StaticArray, so use the StaticArray form. Native does not
|
|
189
|
+
// promote (promotesArrayLiteralsToStaticArray() = false), so its
|
|
190
|
+
// std::vector type is genuine and the vector lowering below applies.
|
|
191
|
+
const typeIsStaticArray = !!resolvedType && (resolvedType.startsWith("StaticArray<") || resolvedType.startsWith("__tc_StaticArray<"));
|
|
192
|
+
const promotesLiterals = getContext().activeStrategy?.promotesArrayLiteralsToStaticArray?.() ?? true;
|
|
193
|
+
const willPromoteToStaticArray = promotesLiterals && arrayLiteralSizes.has(receiverName);
|
|
194
|
+
const isStaticArrayType = typeIsStaticArray || willPromoteToStaticArray;
|
|
195
|
+
if (isStaticArrayType) {
|
|
196
|
+
if (methodName === "pop") {
|
|
197
|
+
return { kind: "raw", value: `${receiverName}.pop()` };
|
|
198
|
+
}
|
|
199
|
+
if (methodName === "push") {
|
|
200
|
+
const argsText = expr.arguments.map(arg => renderExprAsText(expressionToIR(arg, sourceText, diagnostics, pointerVars))).join(", ");
|
|
201
|
+
return { kind: "raw", value: `${receiverName}.push(${argsText})` };
|
|
202
|
+
}
|
|
203
|
+
if (methodName === "indexOf") {
|
|
204
|
+
const argsText = expr.arguments.map(arg => renderExprAsText(expressionToIR(arg, sourceText, diagnostics, pointerVars))).join(", ");
|
|
205
|
+
return { kind: "raw", value: `(${receiverName}).indexOf(${argsText})` };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// ---- String indexOf wrapping (const char* needs String() on Arduino) ---
|
|
210
|
+
if (methodName === "indexOf" &&
|
|
211
|
+
!mutableArrayVars.has(receiverName) &&
|
|
212
|
+
!activeCArrayVars.has(receiverName)) {
|
|
213
|
+
return {
|
|
214
|
+
kind: "method-call",
|
|
215
|
+
callee: `${receiverName}.indexOf`,
|
|
216
|
+
args: expr.arguments.map(arg => expressionToIR(arg, sourceText, diagnostics, pointerVars))
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// ---- Structural vector-method lowering (replaces normalizeRawExpression regexes) ----
|
|
221
|
+
// For a std::vector receiver of ANY shape (bare name OR member chain like
|
|
222
|
+
// `this->ops`, `obj.field`), lower the TS array method to its __tc_* helper
|
|
223
|
+
// at IR-build time. The receiver is rendered via expressionToIR so the
|
|
224
|
+
// pointer/value access decision (this->ops vs obj.field) is already correct,
|
|
225
|
+
// and the helper receives it as a normal argument — no RECV regex needed.
|
|
226
|
+
// Gated on method name only, matching the prior regex behavior (TS type
|
|
227
|
+
// semantics guarantee these names on a non-array/vector would be a type
|
|
228
|
+
// error). `push` is special-cased to `push_back` (the native inlining).
|
|
229
|
+
//
|
|
230
|
+
// Target gate: the __tc_* helpers are native/hosted-only (Arduino/embedded
|
|
231
|
+
// lower arrays to StaticArray, caught by the mutableArrayVars branch above,
|
|
232
|
+
// and have no __tc_* polyfills). requiresLoopFunction() is false exactly on
|
|
233
|
+
// hosted targets — the same set that ran the old native normalizeRawExpression.
|
|
234
|
+
const isHostedTarget = !getContext().activeStrategy?.requiresLoopFunction();
|
|
235
|
+
if (isHostedTarget && ts.isPropertyAccessExpression(expr.expression)) {
|
|
236
|
+
const methodName = expr.expression.name.text;
|
|
237
|
+
// `push` lowers to native push_back (matches the old
|
|
238
|
+
// `${RECV}\.push(([^)]+)\)` → `$1.push_back($2)` regex).
|
|
239
|
+
if (methodName === "push") {
|
|
240
|
+
const receiverNode = expr.expression.expression;
|
|
241
|
+
const receiverText = renderExprAsText(expressionToIR(receiverNode, sourceText, diagnostics, pointerVars));
|
|
242
|
+
const argsText = expr.arguments.map(arg => renderPushArgForElement(arg, receiverNode, sourceText, diagnostics, pointerVars)).join(", ");
|
|
243
|
+
return { kind: "raw", value: `${receiverText}.push_back(${argsText})` };
|
|
244
|
+
}
|
|
245
|
+
// `sort()` with no arg and `reduce` with one arg have distinct helpers.
|
|
246
|
+
if (methodName === "sort" && expr.arguments.length === 0) {
|
|
247
|
+
const receiverText = renderArrayMethodReceiver(expr.expression.expression, sourceText, diagnostics, pointerVars);
|
|
248
|
+
return { kind: "raw", value: `__tc_sort(${receiverText})` };
|
|
249
|
+
}
|
|
250
|
+
if (methodName === "reduce" && expr.arguments.length === 1) {
|
|
251
|
+
const receiverText = renderArrayMethodReceiver(expr.expression.expression, sourceText, diagnostics, pointerVars);
|
|
252
|
+
const cbText = renderExprAsText(expressionToIR(expr.arguments[0], sourceText, diagnostics, pointerVars));
|
|
253
|
+
return { kind: "raw", value: `__tc_reduce_no_init(${receiverText}, ${cbText})` };
|
|
254
|
+
}
|
|
255
|
+
// Callback-arg methods: lower to a method-call IR node (NOT raw) so the
|
|
256
|
+
// emit-time callback hoister (top-level-prep.ts collectCallbacks) can find
|
|
257
|
+
// the structured callback arg in `args`, hoist it to a named _isr_N
|
|
258
|
+
// function, and substitute the name. A raw node is opaque to the hoister.
|
|
259
|
+
const cbHelper = VECTOR_CALLBACK_METHOD_HELPERS[methodName];
|
|
260
|
+
if (cbHelper) {
|
|
261
|
+
// An inline array-literal receiver must be type-qualified so the
|
|
262
|
+
// `__tc_*` template helper can deduce its element type (demo #29 Finding D).
|
|
263
|
+
// Wrap an array-literal receiverIR into a typed raw node.
|
|
264
|
+
const receiverIR = (() => {
|
|
265
|
+
const ir = expressionToIR(expr.expression.expression, sourceText, diagnostics, pointerVars);
|
|
266
|
+
if (ir.kind === "array" && ir.elementType && ir.elementType !== "auto") {
|
|
267
|
+
return { kind: "raw", value: `std::vector<${ir.elementType}>${renderExprAsText(ir)}` };
|
|
268
|
+
}
|
|
269
|
+
return ir;
|
|
270
|
+
})();
|
|
271
|
+
const argIRs = expr.arguments.map(arg => expressionToIR(arg, sourceText, diagnostics, pointerVars));
|
|
272
|
+
return { kind: "method-call", callee: cbHelper, args: [receiverIR, ...argIRs] };
|
|
273
|
+
}
|
|
274
|
+
// Value-arg methods: collapse to a raw helper call (no callbacks to hoist).
|
|
275
|
+
const lowering = VECTOR_VALUE_METHOD_LOWERINGS[methodName];
|
|
276
|
+
if (lowering) {
|
|
277
|
+
const receiverText = renderArrayMethodReceiver(expr.expression.expression, sourceText, diagnostics, pointerVars);
|
|
278
|
+
const argsText = expr.arguments.map(arg => renderExprAsText(expressionToIR(arg, sourceText, diagnostics, pointerVars)));
|
|
279
|
+
const lowered = lowering(receiverText, argsText, expr.arguments.length);
|
|
280
|
+
if (lowered !== null) {
|
|
281
|
+
return { kind: "raw", value: lowered };
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// ---- String-method lowering (structural) ------------------------------
|
|
285
|
+
// Demo #27 Findings D/E — string methods (`s.toLowerCase()`,
|
|
286
|
+
// `s.substring(0,2)`, `s.charAt(i)`, ...) were previously lowered by a
|
|
287
|
+
// post-emit text rewrite (`applyStringMethodRewrites`) whose
|
|
288
|
+
// `RECEIVER_PATTERN` only matched bare identifiers and `.member` chains —
|
|
289
|
+
// NOT `X[i]` element access or `X->member` pointer chains. So
|
|
290
|
+
// `ALPHABET[i].toLowerCase()` was left verbatim (g++: "no member
|
|
291
|
+
// 'toLowerCase'"), and even on a bare local the emitted `__tc_toLowerCase`
|
|
292
|
+
// helper was never registered (the text scan missed it). Array mutators
|
|
293
|
+
// were migrated off the same regex family in demo #22 into this
|
|
294
|
+
// structural path; string methods are routed through the identical path
|
|
295
|
+
// here, so the receiver is rendered via `expressionToIR` (handling
|
|
296
|
+
// bare id / `this.field` / `obj.field` / `X[i]` / chains uniformly) and
|
|
297
|
+
// the helper lands in a `raw` IR node that `program-analysis.ts` scans to
|
|
298
|
+
// register the polyfill.
|
|
299
|
+
//
|
|
300
|
+
// Gate: only fire for a KNOWN string method whose receiver is NOT a known
|
|
301
|
+
// array (the array paths above already handled array `indexOf`/`slice`/
|
|
302
|
+
// etc.). For methods that exist on BOTH strings and arrays
|
|
303
|
+
// (`indexOf`/`includes`/`startsWith`/`endsWith`/`slice`/`substring`), gate
|
|
304
|
+
// on the receiver's resolved C++ type being string-like, so an array
|
|
305
|
+
// `indexOf` is never mis-lowered to the string helper.
|
|
306
|
+
const isStringMethod = STRING_METHOD_NAMES.has(methodName);
|
|
307
|
+
if (isStringMethod) {
|
|
308
|
+
const receiverNode = expr.expression.expression;
|
|
309
|
+
// Use renderArrayMethodReceiver so an INLINE array-literal receiver of an
|
|
310
|
+
// ambiguous string/array method (`.join`, `.concat`, `.slice`, ...) is
|
|
311
|
+
// type-qualified into `std::vector<ElemType>{...}` — the `__tc_*` helpers
|
|
312
|
+
// are templates and a bare brace-init-list cannot drive deduction. A
|
|
313
|
+
// genuine string receiver passes through unchanged. Demo #29 Finding D.
|
|
314
|
+
const receiverText = renderArrayMethodReceiver(receiverNode, sourceText, diagnostics, pointerVars);
|
|
315
|
+
if (shouldLowerAsStringMethod(receiverNode, methodName)) {
|
|
316
|
+
const argsText = expr.arguments.map(arg => renderExprAsText(expressionToIR(arg, sourceText, diagnostics, pointerVars)));
|
|
317
|
+
const lowered = lowerStringMethod(methodName, receiverText, argsText, expr.arguments.length);
|
|
318
|
+
if (lowered !== null) {
|
|
319
|
+
return { kind: "raw", value: lowered };
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
// Methods that exist on BOTH std::string and std::vector (so the receiver
|
|
327
|
+
// type must be consulted to decide). All other STRING_METHOD_NAMES are
|
|
328
|
+
// unambiguously string-only.
|
|
329
|
+
const AMBIGUOUS_STRING_METHODS = new Set(["indexOf", "includes", "startsWith", "endsWith", "slice", "substring"]);
|
|
330
|
+
/**
|
|
331
|
+
* Decide whether `receiver.methodName(...)` should lower as a STRING method
|
|
332
|
+
* (→ `__tc_*` helper) rather than being left for the array path. Returns true
|
|
333
|
+
* for unambiguously-string methods (toLowerCase, charAt, ...), and for the
|
|
334
|
+
* ambiguous overlap methods only when the receiver's resolved C++ type is
|
|
335
|
+
* string-like (`std::string`/`const char*`/`char*`). Known arrays
|
|
336
|
+
* (`mutableArrayVars`/`activeCArrayVars`) are always rejected so an array
|
|
337
|
+
* `indexOf` is never mis-lowered.
|
|
338
|
+
*/
|
|
339
|
+
function shouldLowerAsStringMethod(receiverNode, methodName) {
|
|
340
|
+
// Known array receivers are never string-method receivers.
|
|
341
|
+
if (ts.isIdentifier(receiverNode)) {
|
|
342
|
+
if (mutableArrayVars.has(receiverNode.text) || activeCArrayVars.has(receiverNode.text)) {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (!AMBIGUOUS_STRING_METHODS.has(methodName)) {
|
|
347
|
+
// Unambiguously a string method (toLowerCase/trim/charAt/charCodeAt/...).
|
|
348
|
+
return true;
|
|
349
|
+
}
|
|
350
|
+
// Ambiguous: decide by the receiver's resolved C++ type.
|
|
351
|
+
const resolvedType = resolveReceiverCppType(receiverNode);
|
|
352
|
+
return resolvedType === "std::string" || resolvedType === "const char*" || resolvedType === "char*";
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Resolve the C++ type of a receiver expression for the string/array
|
|
356
|
+
* disambiguation. Handles bare identifiers (scope lookup), `this.field`
|
|
357
|
+
* (`this->field` in the scope map), element access (`arr[i]` — derives the
|
|
358
|
+
* container's element type), and `obj.field` chains (best-effort). Returns
|
|
359
|
+
* undefined if unknown.
|
|
360
|
+
*
|
|
361
|
+
* Element access is the case demo #27 Finding D stressed: `words[i].substring`
|
|
362
|
+
* where `words: string[]`. The container resolves to `std::vector<std::string>`
|
|
363
|
+
* and the element type is `std::string`, so the ambiguous `substring`/`slice`
|
|
364
|
+
* methods lower as string methods. Without this, an indexed receiver resolved
|
|
365
|
+
* to `undefined` and the ambiguous-method gate rejected the lowering.
|
|
366
|
+
*/
|
|
367
|
+
function resolveReceiverCppType(receiverNode) {
|
|
368
|
+
const scope = getCurrentIrTypeScope();
|
|
369
|
+
if (!scope)
|
|
370
|
+
return undefined;
|
|
371
|
+
if (ts.isIdentifier(receiverNode)) {
|
|
372
|
+
return scope.locals.get(receiverNode.text) ?? scope.globals.get(receiverNode.text);
|
|
373
|
+
}
|
|
374
|
+
if (ts.isPropertyAccessExpression(receiverNode) && receiverNode.expression.kind === ts.SyntaxKind.ThisKeyword) {
|
|
375
|
+
return scope.locals.get(`this->${receiverNode.name.text}`);
|
|
376
|
+
}
|
|
377
|
+
if (ts.isElementAccessExpression(receiverNode)) {
|
|
378
|
+
// Derive the element type of the indexed container.
|
|
379
|
+
const containerType = resolveReceiverCppType(receiverNode.expression);
|
|
380
|
+
if (containerType) {
|
|
381
|
+
const element = parsedElementString(containerType);
|
|
382
|
+
if (element)
|
|
383
|
+
return element;
|
|
384
|
+
}
|
|
385
|
+
return undefined;
|
|
386
|
+
}
|
|
387
|
+
return undefined;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Render a `.push(arg)` argument, casting it across the enum↔integral storage
|
|
391
|
+
* boundary when the receiver is an integral-element vector and the argument is
|
|
392
|
+
* a numeric-enum value. The IR-build-time counterpart to the emit-layer
|
|
393
|
+
* `renderValueForTarget`: `.push` lowers to a raw `recv.push_back(ARG)` callee
|
|
394
|
+
* with the argument baked into the text, so the boundary must be handled HERE
|
|
395
|
+
* (the emit layer never sees the argument as a structured value).
|
|
396
|
+
*
|
|
397
|
+
* Detection (structural, not regex):
|
|
398
|
+
* - Resolve the receiver's C++ type and derive its element type
|
|
399
|
+
* (`std::vector<uint8_t>` → `uint8_t`).
|
|
400
|
+
* - Detect a numeric-enum argument: a property access on a name in
|
|
401
|
+
* `activeEnumNames` (`Cell.Dead`), or a bare identifier whose scope type is
|
|
402
|
+
* an enum. String-enum members lower to `const char*` and are never cast.
|
|
403
|
+
*
|
|
404
|
+
* When the element type is integral and the arg is a numeric-enum value, wrap
|
|
405
|
+
* the rendered arg in `static_cast<int>(...)`. Demo #32 Finding A. Mirrors the
|
|
406
|
+
* SUPPORT_MATRIX §1.7 enum↔integral family fixed at the assign/var_decl sites
|
|
407
|
+
* in the emit layer.
|
|
408
|
+
*/
|
|
409
|
+
function renderPushArgForElement(argNode, receiverNode, sourceText, diagnostics, pointerVars) {
|
|
410
|
+
const rendered = renderExprAsText(expressionToIR(argNode, sourceText, diagnostics, pointerVars));
|
|
411
|
+
// Resolve the receiver element type.
|
|
412
|
+
const receiverType = resolveReceiverCppType(receiverNode);
|
|
413
|
+
const elementType = receiverType ? parsedElementString(receiverType) : undefined;
|
|
414
|
+
const targetIsIntegral = !!elementType && INTEGRAL_CPP_TYPE_RE.test(elementType);
|
|
415
|
+
if (!targetIsIntegral)
|
|
416
|
+
return rendered;
|
|
417
|
+
// Detect a numeric-enum argument (excluding string enums).
|
|
418
|
+
const argEnumName = numericEnumNameOfArg(argNode);
|
|
419
|
+
if (!argEnumName)
|
|
420
|
+
return rendered;
|
|
421
|
+
if (/^static_cast<[^>]+>\(/.test(rendered))
|
|
422
|
+
return rendered;
|
|
423
|
+
return `static_cast<int>(${rendered})`;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Returns the enum name when `node` is a numeric-enum value: either
|
|
427
|
+
* `EnumName.Member` (property access on a name in `activeEnumNames`, not a
|
|
428
|
+
* string enum) or a bare identifier whose IR-scope type is a numeric enum.
|
|
429
|
+
* Returns undefined for string enums and non-enum nodes. Demo #32 Finding A.
|
|
430
|
+
*/
|
|
431
|
+
function numericEnumNameOfArg(node) {
|
|
432
|
+
if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression)) {
|
|
433
|
+
const name = node.expression.text;
|
|
434
|
+
if (activeEnumNames.has(name) && !activeStringEnumNames.has(name))
|
|
435
|
+
return name;
|
|
436
|
+
}
|
|
437
|
+
if (ts.isIdentifier(node)) {
|
|
438
|
+
const scope = getCurrentIrTypeScope();
|
|
439
|
+
const t = scope?.locals.get(node.text) ?? scope?.globals.get(node.text);
|
|
440
|
+
if (t && activeEnumNames.has(t) && !activeStringEnumNames.has(t))
|
|
441
|
+
return t;
|
|
442
|
+
}
|
|
443
|
+
return undefined;
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Lower a string method to its `__tc_*` helper call, picking the helper by
|
|
447
|
+
* method name AND argument count (so `substring(0,2)` → `__tc_substring2` and
|
|
448
|
+
* `substring(2)` → `__tc_substring1`). Returns null if no spec matches the
|
|
449
|
+
* call's arity (e.g. the demo's `slice()` zero-arg copy form belongs to the
|
|
450
|
+
* vector path, not here).
|
|
451
|
+
*
|
|
452
|
+
* Built from `STRING_METHODS`, which encodes arity in the helper name
|
|
453
|
+
* (`__tc_substring2` / `__tc_substring1`) and argForm. The original
|
|
454
|
+
* `STRING_METHOD_BY_NAME` map in `string-method-registry.ts` collapses both
|
|
455
|
+
* arities onto one key (fine for the names-set, wrong for lowering), so this
|
|
456
|
+
* lookup is keyed by `${name}:${argForm}` instead.
|
|
457
|
+
*
|
|
458
|
+
* Native `startsWith` maps to `std::string::rfind` (the prior `special`
|
|
459
|
+
* override) instead of the `__tc_*` helper.
|
|
460
|
+
*/
|
|
461
|
+
const STRING_HELPER_BY_NAME_FORM = new Map();
|
|
462
|
+
for (const spec of STRING_METHODS) {
|
|
463
|
+
const name = spec.methodName ?? spec.helper.replace(/^__tc_/, "").replace(/_default$/, "").replace(/\d+$/, "");
|
|
464
|
+
STRING_HELPER_BY_NAME_FORM.set(`${name}:${spec.argForm}`, spec);
|
|
465
|
+
}
|
|
466
|
+
function lowerStringMethod(methodName, receiver, args, argCount) {
|
|
467
|
+
// Native special case: startsWith → rfind prefix test (no helper).
|
|
468
|
+
if (methodName === "startsWith" && argCount >= 1) {
|
|
469
|
+
return `(${receiver}.rfind(${args[0]}, 0) == 0)`;
|
|
470
|
+
}
|
|
471
|
+
// Map the observed arg count to the argForm that handles it. Methods with a
|
|
472
|
+
// default (`padStart`/`padEnd`) have BOTH a binary and a unaryDefault spec;
|
|
473
|
+
// prefer the binary form when 2 args are given, else the default form.
|
|
474
|
+
const formsForCount = argCount === 0 ? ["receiverOnly"]
|
|
475
|
+
: argCount === 1 ? ["unary", "unaryDefault"]
|
|
476
|
+
: argCount === 2 ? ["binary"]
|
|
477
|
+
: [];
|
|
478
|
+
for (const form of formsForCount) {
|
|
479
|
+
const spec = STRING_HELPER_BY_NAME_FORM.get(`${methodName}:${form}`);
|
|
480
|
+
if (!spec)
|
|
481
|
+
continue;
|
|
482
|
+
switch (form) {
|
|
483
|
+
case "receiverOnly":
|
|
484
|
+
return `${spec.helper}(${receiver})`;
|
|
485
|
+
case "unary":
|
|
486
|
+
case "unaryDefault":
|
|
487
|
+
return `${spec.helper}(${receiver}, ${args[0]})`;
|
|
488
|
+
case "binary":
|
|
489
|
+
return `${spec.helper}(${receiver}, ${args[0]}, ${args[1]})`;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import { Diagnostic } from "../../types.js";
|
|
3
|
+
import { StatementIR } from "../../api/index.js";
|
|
4
|
+
import { PointerTracker } from "../build-ir-state.js";
|
|
5
|
+
export declare function callToStatement(statementNode: ts.ExpressionStatement, call: ts.CallExpression, fileName: string, sourceText: string, diagnostics: Diagnostic[], pointerVars?: PointerTracker): StatementIR;
|