@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,559 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type inference utilities for C++ code emission.
|
|
3
|
+
* Pure functions with no side effects - extracted from cpp-emitter.ts
|
|
4
|
+
*/
|
|
5
|
+
import { parsedIsPointer } from "../../api/shared/cpp-type-ir.js";
|
|
6
|
+
/**
|
|
7
|
+
* Infers the C++ type for an object field based on its initializer value.
|
|
8
|
+
*
|
|
9
|
+
* @param value The expression to infer type for
|
|
10
|
+
* @param pointerVarTypes Map of variable names to their pointer types (e.g., "sensor" -> "Sensor*")
|
|
11
|
+
* @param knownFunctionReturnTypes Map of function names to their return types
|
|
12
|
+
* @param knownObjectTypes Map of object names to their types
|
|
13
|
+
* @param knownObjectFieldTypes Map of object names to their field type maps
|
|
14
|
+
* @param largeEnumNames Set of enum names with values outside 16-bit int range
|
|
15
|
+
* @param resolvePinType Optional callback to resolve pin types from the platform strategy
|
|
16
|
+
* @returns The inferred C++ type string
|
|
17
|
+
*/
|
|
18
|
+
export function inferObjectFieldType(value, pointerVarTypes, knownFunctionReturnTypes, knownObjectTypes, knownObjectFieldTypes, largeEnumNames, parentName, fieldName, defaultIntType = "int", resolvePinType) {
|
|
19
|
+
if (value.kind === "number") {
|
|
20
|
+
if (value.cppType === "float" || value.cppType === "double" || !Number.isInteger(value.value)) {
|
|
21
|
+
return value.cppType === "double" ? "double" : "float";
|
|
22
|
+
}
|
|
23
|
+
if (value.value > 32767 || value.value < -32768) {
|
|
24
|
+
return "long";
|
|
25
|
+
}
|
|
26
|
+
return defaultIntType;
|
|
27
|
+
}
|
|
28
|
+
if (value.kind === "boolean") {
|
|
29
|
+
return "bool";
|
|
30
|
+
}
|
|
31
|
+
if (value.kind === "string") {
|
|
32
|
+
return "const char*";
|
|
33
|
+
}
|
|
34
|
+
if (value.kind === "identifier") {
|
|
35
|
+
if (knownObjectTypes && knownObjectTypes.has(value.value)) {
|
|
36
|
+
return knownObjectTypes.get(value.value);
|
|
37
|
+
}
|
|
38
|
+
if (value.value === "Pins") {
|
|
39
|
+
return "_Pins_t";
|
|
40
|
+
}
|
|
41
|
+
// Check if this identifier is a known pointer variable
|
|
42
|
+
if (pointerVarTypes && pointerVarTypes.has(value.value)) {
|
|
43
|
+
return pointerVarTypes.get(value.value);
|
|
44
|
+
}
|
|
45
|
+
// Default to the defaultIntType for unknown identifiers
|
|
46
|
+
// This will be overridden if knownVariableTypes is provided
|
|
47
|
+
return defaultIntType;
|
|
48
|
+
}
|
|
49
|
+
// For enum member access (e.g. I2CSpeed.STANDARD), return `long` if the
|
|
50
|
+
// enum has values outside the 16-bit int range, otherwise `int`.
|
|
51
|
+
if (value.kind === "property-access" && value.object.kind === "identifier") {
|
|
52
|
+
const enumName = value.object.value;
|
|
53
|
+
return largeEnumNames?.has(enumName) ? "long" : defaultIntType;
|
|
54
|
+
}
|
|
55
|
+
if (value.kind === "raw") {
|
|
56
|
+
const memberAccessMatch = value.value.match(/^([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)$/);
|
|
57
|
+
if (memberAccessMatch && knownObjectFieldTypes) {
|
|
58
|
+
const objectName = memberAccessMatch[1];
|
|
59
|
+
const fieldName = memberAccessMatch[2];
|
|
60
|
+
const fields = knownObjectFieldTypes.get(objectName);
|
|
61
|
+
if (fields?.has(fieldName)) {
|
|
62
|
+
return fields.get(fieldName);
|
|
63
|
+
}
|
|
64
|
+
if (objectName === "Pins" && resolvePinType) {
|
|
65
|
+
const resolved = resolvePinType(objectName, fieldName);
|
|
66
|
+
if (resolved)
|
|
67
|
+
return resolved;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const newMatch = value.value.match(/^new\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(/);
|
|
71
|
+
if (newMatch) {
|
|
72
|
+
return `${newMatch[1]}*`;
|
|
73
|
+
}
|
|
74
|
+
const callMatch = value.value.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*\(/);
|
|
75
|
+
if (callMatch && knownFunctionReturnTypes) {
|
|
76
|
+
const inferred = knownFunctionReturnTypes.get(callMatch[1]);
|
|
77
|
+
if (inferred) {
|
|
78
|
+
return inferred;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (value.kind === "array") {
|
|
83
|
+
const elementKinds = value.elements.map((element) => element.kind);
|
|
84
|
+
let elementType = defaultIntType;
|
|
85
|
+
if (elementKinds.includes("string")) {
|
|
86
|
+
elementType = "const char*";
|
|
87
|
+
}
|
|
88
|
+
else if (value.elements.some((element) => element.kind === "number" && !Number.isInteger(element.value))) {
|
|
89
|
+
elementType = "float";
|
|
90
|
+
}
|
|
91
|
+
else if (elementKinds.includes("boolean") && !elementKinds.includes("number")) {
|
|
92
|
+
elementType = "bool";
|
|
93
|
+
}
|
|
94
|
+
return `std::vector<${elementType}>`;
|
|
95
|
+
}
|
|
96
|
+
// Nested object literal — generate a struct type name
|
|
97
|
+
if (value.kind === "object") {
|
|
98
|
+
if (parentName && fieldName) {
|
|
99
|
+
return `_${parentName}_${fieldName}_t`;
|
|
100
|
+
}
|
|
101
|
+
return defaultIntType;
|
|
102
|
+
}
|
|
103
|
+
return defaultIntType;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Recursively collects nested struct definitions from an object ExpressionIR.
|
|
107
|
+
* Returns an array of struct definitions ordered from deepest to shallowest,
|
|
108
|
+
* so that inner structs are defined before outer structs that reference them.
|
|
109
|
+
*
|
|
110
|
+
* @param objExpr The object ExpressionIR to scan for nested objects
|
|
111
|
+
* @param parentName The variable/struct name prefix for generating struct type names
|
|
112
|
+
* @param pointerVarTypes Optional pointer variable type map
|
|
113
|
+
* @param knownFunctionReturnTypes Optional function return type map
|
|
114
|
+
* @param knownObjectTypes Optional object type map
|
|
115
|
+
* @param knownObjectFieldTypes Optional object field type map
|
|
116
|
+
* @param largeEnumNames Optional large enum names set
|
|
117
|
+
*/
|
|
118
|
+
export function collectNestedStructDefs(objExpr, parentName, pointerVarTypes, knownFunctionReturnTypes, knownObjectTypes, knownObjectFieldTypes, largeEnumNames) {
|
|
119
|
+
const result = [];
|
|
120
|
+
for (const field of objExpr.fields) {
|
|
121
|
+
if (field.value.kind === "object") {
|
|
122
|
+
const nestedObj = field.value;
|
|
123
|
+
const nestedStructName = `_${parentName}_${field.name}_t`;
|
|
124
|
+
// Recursively collect deeper nested structs first
|
|
125
|
+
const deeper = collectNestedStructDefs(nestedObj, `${parentName}_${field.name}`, pointerVarTypes, knownFunctionReturnTypes, knownObjectTypes, knownObjectFieldTypes, largeEnumNames);
|
|
126
|
+
result.push(...deeper);
|
|
127
|
+
// Collect this nested struct's fields
|
|
128
|
+
const nestedFields = nestedObj.fields.map((f) => ({
|
|
129
|
+
type: inferObjectFieldType(f.value, pointerVarTypes, knownFunctionReturnTypes, knownObjectTypes, knownObjectFieldTypes, largeEnumNames, `${parentName}_${field.name}`, f.name),
|
|
130
|
+
name: f.name,
|
|
131
|
+
}));
|
|
132
|
+
result.push({ structName: nestedStructName, fields: nestedFields });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Checks if a program contains array literals inside object literals.
|
|
139
|
+
* This affects whether std::vector needs to be included.
|
|
140
|
+
*
|
|
141
|
+
* @param program The program IR to analyze
|
|
142
|
+
* @returns true if arrays in object literals are found
|
|
143
|
+
*/
|
|
144
|
+
export function hasArrayInObjectLiteral(program) {
|
|
145
|
+
const hasArray = (expr) => {
|
|
146
|
+
if (expr.kind === "array") {
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
if (expr.kind === "object") {
|
|
150
|
+
return expr.fields.some((field) => hasArray(field.value));
|
|
151
|
+
}
|
|
152
|
+
if (expr.kind === "ternary") {
|
|
153
|
+
return hasArray(expr.condition) || hasArray(expr.whenTrue) || hasArray(expr.whenFalse);
|
|
154
|
+
}
|
|
155
|
+
return false;
|
|
156
|
+
};
|
|
157
|
+
const hasArrayInStatement = (statement) => {
|
|
158
|
+
if (statement.kind === "var_decl" && statement.initializer) {
|
|
159
|
+
return hasArray(statement.initializer);
|
|
160
|
+
}
|
|
161
|
+
if (statement.kind === "assign") {
|
|
162
|
+
return hasArray(statement.value);
|
|
163
|
+
}
|
|
164
|
+
if (statement.kind === "return" && statement.value) {
|
|
165
|
+
return hasArray(statement.value);
|
|
166
|
+
}
|
|
167
|
+
if (statement.kind === "call") {
|
|
168
|
+
return statement.args.some((arg) => hasArray(arg));
|
|
169
|
+
}
|
|
170
|
+
return false;
|
|
171
|
+
};
|
|
172
|
+
if (program.topLevelStatements.some((statement) => hasArrayInStatement(statement))) {
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
for (const fn of program.functions) {
|
|
176
|
+
if (fn.statements.some((statement) => hasArrayInStatement(statement))) {
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
for (const classDef of program.classes) {
|
|
181
|
+
if (classDef.fields.some((field) => field.initializer ? hasArray(field.initializer) : false)) {
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
for (const method of classDef.methods) {
|
|
185
|
+
if (method.statements.some((statement) => hasArrayInStatement(statement))) {
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (classDef.constructor && classDef.constructor.statements.some((statement) => hasArrayInStatement(statement))) {
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Checks if a program contains throw statements.
|
|
197
|
+
* This affects whether <stdexcept> needs to be included.
|
|
198
|
+
*
|
|
199
|
+
* @param program The program IR to analyze
|
|
200
|
+
* @returns true if throw statements are found
|
|
201
|
+
*/
|
|
202
|
+
export function hasThrowStatements(program) {
|
|
203
|
+
const statementHasThrow = (statement) => {
|
|
204
|
+
if (statement.kind === "throw") {
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
if (statement.kind === "while" || statement.kind === "do_while") {
|
|
208
|
+
return statement.body.some(statementHasThrow);
|
|
209
|
+
}
|
|
210
|
+
if (statement.kind === "for" || statement.kind === "for_of" || statement.kind === "for_in") {
|
|
211
|
+
return statement.body.some(statementHasThrow);
|
|
212
|
+
}
|
|
213
|
+
if (statement.kind === "if") {
|
|
214
|
+
return statement.thenBranch.some(statementHasThrow) || (statement.elseBranch?.some(statementHasThrow) ?? false);
|
|
215
|
+
}
|
|
216
|
+
if (statement.kind === "switch") {
|
|
217
|
+
return statement.cases.some((caseClause) => caseClause.body.some(statementHasThrow));
|
|
218
|
+
}
|
|
219
|
+
if (statement.kind === "try") {
|
|
220
|
+
return statement.tryBlock.some(statementHasThrow) || (statement.catchBlock?.some(statementHasThrow) ?? false);
|
|
221
|
+
}
|
|
222
|
+
return false;
|
|
223
|
+
};
|
|
224
|
+
if (program.topLevelStatements.some(statementHasThrow)) {
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
for (const fn of program.functions) {
|
|
228
|
+
if (fn.statements.some(statementHasThrow)) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
for (const classDef of program.classes) {
|
|
233
|
+
if (classDef.methods.some((method) => method.statements.some(statementHasThrow))) {
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
if (classDef.constructor && classDef.constructor.statements.some(statementHasThrow)) {
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Checks if a program contains std::math function calls.
|
|
244
|
+
* This affects whether <cmath> or <math.h> needs to be included.
|
|
245
|
+
*
|
|
246
|
+
* @param program The program IR to analyze
|
|
247
|
+
* @returns true if math calls are found
|
|
248
|
+
*/
|
|
249
|
+
export function hasStdMathCalls(program) {
|
|
250
|
+
const mathPattern = /\bstd::(floor|ceil|round|trunc|sqrt|pow|sin|cos|tan|asin|acos|atan|abs|max|min)\b/;
|
|
251
|
+
const expressionHasMath = (expr) => {
|
|
252
|
+
if (expr.kind === "raw") {
|
|
253
|
+
return mathPattern.test(expr.value);
|
|
254
|
+
}
|
|
255
|
+
if (expr.kind === "await") {
|
|
256
|
+
return expressionHasMath(expr.value);
|
|
257
|
+
}
|
|
258
|
+
if (expr.kind === "ternary") {
|
|
259
|
+
return expressionHasMath(expr.condition) || expressionHasMath(expr.whenTrue) || expressionHasMath(expr.whenFalse);
|
|
260
|
+
}
|
|
261
|
+
if (expr.kind === "array") {
|
|
262
|
+
return expr.elements.some(expressionHasMath);
|
|
263
|
+
}
|
|
264
|
+
if (expr.kind === "object") {
|
|
265
|
+
return expr.fields.some((field) => expressionHasMath(field.value));
|
|
266
|
+
}
|
|
267
|
+
if (expr.kind === "instanceof") {
|
|
268
|
+
return expressionHasMath(expr.object);
|
|
269
|
+
}
|
|
270
|
+
if (expr.kind === "spread_array") {
|
|
271
|
+
return expressionHasMath(expr.spreadExpr) || expr.additionalElements.some(expressionHasMath);
|
|
272
|
+
}
|
|
273
|
+
return false;
|
|
274
|
+
};
|
|
275
|
+
const statementHasMath = (statement) => {
|
|
276
|
+
if (statement.kind === "call") {
|
|
277
|
+
return statement.args.some(expressionHasMath);
|
|
278
|
+
}
|
|
279
|
+
if (statement.kind === "var_decl") {
|
|
280
|
+
return statement.initializer ? expressionHasMath(statement.initializer) : false;
|
|
281
|
+
}
|
|
282
|
+
if (statement.kind === "assign") {
|
|
283
|
+
return expressionHasMath(statement.value);
|
|
284
|
+
}
|
|
285
|
+
if (statement.kind === "return") {
|
|
286
|
+
return statement.value ? expressionHasMath(statement.value) : false;
|
|
287
|
+
}
|
|
288
|
+
if (statement.kind === "while" || statement.kind === "do_while") {
|
|
289
|
+
return expressionHasMath(statement.condition) || statement.body.some(statementHasMath);
|
|
290
|
+
}
|
|
291
|
+
if (statement.kind === "if") {
|
|
292
|
+
return expressionHasMath(statement.condition) || statement.thenBranch.some(statementHasMath) || (statement.elseBranch?.some(statementHasMath) ?? false);
|
|
293
|
+
}
|
|
294
|
+
if (statement.kind === "for") {
|
|
295
|
+
return (statement.initializer ? statementHasMath(statement.initializer) : false)
|
|
296
|
+
|| (statement.condition ? expressionHasMath(statement.condition) : false)
|
|
297
|
+
|| (statement.increment ? statementHasMath(statement.increment) : false)
|
|
298
|
+
|| statement.body.some(statementHasMath);
|
|
299
|
+
}
|
|
300
|
+
if (statement.kind === "for_of") {
|
|
301
|
+
return statementHasMath(statement.variable) || expressionHasMath(statement.iterable) || statement.body.some(statementHasMath);
|
|
302
|
+
}
|
|
303
|
+
if (statement.kind === "for_in") {
|
|
304
|
+
return statementHasMath(statement.variable) || expressionHasMath(statement.object) || statement.body.some(statementHasMath);
|
|
305
|
+
}
|
|
306
|
+
if (statement.kind === "switch") {
|
|
307
|
+
return expressionHasMath(statement.expression) || statement.cases.some((caseClause) => (caseClause.value ? expressionHasMath(caseClause.value) : false) || caseClause.body.some(statementHasMath));
|
|
308
|
+
}
|
|
309
|
+
if (statement.kind === "try") {
|
|
310
|
+
return statement.tryBlock.some(statementHasMath) || (statement.catchBlock?.some(statementHasMath) ?? false);
|
|
311
|
+
}
|
|
312
|
+
if (statement.kind === "throw") {
|
|
313
|
+
return expressionHasMath(statement.value);
|
|
314
|
+
}
|
|
315
|
+
return false;
|
|
316
|
+
};
|
|
317
|
+
if (program.topLevelStatements.some(statementHasMath)) {
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
if (program.functions.some((fn) => fn.statements.some(statementHasMath))) {
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
for (const classDef of program.classes) {
|
|
324
|
+
if (classDef.methods.some((method) => method.statements.some(statementHasMath))) {
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
if (classDef.constructor && classDef.constructor.statements.some(statementHasMath)) {
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Checks if a program contains console.* calls (console.log, console.error, etc.)
|
|
335
|
+
*
|
|
336
|
+
* @param program The program IR to analyze
|
|
337
|
+
* @returns true if console calls are found
|
|
338
|
+
*/
|
|
339
|
+
export function hasConsoleCalls(program, strategy) {
|
|
340
|
+
const checkStatements = (statements) => {
|
|
341
|
+
for (const stmt of statements) {
|
|
342
|
+
if (stmt.kind === "call" && isConsoleCall(stmt.callee, strategy)) {
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
// Check nested statements in control flow
|
|
346
|
+
if ("body" in stmt && Array.isArray(stmt.body)) {
|
|
347
|
+
if (checkStatements(stmt.body))
|
|
348
|
+
return true;
|
|
349
|
+
}
|
|
350
|
+
if ("thenBranch" in stmt && Array.isArray(stmt.thenBranch)) {
|
|
351
|
+
if (checkStatements(stmt.thenBranch))
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
354
|
+
if ("elseBranch" in stmt && Array.isArray(stmt.elseBranch)) {
|
|
355
|
+
if (checkStatements(stmt.elseBranch))
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
if ("cases" in stmt && Array.isArray(stmt.cases)) {
|
|
359
|
+
for (const c of stmt.cases) {
|
|
360
|
+
if (checkStatements(c.body))
|
|
361
|
+
return true;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if ("tryBlock" in stmt && Array.isArray(stmt.tryBlock)) {
|
|
365
|
+
if (checkStatements(stmt.tryBlock))
|
|
366
|
+
return true;
|
|
367
|
+
}
|
|
368
|
+
if ("catchBlock" in stmt && Array.isArray(stmt.catchBlock)) {
|
|
369
|
+
if (checkStatements(stmt.catchBlock))
|
|
370
|
+
return true;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return false;
|
|
374
|
+
};
|
|
375
|
+
// Check all functions
|
|
376
|
+
for (const fn of program.functions) {
|
|
377
|
+
if (checkStatements(fn.statements))
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
// Check top-level statements
|
|
381
|
+
if (checkStatements(program.topLevelStatements))
|
|
382
|
+
return true;
|
|
383
|
+
// Check class methods
|
|
384
|
+
for (const cls of program.classes) {
|
|
385
|
+
for (const method of cls.methods) {
|
|
386
|
+
if (checkStatements(method.statements))
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
389
|
+
if (cls.constructor && checkStatements(cls.constructor.statements))
|
|
390
|
+
return true;
|
|
391
|
+
}
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Checks if a callee is a console method call (e.g., "console.log", "console.error")
|
|
396
|
+
*/
|
|
397
|
+
export function isConsoleCall(callee, strategy) {
|
|
398
|
+
if (strategy)
|
|
399
|
+
return strategy.isConsoleCall(callee);
|
|
400
|
+
return callee.startsWith("console.");
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Gets the console method name from a callee (e.g., "log" from "console.log")
|
|
404
|
+
*/
|
|
405
|
+
export function getConsoleMethod(callee) {
|
|
406
|
+
return callee.slice("console.".length);
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Checks if an expression requires runtime execution (cannot be evaluated at global scope in C++).
|
|
410
|
+
* Compile-time expressions can be placed at global scope; runtime expressions must go in setup()/main().
|
|
411
|
+
*
|
|
412
|
+
* @param expr The expression to check
|
|
413
|
+
* @returns true if the expression requires runtime execution
|
|
414
|
+
*/
|
|
415
|
+
export function collectExpressionIdentifiers(expr) {
|
|
416
|
+
const names = new Set();
|
|
417
|
+
function scan(e) {
|
|
418
|
+
if (!e || typeof e !== 'object' || !e.kind)
|
|
419
|
+
return;
|
|
420
|
+
if (e.kind === "identifier") {
|
|
421
|
+
names.add(e.value);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
for (const key of Object.keys(e)) {
|
|
425
|
+
if (key === "kind" || key === "loc" || key === "range" || key === "sourceSpan")
|
|
426
|
+
continue;
|
|
427
|
+
const val = e[key];
|
|
428
|
+
if (Array.isArray(val)) {
|
|
429
|
+
for (const item of val) {
|
|
430
|
+
if (item && typeof item === 'object' && item.kind)
|
|
431
|
+
scan(item);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
else if (val && typeof val === 'object' && val.kind) {
|
|
435
|
+
scan(val);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
scan(expr);
|
|
440
|
+
return names;
|
|
441
|
+
}
|
|
442
|
+
export function isRuntimeExpression(expr) {
|
|
443
|
+
// Safety check
|
|
444
|
+
if (!expr || typeof expr !== 'object' || !expr.kind) {
|
|
445
|
+
return false;
|
|
446
|
+
}
|
|
447
|
+
switch (expr.kind) {
|
|
448
|
+
case "number":
|
|
449
|
+
case "string":
|
|
450
|
+
case "boolean":
|
|
451
|
+
// Literals are compile-time constants
|
|
452
|
+
return false;
|
|
453
|
+
case "identifier":
|
|
454
|
+
// Simple identifiers might be compile-time constants, but could also be
|
|
455
|
+
// runtime values. We'll be conservative and allow them at global scope
|
|
456
|
+
// since they might reference constants.
|
|
457
|
+
return false;
|
|
458
|
+
case "array":
|
|
459
|
+
// Arrays are okay at global scope if all elements are compile-time
|
|
460
|
+
return expr.elements.some((e) => isRuntimeExpression(e));
|
|
461
|
+
case "object":
|
|
462
|
+
// Plain object literals can stay at global scope as long as all their
|
|
463
|
+
// field initializers are themselves compile-time-safe.
|
|
464
|
+
return expr.fields.some((f) => isRuntimeExpression(f.value));
|
|
465
|
+
case "raw":
|
|
466
|
+
// Raw expressions might contain method calls - check for common patterns
|
|
467
|
+
// Look for function/method call patterns: identifier(...) or ...(...)
|
|
468
|
+
const rawValue = expr.value;
|
|
469
|
+
// Check for call patterns: ends with ) and contains ( not at start
|
|
470
|
+
if (rawValue.includes("(") && rawValue.includes(")")) {
|
|
471
|
+
// Has function call syntax - likely runtime
|
|
472
|
+
return true;
|
|
473
|
+
}
|
|
474
|
+
// Check for 'new' keyword
|
|
475
|
+
if (rawValue.startsWith("new ") || rawValue.includes(" new ")) {
|
|
476
|
+
return true;
|
|
477
|
+
}
|
|
478
|
+
return false;
|
|
479
|
+
case "await":
|
|
480
|
+
// Await expressions are definitely runtime
|
|
481
|
+
return true;
|
|
482
|
+
case "ternary":
|
|
483
|
+
// Ternary expressions are runtime if any part is runtime
|
|
484
|
+
return (isRuntimeExpression(expr.condition) ||
|
|
485
|
+
isRuntimeExpression(expr.whenTrue) ||
|
|
486
|
+
isRuntimeExpression(expr.whenFalse));
|
|
487
|
+
case "binary":
|
|
488
|
+
return isRuntimeExpression(expr.left) || isRuntimeExpression(expr.right);
|
|
489
|
+
case "unary":
|
|
490
|
+
return isRuntimeExpression(expr.operand);
|
|
491
|
+
case "method-call":
|
|
492
|
+
return true;
|
|
493
|
+
case "instanceof":
|
|
494
|
+
// instanceof requires RTTI and runtime evaluation
|
|
495
|
+
return true;
|
|
496
|
+
case "spread_array":
|
|
497
|
+
// Spread operations are runtime
|
|
498
|
+
return true;
|
|
499
|
+
case "property-access":
|
|
500
|
+
// An enum member access (Mode.Run → Mode::Run) is a compile-time
|
|
501
|
+
// constant, NOT a runtime expression. Without this, a top-level
|
|
502
|
+
// `const currentMode: Mode = Mode.Run` was classified as runtime
|
|
503
|
+
// (the default returns true) and emitted as a local inside setup()
|
|
504
|
+
// instead of a file-scope global, making it invisible to functions
|
|
505
|
+
// (enum stress test Finding B). Only consider it compile-time when
|
|
506
|
+
// it's a static/enum access (no runtime object receiver).
|
|
507
|
+
return false;
|
|
508
|
+
default:
|
|
509
|
+
// Unknown expression types - be conservative
|
|
510
|
+
return true;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* Checks if a statement requires runtime execution.
|
|
515
|
+
*
|
|
516
|
+
* @param statement The statement to check
|
|
517
|
+
* @returns true if the statement requires runtime execution
|
|
518
|
+
*/
|
|
519
|
+
export function statementRequiresRuntime(statement) {
|
|
520
|
+
if (statement.kind === "var_decl") {
|
|
521
|
+
// Variable declaration requires runtime if its initializer does
|
|
522
|
+
return statement.initializer ? isRuntimeExpression(statement.initializer) : false;
|
|
523
|
+
}
|
|
524
|
+
// All other statement types (calls, assignments, etc.) are runtime
|
|
525
|
+
return true;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Collects pointer variable types from program statements.
|
|
529
|
+
* Variables initialized with 'new ClassName()' get type 'ClassName*'.
|
|
530
|
+
*
|
|
531
|
+
* @param statements The statements to analyze
|
|
532
|
+
* @param classNameMap Optional map for transforming simple class names to fully qualified names
|
|
533
|
+
* @returns Map of variable names to their pointer types
|
|
534
|
+
*/
|
|
535
|
+
export function collectPointerVarTypes(statements, classNameMap) {
|
|
536
|
+
const pointerVarTypes = new Map();
|
|
537
|
+
for (const statement of statements) {
|
|
538
|
+
if (statement.kind === "var_decl" && statement.initializer) {
|
|
539
|
+
const declaredType = statement.cppType;
|
|
540
|
+
if (parsedIsPointer(declaredType)) {
|
|
541
|
+
pointerVarTypes.set(statement.name, declaredType);
|
|
542
|
+
continue;
|
|
543
|
+
}
|
|
544
|
+
const init = statement.initializer;
|
|
545
|
+
// Check for raw expression containing 'new'
|
|
546
|
+
if (init.kind === "raw" && init.value.startsWith("new ")) {
|
|
547
|
+
// Extract class name from 'new ClassName(...)'
|
|
548
|
+
const match = init.value.match(/^new\s+(\w+)/);
|
|
549
|
+
if (match) {
|
|
550
|
+
const simpleName = match[1];
|
|
551
|
+
// Use fully qualified name if available (for namespaced classes)
|
|
552
|
+
const fullName = classNameMap?.get(simpleName) ?? simpleName;
|
|
553
|
+
pointerVarTypes.set(statement.name, `${fullName}*`);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
return pointerVarTypes;
|
|
559
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface ESLintError {
|
|
2
|
+
filePath: string;
|
|
3
|
+
line: number;
|
|
4
|
+
column: number;
|
|
5
|
+
message: string;
|
|
6
|
+
ruleId: string;
|
|
7
|
+
sourceLine: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function runEslintCheck(projectRoot: string): Promise<ESLintError[]>;
|
|
10
|
+
export declare function printEslintErrors(errors: ESLintError[]): void;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
const selfRequire = createRequire(import.meta.url);
|
|
7
|
+
export async function runEslintCheck(projectRoot) {
|
|
8
|
+
// Look for eslint config in .cuttlefish/ (new layout) or project root (legacy)
|
|
9
|
+
const cuttlefishConfig = path.join(projectRoot, ".cuttlefish", "eslint.config.mjs");
|
|
10
|
+
const rootConfigNames = ["eslint.config.mjs", "eslint.config.js", "eslint.config.cjs"];
|
|
11
|
+
const rootConfig = rootConfigNames.find(name => fs.existsSync(path.join(projectRoot, name)));
|
|
12
|
+
let overrideConfigFile;
|
|
13
|
+
if (fs.existsSync(cuttlefishConfig)) {
|
|
14
|
+
overrideConfigFile = cuttlefishConfig;
|
|
15
|
+
}
|
|
16
|
+
else if (rootConfig) {
|
|
17
|
+
// Legacy: config at project root (existing demo projects)
|
|
18
|
+
overrideConfigFile = path.join(projectRoot, rootConfig);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
let ESLintCls;
|
|
24
|
+
try {
|
|
25
|
+
// Prefer the user project's own eslint install (handles pinned versions),
|
|
26
|
+
// then fall back to the cuttlefish package's install (monorepo / global).
|
|
27
|
+
let eslintPath;
|
|
28
|
+
try {
|
|
29
|
+
const projectRequire = createRequire(path.join(projectRoot, "package.json"));
|
|
30
|
+
eslintPath = projectRequire.resolve("eslint");
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
eslintPath = selfRequire.resolve("eslint");
|
|
34
|
+
}
|
|
35
|
+
const eslintModule = await import(pathToFileURL(eslintPath).href);
|
|
36
|
+
ESLintCls = eslintModule.ESLint;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
if (!ESLintCls)
|
|
42
|
+
return [];
|
|
43
|
+
const eslint = new ESLintCls({ cwd: projectRoot, overrideConfigFile });
|
|
44
|
+
const srcDir = path.join(projectRoot, "src");
|
|
45
|
+
if (!fs.existsSync(srcDir))
|
|
46
|
+
return [];
|
|
47
|
+
let results;
|
|
48
|
+
try {
|
|
49
|
+
results = await eslint.lintFiles([srcDir]);
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
53
|
+
// "All files matched by ... are ignored" is raised when src/ contains no
|
|
54
|
+
// lintable .ts files (e.g. a pure-.ui entry). That is a legitimate no-op,
|
|
55
|
+
// not a config-load failure — return [] so the build proceeds.
|
|
56
|
+
if (/all files matched/i.test(detail) && /ignored/i.test(detail)) {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
// A config that exists but cannot be loaded (broken import, bad parser
|
|
60
|
+
// reference, etc.) means linting silently no-ops — the exact regression
|
|
61
|
+
// this gate exists to prevent. Surface it instead of returning [].
|
|
62
|
+
throw new Error(`ESLint config could not be loaded from ${overrideConfigFile}:\n${detail}\n\n` +
|
|
63
|
+
`Fix the config so linting runs, or remove it to skip the ESLint gate.`);
|
|
64
|
+
}
|
|
65
|
+
const errors = [];
|
|
66
|
+
for (const result of results) {
|
|
67
|
+
if (result.errorCount === 0)
|
|
68
|
+
continue;
|
|
69
|
+
const sourceLines = result.source
|
|
70
|
+
? result.source.split("\n")
|
|
71
|
+
: fs.existsSync(result.filePath)
|
|
72
|
+
? fs.readFileSync(result.filePath, "utf-8").split("\n")
|
|
73
|
+
: [];
|
|
74
|
+
for (const msg of result.messages) {
|
|
75
|
+
if (msg.severity !== 2)
|
|
76
|
+
continue;
|
|
77
|
+
errors.push({
|
|
78
|
+
filePath: path.relative(projectRoot, result.filePath),
|
|
79
|
+
line: msg.line,
|
|
80
|
+
column: msg.column,
|
|
81
|
+
message: msg.message,
|
|
82
|
+
ruleId: msg.ruleId ?? "unknown",
|
|
83
|
+
sourceLine: sourceLines[msg.line - 1] ?? "",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return errors;
|
|
88
|
+
}
|
|
89
|
+
export function printEslintErrors(errors) {
|
|
90
|
+
for (const err of errors) {
|
|
91
|
+
const location = `${err.filePath}(${err.line},${err.column})`;
|
|
92
|
+
const rule = chalk.gray(`[${err.ruleId}]`);
|
|
93
|
+
console.error(`${chalk.red.bold("error")} ${location} ${rule}: ${chalk.white(err.message)}`);
|
|
94
|
+
if (err.sourceLine) {
|
|
95
|
+
const prefix = " ";
|
|
96
|
+
console.error(prefix + err.sourceLine);
|
|
97
|
+
console.error(chalk.gray(prefix + " ".repeat(Math.max(0, err.column - 1)) + "^"));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
console.error("");
|
|
101
|
+
console.error(chalk.red(`✖ ${errors.length} ESLint error${errors.length === 1 ? "" : "s"} — build stopped`));
|
|
102
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load a framework package and populate the LoadedFramework registry.
|
|
3
|
+
*
|
|
4
|
+
* The loaded module is expected to export at minimum `FrameworkStrategy`.
|
|
5
|
+
* Optional exports include `Toolchain`, `SYMBOL_KINDS`, library resolver,
|
|
6
|
+
* class name map builder, and lib declaration generator.
|
|
7
|
+
*
|
|
8
|
+
* Returns the raw module for backward compatibility.
|
|
9
|
+
*/
|
|
10
|
+
export declare function loadFrameworkPackage(packageName: string, fromDir?: string): any;
|