freelang-editor 11.7.4
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/.config/jest.config.js +47 -0
- package/.config/jest.fast.config.js +9 -0
- package/.config/openapi.yaml +457 -0
- package/.config/tsconfig.json +30 -0
- package/.github/CODE_OF_CONDUCT.md +176 -0
- package/.github/CONTRIBUTING.md +296 -0
- package/.github/DISCUSSIONS.md +179 -0
- package/.github/ISSUE_TEMPLATE/bug-report.md +44 -0
- package/.github/ISSUE_TEMPLATE/feature-request.md +39 -0
- package/.github/pull_request_template.md +87 -0
- package/.github/workflows/phase-3c-l2-proof.yml +101 -0
- package/.github/workflows/phase-c-full-slack.yml +409 -0
- package/.github/workflows/phase-c-full.yml +241 -0
- package/.github/workflows/phase-c-scan.yml +64 -0
- package/.gitmodules +3 -0
- package/.tmp.js +3 -0
- package/CHANGELOG.md +1182 -0
- package/CLAUDE.md +633 -0
- package/Dockerfile +3 -0
- package/LICENSE +21 -0
- package/MISTAKES-COVERAGE.json +115 -0
- package/Makefile +95 -0
- package/PHASE-F-ROADMAP.md +554 -0
- package/README.md +459 -0
- package/SELF_HOSTING_CHECKLIST.md +155 -0
- package/benchmark-freelang.fl +47 -0
- package/benchmark-results.json +37 -0
- package/benchmark-simple.fl +38 -0
- package/bin/freelang-migrate +310 -0
- package/bin/freelang-smart +258 -0
- package/bootstrap.js +1134 -0
- package/docker-compose.yml +3 -0
- package/docs/AI_FRIENDLINESS.md +181 -0
- package/docs/AI_LEARNING_PATH.md +542 -0
- package/docs/AI_MAINTENANCE.md +153 -0
- package/docs/AI_QUICKSTART.md +380 -0
- package/docs/AI_REFERENCE.md +559 -0
- package/docs/AI_RELIABILITY_GUIDE.md +597 -0
- package/docs/AKL_ARCHITECTURE.md +228 -0
- package/docs/AKL_P0_KIMDB.md +215 -0
- package/docs/API.md +380 -0
- package/docs/ARCHITECTURE.md +208 -0
- package/docs/CHANGELOG.md +1014 -0
- package/docs/CLAUDE.md +991 -0
- package/docs/CLAUDE_AI.md +210 -0
- package/docs/CODEGEN_IMPROVEMENTS.md +158 -0
- package/docs/CODE_OF_CONDUCT.md +337 -0
- package/docs/CONTRIBUTING.md +437 -0
- package/docs/CRON_AUTOMATION.md +97 -0
- package/docs/DEPLOYMENT.md +357 -0
- package/docs/DETERMINISM_GUIDE.md +174 -0
- package/docs/HUMAN_GUIDE.md +599 -0
- package/docs/INDEX.md +199 -0
- package/docs/INLINE_TEST_GUIDE.md +231 -0
- package/docs/LANGUAGE-FAULTS.md +583 -0
- package/docs/LANGUAGE_IMPROVEMENT_REQUESTS.md +187 -0
- package/docs/LEARNING.md +257 -0
- package/docs/MISTAKES-100.md +52 -0
- package/docs/MISTAKES.md +131 -0
- package/docs/NAMING_CONVENTIONS.md +158 -0
- package/docs/NIGHTLY-MONITORING.md +222 -0
- package/docs/OFFICIAL_LANGUAGE.md +249 -0
- package/docs/ONBOARDING_1HOUR.md +210 -0
- package/docs/PERFORMANCE.md +329 -0
- package/docs/PHASE-3C-L2-PROOF-PLAN.md +282 -0
- package/docs/PHASE-3C-L2-RESULTS.md +183 -0
- package/docs/PHASE-3D-AI-LIBRARY.md +170 -0
- package/docs/PHASE-3E-VM-OPTIMIZATION.md +192 -0
- package/docs/PHASE-C-CI.md +242 -0
- package/docs/PHASE-C-PR-CHECKLIST.md +267 -0
- package/docs/PHASE-C-ROADMAP.md +141 -0
- package/docs/PHASE-C-SCAN.md +205 -0
- package/docs/PHASE-G-ROADMAP.md +228 -0
- package/docs/PHASE-X-V11.5-ROADMAP.md +267 -0
- package/docs/PHASE4_IMPLEMENTATION_PLAN.md +290 -0
- package/docs/PHASE5-COMPLETION.md +186 -0
- package/docs/PHASE5-SUMMARY.md +82 -0
- package/docs/PLUGIN_GUIDE.md +241 -0
- package/docs/PROJECT_OVERVIEW_KO.md +66 -0
- package/docs/QUICKSTART.md +160 -0
- package/docs/README.md +192 -0
- package/docs/RESERVED.md +125 -0
- package/docs/ROADMAP.md +191 -0
- package/docs/SECURITY.md +518 -0
- package/docs/SETUP.md +127 -0
- package/docs/SLACK-SETUP.md +190 -0
- package/docs/STATE_OF_V11.md +74 -0
- package/docs/STDLIB_NAMING_AUDIT.md +154 -0
- package/docs/STDLIB_REFERENCE.md +2722 -0
- package/docs/STYLE_GUIDE.md +559 -0
- package/docs/TASK5-MAP-DESTRUCTURE-DESIGN.md +136 -0
- package/docs/TOOLS.md +357 -0
- package/docs/TYPE_SYSTEM_GUIDE.md +201 -0
- package/docs/UI-PATTERN.md +265 -0
- package/docs/V11.5-RULES.md +416 -0
- package/docs/V11.6-HARNESS-RULES.md +222 -0
- package/docs/V11.6-MULTI-AGENT-PLAN.md +265 -0
- package/docs/V11.6-STABILIZATION-PLAN.md +290 -0
- package/docs/Y4-2B_DRY_RUN.md +142 -0
- package/docs/_classifications.json +882 -0
- package/docs/api/stdlib.md +134 -0
- package/docs/blog/2026-04-17-v11-ai-first-evolution.md +223 -0
- package/docs/blog/2026-04-20-self-hosting-fixedpoint.md +269 -0
- package/docs/blog/2026-05-04-mistakes-are-language-faults.md +267 -0
- package/docs/blog/2026-05-04-mistakes-coverage-honest.md +170 -0
- package/docs/blog/2026-05-04-phase-g-followup-smart-wrapper.md +188 -0
- package/docs/blog/2026-05-04-phase-g-mistakes-100.md +269 -0
- package/docs/blog/2026-05-04-phase-x-v11.5-launch.md +224 -0
- package/docs/blog/2026-05-04-v11.5.0-real-language-change.md +252 -0
- package/docs/blog-demo/README.md +44 -0
- package/docs/examples/index.md +78 -0
- package/docs/examples/todo-app.md +99 -0
- package/docs/guide/ai-blocks.md +335 -0
- package/docs/guide/basics.md +235 -0
- package/docs/guide/frameworks.md +319 -0
- package/docs/homepage/README.md +164 -0
- package/docs/index.md +183 -0
- package/docs/v12-DESIGN.md +214 -0
- package/examples/README.md +26 -0
- package/examples/browser-test.html +149 -0
- package/examples/csv-tool/analyze.fl +222 -0
- package/examples/csv-tool/employees.csv +16 -0
- package/examples/csv-tool/products.csv +21 -0
- package/examples/factorial.fl +9 -0
- package/examples/fib30.fl +4 -0
- package/examples/hello.fl +2 -0
- package/examples/hello.fl.js +3 -0
- package/examples/learning-session/01-grade-stats.fl +49 -0
- package/examples/learning-session/02-class-stats.fl +52 -0
- package/examples/learning-session/03-cart.fl +61 -0
- package/examples/learning-session/04-word-freq.fl +57 -0
- package/examples/learning-session/05-todo.fl +69 -0
- package/examples/learning-session/06-library.fl +76 -0
- package/examples/learning-session/07-currency.fl +99 -0
- package/examples/learning-session/08-csv-grades.fl +104 -0
- package/examples/learning-session/09-calc-repl.fl +92 -0
- package/examples/learning-session/10-contacts.fl +109 -0
- package/examples/learning-session/11-markdown.fl +107 -0
- package/examples/learning-session/12-calendar.fl +87 -0
- package/examples/learning-session/13-statistics.fl +123 -0
- package/examples/learning-session/14-primes.fl +107 -0
- package/examples/learning-session/15-statistics.fl +123 -0
- package/examples/learning-session/16-calendar.fl +87 -0
- package/examples/learning-session/17-markdown.fl +107 -0
- package/examples/learning-session/18-contacts.fl +109 -0
- package/examples/learning-session/19-json-serializer.fl +86 -0
- package/examples/learning-session/20-template.fl +201 -0
- package/examples/learning-session/21-calc-parser.fl +148 -0
- package/examples/learning-session/22-table.fl +96 -0
- package/examples/patterns/01-map-filter-reduce.fl +50 -0
- package/examples/patterns/02-error-handling.fl +51 -0
- package/examples/patterns/03-type-validation.fl +57 -0
- package/examples/patterns/04-state-management.fl +44 -0
- package/examples/patterns/05-api-integration.fl +95 -0
- package/examples/patterns/06-database-operations.fl +113 -0
- package/examples/patterns/07-string-processing.fl +86 -0
- package/examples/patterns/08-data-transformation.fl +92 -0
- package/examples/patterns/09-decision-logic.fl +122 -0
- package/examples/patterns/10-agent-orchestration.fl +184 -0
- package/examples/patterns/README.md +344 -0
- package/examples/simple-server.fl +29 -0
- package/examples/tables-app.fl +219 -0
- package/examples/test-libs.fl +272 -0
- package/jest.config.js +4 -0
- package/lib/datetime.fl +123 -0
- package/lib/http-client.fl +129 -0
- package/lib/pipeline.fl +150 -0
- package/lib/query.fl +160 -0
- package/lib/result.fl +124 -0
- package/lib/template.fl +161 -0
- package/lib/validate.fl +136 -0
- package/package.json +58 -0
- package/scripts/ai-eval.js +318 -0
- package/scripts/ai-self-verify.js +200 -0
- package/scripts/ai-validate.js +302 -0
- package/scripts/bench.sh +71 -0
- package/scripts/benchmark.js +258 -0
- package/scripts/benchmark.sh +99 -0
- package/scripts/build-binary.sh +68 -0
- package/scripts/build-runtime.sh +46 -0
- package/scripts/build-stage1-final.sh +58 -0
- package/scripts/build-stage1.sh +119 -0
- package/scripts/build-standalone.sh +17 -0
- package/scripts/build.js +130 -0
- package/scripts/check-let-regressions.js +95 -0
- package/scripts/check-parens.py +117 -0
- package/scripts/check-ports.sh +49 -0
- package/scripts/check-syntax.js +30 -0
- package/scripts/cli-extras.js +135 -0
- package/scripts/cron-daily-verify.sh +101 -0
- package/scripts/deploy.sh +91 -0
- package/scripts/dev.sh +37 -0
- package/scripts/extract-fl-examples.js +29 -0
- package/scripts/extract-runtime-helpers.js +122 -0
- package/scripts/fl-compile.sh +14 -0
- package/scripts/fl-fixpoint.sh +28 -0
- package/scripts/fl-repl.sh +21 -0
- package/scripts/fl-run.sh +41 -0
- package/scripts/fl-serve.sh +25 -0
- package/scripts/fuzz-compiler.js +92 -0
- package/scripts/gen-ai-prompt.js +340 -0
- package/scripts/gen-fixtures.js +166 -0
- package/scripts/gen-mistakes-split.js +199 -0
- package/scripts/gen-stdlib-docs.js +69 -0
- package/scripts/lint-stdlib-aliases.js +166 -0
- package/scripts/measure-baseline.sh +43 -0
- package/scripts/property-test.js +885 -0
- package/scripts/push-to-gogs.sh +60 -0
- package/scripts/regression-scan.js +132 -0
- package/scripts/run-jest-shard.sh +85 -0
- package/scripts/safe-push.sh +37 -0
- package/scripts/scan-fl-tokens.js +170 -0
- package/scripts/scan-for-fl-tokens.sh +17 -0
- package/scripts/self-diff.sh +95 -0
- package/scripts/snapshot-v0.sh +35 -0
- package/scripts/test-l2-fixpoint.sh +54 -0
- package/scripts/verify-all.sh +130 -0
- package/scripts/verify-build-deterministic.sh +110 -0
- package/scripts/verify-c7-bootstrap.sh +150 -0
- package/scripts/verify-c8-fixed-point-simple.sh +121 -0
- package/scripts/verify-c8-fixed-point.sh +133 -0
- package/scripts/verify-c9-fuzzing.sh +214 -0
- package/scripts/verify-fixed-point-deep.sh +133 -0
- package/scripts/verify-fixed-point.sh +65 -0
- package/scripts/verify-l2-proof.sh +153 -0
- package/scripts/verify-l3-proof.sh +40 -0
- package/scripts/verify-mistakes-coverage.js +120 -0
- package/scripts/verify-self-host.sh +509 -0
- package/self/CHANGELOG.md +964 -0
- package/self/COMPILER_REDESIGN_FAILURES.md +59 -0
- package/self/INTEGRATION-CHECKLIST.md +113 -0
- package/self/README.md +34 -0
- package/self/all.fl +1353 -0
- package/self/ast.fl +219 -0
- package/self/ast.self.js +47 -0
- package/self/bench/_bi_json-parse.js +46 -0
- package/self/bench/_bi_json-str.js +60 -0
- package/self/bench/_bi_list-distinct.js +37 -0
- package/self/bench/_bi_list-filter-even.js +56 -0
- package/self/bench/_bi_list-first.js +29 -0
- package/self/bench/_bi_list-last.js +28 -0
- package/self/bench/_bi_list-map-inc.js +41 -0
- package/self/bench/_bi_list-range.js +22 -0
- package/self/bench/_bi_list-reduce-sum.js +50 -0
- package/self/bench/_bi_list-reverse.js +28 -0
- package/self/bench/_bi_list-sort.js +35 -0
- package/self/bench/_bi_list-take.js +36 -0
- package/self/bench/_bi_m-abs.js +24 -0
- package/self/bench/_bi_m-floor.js +22 -0
- package/self/bench/_bi_m-max.js +26 -0
- package/self/bench/_bi_m-min.js +26 -0
- package/self/bench/_bi_m-mod.js +21 -0
- package/self/bench/_bi_map-has.js +50 -0
- package/self/bench/_bi_map-keys.js +50 -0
- package/self/bench/_bi_map-values.js +52 -0
- package/self/bench/_bi_str-concat.js +36 -0
- package/self/bench/_bi_str-contains.js +43 -0
- package/self/bench/_bi_str-ends.js +45 -0
- package/self/bench/_bi_str-index.js +34 -0
- package/self/bench/_bi_str-lower.js +23 -0
- package/self/bench/_bi_str-repeat.js +26 -0
- package/self/bench/_bi_str-split.js +41 -0
- package/self/bench/_bi_str-starts.js +47 -0
- package/self/bench/_bi_str-trim.js +26 -0
- package/self/bench/_bi_str-upper.js +23 -0
- package/self/bench/_bi_t-list.js +24 -0
- package/self/bench/_bi_t-null.js +23 -0
- package/self/bench/_bi_t-number.js +23 -0
- package/self/bench/_bi_t-string.js +25 -0
- package/self/bench/_bi_t-typeof.js +25 -0
- package/self/bench/_c_parse_probe.fl +426 -0
- package/self/bench/_c_parse_probe.js +67 -0
- package/self/bench/_codegen_nodrv.fl +751 -0
- package/self/bench/_combined2.fl +423 -0
- package/self/bench/_combined_arr.fl +422 -0
- package/self/bench/_combined_astprobe.fl +429 -0
- package/self/bench/_combined_astprobe.js +69 -0
- package/self/bench/_combined_cg.fl +424 -0
- package/self/bench/_combined_cg.js +64 -0
- package/self/bench/_combined_lex.fl +421 -0
- package/self/bench/_combined_trace.fl +435 -0
- package/self/bench/_combined_trace.js +75 -0
- package/self/bench/_ext_abs-neg.js +2 -0
- package/self/bench/_ext_and.js +2 -0
- package/self/bench/_ext_cond-1.js +2 -0
- package/self/bench/_ext_cond-2.js +2 -0
- package/self/bench/_ext_do-seq.js +2 -0
- package/self/bench/_ext_fact20.js +3 -0
- package/self/bench/_ext_fib25.js +3 -0
- package/self/bench/_ext_length.js +2 -0
- package/self/bench/_ext_let-1d.js +18 -0
- package/self/bench/_ext_let-2d.js +2 -0
- package/self/bench/_ext_list-sum.js +3 -0
- package/self/bench/_ext_math.js +2 -0
- package/self/bench/_ext_or.js +2 -0
- package/self/bench/_ext_str.js +2 -0
- package/self/bench/_fact15.js +2 -0
- package/self/bench/_fb_fact15.js +2 -0
- package/self/bench/_fb_func-fact.js +2 -0
- package/self/bench/_fb_func-multi.js +2 -0
- package/self/bench/_fb_func-mutual.js +3 -0
- package/self/bench/_fb_func-simple.js +21 -0
- package/self/bench/_fib.js +2 -0
- package/self/bench/_fn_async-await-seq.js +40 -0
- package/self/bench/_fn_async-await.js +3 -0
- package/self/bench/_fn_async-print.js +26 -0
- package/self/bench/_fn_async-result.js +1 -0
- package/self/bench/_fn_call-form.js +24 -0
- package/self/bench/_fn_compose.js +37 -0
- package/self/bench/_fn_pipe.js +34 -0
- package/self/bench/_fn_thread-first-sexpr.js +31 -0
- package/self/bench/_fn_thread-first.js +25 -0
- package/self/bench/_fn_thread-last.js +31 -0
- package/self/bench/_generated.js +35 -0
- package/self/bench/_mt_match-default.js +51 -0
- package/self/bench/_mt_match-num.js +58 -0
- package/self/bench/_mt_match-str.js +56 -0
- package/self/bench/_mt_match-var.js +32 -0
- package/self/bench/_mt_match-wild.js +36 -0
- package/self/bench/_mt_struct-make.js +81 -0
- package/self/bench/_mt_struct-tag.js +81 -0
- package/self/bench/_probe.fl +1 -0
- package/self/bench/_probe2.fl +1 -0
- package/self/bench/_probe3.fl +3 -0
- package/self/bench/_probe_apply.fl +1 -0
- package/self/bench/_probe_args.fl +7 -0
- package/self/bench/_probe_arr.fl +7 -0
- package/self/bench/_probe_arr2.fl +5 -0
- package/self/bench/_probe_block.fl +7 -0
- package/self/bench/_probe_call.fl +1 -0
- package/self/bench/_probe_cg.fl +27 -0
- package/self/bench/_probe_compose.fl +13 -0
- package/self/bench/_probe_define.fl +1 -0
- package/self/bench/_probe_keys.fl +5 -0
- package/self/bench/_probe_length.fl +5 -0
- package/self/bench/_probe_lexparse.fl +4 -0
- package/self/bench/_probe_loop.fl +12 -0
- package/self/bench/_probe_map.fl +8 -0
- package/self/bench/_probe_map2.fl +8 -0
- package/self/bench/_probe_map3.fl +5 -0
- package/self/bench/_probe_mapkeys.fl +5 -0
- package/self/bench/_probe_mapv12.fl +1 -0
- package/self/bench/_probe_mapv12.js +2 -0
- package/self/bench/_probe_match.fl +1 -0
- package/self/bench/_probe_match2.fl +1 -0
- package/self/bench/_probe_neg.fl +7 -0
- package/self/bench/_probe_neg2.fl +3 -0
- package/self/bench/_probe_protocol.fl +7 -0
- package/self/bench/_probe_self_parse.fl +6 -0
- package/self/bench/_probe_struct.fl +1 -0
- package/self/bench/_probe_true.fl +3 -0
- package/self/bench/_probe_true2.fl +6 -0
- package/self/bench/_probe_v12_cg.fl +7 -0
- package/self/bench/_probe_v12_extract.fl +8 -0
- package/self/bench/_probe_v12_extract.js +7 -0
- package/self/bench/_probe_v12_internal.fl +21 -0
- package/self/bench/_probe_v12_map_ast.fl +12 -0
- package/self/bench/_rs_combo.js +3 -0
- package/self/bench/_rs_gcd.js +36 -0
- package/self/bench/_rs_grade.js +2 -0
- package/self/bench/_rs_mutual-fib.js +2 -0
- package/self/bench/_rs_range-sum.js +2 -0
- package/self/bench/_rs_sum-10k.js +2 -0
- package/self/bench/_rs_sum-5k.js +2 -0
- package/self/bench/_sf_loop-fact-10.js +73 -0
- package/self/bench/_sf_loop-sum-10k.js +87 -0
- package/self/bench/_sf_set-x.js +40 -0
- package/self/bench/_sf_throw-catch.js +1 -0
- package/self/bench/_sf_while-5.js +62 -0
- package/self/bench/_v12_combined.fl +1184 -0
- package/self/bench/_v12_extract_test.fl +14 -0
- package/self/bench/_v12_extract_test.js +7 -0
- package/self/bench/_v12_inspect.js +26 -0
- package/self/bench/_v12_inspect_mod.js +137 -0
- package/self/bench/_v12_makevartest.fl +8 -0
- package/self/bench/_v12_makevartest.js +6 -0
- package/self/bench/_v12_parse_probe.fl +9 -0
- package/self/bench/_v12_parse_trace.fl +18 -0
- package/self/bench/_v12_simple_fn.fl +2 -0
- package/self/bench/_v12_simple_fn.js +3 -0
- package/self/bench/_v12_trace.fl +2 -0
- package/self/bench/_v12_trace.js +3 -0
- package/self/bench/collection.js +13 -0
- package/self/bench/fib30.fl +4 -0
- package/self/bench/final-test.fl +12 -0
- package/self/bench/final-test.js +2 -0
- package/self/bench/fp1-v12.js +4 -0
- package/self/bench/fp1.fl +3 -0
- package/self/bench/fp1.fp.js +4 -0
- package/self/bench/fp2-v12.js +4 -0
- package/self/bench/fp2.fl +3 -0
- package/self/bench/fp2.fp.js +4 -0
- package/self/bench/fp3-v12.js +8 -0
- package/self/bench/fp3.fl +11 -0
- package/self/bench/fp3.fp.js +8 -0
- package/self/bench/fp4-v12.js +6 -0
- package/self/bench/fp4.fl +9 -0
- package/self/bench/fp4.fp.js +6 -0
- package/self/bench/hello-sample.fl +3 -0
- package/self/bench/hello-sample.js +4 -0
- package/self/bench/hello.fl +1 -0
- package/self/bench/json.js +9 -0
- package/self/bench/json1mb.fl +3 -0
- package/self/bench/math.js +8 -0
- package/self/bench/realworld.fl +17 -0
- package/self/bench/realworld.js +8 -0
- package/self/bench/regex.js +10 -0
- package/self/bench/sample.json +1 -0
- package/self/bench/string.js +7 -0
- package/self/bench/test-ffi.fl +3 -0
- package/self/bench/test-ffi.js +2 -0
- package/self/bench/test-hof-advanced.fl +7 -0
- package/self/bench/test-hof-advanced.js +2 -0
- package/self/bench/test-hof.fl +5 -0
- package/self/bench/test-hof.js +2 -0
- package/self/bench/test-output.txt +1 -0
- package/self/bench/test-phase15-integration.fl +16 -0
- package/self/bench/test-phase15-integration.js +3 -0
- package/self/bench/test-phase19-complete.fl +24 -0
- package/self/bench/test-phase19-complete.js +2 -0
- package/self/bench/test-stdlib-codegen.fl +9 -0
- package/self/bench/test-stdlib-codegen.js +2 -0
- package/self/bench/test-time.fl +8 -0
- package/self/bench/test-time.js +2 -0
- package/self/bench/time.js +9 -0
- package/self/bench/tiny-v12.js +2 -0
- package/self/bench/tiny.fl +1 -0
- package/self/builtins/core.fl +150 -0
- package/self/char-class.fl +45 -0
- package/self/codegen-core.fl +733 -0
- package/self/codegen-final.fl +135 -0
- package/self/codegen.fl +379 -0
- package/self/codegen.fl.bak +856 -0
- package/self/codegen.self.js +75 -0
- package/self/examples/agent-loop.fl +32 -0
- package/self/examples/ci-cd-example.fl +114 -0
- package/self/examples/cloud-real.fl +134 -0
- package/self/examples/docker-example.fl +67 -0
- package/self/examples/fullstack-app.fl +330 -0
- package/self/examples/k8s-example.fl +91 -0
- package/self/examples/microservices.fl +448 -0
- package/self/examples/rest-api.fl +253 -0
- package/self/examples/style-example.fl +133 -0
- package/self/examples/workflow-orchestration.fl +44 -0
- package/self/fixtures/eval/add.fl +1 -0
- package/self/fixtures/eval/and-sc.fl +1 -0
- package/self/fixtures/eval/bool-false.fl +1 -0
- package/self/fixtures/eval/bool-true.fl +1 -0
- package/self/fixtures/eval/closure.fl +1 -0
- package/self/fixtures/eval/cmp-eq.fl +1 -0
- package/self/fixtures/eval/cmp-lt.fl +1 -0
- package/self/fixtures/eval/cond-first.fl +1 -0
- package/self/fixtures/eval/define-fn3.fl +1 -0
- package/self/fixtures/eval/define-var.fl +1 -0
- package/self/fixtures/eval/defn-bare.fl +1 -0
- package/self/fixtures/eval/defn-call.fl +1 -0
- package/self/fixtures/eval/div.fl +1 -0
- package/self/fixtures/eval/filter-hof.fl +1 -0
- package/self/fixtures/eval/floor.fl +1 -0
- package/self/fixtures/eval/fn-apply.fl +1 -0
- package/self/fixtures/eval/get-idx.fl +1 -0
- package/self/fixtures/eval/get-key.fl +1 -0
- package/self/fixtures/eval/higher-order.fl +1 -0
- package/self/fixtures/eval/let-bare.fl +1 -0
- package/self/fixtures/eval/let-flat.fl +1 -0
- package/self/fixtures/eval/let-use.fl +1 -0
- package/self/fixtures/eval/list-first.fl +1 -0
- package/self/fixtures/eval/list-last.fl +1 -0
- package/self/fixtures/eval/list-len.fl +1 -0
- package/self/fixtures/eval/map-hof.fl +1 -0
- package/self/fixtures/eval/mod.fl +1 -0
- package/self/fixtures/eval/mul-nested.fl +1 -0
- package/self/fixtures/eval/not.fl +1 -0
- package/self/fixtures/eval/null-p.fl +1 -0
- package/self/fixtures/eval/or-sc.fl +1 -0
- package/self/fixtures/eval/pow.fl +1 -0
- package/self/fixtures/eval/recursion.fl +1 -0
- package/self/fixtures/eval/reduce-hof.fl +1 -0
- package/self/fixtures/eval/replace.fl +1 -0
- package/self/fixtures/eval/sqrt.fl +1 -0
- package/self/fixtures/eval/str-concat.fl +1 -0
- package/self/fixtures/eval/str-num.fl +1 -0
- package/self/fixtures/eval/sub.fl +1 -0
- package/self/fixtures/eval/substring.fl +1 -0
- package/self/fixtures/lex/array.fl +1 -0
- package/self/fixtures/lex/at-atom.fl +1 -0
- package/self/fixtures/lex/block.fl +1 -0
- package/self/fixtures/lex/comment.fl +2 -0
- package/self/fixtures/lex/deep-array.fl +1 -0
- package/self/fixtures/lex/deep-map.fl +1 -0
- package/self/fixtures/lex/deep-nest.fl +1 -0
- package/self/fixtures/lex/dollar-dot.fl +1 -0
- package/self/fixtures/lex/empty-list.fl +1 -0
- package/self/fixtures/lex/empty-str.fl +1 -0
- package/self/fixtures/lex/empty.fl +1 -0
- package/self/fixtures/lex/float.fl +1 -0
- package/self/fixtures/lex/hex.fl +1 -0
- package/self/fixtures/lex/kebab-sym.fl +1 -0
- package/self/fixtures/lex/keyword.fl +1 -0
- package/self/fixtures/lex/leading-zero.fl +1 -0
- package/self/fixtures/lex/long-num.fl +1 -0
- package/self/fixtures/lex/many-args.fl +1 -0
- package/self/fixtures/lex/map.fl +1 -0
- package/self/fixtures/lex/mixed.fl +1 -0
- package/self/fixtures/lex/neg-float.fl +1 -0
- package/self/fixtures/lex/negative.fl +1 -0
- package/self/fixtures/lex/nested.fl +1 -0
- package/self/fixtures/lex/null.fl +1 -0
- package/self/fixtures/lex/pipe-op.fl +1 -0
- package/self/fixtures/lex/route-like.fl +1 -0
- package/self/fixtures/lex/scientific.fl +1 -0
- package/self/fixtures/lex/single-num.fl +1 -0
- package/self/fixtures/lex/small-sexpr.fl +1 -0
- package/self/fixtures/lex/spaces.fl +1 -0
- package/self/fixtures/lex/str-quote.fl +1 -0
- package/self/fixtures/lex/str-tab.fl +1 -0
- package/self/fixtures/lex/string-esc.fl +1 -0
- package/self/fixtures/lex/string.fl +1 -0
- package/self/fixtures/lex/symbol.fl +1 -0
- package/self/fixtures/lex/tabs-nl.fl +3 -0
- package/self/fixtures/lex/true-false.fl +1 -0
- package/self/fixtures/lex/unicode.fl +1 -0
- package/self/fixtures/lex/variable.fl +1 -0
- package/self/fixtures/lex/zero.fl +1 -0
- package/self/fixtures/parse/and-or.fl +1 -0
- package/self/fixtures/parse/array-lit.fl +1 -0
- package/self/fixtures/parse/block-func.fl +1 -0
- package/self/fixtures/parse/compose.fl +1 -0
- package/self/fixtures/parse/cond.fl +1 -0
- package/self/fixtures/parse/define-fn.fl +1 -0
- package/self/fixtures/parse/define-val.fl +1 -0
- package/self/fixtures/parse/defn.fl +1 -0
- package/self/fixtures/parse/do-begin.fl +1 -0
- package/self/fixtures/parse/dotted.fl +1 -0
- package/self/fixtures/parse/empty-fn.fl +1 -0
- package/self/fixtures/parse/empty-list.fl +1 -0
- package/self/fixtures/parse/fn.fl +1 -0
- package/self/fixtures/parse/if-else.fl +1 -0
- package/self/fixtures/parse/if-only.fl +1 -0
- package/self/fixtures/parse/keyword-arg.fl +1 -0
- package/self/fixtures/parse/kw.fl +1 -0
- package/self/fixtures/parse/let-1d.fl +1 -0
- package/self/fixtures/parse/let-2d.fl +1 -0
- package/self/fixtures/parse/let-bare.fl +1 -0
- package/self/fixtures/parse/literal-num.fl +1 -0
- package/self/fixtures/parse/literal-str.fl +1 -0
- package/self/fixtures/parse/loop-recur.fl +1 -0
- package/self/fixtures/parse/map-lit.fl +1 -0
- package/self/fixtures/parse/match-simple.fl +1 -0
- package/self/fixtures/parse/multi-body.fl +1 -0
- package/self/fixtures/parse/nested-arr.fl +1 -0
- package/self/fixtures/parse/nested-block.fl +1 -0
- package/self/fixtures/parse/nested-map.fl +1 -0
- package/self/fixtures/parse/nested.fl +1 -0
- package/self/fixtures/parse/pipe.fl +1 -0
- package/self/fixtures/parse/quote.fl +1 -0
- package/self/fixtures/parse/set-bang.fl +1 -0
- package/self/fixtures/parse/sexpr.fl +1 -0
- package/self/fixtures/parse/str-interp.fl +1 -0
- package/self/fixtures/parse/sym.fl +1 -0
- package/self/fixtures/parse/thread-first.fl +1 -0
- package/self/fixtures/parse/thread-last.fl +1 -0
- package/self/fixtures/parse/try-catch.fl +1 -0
- package/self/fixtures/parse/var.fl +1 -0
- package/self/full-compiler-fixed.fl +1308 -0
- package/self/full-compiler-v1-bloated.fl +1298 -0
- package/self/full-compiler.fl +1301 -0
- package/self/interpreter.fl +351 -0
- package/self/lexer.fl +200 -0
- package/self/lexer.self.js +30 -0
- package/self/main.fl +22 -0
- package/self/parser.fl +241 -0
- package/self/parser.self.js +33 -0
- package/self/runtime/http-server.js +642 -0
- package/self/runtime/interpreter.js +30787 -0
- package/self/runtime/repl.js +186 -0
- package/self/runtime-helpers.ts +282 -0
- package/self/scope.fl +80 -0
- package/self/scope.self.js +1 -0
- package/self/self/bench/_bi_list-distinct.js +2 -0
- package/self/self/bench/_bi_list-filter-even.js +2 -0
- package/self/self/bench/_bi_list-first.js +2 -0
- package/self/self/bench/_bi_list-last.js +2 -0
- package/self/self/bench/_bi_list-map-inc.js +2 -0
- package/self/self/bench/_bi_list-range.js +2 -0
- package/self/self/bench/_bi_list-reduce-sum.js +2 -0
- package/self/self/bench/_bi_list-reverse.js +2 -0
- package/self/self/bench/_bi_list-sort.js +2 -0
- package/self/self/bench/_bi_list-take.js +2 -0
- package/self/self/bench/_bi_map-has.js +2 -0
- package/self/self/bench/_bi_map-keys.js +2 -0
- package/self/self/bench/_bi_map-values.js +2 -0
- package/self/self/bench/_bi_str-concat.js +2 -0
- package/self/self/bench/_bi_str-contains.js +2 -0
- package/self/self/bench/_bi_str-ends.js +2 -0
- package/self/self/bench/_bi_str-index.js +2 -0
- package/self/self/bench/_bi_str-lower.js +2 -0
- package/self/self/bench/_bi_str-repeat.js +2 -0
- package/self/self/bench/_bi_str-split.js +2 -0
- package/self/self/bench/_bi_str-starts.js +2 -0
- package/self/self/bench/_bi_str-trim.js +2 -0
- package/self/self/bench/_bi_str-upper.js +2 -0
- package/self/self/bench/_ext_abs-neg.js +2 -0
- package/self/self/bench/_ext_and.js +2 -0
- package/self/self/bench/_ext_cond-1.js +2 -0
- package/self/self/bench/_ext_cond-2.js +2 -0
- package/self/self/bench/_ext_do-seq.js +2 -0
- package/self/self/bench/_ext_fact20.js +3 -0
- package/self/self/bench/_ext_fib25.js +3 -0
- package/self/self/bench/_ext_length.js +2 -0
- package/self/self/bench/_ext_let-1d.js +2 -0
- package/self/self/bench/_ext_let-2d.js +2 -0
- package/self/self/bench/_ext_list-sum.js +3 -0
- package/self/self/bench/_ext_math.js +2 -0
- package/self/self/bench/_ext_or.js +2 -0
- package/self/self/bench/_ext_str.js +2 -0
- package/self/self/bench/_fact15.js +2 -0
- package/self/self/bench/_fb_fact15.js +2 -0
- package/self/self/bench/_fb_func-fact.js +2 -0
- package/self/self/bench/_fb_func-multi.js +2 -0
- package/self/self/bench/_fb_func-mutual.js +3 -0
- package/self/self/bench/_fb_func-simple.js +2 -0
- package/self/self/bench/_fib.js +2 -0
- package/self/self/bench/_fn_async-await-seq.js +2 -0
- package/self/self/bench/_fn_async-print.js +2 -0
- package/self/self/bench/_fn_call-form.js +2 -0
- package/self/self/bench/_fn_compose.js +2 -0
- package/self/self/bench/_fn_pipe.js +2 -0
- package/self/self/bench/_fn_thread-first-sexpr.js +2 -0
- package/self/self/bench/_fn_thread-first.js +2 -0
- package/self/self/bench/_fn_thread-last.js +2 -0
- package/self/self/bench/_generated.js +2 -0
- package/self/self/bench/_mt_match-default.js +2 -0
- package/self/self/bench/_mt_match-num.js +2 -0
- package/self/self/bench/_mt_match-str.js +2 -0
- package/self/self/bench/_mt_match-var.js +2 -0
- package/self/self/bench/_mt_match-wild.js +2 -0
- package/self/self/bench/_mt_struct-make.js +4 -0
- package/self/self/bench/_mt_struct-tag.js +4 -0
- package/self/self/bench/_rs_combo.js +3 -0
- package/self/self/bench/_rs_gcd.js +2 -0
- package/self/self/bench/_rs_grade.js +2 -0
- package/self/self/bench/_rs_mutual-fib.js +2 -0
- package/self/self/bench/_rs_range-sum.js +2 -0
- package/self/self/bench/_rs_sum-5k.js +2 -0
- package/self/self/bench/_sf_loop-fact-10.js +1 -0
- package/self/self/bench/_sf_loop-sum-10k.js +1 -0
- package/self/self/bench/_sf_set-x.js +3 -0
- package/self/self/bench/_sf_throw-catch.js +1 -0
- package/self/self/bench/_sf_while-5.js +3 -0
- package/self/stdlib/ai.fl +73 -0
- package/self/stdlib/assert.fl +30 -0
- package/self/stdlib/async.fl +48 -0
- package/self/stdlib/base64.fl +22 -0
- package/self/stdlib/binary.fl +30 -0
- package/self/stdlib/build.fl +69 -0
- package/self/stdlib/collection.fl +37 -0
- package/self/stdlib/color.fl +35 -0
- package/self/stdlib/crypto-hash.fl +29 -0
- package/self/stdlib/crypto.fl +56 -0
- package/self/stdlib/data.fl +69 -0
- package/self/stdlib/db.fl +53 -0
- package/self/stdlib/encoding.fl +49 -0
- package/self/stdlib/error.fl +36 -0
- package/self/stdlib/feed.fl +86 -0
- package/self/stdlib/file.fl +53 -0
- package/self/stdlib/format.fl +34 -0
- package/self/stdlib/graph.fl +72 -0
- package/self/stdlib/hash.fl +32 -0
- package/self/stdlib/heap.fl +76 -0
- package/self/stdlib/http.fl +47 -0
- package/self/stdlib/image.fl +45 -0
- package/self/stdlib/json.fl +11 -0
- package/self/stdlib/list-extra.fl +32 -0
- package/self/stdlib/markdown.fl +59 -0
- package/self/stdlib/math-extra.fl +34 -0
- package/self/stdlib/math.fl +13 -0
- package/self/stdlib/metadata.fl +66 -0
- package/self/stdlib/mongodb/bson.fl +21 -0
- package/self/stdlib/mongodb/schema.fl +95 -0
- package/self/stdlib/mongodb/wire.fl +54 -0
- package/self/stdlib/mongodb.fl +172 -0
- package/self/stdlib/path.fl +72 -0
- package/self/stdlib/plot.fl +56 -0
- package/self/stdlib/process.fl +50 -0
- package/self/stdlib/queue.fl +65 -0
- package/self/stdlib/regex.fl +11 -0
- package/self/stdlib/resource.fl +61 -0
- package/self/stdlib/search.fl +54 -0
- package/self/stdlib/set-extra.fl +35 -0
- package/self/stdlib/sort.fl +61 -0
- package/self/stdlib/stack.fl +54 -0
- package/self/stdlib/stats.fl +110 -0
- package/self/stdlib/stream.fl +56 -0
- package/self/stdlib/string-extended.fl +25 -0
- package/self/stdlib/string-extra.fl +40 -0
- package/self/stdlib/string.fl +21 -0
- package/self/stdlib/test-helpers.fl +51 -0
- package/self/stdlib/test-runner.fl +76 -0
- package/self/stdlib/time-extra.fl +27 -0
- package/self/stdlib/time.fl +31 -0
- package/self/stdlib/tree.fl +57 -0
- package/self/stdlib/types.fl +85 -0
- package/self/stdlib/url.fl +33 -0
- package/self/stdlib/uuid.fl +28 -0
- package/self/stdlib/validation.fl +42 -0
- package/self/stdlib/vector-math.fl +52 -0
- package/self/stdlib/ws.fl +52 -0
- package/self/tests/mongodb-phase3.fl +75 -0
- package/self/tests/mongodb-wire-phase2.fl +45 -0
- package/self/tests/test-ast.fl +36 -0
- package/self/tests/test-builtins.fl +340 -0
- package/self/tests/test-char-class.fl +29 -0
- package/self/tests/test-codegen-builtins.fl +240 -0
- package/self/tests/test-codegen-ext.fl +380 -0
- package/self/tests/test-codegen-ffi.fl +295 -0
- package/self/tests/test-codegen-fn.fl +177 -0
- package/self/tests/test-codegen-match.fl +161 -0
- package/self/tests/test-codegen-run.fl +252 -0
- package/self/tests/test-codegen-sf.fl +262 -0
- package/self/tests/test-codegen.fl +260 -0
- package/self/tests/test-core-fl.fl +63 -0
- package/self/tests/test-forward-decl.fl +5 -0
- package/self/tests/test-func-block.fl +4 -0
- package/self/tests/test-interp-user-fn.fl +296 -0
- package/self/tests/test-interp.fl +225 -0
- package/self/tests/test-lexer.fl +162 -0
- package/self/tests/test-list-debug.fl +21 -0
- package/self/tests/test-mutual-rec.fl +6 -0
- package/self/tests/test-parser-debug.fl +35 -0
- package/self/tests/test-parser-full-debug.fl +66 -0
- package/self/tests/test-parser-lex-debug.fl +107 -0
- package/self/tests/test-parser-lex-only.fl +40 -0
- package/self/tests/test-parser-simple-debug.fl +35 -0
- package/self/tests/test-parser.fl +262 -0
- package/self/tests/test-real-stdlib.fl +377 -0
- package/self/tests/test-scope.fl +74 -0
- package/self/tests/test-selfcompile.fl +325 -0
- package/self/tests/test-stdlib.fl +44 -0
- package/self/tests/test-tco.fl +16 -0
- package/self/tests/test-token.fl +4 -0
- package/self/token.fl +38 -0
- package/self/token.self.js +8 -0
- package/self/v12-driver.fl +16 -0
- package/self/verify.fl +39 -0
- package/self-evolve/1-observe-ast.fl +28 -0
- package/self-evolve/2-inspect-block.fl +35 -0
- package/self-evolve/3-full-ast-json.fl +10 -0
- package/self-evolve/4-trace-block-fields.fl +23 -0
- package/self-evolve/5-analyze-do-ast.fl +29 -0
- package/self-evolve/6-test-do-elim-simple.fl +14 -0
- package/self-evolve/debug-evaluate.fl +24 -0
- package/self-evolve/debug-gen1-const-fold.fl +57 -0
- package/self-evolve/debug-map.fl +15 -0
- package/self-evolve/debug-optimize-simple.fl +95 -0
- package/self-evolve/debug-recursive-map.fl +23 -0
- package/self-evolve/logs/gen-0.fl +1658 -0
- package/self-evolve/logs/gen-1.fl +1658 -0
- package/self-evolve/logs/gen-2.fl +1658 -0
- package/self-evolve/logs/speed-gen-0.fl +1423 -0
- package/self-evolve/run-semantic-test.js +130 -0
- package/self-evolve/test-basic.fl +19 -0
- package/self-evolve/test-fold-direct.fl +60 -0
- package/self-evolve/test-gen1-simple.fl +67 -0
- package/self-evolve/test-optimize-debug.fl +63 -0
- package/self-evolve/tmp-test.fl +1 -0
- package/self-evolve/v11-analyzer.fl +40 -0
- package/self-evolve/v11-benchmark.fl +45 -0
- package/self-evolve/v11-evolution-real.fl +45 -0
- package/self-evolve/v11-evolution.fl +32 -0
- package/self-evolve/v11-gen1-validate.fl +157 -0
- package/self-evolve/v11-optimizer-constant-fold.fl +124 -0
- package/self-evolve/v11-optimizer-dead-expr.fl +111 -0
- package/self-evolve/v11-optimizer-gen1-complete.fl +180 -0
- package/self-evolve/v11-optimizer-gen1.fl +68 -0
- package/self-evolve/v11-optimizer.fl +56 -0
- package/self-evolve/v11-semantic-test.fl +54 -0
- package/self-evolve/v11-speed-benchmark.fl +106 -0
- package/self-evolve/v11-speed-optimizer.fl +79 -0
- package/src/AGENT-DSL.md +527 -0
- package/src/CLI-DEPLOYMENT.md +204 -0
- package/src/CLI.md +361 -0
- package/src/EXPRESS-ADVANCED.md +294 -0
- package/src/EXPRESS-AUTH.md +365 -0
- package/src/EXPRESS-CACHE.md +409 -0
- package/src/EXPRESS-COMPLETE.md +569 -0
- package/src/EXPRESS-README.md +121 -0
- package/src/EXPRESS-TEST.md +492 -0
- package/src/EXPRESS-WEBSOCKET.md +391 -0
- package/src/LOGGING-GUIDE.md +354 -0
- package/src/STORAGE-GUIDE.md +416 -0
- package/src/__tests__/advanced.test.ts +573 -0
- package/src/__tests__/ai-library.test.ts +210 -0
- package/src/__tests__/build-determinism.test.ts +33 -0
- package/src/__tests__/builtins-advanced.test.ts +150 -0
- package/src/__tests__/builtins-sanity.test.ts +180 -0
- package/src/__tests__/codegen.let.test.ts +81 -0
- package/src/__tests__/core.test.ts +188 -0
- package/src/__tests__/coverage-boost.test.ts +626 -0
- package/src/__tests__/cron-scheduler.test.ts +269 -0
- package/src/__tests__/enterprise-blocks.test.ts +63 -0
- package/src/__tests__/errors.test.ts +313 -0
- package/src/__tests__/integration.test.ts +219 -0
- package/src/__tests__/interpreter.test.ts +170 -0
- package/src/__tests__/l2-proof.test.ts +78 -0
- package/src/__tests__/lexer-parser.test.ts +366 -0
- package/src/__tests__/lexer.test.ts +106 -0
- package/src/__tests__/mariadb-prepared-statement.test.ts +206 -0
- package/src/__tests__/migrate.test.ts +403 -0
- package/src/__tests__/mongodb-integration.test.ts.skip +207 -0
- package/src/__tests__/mongodb-phase4.test.ts +132 -0
- package/src/__tests__/p1-1-parallel-tasks.test.ts +179 -0
- package/src/__tests__/p1-2-compensation.test.ts +242 -0
- package/src/__tests__/p1-3-distributed.test.ts +160 -0
- package/src/__tests__/p1-4-observability.test.ts +161 -0
- package/src/__tests__/parser.test.ts +142 -0
- package/src/__tests__/phase151-self-evolve.test.ts +161 -0
- package/src/__tests__/rate-limiter.test.ts +274 -0
- package/src/__tests__/self-hosting.test.ts +174 -0
- package/src/__tests__/semantic-preservation.test.ts +207 -0
- package/src/__tests__/setup.ts +34 -0
- package/src/__tests__/special-forms-advanced.test.ts +106 -0
- package/src/__tests__/stdlib-crypto-rsa.test.ts +122 -0
- package/src/__tests__/stdlib-f4.test.ts +159 -0
- package/src/__tests__/stdlib-helpers.test.ts +128 -0
- package/src/__tests__/stdlib-modules.test.ts +161 -0
- package/src/__tests__/stdlib-mongodb.test.ts +331 -0
- package/src/__tests__/stdlib-new.test.ts +269 -0
- package/src/__tests__/stdlib-perf.test.ts +125 -0
- package/src/__tests__/stdlib-phase-b.test.ts +249 -0
- package/src/__tests__/stdlib.test.ts +163 -0
- package/src/__tests__/v12-alpha.test.ts +205 -0
- package/src/__tests__/vm-optin.test.ts +141 -0
- package/src/__tests__/web-integration.test.ts +1452 -0
- package/src/__tests__/weblibs.test.ts +210 -0
- package/src/_aliases.json +1123 -0
- package/src/_mongodb_helper.js +272 -0
- package/src/_stdlib-signatures.json +1 -0
- package/src/agent-chain.ts +71 -0
- package/src/agent-dsl.fl +168 -0
- package/src/agent-example-error-handling.fl +51 -0
- package/src/agent-example-sequential.fl +66 -0
- package/src/agent-example-state-tracking.fl +70 -0
- package/src/agent.ts +393 -0
- package/src/align.ts +349 -0
- package/src/analogy.ts +78 -0
- package/src/ast-helpers.ts +199 -0
- package/src/ast.ts +817 -0
- package/src/async-runtime.ts +263 -0
- package/src/belief.ts +86 -0
- package/src/benchmark-self.ts +275 -0
- package/src/benchmarks/bench-interpreter.ts +73 -0
- package/src/benchmarks/bench-runner.ts +85 -0
- package/src/benchmarks/bench-vm.ts +128 -0
- package/src/browser-debug-panel.ts +220 -0
- package/src/browser-entry.ts +79 -0
- package/src/browser-stubs/child-process-stubs.ts +6 -0
- package/src/browser-stubs/crypto-stubs.ts +15 -0
- package/src/browser-stubs/misc-stubs.ts +4 -0
- package/src/browser-stubs/node-stubs.ts +17 -0
- package/src/browser-stubs/path-stubs.ts +6 -0
- package/src/bytecode.ts +41 -0
- package/src/causal.ts +258 -0
- package/src/chain-agents.ts +86 -0
- package/src/checkpoint.ts +106 -0
- package/src/ci-runner.ts +279 -0
- package/src/cli.ts +2136 -0
- package/src/codegen-js.ts +847 -0
- package/src/cognitive.ts +95 -0
- package/src/compete.ts +60 -0
- package/src/compiler.ts +267 -0
- package/src/compose-reason.ts +118 -0
- package/src/consensus.ts +109 -0
- package/src/context-window.ts +139 -0
- package/src/cot.ts +241 -0
- package/src/counterfactual.ts +268 -0
- package/src/critique.ts +77 -0
- package/src/crossover.ts +218 -0
- package/src/curiosity.ts +305 -0
- package/src/debate.ts +64 -0
- package/src/debug-api.ts +92 -0
- package/src/debugger.ts +185 -0
- package/src/delegate.ts +78 -0
- package/src/doc-extractor.ts +255 -0
- package/src/doc-renderer.ts +131 -0
- package/src/echo-server-demo.fl +17 -0
- package/src/error-formatter.ts +369 -0
- package/src/error-system.ts +88 -0
- package/src/errors.ts +281 -0
- package/src/ethics-check.ts +408 -0
- package/src/eval-ai-blocks.ts +182 -0
- package/src/eval-ai-handlers.ts +132 -0
- package/src/eval-builtins-ai.ts +1659 -0
- package/src/eval-builtins.ts +5956 -0
- package/src/eval-call-function.ts +665 -0
- package/src/eval-infra-blocks.ts +450 -0
- package/src/eval-module-system.ts +286 -0
- package/src/eval-pattern-match.ts +289 -0
- package/src/eval-phase150.ts +87 -0
- package/src/eval-reasoning-sequence.ts +218 -0
- package/src/eval-special-forms.ts +2154 -0
- package/src/eval-style-blocks.ts +193 -0
- package/src/eval-type-classes.ts +148 -0
- package/src/evolve.ts +276 -0
- package/src/explain.ts +345 -0
- package/src/express-advanced.fl +78 -0
- package/src/express-auth.fl +118 -0
- package/src/express-cache.fl +118 -0
- package/src/express-chat.fl +81 -0
- package/src/express-example.fl +67 -0
- package/src/express-test.fl +331 -0
- package/src/express.fl +92 -0
- package/src/fitness.ts +238 -0
- package/src/fl-app-demo.fl +19 -0
- package/src/fl-files/fl-fmt.fl +29 -0
- package/src/fl-files/fl-lint.fl +61 -0
- package/src/fl-files/fl-test.fl +52 -0
- package/src/fl-http-demo.fl +19 -0
- package/src/fl-list-utils.fl +108 -0
- package/src/fl-math-lib.fl +14 -0
- package/src/fl-sdk.ts +154 -0
- package/src/fl-server-demo.fl +74 -0
- package/src/fl-str-utils.fl +29 -0
- package/src/fl-tutor.ts +126 -0
- package/src/formatter.ts +581 -0
- package/src/freelang-codegen.fl +904 -0
- package/src/freelang-interpreter.fl +483 -0
- package/src/freelang-lexer.fl +337 -0
- package/src/freelang-parser.fl +349 -0
- package/src/freelang-stdlib.fl +246 -0
- package/src/freelang-typechecker.fl +422 -0
- package/src/freelang-v9-complete.ts +474 -0
- package/src/generation.ts +201 -0
- package/src/gpt-mini-p3.fl +316 -0
- package/src/hot-reload.ts +235 -0
- package/src/http-server-runner.ts +89 -0
- package/src/hypothesis.ts +62 -0
- package/src/immutable.ts +140 -0
- package/src/interpreter-context.ts +87 -0
- package/src/interpreter-scope.ts +140 -0
- package/src/interpreter.ts +2351 -0
- package/src/lazy-seq.ts +134 -0
- package/src/learned-facts-store.ts +306 -0
- package/src/lexer.ts +359 -0
- package/src/lint-rules.ts +584 -0
- package/src/linter.ts +237 -0
- package/src/logger.ts +128 -0
- package/src/logging-deterministic.fl +138 -0
- package/src/lsp-server.ts +379 -0
- package/src/macro-expander.ts +195 -0
- package/src/maybe-chain.ts +108 -0
- package/src/maybe-type.ts +163 -0
- package/src/memory-system.ts +142 -0
- package/src/meta-reason.ts +93 -0
- package/src/multi-agent-hub.ts +247 -0
- package/src/multi-agent.ts +101 -0
- package/src/mutate.ts +226 -0
- package/src/negotiate.ts +55 -0
- package/src/optimizer.ts +277 -0
- package/src/orchestrate.ts +154 -0
- package/src/package-manager.ts +375 -0
- package/src/parser.ts +2722 -0
- package/src/peer-review.ts +53 -0
- package/src/predict.ts +365 -0
- package/src/profiler.ts +150 -0
- package/src/prompt-compiler.ts +123 -0
- package/src/protocol.ts +114 -0
- package/src/prune.ts +195 -0
- package/src/quality-loop.ts +105 -0
- package/src/rag.ts +81 -0
- package/src/reasoning-debugger.ts +122 -0
- package/src/refactor-self.ts +362 -0
- package/src/reflect.ts +186 -0
- package/src/repl.ts +323 -0
- package/src/result-type.ts +126 -0
- package/src/return-signal.ts +10 -0
- package/src/runtime-entry.ts +8 -0
- package/src/runtime-helpers.ts +234 -0
- package/src/self-evolution-hub.ts +431 -0
- package/src/self-improve.ts +107 -0
- package/src/source-map.ts +114 -0
- package/src/stdlib-agent.js +164 -0
- package/src/stdlib-agent.ts +225 -0
- package/src/stdlib-ai-native.ts +176 -0
- package/src/stdlib-ai-workflow.ts +308 -0
- package/src/stdlib-ai.ts +180 -0
- package/src/stdlib-async.ts +179 -0
- package/src/stdlib-audit.ts +94 -0
- package/src/stdlib-auth.ts +196 -0
- package/src/stdlib-bits.ts +86 -0
- package/src/stdlib-blog.ts +127 -0
- package/src/stdlib-browser.ts +239 -0
- package/src/stdlib-cache.ts +147 -0
- package/src/stdlib-capture-error.ts +183 -0
- package/src/stdlib-channel.ts +96 -0
- package/src/stdlib-checkpoint.js +109 -0
- package/src/stdlib-checkpoint.ts +97 -0
- package/src/stdlib-cloud.ts +317 -0
- package/src/stdlib-collection.ts +227 -0
- package/src/stdlib-compile.ts +111 -0
- package/src/stdlib-cron.ts +219 -0
- package/src/stdlib-crypto-rsa.ts +82 -0
- package/src/stdlib-crypto.js +203 -0
- package/src/stdlib-crypto.ts +208 -0
- package/src/stdlib-data.ts +614 -0
- package/src/stdlib-db-query.ts +198 -0
- package/src/stdlib-db.ts +185 -0
- package/src/stdlib-distributed.ts +292 -0
- package/src/stdlib-error.ts +90 -0
- package/src/stdlib-fd.ts +130 -0
- package/src/stdlib-feed.ts +171 -0
- package/src/stdlib-file.ts +182 -0
- package/src/stdlib-helpers.ts +273 -0
- package/src/stdlib-http-macro.ts +178 -0
- package/src/stdlib-http-server.ts +1229 -0
- package/src/stdlib-http.ts +405 -0
- package/src/stdlib-image.ts +92 -0
- package/src/stdlib-kebab-aliases.ts +131 -0
- package/src/stdlib-lazy-registry.ts +106 -0
- package/src/stdlib-loader.ts +810 -0
- package/src/stdlib-mail.ts +251 -0
- package/src/stdlib-mariadb.ts +467 -0
- package/src/stdlib-markdown.ts +227 -0
- package/src/stdlib-matrix.ts +170 -0
- package/src/stdlib-middleware.ts +221 -0
- package/src/stdlib-module.ts +178 -0
- package/src/stdlib-mongodb.ts +174 -0
- package/src/stdlib-oci.ts +321 -0
- package/src/stdlib-optional.ts +56 -0
- package/src/stdlib-orm.ts +241 -0
- package/src/stdlib-perf.ts +140 -0
- package/src/stdlib-pg.ts +181 -0
- package/src/stdlib-plot.ts +196 -0
- package/src/stdlib-process.ts +120 -0
- package/src/stdlib-property.ts +157 -0
- package/src/stdlib-pubsub.ts +93 -0
- package/src/stdlib-queue-helpers.ts +92 -0
- package/src/stdlib-registry.ts +78 -0
- package/src/stdlib-resource.ts +553 -0
- package/src/stdlib-rest-crud.ts +146 -0
- package/src/stdlib-service.ts +206 -0
- package/src/stdlib-shell.ts +76 -0
- package/src/stdlib-stats.ts +172 -0
- package/src/stdlib-table.ts +200 -0
- package/src/stdlib-test-enhanced.ts +76 -0
- package/src/stdlib-test.ts +153 -0
- package/src/stdlib-time.js +217 -0
- package/src/stdlib-time.ts +282 -0
- package/src/stdlib-timer.ts +134 -0
- package/src/stdlib-totp.ts +110 -0
- package/src/stdlib-type-predicates.ts +136 -0
- package/src/stdlib-types.ts +107 -0
- package/src/stdlib-validation.ts +248 -0
- package/src/stdlib-verify.ts +181 -0
- package/src/stdlib-webauthn.ts +192 -0
- package/src/stdlib-workflow.js +715 -0
- package/src/stdlib-workflow.ts +950 -0
- package/src/stdlib-ws.ts +333 -0
- package/src/stdlib-wsc.test.ts +122 -0
- package/src/stdlib-wsc.ts +243 -0
- package/src/storage-unified.fl +279 -0
- package/src/streaming.ts +101 -0
- package/src/struct-system.ts +104 -0
- package/src/style-registry.ts +54 -0
- package/src/swarm.ts +89 -0
- package/src/tco.ts +31 -0
- package/src/test-advanced-patterns.ts +211 -0
- package/src/test-ast-debug.ts +20 -0
- package/src/test-ast-helpers.ts +208 -0
- package/src/test-async.ts +406 -0
- package/src/test-bootstrap-self-compile.ts +449 -0
- package/src/test-bootstrap-verification.ts +336 -0
- package/src/test-composition.ts +206 -0
- package/src/test-errors-phase6.ts +166 -0
- package/src/test-extended-monads.ts +313 -0
- package/src/test-field-parsing.ts +135 -0
- package/src/test-first-class-functions.ts +257 -0
- package/src/test-freelang-interpreter.ts +320 -0
- package/src/test-freelang-lexer.ts +306 -0
- package/src/test-freelang-parser.ts +268 -0
- package/src/test-fullstack-core.ts +258 -0
- package/src/test-fullstack-phase7-12.ts +305 -0
- package/src/test-fullstack-practical.ts +338 -0
- package/src/test-integration-stdlib.ts +195 -0
- package/src/test-interpreter-phase6.ts +305 -0
- package/src/test-lexer-comparison.ts +108 -0
- package/src/test-lexer-phase6.ts +271 -0
- package/src/test-modules.ts +325 -0
- package/src/test-monad-laws.ts +383 -0
- package/src/test-monads.ts +197 -0
- package/src/test-p0-checkpoint.ts +304 -0
- package/src/test-p0-conditional.ts +284 -0
- package/src/test-p0-error-handling.ts +231 -0
- package/src/test-p0-error-messages.ts +220 -0
- package/src/test-p1-1-parallel-tasks.js +214 -0
- package/src/test-parser-phase6.ts +222 -0
- package/src/test-performance.ts +206 -0
- package/src/test-phase10-data.ts +259 -0
- package/src/test-phase10-file.ts +216 -0
- package/src/test-phase100-stdlib-ai.ts +343 -0
- package/src/test-phase101-memory.ts +309 -0
- package/src/test-phase102-rag.ts +296 -0
- package/src/test-phase103-multi-agent.ts +418 -0
- package/src/test-phase104-try-reason.ts +459 -0
- package/src/test-phase105-streaming.ts +287 -0
- package/src/test-phase106-quality.ts +397 -0
- package/src/test-phase107-tutor.ts +305 -0
- package/src/test-phase108-debugger.ts +316 -0
- package/src/test-phase109-prompt-compiler.ts +333 -0
- package/src/test-phase11-12-complete.ts +275 -0
- package/src/test-phase11-error.ts +192 -0
- package/src/test-phase110-sdk.ts +320 -0
- package/src/test-phase111-hypothesis.ts +380 -0
- package/src/test-phase112-maybe-chain.ts +313 -0
- package/src/test-phase113-debate.ts +364 -0
- package/src/test-phase114-checkpoint.ts +348 -0
- package/src/test-phase115-meta-reason.ts +277 -0
- package/src/test-phase116-belief.ts +275 -0
- package/src/test-phase117-analogy.ts +325 -0
- package/src/test-phase118-critique.ts +308 -0
- package/src/test-phase119-compose.ts +434 -0
- package/src/test-phase12-http-shell.ts +120 -0
- package/src/test-phase120-cognitive.ts +297 -0
- package/src/test-phase121-consensus.ts +404 -0
- package/src/test-phase122-delegate.ts +411 -0
- package/src/test-phase123-vote.ts +339 -0
- package/src/test-phase124-negotiate.ts +403 -0
- package/src/test-phase125-swarm.ts +321 -0
- package/src/test-phase126-orchestrate.ts +343 -0
- package/src/test-phase127-peer-review.ts +279 -0
- package/src/test-phase128-chain-agents.ts +456 -0
- package/src/test-phase129-compete.ts +256 -0
- package/src/test-phase13-data.ts +223 -0
- package/src/test-phase130-hub.ts +390 -0
- package/src/test-phase131-evolve.ts +536 -0
- package/src/test-phase132-mutate.ts +268 -0
- package/src/test-phase133-crossover.ts +289 -0
- package/src/test-phase134-fitness.ts +306 -0
- package/src/test-phase135-generation.ts +328 -0
- package/src/test-phase136-prune.ts +228 -0
- package/src/test-phase137-refactor-self.ts +354 -0
- package/src/test-phase138-benchmark-self.ts +325 -0
- package/src/test-phase139-version-self.ts +278 -0
- package/src/test-phase14-collection.ts +254 -0
- package/src/test-phase140-self-evolution.ts +410 -0
- package/src/test-phase141-world-model.ts +387 -0
- package/src/test-phase142-causal.ts +384 -0
- package/src/test-phase143-counterfactual.ts +280 -0
- package/src/test-phase144-predict.ts +312 -0
- package/src/test-phase145-explain.ts +287 -0
- package/src/test-phase146-align.ts +439 -0
- package/src/test-phase147-ethics-check.ts +399 -0
- package/src/test-phase148-curiosity.ts +247 -0
- package/src/test-phase149-wisdom.ts +758 -0
- package/src/test-phase15-agent.ts +320 -0
- package/src/test-phase150-complete.ts +481 -0
- package/src/test-phase16-time.ts +292 -0
- package/src/test-phase17-crypto.ts +312 -0
- package/src/test-phase18-integration.ts +429 -0
- package/src/test-phase19-resource.ts +214 -0
- package/src/test-phase20-server-db.ts +160 -0
- package/src/test-phase21-ws-auth-cache-pubsub.ts +212 -0
- package/src/test-phase22-process.ts +166 -0
- package/src/test-phase23-selfhosting.ts +383 -0
- package/src/test-phase24-codegen.ts +236 -0
- package/src/test-phase25-bootstrap.ts +227 -0
- package/src/test-phase26-map-filter-reduce.ts +282 -0
- package/src/test-phase27-stdlib-codegen.ts +325 -0
- package/src/test-phase28-loop-recur.ts +182 -0
- package/src/test-phase29-import.ts +165 -0
- package/src/test-phase3-web-server.ts +234 -0
- package/src/test-phase30-selfcompile.ts +254 -0
- package/src/test-phase31-gen2-lexer.ts +251 -0
- package/src/test-phase33-gen3-bootstrap.ts +323 -0
- package/src/test-phase34-parser-tco.ts +268 -0
- package/src/test-phase35-error-messages.ts +280 -0
- package/src/test-phase36-sourcemap.ts +240 -0
- package/src/test-phase37-variadic.ts +228 -0
- package/src/test-phase38-destructuring.ts +261 -0
- package/src/test-phase39-do-block.ts +288 -0
- package/src/test-phase40-const-fold.ts +249 -0
- package/src/test-phase41-repl.ts +312 -0
- package/src/test-phase42-watch.ts +314 -0
- package/src/test-phase43-format.ts +377 -0
- package/src/test-phase44-check.ts +505 -0
- package/src/test-phase45-interpreter.ts +367 -0
- package/src/test-phase46-match.ts +390 -0
- package/src/test-phase47-file-io.ts +338 -0
- package/src/test-phase48-types.ts +308 -0
- package/src/test-phase49-stdlib.ts +365 -0
- package/src/test-phase5-week1.ts +160 -0
- package/src/test-phase50-fl-server.ts +159 -0
- package/src/test-phase51-transformer.ts +152 -0
- package/src/test-phase52-import.ts +161 -0
- package/src/test-phase53-training.ts +122 -0
- package/src/test-phase54-fl-utils.ts +122 -0
- package/src/test-phase55-http-client.ts +182 -0
- package/src/test-phase56-lexical-scope.ts +230 -0
- package/src/test-phase58-module-refactor.ts +298 -0
- package/src/test-phase59-errors.ts +226 -0
- package/src/test-phase6-compile.ts +227 -0
- package/src/test-phase60-types.ts +457 -0
- package/src/test-phase61-tco.ts +209 -0
- package/src/test-phase63-macros.ts +191 -0
- package/src/test-phase64-protocols.ts +451 -0
- package/src/test-phase65-patterns.ts +301 -0
- package/src/test-phase66-structs.ts +215 -0
- package/src/test-phase67-concurrency.ts +348 -0
- package/src/test-phase68-pipeline.ts +209 -0
- package/src/test-phase69-lazy.ts +237 -0
- package/src/test-phase7-registry.ts +236 -0
- package/src/test-phase70-immutable.ts +488 -0
- package/src/test-phase71-ai-native.ts +252 -0
- package/src/test-phase72-integration.ts +533 -0
- package/src/test-phase73-formatter.ts +361 -0
- package/src/test-phase74-linter.ts +565 -0
- package/src/test-phase75-repl.ts +227 -0
- package/src/test-phase76-testrunner.ts +439 -0
- package/src/test-phase77-doc.ts +416 -0
- package/src/test-phase78-debugger.ts +370 -0
- package/src/test-phase79-watch.ts +224 -0
- package/src/test-phase8-oci.ts +264 -0
- package/src/test-phase80-ci.ts +282 -0
- package/src/test-phase81-pkg.ts +383 -0
- package/src/test-phase82-profiler.ts +336 -0
- package/src/test-phase83-vm.ts +318 -0
- package/src/test-phase84-optimizer.ts +424 -0
- package/src/test-phase85-codegen.ts +380 -0
- package/src/test-phase86-lsp.ts +533 -0
- package/src/test-phase87-packages.ts +277 -0
- package/src/test-phase88-selfhost.ts +412 -0
- package/src/test-phase89-bench.ts +361 -0
- package/src/test-phase9-flnext-v2.ts +372 -0
- package/src/test-phase9-learn.ts +241 -0
- package/src/test-phase9-reasoning.ts +312 -0
- package/src/test-phase9-search.ts +212 -0
- package/src/test-phase90-release.ts +365 -0
- package/src/test-phase91-maybe.ts +257 -0
- package/src/test-phase92-cot.ts +438 -0
- package/src/test-phase93-tot.ts +462 -0
- package/src/test-phase94-reflect.ts +498 -0
- package/src/test-phase95-context.ts +268 -0
- package/src/test-phase96-errors.ts +296 -0
- package/src/test-phase97-tools.ts +344 -0
- package/src/test-phase98-agent.ts +370 -0
- package/src/test-phase99-self-improve.ts +394 -0
- package/src/test-phase9a-websearch.ts +283 -0
- package/src/test-phase9ab-integration.ts +140 -0
- package/src/test-phase9b-persistence.ts +448 -0
- package/src/test-phase9c-conditional.ts +251 -0
- package/src/test-phase9c-extension.ts +270 -0
- package/src/test-phase9c-feedback.ts +263 -0
- package/src/test-phase9c-loop.ts +239 -0
- package/src/test-selfhosting-sh1.ts +41 -0
- package/src/test-selfhosting-sh2.ts +84 -0
- package/src/test-selfhosting-sh3.ts +61 -0
- package/src/test-selfhosting-sh4.ts +65 -0
- package/src/test-type-classes-dispatch.ts +202 -0
- package/src/test-type-classes.ts +320 -0
- package/src/test-type-inference.ts +464 -0
- package/src/test-typeclass-parsing-final.ts +218 -0
- package/src/test-typeclass-parsing.ts +264 -0
- package/src/todo-server-30116.ts +53 -0
- package/src/token.ts +130 -0
- package/src/tool-registry.ts +195 -0
- package/src/tot.ts +258 -0
- package/src/try-reason.ts +109 -0
- package/src/type-check-static.ts +417 -0
- package/src/type-checker.ts +245 -0
- package/src/type-inference.ts +271 -0
- package/src/type-system.ts +169 -0
- package/src/version-self.ts +219 -0
- package/src/vm-eligible.ts +106 -0
- package/src/vm.ts +219 -0
- package/src/vote.ts +127 -0
- package/src/vpm/checksum-test.fl +193 -0
- package/src/vpm/checksum.fl +219 -0
- package/src/vpm/error-test.fl +211 -0
- package/src/vpm/error.fl +218 -0
- package/src/vpm/lock-file.fl +380 -0
- package/src/vpm/logging-test.fl +194 -0
- package/src/vpm/logging.fl +294 -0
- package/src/vpm/resolver-test.fl +117 -0
- package/src/vpm/resolver.fl +618 -0
- package/src/vpm/semver-test.fl +180 -0
- package/src/vpm/semver.fl +143 -0
- package/src/vpm-cli.ts +1955 -0
- package/src/web/app-router.ts +286 -0
- package/src/web/fl-executor.ts +655 -0
- package/src/web/image-optimizer.ts +206 -0
- package/src/web/index.ts +14 -0
- package/src/web/page-renderer.ts +287 -0
- package/src/web/server.ts +553 -0
- package/src/web-search-adapter.ts +348 -0
- package/src/wisdom.ts +441 -0
- package/src/world-model.ts +348 -0
- package/stdlib/cache.fl +42 -0
- package/stdlib/csv.fl +38 -0
- package/stdlib/date-ext.fl +68 -0
- package/stdlib/log.fl +44 -0
- package/stdlib/math-ext.fl +57 -0
- package/stdlib/number-ext.fl +47 -0
- package/stdlib/queue.fl +57 -0
- package/stdlib/string-ext.fl +45 -0
- package/stdlib/validate.fl +123 -0
- package/stdlib/web/components.fl +125 -0
- package/stdlib/web/csrf.fl +61 -0
- package/stdlib/web/format.fl +75 -0
- package/stdlib/web/forms.fl +94 -0
- package/stdlib/web/image.fl +77 -0
- package/stdlib/web/metadata.fl +85 -0
- package/stdlib/web/pagination.fl +62 -0
- package/stdlib/web/state.fl +167 -0
- package/stdlib/web/styles.fl +68 -0
- package/stdlib/web/toast.fl +62 -0
- package/stdlib/web/v9-stdlib-dom.fl +92 -0
- package/stdlib/web/v9-stdlib-fetch.fl +90 -0
- package/stdlib/web/v9-stdlib-storage.fl +70 -0
- package/stdlib/web/v9-stdlib-ui.fl +115 -0
- package/stdlib/web/ws-client.fl +125 -0
- package/tests/.v11-backup/test-migrate-sample.fl +22 -0
- package/tests/PHASE-C-VERIFICATION-REPORT.md +125 -0
- package/tests/benchmark-v11.1.js +103 -0
- package/tests/evidence/01-about.html +1 -0
- package/tests/evidence/02-post-hello.html +1 -0
- package/tests/evidence/02-post-world.html +1 -0
- package/tests/evidence/03-home.html +1 -0
- package/tests/evidence/04-dist/404.html +1 -0
- package/tests/evidence/04-dist/about/index.html +1 -0
- package/tests/evidence/04-dist/index.html +1 -0
- package/tests/evidence/04-dist/post/hello/index.html +1 -0
- package/tests/evidence/04-dist/post/world/index.html +1 -0
- package/tests/evidence/05-dist/404.html +1 -0
- package/tests/evidence/05-dist/about/index.html +1 -0
- package/tests/evidence/05-dist/index.html +1 -0
- package/tests/evidence/05-dist/post/hello/index.html +1 -0
- package/tests/evidence/05-dist/post/world/index.html +1 -0
- package/tests/evidence/06-dist/404.html +1 -0
- package/tests/evidence/06-dist/about/index.html +1 -0
- package/tests/evidence/06-dist/index.html +1 -0
- package/tests/evidence/06-dist/post/hello/index.html +1 -0
- package/tests/evidence/06-dist/post/world/index.html +1 -0
- package/tests/evidence/07-markdown.html +1 -0
- package/tests/evidence/08-rss.xml +25 -0
- package/tests/evidence/09-robots.txt +4 -0
- package/tests/evidence/09-sitemap.xml +12 -0
- package/tests/evidence/10-jsonld.html +1 -0
- package/tests/evidence/_results.txt +11 -0
- package/tests/evidence/about.html +1 -0
- package/tests/evidence/home.html +1 -0
- package/tests/evidence/missing.html +42 -0
- package/tests/evidence/post-hello.html +1 -0
- package/tests/evidence/post-world.html +1 -0
- package/tests/fixtures/basic-app/about/page.fl +1 -0
- package/tests/fixtures/basic-app/api/echo/route.fl +5 -0
- package/tests/fixtures/basic-app/layout.fl +1 -0
- package/tests/fixtures/basic-app/not-found.fl +1 -0
- package/tests/fixtures/basic-app/page.fl +1 -0
- package/tests/fixtures/basic-app/post/[slug]/generate-static-params.fl +1 -0
- package/tests/fixtures/basic-app/post/[slug]/page.fl +1 -0
- package/tests/fixtures/stdlib-probes/blog.fl +6 -0
- package/tests/fixtures/stdlib-probes/jsonld.fl +1 -0
- package/tests/fixtures/stdlib-probes/map-probe.fl +3 -0
- package/tests/fixtures/stdlib-probes/map-shape.fl +2 -0
- package/tests/fixtures/stdlib-probes/md.fl +1 -0
- package/tests/fixtures/stdlib-probes/robots.fl +1 -0
- package/tests/fixtures/stdlib-probes/rss.fl +4 -0
- package/tests/fixtures/stdlib-probes/sitemap.fl +1 -0
- package/tests/l2/case-01-arithmetic.fl +6 -0
- package/tests/l2/case-02-comparisons.fl +15 -0
- package/tests/l2/case-03-logic.fl +15 -0
- package/tests/l2/case-04-control-flow.fl +15 -0
- package/tests/l2/case-05-functions.fl +13 -0
- package/tests/l2/case-06-collections.fl +14 -0
- package/tests/l2/case-07-pattern-matching.fl +14 -0
- package/tests/l2/case-08-recursion.fl +19 -0
- package/tests/l2/case-09-strings.fl +13 -0
- package/tests/l2/case-10-loops.fl +17 -0
- package/tests/l2/case-11-higher-order.fl +13 -0
- package/tests/l2/case-12-edge-cases.fl +14 -0
- package/tests/l2/case-13-ai-vector.fl +22 -0
- package/tests/l2/case-14-ai-cosine.fl +25 -0
- package/tests/l2/case-15-ai-template.fl +22 -0
- package/tests/l2/case-16-ai-ranking.fl +20 -0
- package/tests/l2/case-17-stdlib-extended.fl +32 -0
- package/tests/l2-proof/01-arithmetic.bootstrap.js +141 -0
- package/tests/l2-proof/01-arithmetic.fl +15 -0
- package/tests/l2-proof/01-arithmetic.stage1.js +141 -0
- package/tests/l2-proof/02-comparisons.bootstrap.js +141 -0
- package/tests/l2-proof/02-comparisons.fl +17 -0
- package/tests/l2-proof/03-logic.bootstrap.js +141 -0
- package/tests/l2-proof/03-logic.fl +15 -0
- package/tests/l2-proof/04-control-flow.bootstrap.js +146 -0
- package/tests/l2-proof/04-control-flow.fl +24 -0
- package/tests/l2-proof/05-functions.bootstrap.js +143 -0
- package/tests/l2-proof/05-functions.fl +16 -0
- package/tests/l2-proof/06-collections.bootstrap.js +146 -0
- package/tests/l2-proof/06-collections.fl +27 -0
- package/tests/l2-proof/07-pattern-matching.bootstrap.js +142 -0
- package/tests/l2-proof/07-pattern-matching.fl +23 -0
- package/tests/l2-proof/08-async-errors.bootstrap.js +142 -0
- package/tests/l2-proof/08-async-errors.fl +17 -0
- package/tests/l2-proof/09-strings.bootstrap.js +141 -0
- package/tests/l2-proof/09-strings.fl +13 -0
- package/tests/l2-proof/10-type-checks.bootstrap.js +141 -0
- package/tests/l2-proof/10-type-checks.fl +17 -0
- package/tests/l2-proof/11-recursion.bootstrap.js +143 -0
- package/tests/l2-proof/11-recursion.fl +21 -0
- package/tests/l2-proof/12-edge-cases.bootstrap.js +141 -0
- package/tests/l2-proof/12-edge-cases.fl +17 -0
- package/tests/parity/01-app-router-static.sh +18 -0
- package/tests/parity/02-app-router-dynamic.sh +18 -0
- package/tests/parity/03-layout-children.sh +17 -0
- package/tests/parity/04-not-found.sh +22 -0
- package/tests/parity/05-ssg.sh +21 -0
- package/tests/parity/06-parallel-render.sh +21 -0
- package/tests/parity/07-markdown.sh +16 -0
- package/tests/parity/08-rss-atom.sh +18 -0
- package/tests/parity/09-sitemap-robots.sh +23 -0
- package/tests/parity/10-jsonld.sh +16 -0
- package/tests/parity/11-blog-helpers.sh +28 -0
- package/tests/parity/12-api-routes.sh +25 -0
- package/tests/parity/_lib.sh +53 -0
- package/tests/parity/run-all.sh +89 -0
- package/tests/parity/score.sh +20 -0
- package/tests/phase-c-fuzzing.fl +89 -0
- package/tests/phase-c-property-testing.fl +137 -0
- package/tests/phase-c-sha-verification.fl +117 -0
- package/tests/phase-c-validation.fl +194 -0
- package/tests/property-testing.sh +47 -0
- package/tests/regen/let-in-expr-01.fl +5 -0
- package/tests/regen/let-in-expr-02.fl +6 -0
- package/tests/regen/let-in-expr-03.fl +4 -0
- package/tests/regen/let-in-expr-04.fl +6 -0
- package/tests/regen/let-in-expr-05.fl +5 -0
- package/tests/test-ai-library.fl +36 -0
- package/tests/test-andor.fl +2 -0
- package/tests/test-bootstrap-match.fl +5 -0
- package/tests/test-cg-final.fl +5 -0
- package/tests/test-cli.fl +19 -0
- package/tests/test-codegen.fl +4 -0
- package/tests/test-cond-flat.fl +1 -0
- package/tests/test-fl-exec-op.fl +1 -0
- package/tests/test-fp.fl +10 -0
- package/tests/test-funcs.fl +11 -0
- package/tests/test-fuzz-crash.fl +3 -0
- package/tests/test-http-get.fl +3 -0
- package/tests/test-json-load.fl +32 -0
- package/tests/test-let-flat.fl +2 -0
- package/tests/test-let-order.fl +3 -0
- package/tests/test-let-simple.fl +3 -0
- package/tests/test-let-syntax.fl +2 -0
- package/tests/test-let.fl +3 -0
- package/tests/test-lex-debug.fl +75 -0
- package/tests/test-loop-bug.fl +5 -0
- package/tests/test-map-entries.fl +1 -0
- package/tests/test-map-pattern.fl +3 -0
- package/tests/test-match-syntax.fl +5 -0
- package/tests/test-migrate-sample.fl +22 -0
- package/tests/test-neg.fl +1 -0
- package/tests/test-nested-let.fl +4 -0
- package/tests/test-node-to-pattern.fl +8 -0
- package/tests/test-quant-lib.fl +114 -0
- package/tests/test-reduce.fl +10 -0
- package/tests/test-server.fl +3 -0
- package/tests/test-simple-map.fl +4 -0
- package/tests/test-simple-match.fl +3 -0
- package/tests/test-simple.fl +1 -0
- package/tests/test-try-bootstrap.fl +7 -0
- package/tests/test-try-catch.fl +3 -0
- package/tests/test-watchdog-lib.fl +69 -0
- package/tests/test-which-lex.fl +4 -0
- package/tsconfig.json +39 -0
|
@@ -0,0 +1,2351 @@
|
|
|
1
|
+
// FreeLang v9: Interpreter
|
|
2
|
+
// AST → 실행 (Express 서버 포함)
|
|
3
|
+
|
|
4
|
+
import * as fs from "fs";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
import { lex } from "./lexer";
|
|
7
|
+
import { parse } from "./parser";
|
|
8
|
+
import { ASTNode, Block, Literal, Variable, SExpr, Keyword, TypeAnnotation, Pattern, PatternMatch, MatchCase, LiteralPattern, VariablePattern, WildcardPattern, ListPattern, StructPattern, OrPattern, ModuleBlock, ImportBlock, OpenBlock, SearchBlock, LearnBlock, ReasoningBlock, ReasoningSequence, AsyncFunction, AwaitExpression, TryBlock, CatchClause, ThrowExpression, TypeClass, TypeClassInstance, TypeClassMethod, isModuleBlock, isImportBlock, isOpenBlock, isSearchBlock, isLearnBlock, isReasoningBlock, isReasoningSequence, isTryBlock, isThrowExpression, isFuncBlock, isBlock, isControlBlock } from "./ast";
|
|
9
|
+
import { TypeChecker, createTypeChecker } from "./type-checker";
|
|
10
|
+
import { RuntimeTypeChecker } from "./type-system"; // Phase 60: 런타임 타입 검증
|
|
11
|
+
import { ModuleNotFoundError, SelectiveImportError, FunctionRegistrationError, FunctionNotFoundError, VariableNotFoundError, FLRuntimeError, ErrorCodes } from "./errors";
|
|
12
|
+
import { suggestSimilar } from "./error-formatter";
|
|
13
|
+
import { Logger, StructuredLogger, getGlobalLogger } from "./logger";
|
|
14
|
+
import { extractParamNames, extractFunctions } from "./ast-helpers";
|
|
15
|
+
import { FreeLangPromise, resolvedPromise, rejectedPromise } from "./async-runtime";
|
|
16
|
+
import { ScopeStack } from "./interpreter-scope"; // Phase A: 렉시컬 스코프
|
|
17
|
+
import { WebSearchAdapter } from "./web-search-adapter"; // Phase 9a: WebSearch integration
|
|
18
|
+
import { LearnedFactsStore } from "./learned-facts-store"; // Phase 9b: Learning persistence
|
|
19
|
+
import { evalBuiltin } from "./eval-builtins"; // Phase 57: Built-in functions
|
|
20
|
+
import { evalAiBlock } from "./eval-ai-blocks"; // Phase 57: AI blocks
|
|
21
|
+
import { evalInfraBlock } from "./eval-infra-blocks"; // Phase 58: Infra blocks
|
|
22
|
+
import { evalStyleBlock } from "./eval-style-blocks"; // Phase 59: Style blocks
|
|
23
|
+
import { evalSpecialForm } from "./eval-special-forms"; // Phase 57: Special forms
|
|
24
|
+
import { handleReasoningSequence } from "./eval-reasoning-sequence"; // Phase 57: Reasoning sequence
|
|
25
|
+
import { handleSearchBlock as _handleSearchBlock, handleLearnBlock as _handleLearnBlock, handleReasoningBlock as _handleReasoningBlock } from "./eval-ai-handlers"; // Phase 57: AI handlers
|
|
26
|
+
import { evalModuleBlock as _evalModuleBlock, evalImportBlock as _evalImportBlock, evalImportFromFile as _evalImportFromFile, evalOpenBlock as _evalOpenBlock } from "./eval-module-system"; // Phase 57: Module system
|
|
27
|
+
import { loadAllStdlib } from "./stdlib-loader"; // Phase 58: Stdlib 로딩 분리
|
|
28
|
+
import { evalPatternMatch as _evalPatternMatch, evalTryBlock as _evalTryBlock, evalThrow as _evalThrow, matchPattern as _matchPattern } from "./eval-pattern-match"; // Phase 58: Pattern matching 분리
|
|
29
|
+
import { registerBuiltinTypeClasses as _registerBuiltinTypeClasses, evalTypeClass as _evalTypeClass, evalInstance as _evalInstance } from "./eval-type-classes"; // Phase 58: Type class 분리
|
|
30
|
+
import { callUserFunction as _callUserFunction, callFunctionValue as _callFunctionValue, callAsyncFunctionValue as _callAsyncFunctionValue, callFunction as _callFunction, callUserFunctionTCO as _callUserFunctionTCO, callFunctionValueTCO as _callFunctionValueTCO, callUserFunctionRaw as _callUserFunctionRaw, callFunctionValueRaw as _callFunctionValueRaw } from "./eval-call-function"; // Phase 58+61: Function call 분리 + TCO
|
|
31
|
+
import { MacroExpander } from "./macro-expander"; // Phase 63: 매크로 시스템
|
|
32
|
+
import { ProtocolRegistry } from "./protocol"; // Phase 64: 프로토콜 시스템
|
|
33
|
+
import { StructRegistry } from "./struct-system"; // Phase 66: 구조체 시스템
|
|
34
|
+
import { isLazySeq, take as lazyTake } from "./lazy-seq"; // Phase 69: 레이지 시퀀스
|
|
35
|
+
import { DebugSession, getGlobalDebugSession, handleBreak } from "./debugger"; // Phase 78: 디버거
|
|
36
|
+
import { evalCotForm } from "./cot"; // Phase 92: Chain-of-Thought
|
|
37
|
+
import { evalTotBlock, TreeOfThought } from "./tot"; // Phase 93: Tree-of-Thought
|
|
38
|
+
import { evalReflectForm } from "./reflect"; // Phase 94: REFLECT 자기 평가/반성
|
|
39
|
+
import { globalToolRegistry, ToolRegistry, type ToolDefinition } from "./tool-registry"; // Phase 97: Tool DSL
|
|
40
|
+
import { evalAgentBlock, createAgentBuiltins, type AgentState, type AgentAction } from "./agent"; // Phase 98: AGENT 루프
|
|
41
|
+
|
|
42
|
+
// Phase 3-E: VM opt-in 최적화
|
|
43
|
+
import { isVMEligible } from "./vm-eligible";
|
|
44
|
+
import { BytecodeCompiler } from "./compiler";
|
|
45
|
+
import { VM } from "./vm";
|
|
46
|
+
import { createDefaultOptimizer } from "./optimizer";
|
|
47
|
+
|
|
48
|
+
// Phase 58: 타입 정의는 interpreter-context.ts로 이동, re-export로 호환성 유지
|
|
49
|
+
export type {
|
|
50
|
+
ExecutionContext,
|
|
51
|
+
FreeLangFunction,
|
|
52
|
+
FreeLangRoute,
|
|
53
|
+
Intent,
|
|
54
|
+
FreeLangMiddleware,
|
|
55
|
+
ErrorHandler,
|
|
56
|
+
TypeClassInfo,
|
|
57
|
+
TypeClassInstanceInfo,
|
|
58
|
+
ModuleInfo,
|
|
59
|
+
} from "./interpreter-context";
|
|
60
|
+
|
|
61
|
+
// 로컬 사용을 위해 import
|
|
62
|
+
import type {
|
|
63
|
+
ExecutionContext,
|
|
64
|
+
FreeLangFunction,
|
|
65
|
+
FreeLangRoute,
|
|
66
|
+
Intent,
|
|
67
|
+
FreeLangMiddleware,
|
|
68
|
+
ErrorHandler,
|
|
69
|
+
TypeClassInfo,
|
|
70
|
+
TypeClassInstanceInfo,
|
|
71
|
+
ModuleInfo,
|
|
72
|
+
} from "./interpreter-context";
|
|
73
|
+
|
|
74
|
+
// Interpreter class
|
|
75
|
+
export class Interpreter {
|
|
76
|
+
public context: ExecutionContext; // Public for testing
|
|
77
|
+
public logger: Logger;
|
|
78
|
+
public searchAdapter: WebSearchAdapter; // Phase 9a: WebSearch
|
|
79
|
+
public learnedFactsStore: LearnedFactsStore; // Phase 9b: Learning persistence
|
|
80
|
+
public currentLine = 0; // FreeLang source line tracking
|
|
81
|
+
public callDepth = 0;
|
|
82
|
+
// Phase E 후속 (2026-04-25): mongodb-integration.test 호환 alias
|
|
83
|
+
// 일부 테스트가 interp.globals 사용 — context.functions 가 정확하지만 alias 제공
|
|
84
|
+
public get globals() { return this.context.functions; }
|
|
85
|
+
// Phase E (2026-04-25): 호출 체인 추적 — 에러 발생 시 마지막 100개까지 표시
|
|
86
|
+
public callStack: Array<{ fn: string; line: number }> = [];
|
|
87
|
+
public static readonly CALL_STACK_LIMIT = 100; // 메모리 안전을 위해 마지막 N개만 유지
|
|
88
|
+
public static readonly MAX_CALL_DEPTH = 5000; // Phase 61: 상향 (trampoline이 100만 재귀 처리)
|
|
89
|
+
// Phase 61: TCO 모드 — eval이 꼬리 위치 함수 호출을 TailCall 토큰으로 반환
|
|
90
|
+
public tcoMode = false; // ← 기본값 유지 (TCO 라우팅은 내부용)
|
|
91
|
+
|
|
92
|
+
// Phase 3-E: VM opt-in 최적화
|
|
93
|
+
private static readonly _vmCompiler = new BytecodeCompiler();
|
|
94
|
+
private static readonly _vmOptimizer = createDefaultOptimizer();
|
|
95
|
+
private static readonly _vm = new VM();
|
|
96
|
+
private static readonly _vmEnabled = process.env.FL_VM === "1";
|
|
97
|
+
|
|
98
|
+
// Phase 52: FL 파일 import 지원
|
|
99
|
+
public importedFiles: Set<string> = new Set();
|
|
100
|
+
public currentFilePath: string = process.cwd();
|
|
101
|
+
// Phase 78: 디버거 세션
|
|
102
|
+
public debugSession: DebugSession = getGlobalDebugSession();
|
|
103
|
+
|
|
104
|
+
constructor(logger?: Logger, options?: { strict?: boolean }) {
|
|
105
|
+
this.logger = logger || getGlobalLogger();
|
|
106
|
+
// Phase 60: strict 모드 — 환경변수 FREELANG_STRICT=1 또는 options.strict=true
|
|
107
|
+
const strictMode = options?.strict ?? process.env.FREELANG_STRICT === "1";
|
|
108
|
+
this.context = {
|
|
109
|
+
functions: new Map(),
|
|
110
|
+
routes: new Map(),
|
|
111
|
+
intents: new Map(),
|
|
112
|
+
variables: new ScopeStack(),
|
|
113
|
+
middleware: [],
|
|
114
|
+
errorHandlers: { handlers: new Map() },
|
|
115
|
+
startTime: Date.now(),
|
|
116
|
+
typeChecker: createTypeChecker(), // Phase 3: Initialize type checker
|
|
117
|
+
runtimeTypeChecker: new RuntimeTypeChecker(strictMode), // Phase 60: 런타임 타입 검증
|
|
118
|
+
typeClasses: new Map(), // Phase 5 Week 2: Type class registry
|
|
119
|
+
typeClassInstances: new Map(), // Phase 5 Week 2: Type class instance registry
|
|
120
|
+
modules: new Map(), // Phase 6: Module registry
|
|
121
|
+
macroExpander: new MacroExpander(), // Phase 63: 매크로 시스템
|
|
122
|
+
protocols: new ProtocolRegistry(), // Phase 64: 프로토콜 시스템
|
|
123
|
+
structs: new StructRegistry(), // Phase 66: 구조체/레코드 타입 시스템
|
|
124
|
+
callStack: [], // Phase Y-2: 함수 호출 스택 추적
|
|
125
|
+
lastError: undefined, // Phase Y-2-B: 에러 컨텍스트 저장
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Phase 9a: Initialize WebSearchAdapter (mock mode by default)
|
|
129
|
+
// Production: Use BRAVE_SEARCH_KEY or SERPER_API_KEY from env
|
|
130
|
+
const apiKey = process.env.BRAVE_SEARCH_KEY || process.env.SERPER_API_KEY;
|
|
131
|
+
const provider = process.env.BRAVE_SEARCH_KEY ? "brave" : process.env.SERPER_API_KEY ? "serper" : "mock";
|
|
132
|
+
this.searchAdapter = new WebSearchAdapter(apiKey, provider as any);
|
|
133
|
+
|
|
134
|
+
// Phase 9b: Initialize LearnedFactsStore (persistent learning)
|
|
135
|
+
this.learnedFactsStore = new LearnedFactsStore("./data/learned-facts.json", 30);
|
|
136
|
+
|
|
137
|
+
// Phase 58: 모든 stdlib 모듈 등록 (stdlib-loader.ts로 분리)
|
|
138
|
+
loadAllStdlib(this);
|
|
139
|
+
|
|
140
|
+
// AGENT-2: .env 자동 로드 — 인터프리터 초기화 시 process.cwd()/.env 파일 자동 파싱
|
|
141
|
+
// FL_NO_AUTO_ENV=1 환경변수로 비활성화 가능
|
|
142
|
+
if (!process.env.FL_NO_AUTO_ENV) {
|
|
143
|
+
try {
|
|
144
|
+
const _fsEnv = require("fs");
|
|
145
|
+
const _pathEnv = require("path");
|
|
146
|
+
const _envFile = _pathEnv.resolve(process.cwd(), ".env");
|
|
147
|
+
if (_fsEnv.existsSync(_envFile)) {
|
|
148
|
+
const _content = _fsEnv.readFileSync(_envFile, "utf-8");
|
|
149
|
+
for (const _rawLine of _content.split("\n")) {
|
|
150
|
+
const _line = _rawLine.trim();
|
|
151
|
+
if (!_line || _line.startsWith("#")) continue;
|
|
152
|
+
const _eqIdx = _line.indexOf("=");
|
|
153
|
+
if (_eqIdx === -1) continue;
|
|
154
|
+
const _key = _line.slice(0, _eqIdx).trim();
|
|
155
|
+
let _value = _line.slice(_eqIdx + 1).trim();
|
|
156
|
+
if ((_value.startsWith('"') && _value.endsWith('"')) ||
|
|
157
|
+
(_value.startsWith("'") && _value.endsWith("'"))) {
|
|
158
|
+
_value = _value.slice(1, -1);
|
|
159
|
+
}
|
|
160
|
+
if (_key && process.env[_key] === undefined) {
|
|
161
|
+
process.env[_key] = _value;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
} catch { /* .env 로드 실패는 무시 */ }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Phase 49: FL 표준 라이브러리 (fl-map, fl-filter, fl-reduce, Maybe, Result 등)
|
|
169
|
+
this.loadFlStdlib();
|
|
170
|
+
|
|
171
|
+
// Phase 5 Week 2: Register built-in type classes and instances
|
|
172
|
+
this.registerBuiltinTypeClasses();
|
|
173
|
+
|
|
174
|
+
// Phase 63: 표준 매크로 등록 (when, unless, and2)
|
|
175
|
+
this.registerStandardMacros();
|
|
176
|
+
|
|
177
|
+
// Phase 98: Agent 빌트인 등록 (agent-new, agent-run, agent-done?, agent-result, ...)
|
|
178
|
+
this.registerAgentBuiltins();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Phase 98: Agent 빌트인 함수 등록
|
|
182
|
+
private registerAgentBuiltins(): void {
|
|
183
|
+
const builtins = createAgentBuiltins(this);
|
|
184
|
+
for (const [name, fn] of Object.entries(builtins)) {
|
|
185
|
+
this.context.functions.set(name, { name, params: [], body: fn as any });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Phase 63: 표준 매크로 등록
|
|
190
|
+
private registerStandardMacros(): void {
|
|
191
|
+
const expander = this.context.macroExpander;
|
|
192
|
+
// when/unless는 eval-special-forms.ts에서 다중 body 지원하므로 매크로 등록 제거
|
|
193
|
+
// (and2 $a $b) → (if $a $b false)
|
|
194
|
+
expander.define("and2", ["$a", "$b"], {
|
|
195
|
+
kind: "sexpr",
|
|
196
|
+
op: "if",
|
|
197
|
+
args: [
|
|
198
|
+
{ kind: "variable", name: "$a" },
|
|
199
|
+
{ kind: "variable", name: "$b" },
|
|
200
|
+
{ kind: "literal", type: "boolean", value: false },
|
|
201
|
+
],
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Phase 49: freelang-stdlib.fl 로드 — fl-map, fl-filter, fl-reduce 등
|
|
206
|
+
// TS 내장 some/none/ok/err({tag,kind,value})와 FL 배열 표현["some",v]이 다르므로
|
|
207
|
+
// array util만 로드하고, Maybe/Result는 TS 내장 + ts-compat helper를 제공
|
|
208
|
+
private loadFlStdlib(): void {
|
|
209
|
+
try {
|
|
210
|
+
const stdlibPath = path.join(__dirname, "freelang-stdlib.fl");
|
|
211
|
+
if (!fs.existsSync(stdlibPath)) return;
|
|
212
|
+
const src = fs.readFileSync(stdlibPath, "utf-8");
|
|
213
|
+
this.interpret(parse(lex(src)));
|
|
214
|
+
} catch {
|
|
215
|
+
// stdlib 로드 실패 시 조용히 스킵 (런타임은 계속 동작)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// TS 내장 {tag,kind,value} 표현용 Maybe/Result helpers
|
|
219
|
+
// TS built-in: (ok v)→{tag:"Ok",...} (some v)→{tag:"Some",...} etc.
|
|
220
|
+
const isSomeVal = (m: any) => Array.isArray(m) ? m[0] === "some" : m?.tag === "Some";
|
|
221
|
+
const getVal = (m: any) => Array.isArray(m) ? m[1] : m?.value;
|
|
222
|
+
const callFn = (fn: any, v: any) =>
|
|
223
|
+
typeof fn === "string" ? this.callUserFunction(fn, [v]) : fn(v);
|
|
224
|
+
|
|
225
|
+
const tsHelpers: Record<string, (...a: any[]) => any> = {
|
|
226
|
+
"ok?": (r: any) => r?.tag === "Ok",
|
|
227
|
+
"err?": (r: any) => r?.tag === "Err",
|
|
228
|
+
"some?": (m: any) => isSomeVal(m),
|
|
229
|
+
"none?": (m: any) => Array.isArray(m) ? m[0] === "none" : m?.tag === "None",
|
|
230
|
+
"maybe-or": (m: any, d: any) => isSomeVal(m) ? getVal(m) : d,
|
|
231
|
+
"maybe-map": (m: any, fn: any) => isSomeVal(m)
|
|
232
|
+
? { tag: "Some", kind: "Option", value: callFn(fn, getVal(m)) }
|
|
233
|
+
: m,
|
|
234
|
+
"maybe-chain": (m: any, fn: any) => isSomeVal(m) ? callFn(fn, getVal(m)) : m,
|
|
235
|
+
"result-or": (r: any, d: any) => (r?._tag === "Ok" || r?.tag === "Ok") ? r.value : d,
|
|
236
|
+
"result-map": (r: any, fn: any) => (r?._tag === "Ok" || r?.tag === "Ok")
|
|
237
|
+
? { _tag: "Ok", tag: "Ok", kind: "Result", value: callFn(fn, r.value) }
|
|
238
|
+
: r,
|
|
239
|
+
"result-chain": (r: any, fn: any) => (r?._tag === "Ok" || r?.tag === "Ok") ? callFn(fn, r.value) : r,
|
|
240
|
+
};
|
|
241
|
+
for (const [name, fn] of Object.entries(tsHelpers)) {
|
|
242
|
+
this.context.functions.set(name, { name, params: [], body: fn as any });
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
public registerModule(module: Record<string, unknown>): void {
|
|
247
|
+
for (const [name, fn] of Object.entries(module)) {
|
|
248
|
+
this.context.functions.set(name, { name, params: [], body: fn as any });
|
|
249
|
+
if (this.context.typeChecker) {
|
|
250
|
+
const paramTypes = Array((fn as Function).length).fill({ kind: "type" as const, name: "any" });
|
|
251
|
+
this.context.typeChecker.registerFunction(name, paramTypes, { kind: "type" as const, name: "any" });
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
interpret(blocks: ASTNode[]): ExecutionContext {
|
|
257
|
+
try {
|
|
258
|
+
// Phase 63: 매크로 확장 전처리 — defmacro 먼저 처리, 나머지는 확장
|
|
259
|
+
// 1단계: defmacro 노드 선처리
|
|
260
|
+
for (const node of blocks) {
|
|
261
|
+
if ((node as any).kind === "sexpr" && (node as any).op === "defmacro") {
|
|
262
|
+
this.evalDefmacro(node as any);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
// 2단계: defmacro가 아닌 노드는 매크로 확장 후 실행
|
|
266
|
+
blocks = blocks.map((node) => {
|
|
267
|
+
if ((node as any).kind === "sexpr" && (node as any).op === "defmacro") return node;
|
|
268
|
+
return this.context.macroExpander.expand(node);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
for (const node of blocks) {
|
|
272
|
+
if ((node as any).kind === "sexpr" && (node as any).op === "defmacro") continue; // 이미 처리됨
|
|
273
|
+
if (isImportBlock(node)) {
|
|
274
|
+
this.evalImportBlock(node);
|
|
275
|
+
} else if (isOpenBlock(node)) {
|
|
276
|
+
this.evalOpenBlock(node);
|
|
277
|
+
} else if (isSearchBlock(node)) {
|
|
278
|
+
this.context.lastValue = this.handleSearchBlock(node);
|
|
279
|
+
} else if (isLearnBlock(node)) {
|
|
280
|
+
this.context.lastValue = this.handleLearnBlock(node);
|
|
281
|
+
} else if (isReasoningBlock(node)) {
|
|
282
|
+
this.context.lastValue = this.handleReasoningBlock(node);
|
|
283
|
+
} else if (isReasoningSequence(node)) {
|
|
284
|
+
this.context.lastValue = this.handleReasoningSequence(node);
|
|
285
|
+
} else if (isModuleBlock(node)) {
|
|
286
|
+
this.evalModuleBlock(node);
|
|
287
|
+
} else if (isBlock(node)) {
|
|
288
|
+
this.evalBlock(node);
|
|
289
|
+
} else {
|
|
290
|
+
this.context.lastValue = this.eval(node);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
} catch (e: any) {
|
|
294
|
+
if (e && e.constructor && e.constructor.name === "ReturnSignal") throw e;
|
|
295
|
+
if (e instanceof Error && this.currentLine > 0 && !e.message.includes("FreeLang line")) {
|
|
296
|
+
e.message = `FreeLang line ${this.currentLine}: ${e.message}`;
|
|
297
|
+
}
|
|
298
|
+
if (e instanceof Error && !(e as any).__flCallStack && this.context.callStack.length > 0) {
|
|
299
|
+
(e as any).__flCallStack = [...this.context.callStack];
|
|
300
|
+
}
|
|
301
|
+
throw e;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return this.context;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Phase 59: 소스 코드 문자열을 받아 lex → parse → interpret 후 ExecutionContext 반환
|
|
309
|
+
* 테스트와 인라인 실행에 편리한 단축 메서드
|
|
310
|
+
*/
|
|
311
|
+
run(source: string): ExecutionContext {
|
|
312
|
+
return this.interpret(parse(lex(source)));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
private evalBlock(block: Block): void {
|
|
316
|
+
switch (block.type) {
|
|
317
|
+
case "PAGE":
|
|
318
|
+
// Phase 11 v11: [PAGE :path "/" :render "<h1>...</h1>"]
|
|
319
|
+
this.context.lastValue = this.handlePageBlock(block);
|
|
320
|
+
break;
|
|
321
|
+
case "API":
|
|
322
|
+
// Phase 11 MVP: [API :methods ["POST"] :path "/auth/signup" :do (create $db.users __body)]
|
|
323
|
+
this.context.lastValue = this.handleApiBlock(block);
|
|
324
|
+
break;
|
|
325
|
+
case "COMPONENT":
|
|
326
|
+
// Phase 11 v11: [COMPONENT name :render ... :state {...} :methods {...}]
|
|
327
|
+
this.context.lastValue = this.handleComponentBlock(block);
|
|
328
|
+
break;
|
|
329
|
+
case "FORM":
|
|
330
|
+
// Phase 11 v11: [FORM name :fields [...] :validation ...]
|
|
331
|
+
this.context.lastValue = this.handleFormBlock(block);
|
|
332
|
+
break;
|
|
333
|
+
case "SERVICE":
|
|
334
|
+
// Phase 11 v11: [SERVICE name :inject [...] :methods {...}]
|
|
335
|
+
this.context.lastValue = this.handleServiceBlock(block);
|
|
336
|
+
break;
|
|
337
|
+
case "CONTROLLER":
|
|
338
|
+
// Phase 11 v11: [CONTROLLER name :prefix "/api" :routes {...}]
|
|
339
|
+
this.context.lastValue = this.handleControllerBlock(block);
|
|
340
|
+
break;
|
|
341
|
+
case "GUARD":
|
|
342
|
+
// Phase 11 v11: [GUARD name :strategy "jwt" :check (fn ...)]
|
|
343
|
+
this.context.lastValue = this.handleGuardBlock(block);
|
|
344
|
+
break;
|
|
345
|
+
case "MODEL":
|
|
346
|
+
// Phase 11 v11: [MODEL name :table "users" :fields {...}]
|
|
347
|
+
this.context.lastValue = this.handleModelBlock(block);
|
|
348
|
+
break;
|
|
349
|
+
case "QUERY":
|
|
350
|
+
// Phase 11 v11: [QUERY name :model User :where {...}]
|
|
351
|
+
this.context.lastValue = this.handleQueryBlock(block);
|
|
352
|
+
break;
|
|
353
|
+
case "MIGRATION":
|
|
354
|
+
// Phase 11 v11: [MIGRATION name :version "001" :up (...) :down (...)]
|
|
355
|
+
this.context.lastValue = this.handleMigrationBlock(block);
|
|
356
|
+
break;
|
|
357
|
+
case "REPOSITORY":
|
|
358
|
+
// Phase 11 v11: [REPOSITORY name :model User :methods {...}]
|
|
359
|
+
this.context.lastValue = this.handleRepositoryBlock(block);
|
|
360
|
+
break;
|
|
361
|
+
case "DATABASE":
|
|
362
|
+
// Phase 11 v11: [DATABASE name :type :postgresql :host "localhost"]
|
|
363
|
+
this.context.lastValue = this.handleDatabaseBlock(block);
|
|
364
|
+
break;
|
|
365
|
+
case "CACHE":
|
|
366
|
+
// Phase 11 v11: [CACHE name :host "localhost" :port 6379 :ttl 3600]
|
|
367
|
+
this.context.lastValue = this.handleCacheBlock(block);
|
|
368
|
+
break;
|
|
369
|
+
case "CACHED":
|
|
370
|
+
// Phase 11 v11: [CACHED name :cache redis-main :key (...) :fn (...)]
|
|
371
|
+
this.context.lastValue = this.handleCachedBlock(block);
|
|
372
|
+
break;
|
|
373
|
+
case "KAFKA":
|
|
374
|
+
// Phase 11 v11: [KAFKA name :brokers [...] :client-id "app"]
|
|
375
|
+
this.context.lastValue = this.handleKafkaBlock(block);
|
|
376
|
+
break;
|
|
377
|
+
case "PRODUCER":
|
|
378
|
+
// Phase 11 v11: [PRODUCER name :kafka kafka-main :topic "events"]
|
|
379
|
+
this.context.lastValue = this.handleProducerBlock(block);
|
|
380
|
+
break;
|
|
381
|
+
case "CONSUMER":
|
|
382
|
+
// Phase 11 v11: [CONSUMER name :kafka kafka-main :topic "events" :handler (...)]
|
|
383
|
+
this.context.lastValue = this.handleConsumerBlock(block);
|
|
384
|
+
break;
|
|
385
|
+
case "QUEUE":
|
|
386
|
+
// Phase 11 v11: [QUEUE name :rabbitmq rabbit-main :exchange "app" :handler (...)]
|
|
387
|
+
this.context.lastValue = this.handleQueueBlock(block);
|
|
388
|
+
break;
|
|
389
|
+
case "RABBITMQ":
|
|
390
|
+
// Phase 11 v11: [RABBITMQ name :url "amqp://localhost"]
|
|
391
|
+
this.context.lastValue = this.handleRabbitMQBlock(block);
|
|
392
|
+
break;
|
|
393
|
+
case "JWT":
|
|
394
|
+
// Phase 11 v11: [JWT name :secret "secret" :expires-in "7d"]
|
|
395
|
+
this.context.lastValue = this.handleJWTBlock(block);
|
|
396
|
+
break;
|
|
397
|
+
case "OAUTH":
|
|
398
|
+
// Phase 11 v11: [OAUTH name :provider "google" :client-id "..."]
|
|
399
|
+
this.context.lastValue = this.handleOAuthBlock(block);
|
|
400
|
+
break;
|
|
401
|
+
case "DOCKERFILE":
|
|
402
|
+
// Phase 11 v11: [DOCKERFILE name :base "node:20" :workdir "/app" ...]
|
|
403
|
+
this.context.lastValue = this.handleDockerfileBlock(block);
|
|
404
|
+
break;
|
|
405
|
+
case "DOCKER-COMPOSE":
|
|
406
|
+
// Phase 11 v11: [DOCKER-COMPOSE name :services {...} :volumes {...}]
|
|
407
|
+
this.context.lastValue = this.handleDockerComposeBlock(block);
|
|
408
|
+
break;
|
|
409
|
+
case "K8S-DEPLOYMENT":
|
|
410
|
+
// Phase 11 v11: [K8S-DEPLOYMENT name :replicas 3 :image "..."]
|
|
411
|
+
this.context.lastValue = this.handleK8sDeploymentBlock(block);
|
|
412
|
+
break;
|
|
413
|
+
case "K8S-SERVICE":
|
|
414
|
+
// Phase 11 v11: [K8S-SERVICE name :selector {:app "name"} :ports [...]]
|
|
415
|
+
this.context.lastValue = this.handleK8sServiceBlock(block);
|
|
416
|
+
break;
|
|
417
|
+
case "K8S-INGRESS":
|
|
418
|
+
// Phase 11 v11: [K8S-INGRESS name :rules [...]]
|
|
419
|
+
this.context.lastValue = this.handleK8sIngressBlock(block);
|
|
420
|
+
break;
|
|
421
|
+
case "AWS":
|
|
422
|
+
case "AWS-S3":
|
|
423
|
+
case "AWS-LAMBDA":
|
|
424
|
+
case "AWS-RDS":
|
|
425
|
+
case "AWS-SQS":
|
|
426
|
+
// Phase 11 v11: AWS cloud blocks
|
|
427
|
+
this.context.lastValue = this.handleAwsBlock(block);
|
|
428
|
+
break;
|
|
429
|
+
case "GCP":
|
|
430
|
+
case "GCP-CLOUD-RUN":
|
|
431
|
+
case "GCP-BIGQUERY":
|
|
432
|
+
// Phase 11 v11: GCP cloud blocks
|
|
433
|
+
this.context.lastValue = this.handleGcpBlock(block);
|
|
434
|
+
break;
|
|
435
|
+
case "AZURE":
|
|
436
|
+
case "AZURE-FUNCTION":
|
|
437
|
+
case "AZURE-COSMOS":
|
|
438
|
+
// Phase 11 v11: Azure cloud blocks
|
|
439
|
+
this.context.lastValue = this.handleAzureBlock(block);
|
|
440
|
+
break;
|
|
441
|
+
case "SERVER":
|
|
442
|
+
this.handleServerBlock(block);
|
|
443
|
+
break;
|
|
444
|
+
case "ROUTE":
|
|
445
|
+
this.handleRouteBlock(block);
|
|
446
|
+
break;
|
|
447
|
+
case "FUNC":
|
|
448
|
+
this.handleFuncBlock(block);
|
|
449
|
+
break;
|
|
450
|
+
case "INTENT":
|
|
451
|
+
this.handleIntentBlock(block);
|
|
452
|
+
break;
|
|
453
|
+
case "MIDDLEWARE":
|
|
454
|
+
this.handleMiddlewareBlock(block);
|
|
455
|
+
break;
|
|
456
|
+
case "WEBSOCKET":
|
|
457
|
+
this.handleWebSocketBlock(block);
|
|
458
|
+
break;
|
|
459
|
+
case "ERROR-HANDLER":
|
|
460
|
+
this.handleErrorHandlerBlock(block);
|
|
461
|
+
break;
|
|
462
|
+
case "TOOL":
|
|
463
|
+
// Phase 97: [TOOL name :desc "..." :input {...} :output :T :body expr]
|
|
464
|
+
this.context.lastValue = this.handleToolBlock(block);
|
|
465
|
+
break;
|
|
466
|
+
case "USE-TOOL":
|
|
467
|
+
// Phase 97: [USE-TOOL toolname :args {key val ...}]
|
|
468
|
+
this.context.lastValue = this.handleUseToolBlock(block);
|
|
469
|
+
break;
|
|
470
|
+
case "AGENT":
|
|
471
|
+
// Phase 98: [AGENT :goal "..." :max-steps 10 :step (fn ...) :stop-when (fn ...)]
|
|
472
|
+
this.handleAgentBlock(block);
|
|
473
|
+
break;
|
|
474
|
+
default:
|
|
475
|
+
// Unknown block type, skip
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Phase 98: AGENT 블록 처리
|
|
481
|
+
private handleAgentBlock(block: Block): void {
|
|
482
|
+
const interp = this;
|
|
483
|
+
const ev = (node: any) => interp.eval(node);
|
|
484
|
+
const callFnVal = (fn: any, args: any[]) => interp.callFunctionValue(fn, args);
|
|
485
|
+
const state = evalAgentBlock(block.fields as Map<string, any>, ev, callFnVal);
|
|
486
|
+
this.context.lastValue = state;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Phase 11 v11: [PAGE :path "/" :name "Home" :render "<h1>...</h1>"]
|
|
490
|
+
private handlePageBlock(block: Block): any {
|
|
491
|
+
const renderNode = block.fields.get("render");
|
|
492
|
+
if (!renderNode) {
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// params를 $key 형태로 바인딩
|
|
497
|
+
const params: Record<string, string> = (this.context as any).__params || {};
|
|
498
|
+
this.context.variables.push();
|
|
499
|
+
for (const [key, value] of Object.entries(params)) {
|
|
500
|
+
this.context.variables.set(`$${key}`, value);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
try {
|
|
504
|
+
let html: any;
|
|
505
|
+
|
|
506
|
+
// :render이 Block인 경우 (예: AGENT 블록)
|
|
507
|
+
if (renderNode.kind === "block") {
|
|
508
|
+
this.evalBlock(renderNode as Block);
|
|
509
|
+
html = this.context.lastValue;
|
|
510
|
+
} else {
|
|
511
|
+
// 일반 S-expression
|
|
512
|
+
html = this.eval(renderNode);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// {{ param }} 템플릿 보간 지원
|
|
516
|
+
if (typeof html === "string") {
|
|
517
|
+
let interpolated = html;
|
|
518
|
+
for (const [key, value] of Object.entries(params)) {
|
|
519
|
+
interpolated = interpolated.replace(new RegExp(`{{\\s*${key}\\s*}}`, "g"), String(value));
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// :state 추출 및 클라이언트 상태 주입
|
|
523
|
+
const stateNode = block.fields.get("state");
|
|
524
|
+
if (stateNode) {
|
|
525
|
+
const stateVal = this.eval(stateNode);
|
|
526
|
+
const stateJson = JSON.stringify(stateVal);
|
|
527
|
+
interpolated = interpolated + `\n<script>window.__state = ${stateJson};</script>`;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
return interpolated;
|
|
531
|
+
}
|
|
532
|
+
return html;
|
|
533
|
+
} finally {
|
|
534
|
+
this.context.variables.pop();
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Phase 11 MVP: [API :methods ["POST"] :path "/..." :do (create ...)]
|
|
539
|
+
private handleApiBlock(block: Block): any {
|
|
540
|
+
const doNode = block.fields.get("do");
|
|
541
|
+
if (!doNode) {
|
|
542
|
+
return { status: 400, json: { error: "missing :do" } };
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// :do 실행
|
|
546
|
+
this.context.variables.push();
|
|
547
|
+
try {
|
|
548
|
+
const result = this.eval(doNode);
|
|
549
|
+
|
|
550
|
+
// 응답 형식
|
|
551
|
+
// {:status 201 :json {...}} 형태로 반환
|
|
552
|
+
if (result && typeof result === "object") {
|
|
553
|
+
if (result.status !== undefined && result.json !== undefined) {
|
|
554
|
+
return result;
|
|
555
|
+
}
|
|
556
|
+
// 일반 객체면 200 OK로 반환
|
|
557
|
+
return { status: 200, json: result };
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
return { status: 200, json: result };
|
|
561
|
+
} finally {
|
|
562
|
+
this.context.variables.pop();
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Phase 11 v11: [COMPONENT name :render ... :state {...} :methods {...}]
|
|
567
|
+
private handleComponentBlock(block: Block): any {
|
|
568
|
+
const renderNode = block.fields.get("render");
|
|
569
|
+
if (!renderNode) {
|
|
570
|
+
return null;
|
|
571
|
+
}
|
|
572
|
+
// :state 초기값 설정
|
|
573
|
+
const stateNode = block.fields.get("state");
|
|
574
|
+
if (stateNode && stateNode.kind === "block" && stateNode.type === "Map") {
|
|
575
|
+
const entries = (stateNode.fields.get("entries") || []) as any[];
|
|
576
|
+
this.context.variables.push();
|
|
577
|
+
for (let i = 0; i < entries.length - 1; i += 2) {
|
|
578
|
+
const key = entries[i]?.kind === "keyword" ? entries[i].name : String(entries[i]);
|
|
579
|
+
const value = this.eval(entries[i + 1]);
|
|
580
|
+
this.context.variables.set(`$${key}`, value);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
// :methods 등록 (필요시)
|
|
584
|
+
const methodsNode = block.fields.get("methods");
|
|
585
|
+
// ... methods 처리는 추후 확장
|
|
586
|
+
|
|
587
|
+
// :render 평가
|
|
588
|
+
const html = this.eval(renderNode);
|
|
589
|
+
if (stateNode) {
|
|
590
|
+
this.context.variables.pop();
|
|
591
|
+
}
|
|
592
|
+
return html;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// Phase 11 v11: [FORM name :fields [...] :validation ...]
|
|
596
|
+
private handleFormBlock(block: Block): any {
|
|
597
|
+
const fieldsNode = block.fields.get("fields");
|
|
598
|
+
if (!fieldsNode) {
|
|
599
|
+
return null;
|
|
600
|
+
}
|
|
601
|
+
// 간단한 form HTML 생성
|
|
602
|
+
const fields = Array.isArray(fieldsNode) ? fieldsNode : [fieldsNode];
|
|
603
|
+
let formHtml = '<form>\n';
|
|
604
|
+
for (const field of fields) {
|
|
605
|
+
if (field?.kind === "block" && field?.type === "Map") {
|
|
606
|
+
const name = field.fields.get("name") || "field";
|
|
607
|
+
const type = field.fields.get("type") || "text";
|
|
608
|
+
formHtml += ` <input type="${type}" name="${name}" />\n`;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
formHtml += '</form>';
|
|
612
|
+
return formHtml;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// Phase 11 v11: [SERVICE name :inject [...] :methods {...}]
|
|
616
|
+
private handleServiceBlock(block: Block): any {
|
|
617
|
+
const name = block.name;
|
|
618
|
+
const injectNode = block.fields.get("inject");
|
|
619
|
+
const methodsNode = block.fields.get("methods");
|
|
620
|
+
|
|
621
|
+
// 서비스 등록
|
|
622
|
+
const service: any = {
|
|
623
|
+
name,
|
|
624
|
+
methods: {},
|
|
625
|
+
};
|
|
626
|
+
|
|
627
|
+
// :methods를 함수로 등록
|
|
628
|
+
if (methodsNode && methodsNode.kind === "block" && methodsNode.type === "Map") {
|
|
629
|
+
const entries = (methodsNode.fields.get("entries") || []) as any[];
|
|
630
|
+
for (let i = 0; i < entries.length - 1; i += 2) {
|
|
631
|
+
const methodName = entries[i]?.kind === "keyword" ? entries[i].name : String(entries[i]);
|
|
632
|
+
const methodFn = this.eval(entries[i + 1]);
|
|
633
|
+
service.methods[methodName] = methodFn;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// 글로벌 서비스 레지스트리에 등록
|
|
638
|
+
(this.context as any).services = (this.context as any).services || new Map();
|
|
639
|
+
(this.context as any).services.set(name, service);
|
|
640
|
+
|
|
641
|
+
return { status: "registered", service: name };
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// Phase 11 v11: [CONTROLLER name :prefix "/api" :routes {...}]
|
|
645
|
+
private handleControllerBlock(block: Block): any {
|
|
646
|
+
const name = block.name;
|
|
647
|
+
const prefix = this.eval(block.fields.get("prefix")) || "";
|
|
648
|
+
const routesNode = block.fields.get("routes");
|
|
649
|
+
|
|
650
|
+
// 컨트롤러 등록
|
|
651
|
+
const controller: any = {
|
|
652
|
+
name,
|
|
653
|
+
prefix,
|
|
654
|
+
routes: [],
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
// :routes를 HTTP 라우트로 등록
|
|
658
|
+
if (routesNode && routesNode.kind === "block" && routesNode.type === "Map") {
|
|
659
|
+
const entries = (routesNode.fields.get("entries") || []) as any[];
|
|
660
|
+
for (let i = 0; i < entries.length - 1; i += 2) {
|
|
661
|
+
const routeKey = entries[i]?.kind === "keyword" ? entries[i].name : String(entries[i]);
|
|
662
|
+
const routeHandler = this.eval(entries[i + 1]);
|
|
663
|
+
controller.routes.push({ method: "GET", path: routeKey, handler: routeHandler });
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// 글로벌 컨트롤러 레지스트리에 등록
|
|
668
|
+
(this.context as any).controllers = (this.context as any).controllers || new Map();
|
|
669
|
+
(this.context as any).controllers.set(name, controller);
|
|
670
|
+
|
|
671
|
+
return { status: "registered", controller: name };
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// Phase 11 v11: [GUARD name :strategy "jwt" :check (fn ...)]
|
|
675
|
+
private handleGuardBlock(block: Block): any {
|
|
676
|
+
const name = block.name;
|
|
677
|
+
const strategy = this.eval(block.fields.get("strategy")) || "none";
|
|
678
|
+
const checkFn = this.eval(block.fields.get("check"));
|
|
679
|
+
|
|
680
|
+
// 가드 등록
|
|
681
|
+
const guard: any = {
|
|
682
|
+
name,
|
|
683
|
+
strategy,
|
|
684
|
+
check: checkFn,
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
// 글로벌 가드 레지스트리에 등록
|
|
688
|
+
(this.context as any).guards = (this.context as any).guards || new Map();
|
|
689
|
+
(this.context as any).guards.set(name, guard);
|
|
690
|
+
|
|
691
|
+
return { status: "registered", guard: name };
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// Phase 11 v11: [MODEL name :table "users" :fields {...}]
|
|
695
|
+
private handleModelBlock(block: Block): any {
|
|
696
|
+
const name = block.name;
|
|
697
|
+
const tableName = this.eval(block.fields.get("table")) || name.toLowerCase();
|
|
698
|
+
const dbType = this.eval(block.fields.get("db")) || "postgresql";
|
|
699
|
+
const fieldsNode = block.fields.get("fields");
|
|
700
|
+
|
|
701
|
+
const model: any = {
|
|
702
|
+
name,
|
|
703
|
+
tableName,
|
|
704
|
+
dbType,
|
|
705
|
+
fields: {},
|
|
706
|
+
};
|
|
707
|
+
|
|
708
|
+
// :fields 정의
|
|
709
|
+
if (fieldsNode && fieldsNode.kind === "block" && fieldsNode.type === "Map") {
|
|
710
|
+
const entries = (fieldsNode.fields.get("entries") || []) as any[];
|
|
711
|
+
for (let i = 0; i < entries.length - 1; i += 2) {
|
|
712
|
+
const fieldName = entries[i]?.kind === "keyword" ? entries[i].name : String(entries[i]);
|
|
713
|
+
const fieldType = this.eval(entries[i + 1]);
|
|
714
|
+
model.fields[fieldName] = fieldType;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// 글로벌 모델 레지스트리에 등록
|
|
719
|
+
(this.context as any).models = (this.context as any).models || new Map();
|
|
720
|
+
(this.context as any).models.set(name, model);
|
|
721
|
+
|
|
722
|
+
return { status: "registered", model: name };
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// Phase 11 v11: [QUERY name :model User :where {...}]
|
|
726
|
+
private handleQueryBlock(block: Block): any {
|
|
727
|
+
const name = block.name;
|
|
728
|
+
const modelName = this.eval(block.fields.get("model"));
|
|
729
|
+
const whereNode = block.fields.get("where");
|
|
730
|
+
|
|
731
|
+
const query: any = {
|
|
732
|
+
name,
|
|
733
|
+
model: modelName,
|
|
734
|
+
where: {},
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
// :where 조건 추출
|
|
738
|
+
if (whereNode && whereNode.kind === "block" && whereNode.type === "Map") {
|
|
739
|
+
const entries = (whereNode.fields.get("entries") || []) as any[];
|
|
740
|
+
for (let i = 0; i < entries.length - 1; i += 2) {
|
|
741
|
+
const fieldName = entries[i]?.kind === "keyword" ? entries[i].name : String(entries[i]);
|
|
742
|
+
const fieldVal = this.eval(entries[i + 1]);
|
|
743
|
+
query.where[fieldName] = fieldVal;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// 글로벌 쿼리 레지스트리에 등록
|
|
748
|
+
(this.context as any).queries = (this.context as any).queries || new Map();
|
|
749
|
+
(this.context as any).queries.set(name, query);
|
|
750
|
+
|
|
751
|
+
return { status: "registered", query: name };
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// Phase 11 v11: [MIGRATION name :version "001" :up (...) :down (...)]
|
|
755
|
+
private handleMigrationBlock(block: Block): any {
|
|
756
|
+
const name = block.name;
|
|
757
|
+
const version = this.eval(block.fields.get("version")) || "001";
|
|
758
|
+
const upFn = this.eval(block.fields.get("up"));
|
|
759
|
+
const downFn = this.eval(block.fields.get("down"));
|
|
760
|
+
|
|
761
|
+
const migration: any = {
|
|
762
|
+
name,
|
|
763
|
+
version,
|
|
764
|
+
up: upFn,
|
|
765
|
+
down: downFn,
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
// 글로벌 마이그레이션 레지스트리에 등록
|
|
769
|
+
(this.context as any).migrations = (this.context as any).migrations || new Map();
|
|
770
|
+
(this.context as any).migrations.set(name, migration);
|
|
771
|
+
|
|
772
|
+
return { status: "registered", migration: name };
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// Phase 11 v11: [REPOSITORY name :model User :methods {...}]
|
|
776
|
+
private handleRepositoryBlock(block: Block): any {
|
|
777
|
+
const name = block.name;
|
|
778
|
+
const modelName = this.eval(block.fields.get("model"));
|
|
779
|
+
const methodsNode = block.fields.get("methods");
|
|
780
|
+
|
|
781
|
+
const repository: any = {
|
|
782
|
+
name,
|
|
783
|
+
model: modelName,
|
|
784
|
+
methods: {},
|
|
785
|
+
};
|
|
786
|
+
|
|
787
|
+
// :methods 정의
|
|
788
|
+
if (methodsNode && methodsNode.kind === "block" && methodsNode.type === "Map") {
|
|
789
|
+
const entries = (methodsNode.fields.get("entries") || []) as any[];
|
|
790
|
+
for (let i = 0; i < entries.length - 1; i += 2) {
|
|
791
|
+
const methodName = entries[i]?.kind === "keyword" ? entries[i].name : String(entries[i]);
|
|
792
|
+
const methodFn = this.eval(entries[i + 1]);
|
|
793
|
+
repository.methods[methodName] = methodFn;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// 글로벌 리포지토리 레지스트리에 등록
|
|
798
|
+
(this.context as any).repositories = (this.context as any).repositories || new Map();
|
|
799
|
+
(this.context as any).repositories.set(name, repository);
|
|
800
|
+
|
|
801
|
+
return { status: "registered", repository: name };
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// Phase 11 v11: [DATABASE name :type :postgresql :host "localhost"]
|
|
805
|
+
private handleDatabaseBlock(block: Block): any {
|
|
806
|
+
const name = block.name;
|
|
807
|
+
const dbType = this.eval(block.fields.get("type")) || "postgresql";
|
|
808
|
+
const host = this.eval(block.fields.get("host")) || "localhost";
|
|
809
|
+
const port = this.eval(block.fields.get("port")) || 5432;
|
|
810
|
+
const database = this.eval(block.fields.get("name")) || "freelang_db";
|
|
811
|
+
|
|
812
|
+
const dbConfig: any = {
|
|
813
|
+
name,
|
|
814
|
+
type: dbType,
|
|
815
|
+
host,
|
|
816
|
+
port,
|
|
817
|
+
database,
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
// 글로벌 데이터베이스 레지스트리에 등록
|
|
821
|
+
(this.context as any).databases = (this.context as any).databases || new Map();
|
|
822
|
+
(this.context as any).databases.set(name, dbConfig);
|
|
823
|
+
|
|
824
|
+
return { status: "registered", database: name };
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
// Phase 11 v11: [CACHE name :host "localhost" :port 6379 :ttl 3600]
|
|
828
|
+
private handleCacheBlock(block: Block): any {
|
|
829
|
+
const name = block.name;
|
|
830
|
+
const host = this.eval(block.fields.get("host")) || "localhost";
|
|
831
|
+
const port = this.eval(block.fields.get("port")) || 6379;
|
|
832
|
+
const ttl = this.eval(block.fields.get("ttl")) || 3600;
|
|
833
|
+
|
|
834
|
+
const cacheConfig: any = {
|
|
835
|
+
name,
|
|
836
|
+
host,
|
|
837
|
+
port,
|
|
838
|
+
ttl,
|
|
839
|
+
};
|
|
840
|
+
|
|
841
|
+
// 글로벌 캐시 레지스트리에 등록
|
|
842
|
+
(this.context as any).caches = (this.context as any).caches || new Map();
|
|
843
|
+
(this.context as any).caches.set(name, cacheConfig);
|
|
844
|
+
|
|
845
|
+
return { status: "registered", cache: name };
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// Phase 11 v11: [CACHED name :cache redis-main :key (...) :fn (...)]
|
|
849
|
+
private handleCachedBlock(block: Block): any {
|
|
850
|
+
const name = block.name;
|
|
851
|
+
const cacheName = this.eval(block.fields.get("cache"));
|
|
852
|
+
const keyFn = this.eval(block.fields.get("key"));
|
|
853
|
+
const fn = this.eval(block.fields.get("fn"));
|
|
854
|
+
const ttl = this.eval(block.fields.get("ttl")) || 300;
|
|
855
|
+
|
|
856
|
+
const cached: any = {
|
|
857
|
+
name,
|
|
858
|
+
cache: cacheName,
|
|
859
|
+
key: keyFn,
|
|
860
|
+
fn,
|
|
861
|
+
ttl,
|
|
862
|
+
};
|
|
863
|
+
|
|
864
|
+
(this.context as any).cacheds = (this.context as any).cacheds || new Map();
|
|
865
|
+
(this.context as any).cacheds.set(name, cached);
|
|
866
|
+
|
|
867
|
+
return { status: "registered", cached: name };
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// Phase 11 v11: [KAFKA name :brokers [...] :client-id "app"]
|
|
871
|
+
private handleKafkaBlock(block: Block): any {
|
|
872
|
+
const name = block.name;
|
|
873
|
+
const brokersNode = block.fields.get("brokers");
|
|
874
|
+
const brokers = Array.isArray(brokersNode) ? brokersNode.map((b: any) => this.eval(b)) : ["localhost:9092"];
|
|
875
|
+
const clientId = this.eval(block.fields.get("client-id")) || "freelang-app";
|
|
876
|
+
|
|
877
|
+
const kafkaConfig: any = {
|
|
878
|
+
name,
|
|
879
|
+
brokers,
|
|
880
|
+
clientId,
|
|
881
|
+
};
|
|
882
|
+
|
|
883
|
+
(this.context as any).kafkas = (this.context as any).kafkas || new Map();
|
|
884
|
+
(this.context as any).kafkas.set(name, kafkaConfig);
|
|
885
|
+
|
|
886
|
+
return { status: "registered", kafka: name };
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// Phase 11 v11: [PRODUCER name :kafka kafka-main :topic "events"]
|
|
890
|
+
private handleProducerBlock(block: Block): any {
|
|
891
|
+
const name = block.name;
|
|
892
|
+
const kafkaName = this.eval(block.fields.get("kafka"));
|
|
893
|
+
const topic = this.eval(block.fields.get("topic")) || "events";
|
|
894
|
+
|
|
895
|
+
const producer: any = {
|
|
896
|
+
name,
|
|
897
|
+
kafka: kafkaName,
|
|
898
|
+
topic,
|
|
899
|
+
};
|
|
900
|
+
|
|
901
|
+
(this.context as any).producers = (this.context as any).producers || new Map();
|
|
902
|
+
(this.context as any).producers.set(name, producer);
|
|
903
|
+
|
|
904
|
+
return { status: "registered", producer: name };
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// Phase 11 v11: [CONSUMER name :kafka kafka-main :topic "events" :handler (...)]
|
|
908
|
+
private handleConsumerBlock(block: Block): any {
|
|
909
|
+
const name = block.name;
|
|
910
|
+
const kafkaName = this.eval(block.fields.get("kafka"));
|
|
911
|
+
const topic = this.eval(block.fields.get("topic")) || "events";
|
|
912
|
+
const groupId = this.eval(block.fields.get("group")) || "default";
|
|
913
|
+
const handler = this.eval(block.fields.get("handler"));
|
|
914
|
+
|
|
915
|
+
const consumer: any = {
|
|
916
|
+
name,
|
|
917
|
+
kafka: kafkaName,
|
|
918
|
+
topic,
|
|
919
|
+
groupId,
|
|
920
|
+
handler,
|
|
921
|
+
};
|
|
922
|
+
|
|
923
|
+
(this.context as any).consumers = (this.context as any).consumers || new Map();
|
|
924
|
+
(this.context as any).consumers.set(name, consumer);
|
|
925
|
+
|
|
926
|
+
return { status: "registered", consumer: name };
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// Phase 11 v11: [QUEUE name :rabbitmq rabbit-main :exchange "app" :handler (...)]
|
|
930
|
+
private handleQueueBlock(block: Block): any {
|
|
931
|
+
const name = block.name;
|
|
932
|
+
const rabbitName = this.eval(block.fields.get("rabbitmq"));
|
|
933
|
+
const exchange = this.eval(block.fields.get("exchange")) || "app";
|
|
934
|
+
const routingKey = this.eval(block.fields.get("routing-key")) || "";
|
|
935
|
+
const handler = this.eval(block.fields.get("handler"));
|
|
936
|
+
|
|
937
|
+
const queue: any = {
|
|
938
|
+
name,
|
|
939
|
+
rabbitmq: rabbitName,
|
|
940
|
+
exchange,
|
|
941
|
+
routingKey,
|
|
942
|
+
handler,
|
|
943
|
+
};
|
|
944
|
+
|
|
945
|
+
(this.context as any).queues = (this.context as any).queues || new Map();
|
|
946
|
+
(this.context as any).queues.set(name, queue);
|
|
947
|
+
|
|
948
|
+
return { status: "registered", queue: name };
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
// Phase 11 v11: [RABBITMQ name :url "amqp://localhost"]
|
|
952
|
+
private handleRabbitMQBlock(block: Block): any {
|
|
953
|
+
const name = block.name;
|
|
954
|
+
const url = this.eval(block.fields.get("url")) || "amqp://localhost:5672";
|
|
955
|
+
|
|
956
|
+
const rabbitConfig: any = {
|
|
957
|
+
name,
|
|
958
|
+
url,
|
|
959
|
+
};
|
|
960
|
+
|
|
961
|
+
(this.context as any).rabbitmqs = (this.context as any).rabbitmqs || new Map();
|
|
962
|
+
(this.context as any).rabbitmqs.set(name, rabbitConfig);
|
|
963
|
+
|
|
964
|
+
return { status: "registered", rabbitmq: name };
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
// Phase 11 v11: [JWT name :secret "secret" :expires-in "7d"]
|
|
968
|
+
private handleJWTBlock(block: Block): any {
|
|
969
|
+
const name = block.name;
|
|
970
|
+
const secret = this.eval(block.fields.get("secret")) || "change-me";
|
|
971
|
+
const expiresIn = this.eval(block.fields.get("expires-in")) || "7d";
|
|
972
|
+
const algorithm = this.eval(block.fields.get("algorithm")) || "HS256";
|
|
973
|
+
|
|
974
|
+
const jwtConfig: any = {
|
|
975
|
+
name,
|
|
976
|
+
secret,
|
|
977
|
+
expiresIn,
|
|
978
|
+
algorithm,
|
|
979
|
+
};
|
|
980
|
+
|
|
981
|
+
// 글로벌 JWT 레지스트리에 등록
|
|
982
|
+
(this.context as any).jwts = (this.context as any).jwts || new Map();
|
|
983
|
+
(this.context as any).jwts.set(name, jwtConfig);
|
|
984
|
+
|
|
985
|
+
return { status: "registered", jwt: name };
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
// Phase 11 v11: [OAUTH name :provider "google" :client-id "..."]
|
|
989
|
+
private handleOAuthBlock(block: Block): any {
|
|
990
|
+
const name = block.name;
|
|
991
|
+
const provider = this.eval(block.fields.get("provider")) || "google";
|
|
992
|
+
const clientId = this.eval(block.fields.get("client-id")) || "";
|
|
993
|
+
const clientSecret = this.eval(block.fields.get("client-secret")) || "";
|
|
994
|
+
const callbackUrl = this.eval(block.fields.get("callback-url")) || "/auth/callback";
|
|
995
|
+
const scope = this.eval(block.fields.get("scope")) || ["email", "profile"];
|
|
996
|
+
const onSuccess = this.eval(block.fields.get("on-success"));
|
|
997
|
+
|
|
998
|
+
const oauthConfig: any = {
|
|
999
|
+
name,
|
|
1000
|
+
provider,
|
|
1001
|
+
clientId,
|
|
1002
|
+
clientSecret,
|
|
1003
|
+
callbackUrl,
|
|
1004
|
+
scope,
|
|
1005
|
+
onSuccess,
|
|
1006
|
+
};
|
|
1007
|
+
|
|
1008
|
+
// 글로벌 OAuth 레지스트리에 등록
|
|
1009
|
+
(this.context as any).oauths = (this.context as any).oauths || new Map();
|
|
1010
|
+
(this.context as any).oauths.set(name, oauthConfig);
|
|
1011
|
+
|
|
1012
|
+
return { status: "registered", oauth: name };
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// Phase 11 v11: [DOCKERFILE name :base "node:20" :workdir "/app" ...]
|
|
1016
|
+
private handleDockerfileBlock(block: Block): any {
|
|
1017
|
+
const name = block.name;
|
|
1018
|
+
const base = this.eval(block.fields.get("base")) || "node:20-alpine";
|
|
1019
|
+
const workdir = this.eval(block.fields.get("workdir")) || "/app";
|
|
1020
|
+
const expose = this.eval(block.fields.get("expose")) || 3000;
|
|
1021
|
+
const cmd = this.eval(block.fields.get("cmd")) || "npm start";
|
|
1022
|
+
|
|
1023
|
+
const dockerfile: any = {
|
|
1024
|
+
name,
|
|
1025
|
+
base,
|
|
1026
|
+
workdir,
|
|
1027
|
+
expose,
|
|
1028
|
+
cmd,
|
|
1029
|
+
};
|
|
1030
|
+
|
|
1031
|
+
(this.context as any).dockerfiles = (this.context as any).dockerfiles || new Map();
|
|
1032
|
+
(this.context as any).dockerfiles.set(name, dockerfile);
|
|
1033
|
+
|
|
1034
|
+
return { status: "registered", dockerfile: name };
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
// Phase 11 v11: [DOCKER-COMPOSE name :services {...} :volumes {...}]
|
|
1038
|
+
private handleDockerComposeBlock(block: Block): any {
|
|
1039
|
+
const name = block.name;
|
|
1040
|
+
const servicesNode = block.fields.get("services");
|
|
1041
|
+
const volumesNode = block.fields.get("volumes");
|
|
1042
|
+
|
|
1043
|
+
const compose: any = {
|
|
1044
|
+
name,
|
|
1045
|
+
services: {},
|
|
1046
|
+
volumes: {},
|
|
1047
|
+
};
|
|
1048
|
+
|
|
1049
|
+
// Extract services
|
|
1050
|
+
if (servicesNode && servicesNode.kind === "block" && servicesNode.type === "Map") {
|
|
1051
|
+
const entries = (servicesNode.fields.get("entries") || []) as any[];
|
|
1052
|
+
for (let i = 0; i < entries.length - 1; i += 2) {
|
|
1053
|
+
const serviceName = entries[i]?.kind === "keyword" ? entries[i].name : String(entries[i]);
|
|
1054
|
+
const serviceConfig = this.eval(entries[i + 1]);
|
|
1055
|
+
compose.services[serviceName] = serviceConfig;
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
(this.context as any).composes = (this.context as any).composes || new Map();
|
|
1060
|
+
(this.context as any).composes.set(name, compose);
|
|
1061
|
+
|
|
1062
|
+
return { status: "registered", "docker-compose": name };
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
// Phase 11 v11: [K8S-DEPLOYMENT name :replicas 3 :image "..."]
|
|
1066
|
+
private handleK8sDeploymentBlock(block: Block): any {
|
|
1067
|
+
const name = block.name;
|
|
1068
|
+
const replicas = this.eval(block.fields.get("replicas")) || 1;
|
|
1069
|
+
const image = this.eval(block.fields.get("image")) || "app:latest";
|
|
1070
|
+
const namespace = this.eval(block.fields.get("namespace")) || "default";
|
|
1071
|
+
|
|
1072
|
+
const deployment: any = {
|
|
1073
|
+
name,
|
|
1074
|
+
replicas,
|
|
1075
|
+
image,
|
|
1076
|
+
namespace,
|
|
1077
|
+
};
|
|
1078
|
+
|
|
1079
|
+
(this.context as any).k8sDeployments = (this.context as any).k8sDeployments || new Map();
|
|
1080
|
+
(this.context as any).k8sDeployments.set(name, deployment);
|
|
1081
|
+
|
|
1082
|
+
return { status: "registered", "k8s-deployment": name };
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
// Phase 11 v11: [K8S-SERVICE name :selector {:app "name"} :ports [...]]
|
|
1086
|
+
private handleK8sServiceBlock(block: Block): any {
|
|
1087
|
+
const name = block.name;
|
|
1088
|
+
const selectorNode = block.fields.get("selector");
|
|
1089
|
+
const portsNode = block.fields.get("ports");
|
|
1090
|
+
const type = this.eval(block.fields.get("type")) || "ClusterIP";
|
|
1091
|
+
|
|
1092
|
+
const service: any = {
|
|
1093
|
+
name,
|
|
1094
|
+
selector: {},
|
|
1095
|
+
ports: [],
|
|
1096
|
+
type,
|
|
1097
|
+
};
|
|
1098
|
+
|
|
1099
|
+
// Extract selector
|
|
1100
|
+
if (selectorNode && selectorNode.kind === "block" && selectorNode.type === "Map") {
|
|
1101
|
+
const entries = (selectorNode.fields.get("entries") || []) as any[];
|
|
1102
|
+
for (let i = 0; i < entries.length - 1; i += 2) {
|
|
1103
|
+
const key = entries[i]?.kind === "keyword" ? entries[i].name : String(entries[i]);
|
|
1104
|
+
const val = this.eval(entries[i + 1]);
|
|
1105
|
+
service.selector[key] = val;
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
(this.context as any).k8sServices = (this.context as any).k8sServices || new Map();
|
|
1110
|
+
(this.context as any).k8sServices.set(name, service);
|
|
1111
|
+
|
|
1112
|
+
return { status: "registered", "k8s-service": name };
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
// Phase 11 v11: [K8S-INGRESS name :rules [...]]
|
|
1116
|
+
private handleK8sIngressBlock(block: Block): any {
|
|
1117
|
+
const name = block.name;
|
|
1118
|
+
const rulesNode = block.fields.get("rules");
|
|
1119
|
+
const annotations = this.eval(block.fields.get("annotations")) || {};
|
|
1120
|
+
|
|
1121
|
+
const ingress: any = {
|
|
1122
|
+
name,
|
|
1123
|
+
rules: [],
|
|
1124
|
+
annotations,
|
|
1125
|
+
};
|
|
1126
|
+
|
|
1127
|
+
(this.context as any).k8sIngresses = (this.context as any).k8sIngresses || new Map();
|
|
1128
|
+
(this.context as any).k8sIngresses.set(name, ingress);
|
|
1129
|
+
|
|
1130
|
+
return { status: "registered", "k8s-ingress": name };
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
// Phase 11 v11: AWS cloud blocks (AWS, AWS-S3, AWS-LAMBDA, AWS-RDS, AWS-SQS)
|
|
1134
|
+
private handleAwsBlock(block: Block): any {
|
|
1135
|
+
const name = block.name;
|
|
1136
|
+
const blockType = block.type;
|
|
1137
|
+
const region = this.eval(block.fields.get("region")) || "ap-northeast-2";
|
|
1138
|
+
const endpoint = this.eval(block.fields.get("endpoint")) || "";
|
|
1139
|
+
|
|
1140
|
+
const awsConfig: any = {
|
|
1141
|
+
name,
|
|
1142
|
+
type: blockType,
|
|
1143
|
+
region,
|
|
1144
|
+
endpoint,
|
|
1145
|
+
};
|
|
1146
|
+
|
|
1147
|
+
(this.context as any).aws = (this.context as any).aws || new Map();
|
|
1148
|
+
(this.context as any).aws.set(name, awsConfig);
|
|
1149
|
+
|
|
1150
|
+
return { status: "registered", aws: name };
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
// Phase 11 v11: GCP cloud blocks (GCP, GCP-CLOUD-RUN, GCP-BIGQUERY)
|
|
1154
|
+
private handleGcpBlock(block: Block): any {
|
|
1155
|
+
const name = block.name;
|
|
1156
|
+
const blockType = block.type;
|
|
1157
|
+
const projectId = this.eval(block.fields.get("project-id")) || "project";
|
|
1158
|
+
const region = this.eval(block.fields.get("region")) || "asia-northeast3";
|
|
1159
|
+
|
|
1160
|
+
const gcpConfig: any = {
|
|
1161
|
+
name,
|
|
1162
|
+
type: blockType,
|
|
1163
|
+
projectId,
|
|
1164
|
+
region,
|
|
1165
|
+
};
|
|
1166
|
+
|
|
1167
|
+
(this.context as any).gcp = (this.context as any).gcp || new Map();
|
|
1168
|
+
(this.context as any).gcp.set(name, gcpConfig);
|
|
1169
|
+
|
|
1170
|
+
return { status: "registered", gcp: name };
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
// Phase 11 v11: Azure cloud blocks (AZURE, AZURE-FUNCTION, AZURE-COSMOS)
|
|
1174
|
+
private handleAzureBlock(block: Block): any {
|
|
1175
|
+
const name = block.name;
|
|
1176
|
+
const blockType = block.type;
|
|
1177
|
+
const subscriptionId = this.eval(block.fields.get("subscription-id")) || "";
|
|
1178
|
+
const resourceGroup = this.eval(block.fields.get("resource-group")) || "default";
|
|
1179
|
+
|
|
1180
|
+
const azureConfig: any = {
|
|
1181
|
+
name,
|
|
1182
|
+
type: blockType,
|
|
1183
|
+
subscriptionId,
|
|
1184
|
+
resourceGroup,
|
|
1185
|
+
};
|
|
1186
|
+
|
|
1187
|
+
(this.context as any).azure = (this.context as any).azure || new Map();
|
|
1188
|
+
(this.context as any).azure.set(name, azureConfig);
|
|
1189
|
+
|
|
1190
|
+
return { status: "registered", azure: name };
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
// Phase 97: [TOOL name :desc "..." :input {x :number y :number} :output :number :body (+ $x $y)]
|
|
1194
|
+
private handleToolBlock(block: any): any {
|
|
1195
|
+
const name = block.name as string;
|
|
1196
|
+
const desc = block.fields.has("desc") ? this.eval(block.fields.get("desc")) : "";
|
|
1197
|
+
const bodyNode = block.fields.get("body");
|
|
1198
|
+
|
|
1199
|
+
// :input 스키마 파싱
|
|
1200
|
+
const inputSchema: Record<string, any> = {};
|
|
1201
|
+
if (block.fields.has("input")) {
|
|
1202
|
+
const inputNode = block.fields.get("input");
|
|
1203
|
+
// Map 블록이면 key→keyword value 형태
|
|
1204
|
+
if (inputNode?.kind === "block" && inputNode?.type === "Map") {
|
|
1205
|
+
const entries = inputNode.fields.get("entries");
|
|
1206
|
+
if (Array.isArray(entries)) {
|
|
1207
|
+
for (let i = 0; i < entries.length - 1; i += 2) {
|
|
1208
|
+
const key = entries[i]?.kind === "keyword" ? entries[i].name
|
|
1209
|
+
: entries[i]?.kind === "literal" ? String(entries[i].value)
|
|
1210
|
+
: String(entries[i]);
|
|
1211
|
+
const valNode = entries[i + 1];
|
|
1212
|
+
const valStr = valNode?.kind === "keyword" ? valNode.name
|
|
1213
|
+
: valNode?.kind === "literal" ? String(valNode.value)
|
|
1214
|
+
: "any";
|
|
1215
|
+
inputSchema[key] = valStr;
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
// :output 스키마
|
|
1222
|
+
let outputSchema: string = "any";
|
|
1223
|
+
if (block.fields.has("output")) {
|
|
1224
|
+
const outNode = block.fields.get("output");
|
|
1225
|
+
outputSchema = outNode?.kind === "keyword" ? outNode.name
|
|
1226
|
+
: outNode?.kind === "literal" ? String(outNode.value)
|
|
1227
|
+
: "any";
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
const interp = this;
|
|
1231
|
+
const toolDef: ToolDefinition = {
|
|
1232
|
+
name,
|
|
1233
|
+
description: String(desc || ""),
|
|
1234
|
+
inputSchema,
|
|
1235
|
+
outputSchema: outputSchema as any,
|
|
1236
|
+
execute: (args: Record<string, any>) => {
|
|
1237
|
+
// 인자들을 $key 변수로 바인딩하여 body 실행
|
|
1238
|
+
interp.context.variables.push();
|
|
1239
|
+
try {
|
|
1240
|
+
for (const [k, v] of Object.entries(args)) {
|
|
1241
|
+
interp.context.variables.set(`$${k}`, v);
|
|
1242
|
+
}
|
|
1243
|
+
return interp.eval(bodyNode);
|
|
1244
|
+
} finally {
|
|
1245
|
+
interp.context.variables.pop();
|
|
1246
|
+
}
|
|
1247
|
+
},
|
|
1248
|
+
};
|
|
1249
|
+
|
|
1250
|
+
globalToolRegistry.register(toolDef);
|
|
1251
|
+
return toolDef;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
// Phase 97: [USE-TOOL toolname :args {key val ...}]
|
|
1255
|
+
private handleUseToolBlock(block: any): any {
|
|
1256
|
+
const name = block.name as string;
|
|
1257
|
+
const args: Record<string, any> = {};
|
|
1258
|
+
|
|
1259
|
+
if (block.fields.has("args")) {
|
|
1260
|
+
const argsNode = block.fields.get("args");
|
|
1261
|
+
if (argsNode?.kind === "block" && argsNode?.type === "Map") {
|
|
1262
|
+
const entries = argsNode.fields.get("entries");
|
|
1263
|
+
if (Array.isArray(entries)) {
|
|
1264
|
+
for (let i = 0; i < entries.length - 1; i += 2) {
|
|
1265
|
+
const key = entries[i]?.kind === "keyword" ? entries[i].name
|
|
1266
|
+
: entries[i]?.kind === "literal" ? String(entries[i].value)
|
|
1267
|
+
: String(entries[i]);
|
|
1268
|
+
args[key] = this.eval(entries[i + 1]);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
const result = globalToolRegistry.executeSync(name, args);
|
|
1275
|
+
return result.success ? result.output : (() => { throw new Error(result.error || `Tool failed: ${name}`); })();
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
private serverConfig: { port: number; host: string } | null = null;
|
|
1279
|
+
|
|
1280
|
+
private handleServerBlock(block: Block): void {
|
|
1281
|
+
// [SERVER name :port 3009 :host "0.0.0.0" ...]
|
|
1282
|
+
const port = Number(this.getFieldValue(block, "port") || 3009);
|
|
1283
|
+
const host = String(this.getFieldValue(block, "host") || "0.0.0.0");
|
|
1284
|
+
this.serverConfig = { port, host };
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
private handleRouteBlock(block: Block): void {
|
|
1288
|
+
// [ROUTE name :method "GET" :path "/api/health" :handler (json-response ...)]
|
|
1289
|
+
const method = this.getFieldValue(block, "method", "GET");
|
|
1290
|
+
const path = this.getFieldValue(block, "path", "/");
|
|
1291
|
+
const handler = block.fields.get("handler") as ASTNode;
|
|
1292
|
+
|
|
1293
|
+
if (!handler) {
|
|
1294
|
+
throw new Error(`[ROUTE ${block.name}] Missing :handler`);
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
this.context.routes.set(block.name, {
|
|
1298
|
+
name: block.name,
|
|
1299
|
+
method: method.toLowerCase(),
|
|
1300
|
+
path,
|
|
1301
|
+
handler,
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
private handleFuncBlock(block: Block): void {
|
|
1306
|
+
// [FUNC name :params [$x $y] :body (+ $x $y)]
|
|
1307
|
+
// Phase 3: [FUNC name :params [[$x int] [$y int]] :return int :body (+ $x $y)]
|
|
1308
|
+
const paramsField = block.fields.get("params");
|
|
1309
|
+
const params: string[] = [];
|
|
1310
|
+
|
|
1311
|
+
if (paramsField && (paramsField as any).kind === "variable") {
|
|
1312
|
+
params.push(((paramsField as any) as any).name);
|
|
1313
|
+
} else if ((paramsField as any)?.kind === "block" && (paramsField as any).type === "Array") {
|
|
1314
|
+
const items = (paramsField as any).fields.get("items");
|
|
1315
|
+
if (Array.isArray(items)) {
|
|
1316
|
+
params.push(...items.map((item: any) => {
|
|
1317
|
+
// Phase 3: New syntax [[$x int] [$y string]] or [[x int] [y string]] - item is an Array block with [Name, Type]
|
|
1318
|
+
if (item.kind === "block" && item.type === "Array") {
|
|
1319
|
+
const innerItems = item.fields.get("items");
|
|
1320
|
+
if (Array.isArray(innerItems) && innerItems.length > 0) {
|
|
1321
|
+
const firstItem = innerItems[0];
|
|
1322
|
+
if (firstItem.kind === "variable") {
|
|
1323
|
+
return firstItem.name; // Extract parameter name from Variable ($x)
|
|
1324
|
+
}
|
|
1325
|
+
// Phase 4: Support symbol literals as parameter names (x without $)
|
|
1326
|
+
if (firstItem.kind === "literal" && firstItem.type === "symbol") {
|
|
1327
|
+
return "$" + firstItem.value; // Convert symbol to variable: x → $x
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
// Old syntax [$x $y] - item is a Variable directly
|
|
1332
|
+
if (item.kind === "variable") {
|
|
1333
|
+
// Normalize to $-prefix so callUserFunction stores correctly
|
|
1334
|
+
return item.name.startsWith("$") ? item.name : "$" + item.name;
|
|
1335
|
+
}
|
|
1336
|
+
// Fallback
|
|
1337
|
+
return item.name ? (item.name.startsWith("$") ? item.name : "$" + item.name) : "$unknown";
|
|
1338
|
+
}));
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
const body = block.fields.get("body") as ASTNode;
|
|
1343
|
+
if (!body) {
|
|
1344
|
+
throw new Error(`[FUNC ${block.name}] Missing :body`);
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
// Get parameter types from typeAnnotations
|
|
1348
|
+
let paramTypes: TypeAnnotation[] = [];
|
|
1349
|
+
let returnType: TypeAnnotation = { kind: "type" as const, name: "any" };
|
|
1350
|
+
|
|
1351
|
+
if (block.typeAnnotations && this.context.typeChecker) {
|
|
1352
|
+
const paramsTypeAnnotations = block.typeAnnotations.get("params");
|
|
1353
|
+
if (Array.isArray(paramsTypeAnnotations)) {
|
|
1354
|
+
paramTypes = paramsTypeAnnotations;
|
|
1355
|
+
} else {
|
|
1356
|
+
paramTypes = params.map(() => ({ kind: "type" as const, name: "any" }));
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
const returnTypeAnnotation = block.typeAnnotations.get("return");
|
|
1360
|
+
if (returnTypeAnnotation) {
|
|
1361
|
+
returnType = returnTypeAnnotation as TypeAnnotation;
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
// Phase 4: Register generic function if :generics present
|
|
1366
|
+
const isGeneric = block.generics && block.generics.length > 0;
|
|
1367
|
+
|
|
1368
|
+
this.context.functions.set(block.name, {
|
|
1369
|
+
name: block.name,
|
|
1370
|
+
params,
|
|
1371
|
+
body,
|
|
1372
|
+
generics: block.generics, // Store generic type variables
|
|
1373
|
+
paramTypes,
|
|
1374
|
+
returnType,
|
|
1375
|
+
});
|
|
1376
|
+
|
|
1377
|
+
// Phase 3-4: Register function type in type checker
|
|
1378
|
+
if (this.context.typeChecker) {
|
|
1379
|
+
if (isGeneric && block.generics) {
|
|
1380
|
+
// Phase 4: Register generic function
|
|
1381
|
+
this.context.typeChecker.registerGenericFunction(block.name, block.generics, paramTypes, returnType);
|
|
1382
|
+
} else {
|
|
1383
|
+
// Phase 3: Register regular function
|
|
1384
|
+
this.context.typeChecker.registerFunction(block.name, paramTypes, returnType);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
// Phase 60: RuntimeTypeChecker에도 시그니처 등록 (타입 어노테이션이 있는 함수만)
|
|
1389
|
+
// typeAnnotations가 있으면 = 명시적 타입 어노테이션이 있는 함수
|
|
1390
|
+
if (this.context.runtimeTypeChecker && block.typeAnnotations) {
|
|
1391
|
+
const paramTypeNames = paramTypes.map((p) => p.name || "any");
|
|
1392
|
+
const retTypeName = returnType.name || "any";
|
|
1393
|
+
this.context.runtimeTypeChecker.registerFunc(block.name, paramTypeNames, retTypeName);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
private handleIntentBlock(block: Block): void {
|
|
1398
|
+
// [INTENT name :key1 val1 :key2 val2 ...]
|
|
1399
|
+
const intentFields = new Map<string, ASTNode>();
|
|
1400
|
+
for (const [key, value] of block.fields) {
|
|
1401
|
+
if (key.startsWith(":")) {
|
|
1402
|
+
intentFields.set(key, value as ASTNode);
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
this.context.intents.set(block.name, {
|
|
1407
|
+
name: block.name,
|
|
1408
|
+
fields: intentFields,
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
private handleMiddlewareBlock(block: Block): void {
|
|
1413
|
+
const config = new Map<string, any>();
|
|
1414
|
+
for (const [key, value] of block.fields) {
|
|
1415
|
+
if (key.startsWith(":")) {
|
|
1416
|
+
config.set(key, this.eval(value as ASTNode));
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
this.context.middleware.push({
|
|
1421
|
+
name: block.name,
|
|
1422
|
+
config,
|
|
1423
|
+
});
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
private handleWebSocketBlock(block: Block): void {
|
|
1427
|
+
// [WEBSOCKET name :path "/ws/events" :on-connect ... :on-message ... :on-disconnect ...]
|
|
1428
|
+
// WebSocket support (implement later)
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
private handleErrorHandlerBlock(block: Block): void {
|
|
1432
|
+
// [ERROR-HANDLER name :on-404 ... :on-500 ...]
|
|
1433
|
+
const on404 = block.fields.get("on-404");
|
|
1434
|
+
const on500 = block.fields.get("on-500");
|
|
1435
|
+
|
|
1436
|
+
if (on404) this.context.errorHandlers.handlers.set(404, on404 as ASTNode);
|
|
1437
|
+
if (on500) this.context.errorHandlers.handlers.set(500, on500 as ASTNode);
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
eval(node: ASTNode): any {
|
|
1441
|
+
if (!node) return null;
|
|
1442
|
+
|
|
1443
|
+
// Literal values
|
|
1444
|
+
if ((node as any).kind === "literal") {
|
|
1445
|
+
const lit = node as Literal;
|
|
1446
|
+
// 문자열 보간: "Hello {$name}!" 또는 "결과: {(+ $a $b)}"
|
|
1447
|
+
if (lit.type === "string" && typeof lit.value === "string" &&
|
|
1448
|
+
(lit.value.includes("{$") || lit.value.includes("{("))) {
|
|
1449
|
+
return this.interpolateString(lit.value as string);
|
|
1450
|
+
}
|
|
1451
|
+
// Self-hosting: bare symbol (without $) as variable reference
|
|
1452
|
+
// e.g. (define i 0) then (+ i 1) → looks up $i
|
|
1453
|
+
if (lit.type === "symbol" && typeof lit.value === "string") {
|
|
1454
|
+
// true/false/null always evaluate to their proper JS values
|
|
1455
|
+
if (lit.value === "true") return true;
|
|
1456
|
+
if (lit.value === "false") return false;
|
|
1457
|
+
if (lit.value === "null") return null;
|
|
1458
|
+
// P0-1 (2026-04-25): nil은 JS null과 동일 (Lisp/Clojure 관례)
|
|
1459
|
+
// 사용자 평가 P0-1 — (if nil ...) → "yes" 잘못된 truthy 평가 해소
|
|
1460
|
+
if (lit.value === "nil") return null;
|
|
1461
|
+
const bareName = lit.value as string;
|
|
1462
|
+
const varName = "$" + bareName;
|
|
1463
|
+
if (this.context.variables.has(varName)) {
|
|
1464
|
+
return this.context.variables.get(varName);
|
|
1465
|
+
}
|
|
1466
|
+
if (this.context.variables.has(bareName)) {
|
|
1467
|
+
return this.context.variables.get(bareName);
|
|
1468
|
+
}
|
|
1469
|
+
// v11.10: FL_STRICT=1 opt-in → 정의 없는 심볼을 throw.
|
|
1470
|
+
// 기본은 permissive (하위 호환). AI-first 프로젝트는 FL_STRICT=1 권장.
|
|
1471
|
+
if (process.env.FL_STRICT === "1" && !this.context.functions.has(bareName)) {
|
|
1472
|
+
const line = (lit as any).line;
|
|
1473
|
+
throw new Error(`[E_UNRESOLVED_SYMBOL] '${bareName}' at line ${line || this.currentLine}, col 0 — set FL_STRICT=0 to silence`);
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
return lit.value;
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
// Variables
|
|
1480
|
+
if ((node as any).kind === "variable") {
|
|
1481
|
+
let varName = (node as Variable).name;
|
|
1482
|
+
const line = (node as any).line;
|
|
1483
|
+
const fileHint = this.currentFilePath
|
|
1484
|
+
? this.currentFilePath.replace(/^.*\//, "")
|
|
1485
|
+
: "";
|
|
1486
|
+
const locSuffix = line
|
|
1487
|
+
? ` (${fileHint ? fileHint + ":" : "at line "}${line})`
|
|
1488
|
+
: "";
|
|
1489
|
+
// Self-hosting: dot field access — "env.vars" → resolve "env", then access "vars"
|
|
1490
|
+
if (varName.includes(".")) {
|
|
1491
|
+
const parts = varName.split(".");
|
|
1492
|
+
let obj = this.context.variables.has("$" + parts[0])
|
|
1493
|
+
? this.context.variables.get("$" + parts[0])
|
|
1494
|
+
: this.context.variables.get(parts[0]);
|
|
1495
|
+
if (obj === undefined && !this.context.variables.has("$" + parts[0]) && !this.context.variables.has(parts[0])) {
|
|
1496
|
+
// Phase Y-1: VariableNotFoundError로 변경
|
|
1497
|
+
const scopeVars = this.context.variables.getAllVars();
|
|
1498
|
+
const similar = suggestSimilar(parts[0], scopeVars);
|
|
1499
|
+
const err = new VariableNotFoundError(
|
|
1500
|
+
parts[0],
|
|
1501
|
+
scopeVars.filter(v => !v.startsWith("__")),
|
|
1502
|
+
similar,
|
|
1503
|
+
fileHint,
|
|
1504
|
+
line || this.currentLine,
|
|
1505
|
+
0
|
|
1506
|
+
);
|
|
1507
|
+
// Phase Y-2-B: callStack을 에러 발생 시점에 즉시 캡처 (finally pop 전)
|
|
1508
|
+
(err as any).__flCallStack = this.context.callStack.slice();
|
|
1509
|
+
this.context.lastError = {
|
|
1510
|
+
message: err.message,
|
|
1511
|
+
code: err.code,
|
|
1512
|
+
file: err.file,
|
|
1513
|
+
line: err.line,
|
|
1514
|
+
col: err.col,
|
|
1515
|
+
callStack: this.context.callStack.slice(),
|
|
1516
|
+
variables: Object.fromEntries(
|
|
1517
|
+
this.context.variables.getAllVars().slice(0, 10).map(v => [v, this.context.variables.get(v)])
|
|
1518
|
+
),
|
|
1519
|
+
hint: err.hint,
|
|
1520
|
+
};
|
|
1521
|
+
throw err;
|
|
1522
|
+
}
|
|
1523
|
+
for (let p = 1; p < parts.length; p++) {
|
|
1524
|
+
if (obj === null || obj === undefined) return null;
|
|
1525
|
+
obj = typeof obj === "object" ? obj[parts[p]] : null;
|
|
1526
|
+
}
|
|
1527
|
+
return obj ?? null;
|
|
1528
|
+
}
|
|
1529
|
+
// Phase 4: Handle variable name resolution
|
|
1530
|
+
if (this.context.variables.has("$" + varName)) {
|
|
1531
|
+
return this.context.variables.get("$" + varName);
|
|
1532
|
+
}
|
|
1533
|
+
if (this.context.variables.has(varName)) {
|
|
1534
|
+
return this.context.variables.get(varName);
|
|
1535
|
+
}
|
|
1536
|
+
// builtin 함수명을 first-class value로: (comp inc str-to-upper) 같은 패턴 지원
|
|
1537
|
+
if (this.context.functions.has(varName) || this.context.functions.has("$" + varName)) {
|
|
1538
|
+
return { kind: "builtin-fn", name: varName };
|
|
1539
|
+
}
|
|
1540
|
+
// Phase Y-1: VariableNotFoundError로 변경
|
|
1541
|
+
const scopeVars = this.context.variables.getAllVars();
|
|
1542
|
+
const similar = suggestSimilar(varName, scopeVars);
|
|
1543
|
+
const err = new VariableNotFoundError(
|
|
1544
|
+
varName,
|
|
1545
|
+
scopeVars.filter(v => !v.startsWith("__")),
|
|
1546
|
+
similar,
|
|
1547
|
+
fileHint,
|
|
1548
|
+
line || this.currentLine,
|
|
1549
|
+
0
|
|
1550
|
+
);
|
|
1551
|
+
// Phase Y-2-B: callStack을 에러 발생 시점에 즉시 캡처 (finally pop 전)
|
|
1552
|
+
(err as any).__flCallStack = this.context.callStack.slice();
|
|
1553
|
+
this.context.lastError = {
|
|
1554
|
+
message: err.message,
|
|
1555
|
+
code: err.code,
|
|
1556
|
+
file: err.file,
|
|
1557
|
+
line: err.line,
|
|
1558
|
+
col: err.col,
|
|
1559
|
+
callStack: this.context.callStack.slice(),
|
|
1560
|
+
variables: Object.fromEntries(
|
|
1561
|
+
scopeVars.slice(0, 10).map(v => [v, this.context.variables.get(v)])
|
|
1562
|
+
),
|
|
1563
|
+
hint: err.hint,
|
|
1564
|
+
};
|
|
1565
|
+
throw err;
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
// Keywords
|
|
1569
|
+
if ((node as any).kind === "keyword") {
|
|
1570
|
+
return (node as Keyword).name;
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
// S-expressions: (op arg1 arg2 ...)
|
|
1574
|
+
if ((node as any).kind === "sexpr") {
|
|
1575
|
+
return this.evalSExpr(node as SExpr);
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
// Blocks (nested structures)
|
|
1579
|
+
if ((node as any).kind === "block") {
|
|
1580
|
+
const block = node as Block;
|
|
1581
|
+
|
|
1582
|
+
// Control blocks (FUNC, SERVER, ROUTE, etc.) must NOT be eval'd here
|
|
1583
|
+
// They should be handled by interpret() or evalBlock(), not eval()
|
|
1584
|
+
if (isBlock(block) && isControlBlock(block)) {
|
|
1585
|
+
throw new Error(`Control block [${block.type}] should not be eval'd directly. This block must be processed by interpret() or evalBlock().`);
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
if (block.type === "Array") {
|
|
1589
|
+
const items = block.fields.get("items");
|
|
1590
|
+
if (Array.isArray(items)) {
|
|
1591
|
+
return items.map((item) => this.eval(item));
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
// Phase 9d: Map literal: {:key1 value1 :key2 value2 ...}
|
|
1595
|
+
if (block.type === "Map") {
|
|
1596
|
+
const result: Record<string, any> = {};
|
|
1597
|
+
for (const [key, value] of block.fields) {
|
|
1598
|
+
result[key] = Array.isArray(value) ? value.map((v) => this.eval(v)) : this.eval(value);
|
|
1599
|
+
}
|
|
1600
|
+
return result;
|
|
1601
|
+
}
|
|
1602
|
+
// For other block types (should only be Array/Map), throw error
|
|
1603
|
+
throw new Error(`Unknown block type: ${block.type}`);
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
// Pattern matching (Phase 4 Week 3-4)
|
|
1607
|
+
if ((node as any).kind === "pattern-match") {
|
|
1608
|
+
return this.evalPatternMatch(node as PatternMatch);
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
// Function value (Phase 4 Week 1: First-class functions)
|
|
1612
|
+
if ((node as any).kind === "function-value") {
|
|
1613
|
+
return node; // Return the function value as-is
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
// Type Class (Phase 5 Week 2: Type Classes)
|
|
1617
|
+
if ((node as any).kind === "type-class") {
|
|
1618
|
+
return this.evalTypeClass(node as TypeClass);
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
// Type Class Instance (Phase 5 Week 2: Type Classes)
|
|
1622
|
+
if ((node as any).kind === "type-class-instance") {
|
|
1623
|
+
return this.evalInstance(node as TypeClassInstance);
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
// Template strings (Phase A-2: "Hello ${name}")
|
|
1627
|
+
if ((node as any).kind === "template-string") {
|
|
1628
|
+
const templateNode = node as TemplateString;
|
|
1629
|
+
return this.interpolateString(templateNode.value);
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
// Try-catch-finally blocks (Phase 11)
|
|
1633
|
+
if ((node as any).kind === "try-block") {
|
|
1634
|
+
return this.evalTryBlock(node as TryBlock);
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
// Throw expressions (Phase 11)
|
|
1638
|
+
if ((node as any).kind === "throw") {
|
|
1639
|
+
return this.evalThrow(node as ThrowExpression);
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
// Loop blocks (Phase B-1)
|
|
1643
|
+
if ((node as any).kind === "loop") {
|
|
1644
|
+
return this.evalLoop(node as any);
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
return null;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
private evalSExpr(expr: SExpr): any {
|
|
1651
|
+
if (expr.line !== undefined) this.currentLine = expr.line;
|
|
1652
|
+
let op = expr.op;
|
|
1653
|
+
|
|
1654
|
+
// Phase L1: op MUST be normalized before any use
|
|
1655
|
+
if (typeof op !== "string") {
|
|
1656
|
+
op = (op as any)?.name ? (op as any).name : String(op);
|
|
1657
|
+
}
|
|
1658
|
+
op = String(op).trim();
|
|
1659
|
+
|
|
1660
|
+
// Phase 5 Week 2: Method dispatch (ClassName:methodName pattern)
|
|
1661
|
+
if (op.includes(":")) {
|
|
1662
|
+
const [className, methodName] = op.split(":");
|
|
1663
|
+
|
|
1664
|
+
// For now, assume the first argument is the concrete type value
|
|
1665
|
+
// In future, we can extract the type from the value itself
|
|
1666
|
+
if (expr.args.length > 0) {
|
|
1667
|
+
const concreteValue = this.eval(expr.args[0]);
|
|
1668
|
+
const concreteType = this.getConcreteType(concreteValue);
|
|
1669
|
+
|
|
1670
|
+
if (concreteType) {
|
|
1671
|
+
const method = this.resolveMethod(className, concreteType, methodName);
|
|
1672
|
+
if (method) {
|
|
1673
|
+
// Evaluate remaining arguments
|
|
1674
|
+
const args = expr.args.slice(1).map((arg) => this.eval(arg));
|
|
1675
|
+
|
|
1676
|
+
// Call the method with the concrete value as first argument
|
|
1677
|
+
if ((method as any).kind === "function-value") {
|
|
1678
|
+
return this.callFunctionValue(method, [concreteValue, ...args]);
|
|
1679
|
+
} else if (typeof method === "function") {
|
|
1680
|
+
return method(concreteValue, ...args);
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
// If method dispatch fails, fall through to standard function lookup
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
// Phase 57: Dispatch to specialized modules
|
|
1690
|
+
const AI_OPS = new Set(["search","fetch","learn","recall","remember","forget","observe","analyze","decide","act","verify","await"]);
|
|
1691
|
+
const INFRA_OPS = new Set(["DOCKERFILE","dockerfile","DOCKER-COMPOSE","docker-compose","K8S-DEPLOYMENT","deployment","K8S-SERVICE","service","K8S-INGRESS","ingress","GITHUB-ACTIONS","github-actions","ci","AWS-S3","aws-s3","AWS-LAMBDA","aws-lambda","AWS-RDS","aws-rds","GCP-RUN","gcp-run","AZURE-FUNCTION","azure-function"]);
|
|
1692
|
+
const STYLE_OPS = new Set(["STYLE","style","THEME","theme"]);
|
|
1693
|
+
const SPECIAL_OPS = new Set(["fn","defn","defun","async","set!","define","func-ref","call","compose","comp","pipe","->","->>","as->","?.","?.","|>","??","let","set","if","if-let","when","when-not","when-let","unless","cond","case","for","do","begin","progn","loop","recur","while","doseq","dotimes","and","or","defmacro","macroexpand","defstruct","defprotocol","impl","parallel","race","with-timeout","fl-try","use","defprop","map-keys","map_keys","map-vals","map_vals","return","group-by","group_by","partial","memoize","deftest","describe","it","is","is=","run-tests","test-summary","import","migrate"]);
|
|
1694
|
+
|
|
1695
|
+
if (AI_OPS.has(op)) return evalAiBlock(this, op, expr);
|
|
1696
|
+
if (INFRA_OPS.has(op)) return evalInfraBlock(this, op, expr);
|
|
1697
|
+
if (STYLE_OPS.has(op)) return evalStyleBlock(this, op, expr);
|
|
1698
|
+
if (SPECIAL_OPS.has(op)) return evalSpecialForm(this, op, expr);
|
|
1699
|
+
|
|
1700
|
+
// Phase 94: REFLECT — 자기 평가/반성 특수 폼
|
|
1701
|
+
if (op === "REFLECT") {
|
|
1702
|
+
const interp = this;
|
|
1703
|
+
const ev = (node: any) => interp.eval(node);
|
|
1704
|
+
const callFnVal = (fn: any, args: any[]) => interp.callFunctionValue(fn, args);
|
|
1705
|
+
|
|
1706
|
+
// 키워드 파싱: :output :criteria :threshold :on-fail :revise
|
|
1707
|
+
let outputExpr: any = null;
|
|
1708
|
+
let criteriaExpr: any = null;
|
|
1709
|
+
let thresholdExpr: any = null;
|
|
1710
|
+
let onFailExpr: any = null;
|
|
1711
|
+
let reviseExpr: any = null;
|
|
1712
|
+
|
|
1713
|
+
// 키워드 추출 헬퍼 — kind="keyword" 또는 kind="literal"(string) 모두 지원
|
|
1714
|
+
const getKeyword = (arg: any): string | null => {
|
|
1715
|
+
if (arg?.kind === "keyword") return arg.name;
|
|
1716
|
+
if (arg?.kind === "literal" && arg?.type === "string") return arg.value;
|
|
1717
|
+
return null;
|
|
1718
|
+
};
|
|
1719
|
+
|
|
1720
|
+
for (let i = 0; i < expr.args.length; i++) {
|
|
1721
|
+
const arg = expr.args[i];
|
|
1722
|
+
const kw = getKeyword(arg);
|
|
1723
|
+
if (kw !== null && i + 1 < expr.args.length) {
|
|
1724
|
+
const next = expr.args[i + 1];
|
|
1725
|
+
if (kw === "output") { outputExpr = next; i++; }
|
|
1726
|
+
else if (kw === "criteria") { criteriaExpr = next; i++; }
|
|
1727
|
+
else if (kw === "threshold") { thresholdExpr = next; i++; }
|
|
1728
|
+
else if (kw === "on-fail") { onFailExpr = next; i++; }
|
|
1729
|
+
else if (kw === "revise") { reviseExpr = next; i++; }
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
// output 실행
|
|
1734
|
+
const outputVal = outputExpr != null ? ev(outputExpr) : null;
|
|
1735
|
+
|
|
1736
|
+
// criteria 배열 실행 — 각 원소는 함수 또는 fn 값
|
|
1737
|
+
const criteriaFns: Array<(v: any) => number> = [];
|
|
1738
|
+
if (criteriaExpr != null) {
|
|
1739
|
+
const criteriaRaw = ev(criteriaExpr);
|
|
1740
|
+
if (Array.isArray(criteriaRaw)) {
|
|
1741
|
+
for (const c of criteriaRaw) {
|
|
1742
|
+
if (typeof c === "function") {
|
|
1743
|
+
criteriaFns.push(c);
|
|
1744
|
+
} else if (c?.kind === "function-value") {
|
|
1745
|
+
criteriaFns.push((v: any) => {
|
|
1746
|
+
const r = callFnVal(c, [v]);
|
|
1747
|
+
return typeof r === "number" ? r : (r ? 1 : 0);
|
|
1748
|
+
});
|
|
1749
|
+
} else if (typeof c === "number") {
|
|
1750
|
+
const score = c;
|
|
1751
|
+
criteriaFns.push(() => score);
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
// threshold
|
|
1758
|
+
const threshold = thresholdExpr != null ? Number(ev(thresholdExpr)) : 0.7;
|
|
1759
|
+
|
|
1760
|
+
// on-fail
|
|
1761
|
+
let onFail: ((r: any) => any) | undefined;
|
|
1762
|
+
if (onFailExpr != null) {
|
|
1763
|
+
const onFailVal = ev(onFailExpr);
|
|
1764
|
+
if (onFailVal?.kind === "function-value") {
|
|
1765
|
+
onFail = (r: any) => callFnVal(onFailVal, [r]);
|
|
1766
|
+
} else if (typeof onFailVal === "function") {
|
|
1767
|
+
onFail = onFailVal;
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
// revise
|
|
1772
|
+
let revise: ((r: any) => any) | undefined;
|
|
1773
|
+
if (reviseExpr != null) {
|
|
1774
|
+
const reviseVal = ev(reviseExpr);
|
|
1775
|
+
if (reviseVal?.kind === "function-value") {
|
|
1776
|
+
revise = (r: any) => callFnVal(reviseVal, [r]);
|
|
1777
|
+
} else if (typeof reviseVal === "function") {
|
|
1778
|
+
revise = reviseVal;
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
return evalReflectForm({
|
|
1783
|
+
output: outputVal,
|
|
1784
|
+
criteria: criteriaFns,
|
|
1785
|
+
threshold,
|
|
1786
|
+
onFail,
|
|
1787
|
+
revise,
|
|
1788
|
+
});
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
// Phase 92: COT — Chain-of-Thought 특수 폼
|
|
1792
|
+
if (op === "COT") {
|
|
1793
|
+
const interp = this;
|
|
1794
|
+
const result = evalCotForm(
|
|
1795
|
+
expr.args,
|
|
1796
|
+
(node: any) => interp.eval(node),
|
|
1797
|
+
(name: string, value: any) => interp.context.variables.set(name, value),
|
|
1798
|
+
(name: string) => interp.context.variables.get(name),
|
|
1799
|
+
);
|
|
1800
|
+
// conclude fn이 function-value인 경우 실제 호출
|
|
1801
|
+
if (result.conclusion?.kind === "function-value") {
|
|
1802
|
+
const stepsVar = interp.context.variables.get("$__cot_steps__");
|
|
1803
|
+
result.conclusion = interp.callFunctionValue(result.conclusion, [stepsVar]);
|
|
1804
|
+
}
|
|
1805
|
+
return result;
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
// Phase 93: TOT — Tree-of-Thought 특수 폼
|
|
1809
|
+
// (TOT :branch "가설A" (expr-a) :branch "가설B" (expr-b) :eval (fn [$r] ...) :prune 0.3 :select best)
|
|
1810
|
+
if (op === "TOT") {
|
|
1811
|
+
const interp = this;
|
|
1812
|
+
const tot = new TreeOfThought();
|
|
1813
|
+
|
|
1814
|
+
// FL 파서에서 :keyword는 두 형태로 올 수 있음:
|
|
1815
|
+
// 1. {kind: "keyword", name: "branch"}
|
|
1816
|
+
// 2. {kind: "literal", type: "string", value: "branch"} (Colon + Symbol → string literal)
|
|
1817
|
+
function isTotKeyword(node: any, name: string): boolean {
|
|
1818
|
+
if (!node) return false;
|
|
1819
|
+
if (node.kind === "keyword" && node.name === name) return true;
|
|
1820
|
+
if (node.kind === "literal" && node.type === "string" && node.value === name) return true;
|
|
1821
|
+
return false;
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
const args = expr.args;
|
|
1825
|
+
let i = 0;
|
|
1826
|
+
let scoreFnNode: any = null;
|
|
1827
|
+
let pruneThreshold: number | null = null;
|
|
1828
|
+
let selectStrategy: 'best' | 'top-k' = 'best';
|
|
1829
|
+
let selectK = 1;
|
|
1830
|
+
|
|
1831
|
+
while (i < args.length) {
|
|
1832
|
+
const arg = args[i];
|
|
1833
|
+
if (isTotKeyword(arg, "branch")) {
|
|
1834
|
+
i++;
|
|
1835
|
+
const hypoNode = args[i]; i++;
|
|
1836
|
+
const exprNode = args[i]; i++;
|
|
1837
|
+
const hypo = String(interp.eval(hypoNode));
|
|
1838
|
+
const capturedNode = exprNode;
|
|
1839
|
+
tot.branch(hypo, () => interp.eval(capturedNode));
|
|
1840
|
+
} else if (isTotKeyword(arg, "eval")) {
|
|
1841
|
+
i++;
|
|
1842
|
+
scoreFnNode = args[i]; i++;
|
|
1843
|
+
} else if (isTotKeyword(arg, "prune")) {
|
|
1844
|
+
i++;
|
|
1845
|
+
pruneThreshold = Number(interp.eval(args[i])); i++;
|
|
1846
|
+
} else if (isTotKeyword(arg, "select")) {
|
|
1847
|
+
i++;
|
|
1848
|
+
const selVal = interp.eval(args[i]); i++;
|
|
1849
|
+
if (selVal === "top-k") selectStrategy = "top-k";
|
|
1850
|
+
else selectStrategy = "best";
|
|
1851
|
+
} else if (isTotKeyword(arg, "k")) {
|
|
1852
|
+
i++;
|
|
1853
|
+
selectK = Number(interp.eval(args[i])); i++;
|
|
1854
|
+
} else {
|
|
1855
|
+
i++;
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
if (scoreFnNode != null) {
|
|
1860
|
+
const scoreFnVal = interp.eval(scoreFnNode);
|
|
1861
|
+
tot.evaluate((result: any) => {
|
|
1862
|
+
if (typeof scoreFnVal === "function") return Number(scoreFnVal(result)) || 0;
|
|
1863
|
+
if (scoreFnVal?.kind === "function-value") {
|
|
1864
|
+
return Number(interp.callFunctionValue(scoreFnVal, [result])) || 0;
|
|
1865
|
+
}
|
|
1866
|
+
return 0.5;
|
|
1867
|
+
});
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
if (pruneThreshold !== null && !isNaN(pruneThreshold)) {
|
|
1871
|
+
tot.prune(pruneThreshold);
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
return tot.select(selectStrategy, selectK);
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
// Phase 78: (break!) — 디버그 중단점
|
|
1878
|
+
if (op === "break!") {
|
|
1879
|
+
const loc = {
|
|
1880
|
+
file: this.currentFilePath || "<unknown>",
|
|
1881
|
+
line: expr.line ?? this.currentLine,
|
|
1882
|
+
col: 0,
|
|
1883
|
+
};
|
|
1884
|
+
// 현재 환경 스냅샷
|
|
1885
|
+
const env: Record<string, any> = {};
|
|
1886
|
+
try {
|
|
1887
|
+
const snapshot = this.context.variables.snapshot();
|
|
1888
|
+
for (const [k, v] of Object.entries(snapshot)) {
|
|
1889
|
+
env[k] = v;
|
|
1890
|
+
}
|
|
1891
|
+
} catch (_) { /* snapshot 실패 시 무시 */ }
|
|
1892
|
+
handleBreak(this.debugSession, loc, env);
|
|
1893
|
+
return null; // (break!)는 null 반환
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
// map (3-arg comprehension form) → evalSpecialForm; otherwise falls through to builtins
|
|
1897
|
+
if (op === "map" && expr.args.length === 3) {
|
|
1898
|
+
const mapResult = evalSpecialForm(this, op, expr);
|
|
1899
|
+
if (mapResult !== undefined) return mapResult;
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
// Phase 3-E: VM opt-in — FL_VM=1 환경 변수로 활성화
|
|
1903
|
+
// isVMEligible() 통과 시 VM 경로 실행, 실패 시 기존 evalBuiltin으로 fallback
|
|
1904
|
+
if (Interpreter._vmEnabled && isVMEligible(expr)) {
|
|
1905
|
+
try {
|
|
1906
|
+
const chunk = Interpreter._vmCompiler.compile(expr);
|
|
1907
|
+
const optimized = Interpreter._vmOptimizer.optimize(chunk);
|
|
1908
|
+
const initialVars = this.context.variables.snapshot();
|
|
1909
|
+
const vmResult = Interpreter._vm.run(optimized, initialVars);
|
|
1910
|
+
return vmResult;
|
|
1911
|
+
} catch (_vmErr) {
|
|
1912
|
+
// VM 실패 시 조용히 fallback — 기존 경로로 계속 진행
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
// Evaluate args for builtins
|
|
1917
|
+
const args = expr.args.map((arg) => this.eval(arg));
|
|
1918
|
+
|
|
1919
|
+
// Phase 52: qualified function call (module:func pattern) — check BEFORE switch
|
|
1920
|
+
if (args.length >= 1 && typeof args[0] === "string") {
|
|
1921
|
+
const qualifiedName = `${op}:${args[0]}`;
|
|
1922
|
+
if (this.context.functions.has(qualifiedName)) {
|
|
1923
|
+
return this.callUserFunction(qualifiedName, args.slice(1));
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
// Phase 64: 프로토콜 메서드 dispatch — 함수 미발견 시 프로토콜 레지스트리 탐색
|
|
1928
|
+
if (this.context.protocols.hasMethod(op)) {
|
|
1929
|
+
// 첫 번째 인자를 $self로 사용해 타입 확인
|
|
1930
|
+
if (args.length >= 1) {
|
|
1931
|
+
const selfVal = args[0];
|
|
1932
|
+
const impl = this.context.protocols.resolveMethod(op, selfVal);
|
|
1933
|
+
if (impl) {
|
|
1934
|
+
const methodDef = impl.methods.get(op)!;
|
|
1935
|
+
return this.callProtocolMethod(methodDef, selfVal, args.slice(1));
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
// Phase 66: struct native 함수 dispatch (constructor, predicate, accessor)
|
|
1941
|
+
const nativeKey = `__native_${op}`;
|
|
1942
|
+
if ((this.context as any)[nativeKey] !== undefined) {
|
|
1943
|
+
const nativeFn: (...a: any[]) => any = (this.context as any)[nativeKey];
|
|
1944
|
+
return nativeFn(...args);
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
try {
|
|
1948
|
+
return evalBuiltin(this, op, args, expr);
|
|
1949
|
+
} catch (err: any) {
|
|
1950
|
+
// ReturnSignal은 가로채지 않고 함수 경계로 전파
|
|
1951
|
+
if (err && err.constructor && err.constructor.name === "ReturnSignal") throw err;
|
|
1952
|
+
const line = expr.line ?? this.currentLine;
|
|
1953
|
+
const col = (expr as any).col ?? 0;
|
|
1954
|
+
const rawMsg = (err.message ?? String(err));
|
|
1955
|
+
// 함수명 접두어 — 이미 포함되어 있으면 생략
|
|
1956
|
+
const withOp = (rawMsg.startsWith(`[${op}]`) || rawMsg.startsWith(`(${op})`))
|
|
1957
|
+
? rawMsg
|
|
1958
|
+
: `[${op}] ${rawMsg}`;
|
|
1959
|
+
// 첫 번째 (at line ...) 만 유지 — 중복 방지
|
|
1960
|
+
const enhancedMsg = withOp.includes("(at line ")
|
|
1961
|
+
? withOp
|
|
1962
|
+
: `${withOp} (at line ${line}, col ${col})`;
|
|
1963
|
+
|
|
1964
|
+
if (err.code || err.name === "FLRuntimeError") {
|
|
1965
|
+
err.message = enhancedMsg;
|
|
1966
|
+
throw err;
|
|
1967
|
+
} else {
|
|
1968
|
+
const e2 = new Error(enhancedMsg);
|
|
1969
|
+
(e2 as any).__flCallStack = (err as any).__flCallStack ?? (this.callStack.length > 0 ? [...this.callStack] : undefined);
|
|
1970
|
+
throw e2;
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
// Phase 64: 프로토콜 메서드 호출 — $self + 나머지 인자 바인딩
|
|
1976
|
+
private callProtocolMethod(methodDef: { params: string[]; body: any }, selfVal: any, restArgs: any[]): any {
|
|
1977
|
+
// params: ["$self", "$data", ...] 형태
|
|
1978
|
+
const params = methodDef.params;
|
|
1979
|
+
this.context.variables.push();
|
|
1980
|
+
try {
|
|
1981
|
+
// $self 바인딩 (첫 번째 파라미터)
|
|
1982
|
+
if (params.length > 0) {
|
|
1983
|
+
this.context.variables.set(params[0], selfVal);
|
|
1984
|
+
}
|
|
1985
|
+
// 나머지 파라미터 바인딩
|
|
1986
|
+
for (let i = 1; i < params.length; i++) {
|
|
1987
|
+
this.context.variables.set(params[i], restArgs[i - 1] ?? null);
|
|
1988
|
+
}
|
|
1989
|
+
return this.eval(methodDef.body);
|
|
1990
|
+
} finally {
|
|
1991
|
+
this.context.variables.pop();
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
|
|
1995
|
+
// 문자열 보간 처리: {$var} 와 {(expr)} 모두 지원
|
|
1996
|
+
public interpolateString(template: string): string {
|
|
1997
|
+
let result = "";
|
|
1998
|
+
let i = 0;
|
|
1999
|
+
while (i < template.length) {
|
|
2000
|
+
// Pattern 1: ${varName} or ${expr} (preferred syntax)
|
|
2001
|
+
if (template[i] === "$" && i + 1 < template.length && template[i + 1] === "{") {
|
|
2002
|
+
const start = i + 2;
|
|
2003
|
+
const end = template.indexOf("}", start);
|
|
2004
|
+
if (end > start) {
|
|
2005
|
+
const content = template.slice(start, end).trim();
|
|
2006
|
+
let val: any;
|
|
2007
|
+
|
|
2008
|
+
// Try to evaluate as variable or expression
|
|
2009
|
+
if (content.includes("(")) {
|
|
2010
|
+
// ${(expr)} — evaluate as expression
|
|
2011
|
+
try {
|
|
2012
|
+
const tokens = lex("(" + content + ")");
|
|
2013
|
+
const ast = parse(tokens);
|
|
2014
|
+
val = ast.length > 0 ? this.eval(ast[0]) : null;
|
|
2015
|
+
} catch (e: any) {
|
|
2016
|
+
const cause = e?.message ?? String(e);
|
|
2017
|
+
throw new FLRuntimeError(
|
|
2018
|
+
ErrorCodes.E_INVALID_FORM,
|
|
2019
|
+
`문자열 보간 실패 — \${(${content})}\n 원인: ${cause}`,
|
|
2020
|
+
undefined, undefined, undefined,
|
|
2021
|
+
`올바른 표현식인지 확인하세요. 예: "\${(+ 1 2)}" 또는 "\${varName}"`
|
|
2022
|
+
);
|
|
2023
|
+
}
|
|
2024
|
+
} else {
|
|
2025
|
+
// ${varName} — evaluate as variable
|
|
2026
|
+
if (content.includes(".")) {
|
|
2027
|
+
const parts = content.split(".");
|
|
2028
|
+
val = this.context.variables.has("$" + parts[0])
|
|
2029
|
+
? this.context.variables.get("$" + parts[0])
|
|
2030
|
+
: this.context.variables.get(parts[0]);
|
|
2031
|
+
for (let p = 1; p < parts.length; p++) {
|
|
2032
|
+
if (val === null || val === undefined) { val = null; break; }
|
|
2033
|
+
val = typeof val === "object" ? val[parts[p]] : null;
|
|
2034
|
+
}
|
|
2035
|
+
} else {
|
|
2036
|
+
val = this.context.variables.has("$" + content)
|
|
2037
|
+
? this.context.variables.get("$" + content)
|
|
2038
|
+
: this.context.variables.get(content);
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
result += val === null || val === undefined ? "" : String(val);
|
|
2042
|
+
i = end + 1;
|
|
2043
|
+
continue;
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
// Pattern 2: {$varName} (legacy syntax for backward compatibility)
|
|
2048
|
+
if (template[i] === "{" && i + 1 < template.length) {
|
|
2049
|
+
const next = template[i + 1];
|
|
2050
|
+
if (next === "$") {
|
|
2051
|
+
// {$varName} 패턴
|
|
2052
|
+
const end = template.indexOf("}", i);
|
|
2053
|
+
if (end > i) {
|
|
2054
|
+
const varName = template.slice(i + 2, end);
|
|
2055
|
+
let val: any;
|
|
2056
|
+
if (varName.includes(".")) {
|
|
2057
|
+
const parts = varName.split(".");
|
|
2058
|
+
val = this.context.variables.has("$" + parts[0])
|
|
2059
|
+
? this.context.variables.get("$" + parts[0])
|
|
2060
|
+
: this.context.variables.get(parts[0]);
|
|
2061
|
+
for (let p = 1; p < parts.length; p++) {
|
|
2062
|
+
if (val === null || val === undefined) { val = null; break; }
|
|
2063
|
+
val = typeof val === "object" ? val[parts[p]] : null;
|
|
2064
|
+
}
|
|
2065
|
+
} else {
|
|
2066
|
+
val = this.context.variables.has("$" + varName)
|
|
2067
|
+
? this.context.variables.get("$" + varName)
|
|
2068
|
+
: this.context.variables.get(varName);
|
|
2069
|
+
}
|
|
2070
|
+
result += val === null || val === undefined ? "" : String(val);
|
|
2071
|
+
i = end + 1;
|
|
2072
|
+
continue;
|
|
2073
|
+
}
|
|
2074
|
+
} else if (next === "(") {
|
|
2075
|
+
// {(expr)} 패턴 — 균형 잡힌 괄호 탐색
|
|
2076
|
+
let depth = 0;
|
|
2077
|
+
let j = i + 1; // '{' 이후
|
|
2078
|
+
while (j < template.length) {
|
|
2079
|
+
if (template[j] === "(") depth++;
|
|
2080
|
+
else if (template[j] === ")") { depth--; if (depth === 0) break; }
|
|
2081
|
+
j++;
|
|
2082
|
+
}
|
|
2083
|
+
if (j < template.length && j + 1 < template.length && template[j + 1] === "}") {
|
|
2084
|
+
const exprStr = template.slice(i + 1, j + 1); // "(expr)"
|
|
2085
|
+
try {
|
|
2086
|
+
const tokens = lex(exprStr);
|
|
2087
|
+
const ast = parse(tokens);
|
|
2088
|
+
const val = ast.length > 0 ? this.eval(ast[0]) : null;
|
|
2089
|
+
result += val === null || val === undefined ? "" : String(val);
|
|
2090
|
+
} catch (e: any) {
|
|
2091
|
+
const cause = e?.message ?? String(e);
|
|
2092
|
+
throw new FLRuntimeError(
|
|
2093
|
+
ErrorCodes.E_INVALID_FORM,
|
|
2094
|
+
`문자열 보간 실패 — {(${exprStr})}\n 원인: ${cause}`,
|
|
2095
|
+
undefined, undefined, undefined,
|
|
2096
|
+
`올바른 표현식인지 확인하세요.`
|
|
2097
|
+
);
|
|
2098
|
+
}
|
|
2099
|
+
i = j + 2; // ')' + '}' 건너뜀
|
|
2100
|
+
continue;
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
result += template[i];
|
|
2106
|
+
i++;
|
|
2107
|
+
}
|
|
2108
|
+
return result;
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
// 값을 사람/AI가 읽기 좋은 문자열로 변환
|
|
2112
|
+
public toDisplayString(val: any, _inner = false): string {
|
|
2113
|
+
if (val === null || val === undefined) return "null";
|
|
2114
|
+
if (typeof val === "string") return _inner ? `"${val}"` : val;
|
|
2115
|
+
if (typeof val === "number" || typeof val === "boolean") return String(val);
|
|
2116
|
+
if (Array.isArray(val)) return "[" + val.map((v) => this.toDisplayString(v, true)).join(", ") + "]";
|
|
2117
|
+
// Phase 69: 레이지 시퀀스 — 앞 3개만 미리보기
|
|
2118
|
+
if (isLazySeq(val)) {
|
|
2119
|
+
const preview = lazyTake(3, val).map((v) => this.toDisplayString(v)).join(", ");
|
|
2120
|
+
return `<lazy-seq: ${preview}...>`;
|
|
2121
|
+
}
|
|
2122
|
+
if (val instanceof Map) {
|
|
2123
|
+
// Error maps: show message prominently for easy println debugging
|
|
2124
|
+
if (val.has("message") && val.has("raw")) {
|
|
2125
|
+
const msg = val.get("message") ?? "";
|
|
2126
|
+
const line = val.get("line");
|
|
2127
|
+
return line ? `[line ${line}] ${msg}` : String(msg);
|
|
2128
|
+
}
|
|
2129
|
+
const entries: string[] = [];
|
|
2130
|
+
val.forEach((v, k) => entries.push(`${k}: ${this.toDisplayString(v, true)}`));
|
|
2131
|
+
return "{" + entries.join(", ") + "}";
|
|
2132
|
+
}
|
|
2133
|
+
if (typeof val === "object") {
|
|
2134
|
+
if (val.kind === "function-value") return `<fn:${val.name || "λ"}>`;
|
|
2135
|
+
// JSON 표준 형식으로 출력 (키에 따옴표 포함)
|
|
2136
|
+
const entries = Object.entries(val)
|
|
2137
|
+
.filter(([k]) => !k.startsWith("__"))
|
|
2138
|
+
.map(([k, v]) => `"${k}": ${this.toDisplayString(v, true)}`);
|
|
2139
|
+
return "{" + entries.join(", ") + "}";
|
|
2140
|
+
}
|
|
2141
|
+
return String(val);
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
// Phase 58: 함수 호출 로직 eval-call-function.ts로 분리
|
|
2145
|
+
public callUserFunction(name: string, args: any[]): any { return _callUserFunction(this, name, args); }
|
|
2146
|
+
public callFunctionValue(fn: any, args: any[]): any { return _callFunctionValue(this, fn, args); }
|
|
2147
|
+
public callAsyncFunctionValue(fn: any, args: any[]): FreeLangPromise { return _callAsyncFunctionValue(this, fn, args); }
|
|
2148
|
+
public callFunction(fn: any, args: any[]): any { return _callFunction(this, fn, args); }
|
|
2149
|
+
|
|
2150
|
+
// Phase 61: TCO 메서드 — 꼬리 재귀를 반복문으로 (100만 재귀 스택 없이)
|
|
2151
|
+
public callUserFunctionTCO(name: string, args: any[]): any { return _callUserFunctionTCO(this, name, args); }
|
|
2152
|
+
public callFunctionValueTCO(fn: any, args: any[]): any { return _callFunctionValueTCO(this, fn, args); }
|
|
2153
|
+
// trampoline용 raw 메서드 (TailCall 토큰 그대로 반환)
|
|
2154
|
+
public callUserFunctionRaw(name: string, args: any[]): any { return _callUserFunctionRaw(this, name, args); }
|
|
2155
|
+
public callFunctionValueRaw(fn: any, args: any[]): any { return _callFunctionValueRaw(this, fn, args); }
|
|
2156
|
+
|
|
2157
|
+
private getFieldValue(block: Block, key: string, defaultValue: any = null): any {
|
|
2158
|
+
const field = block.fields.get(key);
|
|
2159
|
+
if (field === undefined) {
|
|
2160
|
+
return defaultValue;
|
|
2161
|
+
}
|
|
2162
|
+
return this.eval(field as ASTNode);
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
// ===== Pattern Matching (Phase 4 Week 3-4) — Phase 58: eval-pattern-match.ts로 분리 =====
|
|
2166
|
+
public evalPatternMatch(match: PatternMatch): any { return _evalPatternMatch(this, match); }
|
|
2167
|
+
public evalTryBlock(tryBlock: TryBlock): any { return _evalTryBlock(this, tryBlock); }
|
|
2168
|
+
public evalThrow(throwExpr: ThrowExpression): any { return _evalThrow(this, throwExpr); }
|
|
2169
|
+
|
|
2170
|
+
// Loop special form (Phase B-1)
|
|
2171
|
+
public evalLoop(loopNode: any): any {
|
|
2172
|
+
if (!loopNode) {
|
|
2173
|
+
throw new Error("Loop node is null");
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
const init = loopNode.init;
|
|
2177
|
+
const condition = loopNode.condition;
|
|
2178
|
+
const update = loopNode.update;
|
|
2179
|
+
const body = loopNode.body;
|
|
2180
|
+
|
|
2181
|
+
if (!init || !condition || !update || !body) {
|
|
2182
|
+
throw new Error(`Loop parts missing: init=${!!init} condition=${!!condition} update=${!!update} body=${!!body}`);
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
// S-expr 형식 확인
|
|
2186
|
+
if ((init as any).kind !== "sexpr") {
|
|
2187
|
+
throw new Error(`Loop init must be sexpr, got: ${(init as any).kind}`);
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
const op = (init as any).op;
|
|
2191
|
+
const args = (init as any).args || [];
|
|
2192
|
+
|
|
2193
|
+
if (!op || args.length < 1) {
|
|
2194
|
+
throw new Error("Loop init must be ($var val)");
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
const varName = String(op); // "$i" 유지
|
|
2198
|
+
const initVal = this.eval(args[0]);
|
|
2199
|
+
|
|
2200
|
+
// 새 스코프에서 루프 실행
|
|
2201
|
+
const loopScope = new Map<string, any>();
|
|
2202
|
+
loopScope.set(varName, initVal);
|
|
2203
|
+
this.scopes.push(loopScope);
|
|
2204
|
+
|
|
2205
|
+
try {
|
|
2206
|
+
let result = null;
|
|
2207
|
+
|
|
2208
|
+
// 조건 확인 및 루프 실행
|
|
2209
|
+
while (this.isTruthy(this.eval(condition))) {
|
|
2210
|
+
// 본문 실행
|
|
2211
|
+
result = this.eval(body);
|
|
2212
|
+
|
|
2213
|
+
// 업데이트 식 평가
|
|
2214
|
+
const updatedVal = this.eval(update);
|
|
2215
|
+
loopScope.set(varName, updatedVal);
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
return result;
|
|
2219
|
+
} finally {
|
|
2220
|
+
this.scopes.pop();
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
public matchPattern(pattern: Pattern, value: any): { matched: boolean; bindings: Map<string, any> } {
|
|
2225
|
+
return _matchPattern(this, pattern, value);
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
// Utility: Get context
|
|
2229
|
+
getContext(): ExecutionContext {
|
|
2230
|
+
return this.context;
|
|
2231
|
+
}
|
|
2232
|
+
|
|
2233
|
+
// Utility: Set variable
|
|
2234
|
+
setVariable(name: string, value: any): void {
|
|
2235
|
+
this.context.variables.set(name, value);
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
// Phase 63: defmacro 처리 (interpret() 선처리용)
|
|
2239
|
+
public evalDefmacro(expr: any): void {
|
|
2240
|
+
if (expr.args.length < 3) throw new Error(`defmacro requires name, params, and body`);
|
|
2241
|
+
const nameNode = expr.args[0] as any;
|
|
2242
|
+
const macroName: string = nameNode.kind === "literal" ? String(nameNode.value)
|
|
2243
|
+
: nameNode.kind === "variable" ? nameNode.name
|
|
2244
|
+
: String(nameNode.value ?? nameNode.name ?? "");
|
|
2245
|
+
|
|
2246
|
+
const paramsNode = expr.args[1] as any;
|
|
2247
|
+
const params: string[] = [];
|
|
2248
|
+
if (paramsNode.kind === "block" && paramsNode.type === "Array") {
|
|
2249
|
+
const items = paramsNode.fields.get("items");
|
|
2250
|
+
if (Array.isArray(items)) {
|
|
2251
|
+
for (const item of items as any[]) {
|
|
2252
|
+
if (item.kind === "variable") params.push(item.name.startsWith("$") ? item.name : "$" + item.name);
|
|
2253
|
+
else if (item.kind === "literal") params.push("$" + item.value);
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
const body = expr.args[2];
|
|
2258
|
+
this.context.macroExpander.define(macroName, params, body);
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
// Phase 5 Week 2: Register built-in type classes and instances
|
|
2262
|
+
// Phase 58: Type class 관련 로직 eval-type-classes.ts로 분리
|
|
2263
|
+
public registerBuiltinTypeClasses(): void { _registerBuiltinTypeClasses(this); }
|
|
2264
|
+
public evalTypeClass(typeClass: TypeClass): void { _evalTypeClass(this, typeClass); }
|
|
2265
|
+
public evalInstance(instance: TypeClassInstance): void { _evalInstance(this, instance); }
|
|
2266
|
+
|
|
2267
|
+
// Type class query helpers
|
|
2268
|
+
getTypeClass(name: string): TypeClassInfo | undefined {
|
|
2269
|
+
return this.context.typeClasses?.get(name);
|
|
2270
|
+
}
|
|
2271
|
+
getTypeClassInstance(className: string, concreteType: string): TypeClassInstanceInfo | undefined {
|
|
2272
|
+
return this.context.typeClassInstances?.get(`${className}[${concreteType}]`);
|
|
2273
|
+
}
|
|
2274
|
+
satisfiesConstraint(type: string, constraintClass: string): boolean {
|
|
2275
|
+
return !!this.getTypeClassInstance(constraintClass, type);
|
|
2276
|
+
}
|
|
2277
|
+
public getConcreteType(value: any): string | undefined {
|
|
2278
|
+
if (!value || typeof value !== "object") return undefined;
|
|
2279
|
+
if (value.tag === "Ok" || value.tag === "Err") return "Result";
|
|
2280
|
+
if (value.tag === "Some" || value.tag === "None") return "Option";
|
|
2281
|
+
if (Array.isArray(value)) return "List";
|
|
2282
|
+
if (value.kind === "Result") return "Result";
|
|
2283
|
+
if (value.kind === "Option") return "Option";
|
|
2284
|
+
if (value.kind === "List") return "List";
|
|
2285
|
+
return undefined;
|
|
2286
|
+
}
|
|
2287
|
+
public resolveMethod(className: string, concreteType: string, methodName: string): any {
|
|
2288
|
+
const instance = this.getTypeClassInstance(className, concreteType);
|
|
2289
|
+
return instance?.implementations.get(methodName);
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
// Phase 6 Step 4: Helper to ensure modules map exists
|
|
2293
|
+
public getModules(): Map<string, ModuleInfo> {
|
|
2294
|
+
if (!this.context.modules) {
|
|
2295
|
+
this.context.modules = new Map();
|
|
2296
|
+
}
|
|
2297
|
+
return this.context.modules;
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
// Phase 57: delegated to eval-module-system.ts
|
|
2301
|
+
public evalModuleBlock(moduleBlock: ModuleBlock): void { _evalModuleBlock(this, moduleBlock); }
|
|
2302
|
+
public evalImportBlock(importBlock: ImportBlock): void { _evalImportBlock(this, importBlock); }
|
|
2303
|
+
public evalImportFromFile(relPath: string, prefix: string, selective: string[] | undefined, alias: string | undefined): void {
|
|
2304
|
+
_evalImportFromFile(this, relPath, prefix, selective, alias);
|
|
2305
|
+
}
|
|
2306
|
+
public evalOpenBlock(openBlock: OpenBlock): void { _evalOpenBlock(this, openBlock); }
|
|
2307
|
+
// Phase 57: delegated to eval-ai-handlers.ts
|
|
2308
|
+
public handleSearchBlock(searchBlock: SearchBlock): any { return _handleSearchBlock(this, searchBlock); }
|
|
2309
|
+
public handleLearnBlock(learnBlock: LearnBlock): any { return _handleLearnBlock(this, learnBlock); }
|
|
2310
|
+
public handleReasoningBlock(reasoningBlock: ReasoningBlock): any { return _handleReasoningBlock(this, reasoningBlock); }
|
|
2311
|
+
public handleReasoningSequence(reasoningSeq: ReasoningSequence): any { return handleReasoningSequence(this, reasoningSeq); }
|
|
2312
|
+
|
|
2313
|
+
/**
|
|
2314
|
+
* Cleanup: Destroy all resources and stop timers
|
|
2315
|
+
* Call this when shutting down the interpreter to prevent memory leaks
|
|
2316
|
+
* (e.g., after all tests complete or on process exit)
|
|
2317
|
+
*/
|
|
2318
|
+
destroy(): void {
|
|
2319
|
+
// Clean up LearnedFactsStore timer
|
|
2320
|
+
this.learnedFactsStore.destroy();
|
|
2321
|
+
|
|
2322
|
+
// Close HTTP server if running
|
|
2323
|
+
if (this.context.server) {
|
|
2324
|
+
this.context.server.close();
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2327
|
+
this.logger.info("Interpreter cleanup completed");
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
// Global interpreter reference for cleanup on process exit
|
|
2332
|
+
let globalInterpreterInstance: Interpreter | null = null;
|
|
2333
|
+
|
|
2334
|
+
export function interpret(blocks: ASTNode[], logger?: Logger): ExecutionContext {
|
|
2335
|
+
const interpreter = new Interpreter(logger);
|
|
2336
|
+
globalInterpreterInstance = interpreter;
|
|
2337
|
+
|
|
2338
|
+
// Register cleanup handler on first interpret() call
|
|
2339
|
+
if (!process.listeners("exit").some(l => l.name === "cleanupInterpreter")) {
|
|
2340
|
+
const cleanupInterpreter = function() {
|
|
2341
|
+
if (globalInterpreterInstance) {
|
|
2342
|
+
globalInterpreterInstance.destroy();
|
|
2343
|
+
globalInterpreterInstance = null;
|
|
2344
|
+
}
|
|
2345
|
+
};
|
|
2346
|
+
Object.defineProperty(cleanupInterpreter, "name", { value: "cleanupInterpreter" });
|
|
2347
|
+
process.on("exit", cleanupInterpreter);
|
|
2348
|
+
}
|
|
2349
|
+
|
|
2350
|
+
return interpreter.interpret(blocks);
|
|
2351
|
+
}
|