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
package/src/parser.ts
ADDED
|
@@ -0,0 +1,2722 @@
|
|
|
1
|
+
// FreeLang v9: Parser
|
|
2
|
+
// Token[] → AST (Block[])
|
|
3
|
+
|
|
4
|
+
import { Token, TokenType as T } from "./token";
|
|
5
|
+
import {
|
|
6
|
+
ASTNode,
|
|
7
|
+
Block,
|
|
8
|
+
Literal,
|
|
9
|
+
TemplateString,
|
|
10
|
+
Variable,
|
|
11
|
+
SExpr,
|
|
12
|
+
Keyword,
|
|
13
|
+
TypeAnnotation,
|
|
14
|
+
TypeVariable,
|
|
15
|
+
Pattern,
|
|
16
|
+
PatternMatch,
|
|
17
|
+
MatchCase,
|
|
18
|
+
ModuleBlock,
|
|
19
|
+
ImportBlock,
|
|
20
|
+
OpenBlock,
|
|
21
|
+
SearchBlock,
|
|
22
|
+
LearnBlock,
|
|
23
|
+
ReasoningBlock,
|
|
24
|
+
ReasoningSequence,
|
|
25
|
+
ReasoningTransition,
|
|
26
|
+
AsyncFunction,
|
|
27
|
+
AwaitExpression,
|
|
28
|
+
TryBlock,
|
|
29
|
+
CatchClause,
|
|
30
|
+
ThrowExpression,
|
|
31
|
+
TypeClass,
|
|
32
|
+
TypeClassInstance,
|
|
33
|
+
TypeClassMethod,
|
|
34
|
+
PageNode,
|
|
35
|
+
RouteNode,
|
|
36
|
+
ComponentNode,
|
|
37
|
+
FormNode,
|
|
38
|
+
makeBlock,
|
|
39
|
+
makeLiteral,
|
|
40
|
+
makeTemplateString,
|
|
41
|
+
makeVariable,
|
|
42
|
+
makeSExpr,
|
|
43
|
+
makeKeyword,
|
|
44
|
+
makeTypeAnnotation,
|
|
45
|
+
makeTypeVariable,
|
|
46
|
+
makeLiteralPattern,
|
|
47
|
+
makeVariablePattern,
|
|
48
|
+
makeWildcardPattern,
|
|
49
|
+
makeListPattern,
|
|
50
|
+
makeStructPattern,
|
|
51
|
+
makeOrPattern,
|
|
52
|
+
makeRangePattern,
|
|
53
|
+
makeMatchCase,
|
|
54
|
+
makePatternMatch,
|
|
55
|
+
RangePattern,
|
|
56
|
+
makeModuleBlock,
|
|
57
|
+
makeImportBlock,
|
|
58
|
+
makeOpenBlock,
|
|
59
|
+
makeAsyncFunction,
|
|
60
|
+
makeAwaitExpression,
|
|
61
|
+
makeTryBlock,
|
|
62
|
+
makeCatchClause,
|
|
63
|
+
makeThrowExpression,
|
|
64
|
+
makeReasoningBlock,
|
|
65
|
+
makeReasoningTransition,
|
|
66
|
+
makeReasoningSequence,
|
|
67
|
+
makeTypeClass,
|
|
68
|
+
makeTypeClassInstance,
|
|
69
|
+
makePageNode,
|
|
70
|
+
makeRouteNode,
|
|
71
|
+
makeComponentNode,
|
|
72
|
+
makeFormNode,
|
|
73
|
+
} from "./ast";
|
|
74
|
+
|
|
75
|
+
export class ParserError extends Error {
|
|
76
|
+
constructor(
|
|
77
|
+
message: string,
|
|
78
|
+
public line: number,
|
|
79
|
+
public col: number,
|
|
80
|
+
public hint?: string,
|
|
81
|
+
public code?: string,
|
|
82
|
+
public stage: string = "parse"
|
|
83
|
+
) {
|
|
84
|
+
const codeStr = code ? `[${code}]` : "";
|
|
85
|
+
const loc = `[${line}:${col}]`;
|
|
86
|
+
const hintLine = hint ? `\n 힌트: ${hint}` : "";
|
|
87
|
+
super(`${codeStr} ${loc} ${message}${hintLine}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
toJSON() {
|
|
91
|
+
return {
|
|
92
|
+
code: this.code || "E_PARSE_SYNTAX_ERROR",
|
|
93
|
+
stage: this.stage,
|
|
94
|
+
message: this.message,
|
|
95
|
+
file: undefined,
|
|
96
|
+
line: this.line,
|
|
97
|
+
column: this.col,
|
|
98
|
+
symbol: undefined,
|
|
99
|
+
cause: undefined,
|
|
100
|
+
hint: this.hint,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// 에러 메시지 힌트 테이블
|
|
106
|
+
const ERROR_HINTS: Record<string, string> = {
|
|
107
|
+
"Expected RParen, got Symbol": "괄호가 닫히지 않았거나 와일드카드 패턴에 괄호가 필요합니다: _ expr → (_ expr)",
|
|
108
|
+
"Expected RParen, got EOF": "괄호가 닫히지 않았습니다. 여는 ( 와 닫는 ) 수를 확인하세요.",
|
|
109
|
+
"Expected operator": "S-expression의 첫 요소는 함수명이어야 합니다: (함수명 인자...)",
|
|
110
|
+
"Expected ':' keyword in map literal": "맵 리터럴의 키는 :으로 시작해야 합니다: {:key value}",
|
|
111
|
+
"Unexpected token: Colon": "콜론 키워드 (:key)는 맵 리터럴 또는 블록 필드에서만 사용할 수 있습니다.",
|
|
112
|
+
"Expected block type": "블록은 [FUNC name :params [...] :body ...] 형식이어야 합니다.",
|
|
113
|
+
"Unterminated string": "문자열이 닫히지 않았습니다. 닫는 \" 를 추가하세요.",
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export class Parser {
|
|
117
|
+
private pos = 0;
|
|
118
|
+
private tokens: Token[];
|
|
119
|
+
// 자잘 #4 (2026-04-25): paren matching info — opening LParen 위치 추적
|
|
120
|
+
private parenStack: Array<{ type: string; line: number; col: number }> = [];
|
|
121
|
+
|
|
122
|
+
constructor(tokens: Token[]) {
|
|
123
|
+
this.tokens = tokens;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
parse(): ASTNode[] {
|
|
127
|
+
const nodes: ASTNode[] = [];
|
|
128
|
+
while (!this.isAtEnd()) {
|
|
129
|
+
if (this.check(T.EOF)) break;
|
|
130
|
+
|
|
131
|
+
// Phase 6: Handle blocks, arrays, and S-expressions at top level
|
|
132
|
+
if (this.check(T.LBracket)) {
|
|
133
|
+
// Distinguish between block [TYPE ...] and array [val1 val2 ...]
|
|
134
|
+
const nextIdx = this.pos + 1;
|
|
135
|
+
const knownBlockTypes = ["FUNC", "INTENT", "PROMPT", "PIPE", "AGENT", "LOAD", "RULE", "MODULE", "TYPECLASS", "INSTANCE", "SERVER", "ROUTE", "MIDDLEWARE", "WEBSOCKET", "ERROR-HANDLER", "PAGE", "COMPONENT", "FORM", "SERVICE", "CONTROLLER", "GUARD", "MODEL", "QUERY", "MIGRATION", "REPOSITORY", "DATABASE", "CACHE", "CACHED", "KAFKA", "PRODUCER", "CONSUMER", "QUEUE", "RABBITMQ", "JWT", "OAUTH", "DOCKERFILE", "DOCKER-COMPOSE", "K8S-DEPLOYMENT", "K8S-SERVICE", "K8S-INGRESS", "AWS", "AWS-S3", "AWS-LAMBDA", "AWS-RDS", "AWS-SQS", "GCP", "GCP-CLOUD-RUN", "GCP-BIGQUERY", "AZURE", "AZURE-FUNCTION", "AZURE-COSMOS"];
|
|
136
|
+
|
|
137
|
+
if (nextIdx < this.tokens.length) {
|
|
138
|
+
const nextToken = this.tokens[nextIdx];
|
|
139
|
+
const isBlockKeyword = nextToken.type === T.Module || nextToken.type === T.TypeClass || nextToken.type === T.Instance || nextToken.type === T.Page || nextToken.type === T.Api || nextToken.type === T.Component || nextToken.type === T.Form || nextToken.type === T.Route || nextToken.type === T.Service || nextToken.type === T.Controller || nextToken.type === T.Guard || nextToken.type === T.Model || nextToken.type === T.Query || nextToken.type === T.Migration || nextToken.type === T.Repository || nextToken.type === T.Database || nextToken.type === T.Cached || nextToken.type === T.Kafka || nextToken.type === T.JWT || nextToken.type === T.OAuth || nextToken.type === T.Dockerfile || nextToken.type === T.K8sDeployment || nextToken.type === T.AWS || nextToken.type === T.GCP || nextToken.type === T.Azure;
|
|
140
|
+
// 블록 타입은 반드시 UPPERCASE여야 함 (소문자 page/rule/form 등은 일반 변수)
|
|
141
|
+
const isKnownBlockType = nextToken.type === T.Symbol && knownBlockTypes.includes(nextToken.value);
|
|
142
|
+
const hasKeywordAfter = nextIdx + 1 < this.tokens.length &&
|
|
143
|
+
(this.tokens[nextIdx + 1].type === T.Keyword || this.tokens[nextIdx + 1].type === T.Colon);
|
|
144
|
+
|
|
145
|
+
if (isBlockKeyword || isKnownBlockType || hasKeywordAfter) {
|
|
146
|
+
// It's a block
|
|
147
|
+
nodes.push(this.parseBlock());
|
|
148
|
+
} else {
|
|
149
|
+
// It's an array
|
|
150
|
+
nodes.push(this.parseArray());
|
|
151
|
+
}
|
|
152
|
+
} else {
|
|
153
|
+
// Empty or end of tokens, treat as block (will error)
|
|
154
|
+
nodes.push(this.parseBlock());
|
|
155
|
+
}
|
|
156
|
+
} else if (this.check(T.LParen)) {
|
|
157
|
+
nodes.push(this.parseSExpr());
|
|
158
|
+
} else {
|
|
159
|
+
throw this.error(
|
|
160
|
+
`Expected block or S-expression, got ${this.peek().type}`,
|
|
161
|
+
this.peek()
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return nodes;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// [BLOCK_TYPE name :key1 val1 :key2 val2 ...]
|
|
169
|
+
// Phase 6: BLOCK_TYPE can be Symbol (old) or keyword token (MODULE, TYPECLASS, INSTANCE)
|
|
170
|
+
private parseBlock(): Block | ModuleBlock | TypeClass | TypeClassInstance | PageNode | RouteNode | ComponentNode | FormNode {
|
|
171
|
+
const lbracket = this.expect(T.LBracket);
|
|
172
|
+
const blockLine = lbracket.line;
|
|
173
|
+
const typeToken = this.advance();
|
|
174
|
+
|
|
175
|
+
let blockType: string;
|
|
176
|
+
if (typeToken.type === T.Symbol) {
|
|
177
|
+
blockType = typeToken.value;
|
|
178
|
+
// [FUNC ...] deprecated — use (defn ...) instead
|
|
179
|
+
if (blockType === "FUNC" && !process.env.FL_NO_DEPRECATION_WARN) {
|
|
180
|
+
process.stderr.write(`\x1b[33m[deprecated]\x1b[0m [FUNC ...] 문법은 deprecated입니다. (defn ...) 을 사용하세요. (line ${typeToken.line})\n`);
|
|
181
|
+
}
|
|
182
|
+
} else if (typeToken.type === T.Module) {
|
|
183
|
+
blockType = "MODULE";
|
|
184
|
+
} else if (typeToken.type === T.TypeClass) {
|
|
185
|
+
blockType = "TYPECLASS";
|
|
186
|
+
} else if (typeToken.type === T.Instance) {
|
|
187
|
+
blockType = "INSTANCE";
|
|
188
|
+
} else if (typeToken.type === T.Page) {
|
|
189
|
+
blockType = "PAGE";
|
|
190
|
+
} else if (typeToken.type === T.Api) {
|
|
191
|
+
blockType = "API";
|
|
192
|
+
} else if (typeToken.type === T.Route) {
|
|
193
|
+
blockType = "ROUTE";
|
|
194
|
+
} else if (typeToken.type === T.Component) {
|
|
195
|
+
blockType = "COMPONENT";
|
|
196
|
+
} else if (typeToken.type === T.Form) {
|
|
197
|
+
blockType = "FORM";
|
|
198
|
+
} else if (typeToken.type === T.Service) {
|
|
199
|
+
blockType = "SERVICE";
|
|
200
|
+
} else if (typeToken.type === T.Controller) {
|
|
201
|
+
blockType = "CONTROLLER";
|
|
202
|
+
} else if (typeToken.type === T.Guard) {
|
|
203
|
+
blockType = "GUARD";
|
|
204
|
+
} else if (typeToken.type === T.Model) {
|
|
205
|
+
blockType = "MODEL";
|
|
206
|
+
} else {
|
|
207
|
+
throw this.error(`Expected block type (symbol or keyword), got ${typeToken.type}`, typeToken);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Phase 12+: Support anonymous FUNC blocks for inline function definitions
|
|
211
|
+
// [FUNC :params [...] :body (...)] without explicit name
|
|
212
|
+
let blockName: string;
|
|
213
|
+
if (blockType === "FUNC" && (this.check(T.Colon) || this.check(T.Keyword))) {
|
|
214
|
+
// Anonymous FUNC: [FUNC :params [...] :body (...)]
|
|
215
|
+
// Generate unique internal name for this anonymous function
|
|
216
|
+
blockName = `__anon_func_${this.pos}`;
|
|
217
|
+
} else {
|
|
218
|
+
const nameToken = this.advance();
|
|
219
|
+
if (nameToken.type !== T.Symbol) {
|
|
220
|
+
throw this.error(`Expected block name (symbol), got ${nameToken.type}`, nameToken);
|
|
221
|
+
}
|
|
222
|
+
blockName = nameToken.value;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const fields = new Map<string, ASTNode | ASTNode[]>();
|
|
226
|
+
const typeAnnotations = new Map<string, TypeAnnotation>();
|
|
227
|
+
let generics: string[] | undefined;
|
|
228
|
+
|
|
229
|
+
// Parse fields: :key value :key value ... (Phase 6: T.Colon + T.Symbol instead of T.Keyword)
|
|
230
|
+
while (!this.check(T.RBracket) && !this.isAtEnd()) {
|
|
231
|
+
// Expect a keyword at the start of each field
|
|
232
|
+
// Phase 6: Accept T.Colon (new) or T.Keyword (backward compat with Phase 5)
|
|
233
|
+
let keyName: string;
|
|
234
|
+
const startToken = this.peek(); // Save token for error reporting
|
|
235
|
+
|
|
236
|
+
if (this.check(T.Colon)) {
|
|
237
|
+
// Phase 6 syntax: T.Colon + T.Symbol
|
|
238
|
+
this.advance(); // consume ':'
|
|
239
|
+
if (!this.check(T.Symbol)) {
|
|
240
|
+
throw this.error(
|
|
241
|
+
`Expected symbol after ':', got ${this.peek().type}`,
|
|
242
|
+
this.peek()
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
const keyToken = this.advance();
|
|
246
|
+
keyName = keyToken.value;
|
|
247
|
+
} else if (this.check(T.Keyword)) {
|
|
248
|
+
// Phase 5 backward compatibility: T.Keyword
|
|
249
|
+
const keyToken = this.advance();
|
|
250
|
+
keyName = keyToken.value; // e.g., ":body" or ":params"
|
|
251
|
+
// Remove leading ':' if present
|
|
252
|
+
if (keyName.startsWith(":")) {
|
|
253
|
+
keyName = keyName.substring(1);
|
|
254
|
+
}
|
|
255
|
+
} else {
|
|
256
|
+
throw this.error(
|
|
257
|
+
`Expected keyword field (starting with :), got ${this.peek().type}`,
|
|
258
|
+
this.peek()
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Collect values for this key (parse until next keyword or closing bracket)
|
|
263
|
+
const values: ASTNode[] = [];
|
|
264
|
+
|
|
265
|
+
// Single value case (most common): :key value
|
|
266
|
+
if (!this.check(T.Keyword) && !this.check(T.Colon) && !this.check(T.RBracket)) {
|
|
267
|
+
values.push(this.parseValue());
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Multiple values case: :key val1 val2 ... (until next keyword/colon or ])
|
|
271
|
+
while (!this.check(T.Keyword) && !this.check(T.Colon) && !this.check(T.RBracket) && !this.isAtEnd()) {
|
|
272
|
+
values.push(this.parseValue());
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Store field
|
|
276
|
+
if (values.length === 0) {
|
|
277
|
+
throw this.error(`Expected at least one value for keyword ${keyName}`, startToken);
|
|
278
|
+
} else if (values.length === 1) {
|
|
279
|
+
fields.set(keyName, values[0]);
|
|
280
|
+
} else {
|
|
281
|
+
fields.set(keyName, values);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Phase 3: Extract type annotations from :return field
|
|
285
|
+
if (keyName === "return" && values.length === 1) {
|
|
286
|
+
const returnValue = values[0];
|
|
287
|
+
if ((returnValue as any).kind === "literal" && (returnValue as any).type === "symbol") {
|
|
288
|
+
const typeName = (returnValue as any).value;
|
|
289
|
+
typeAnnotations.set("return", makeTypeAnnotation(typeName));
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Phase 4: Extract generic type variables from :generics field (new syntax: [T K V])
|
|
294
|
+
if (keyName === "generics" && values.length === 1) {
|
|
295
|
+
const genericsValue = values[0];
|
|
296
|
+
if ((genericsValue as any).kind === "block" && (genericsValue as any).type === "Array") {
|
|
297
|
+
// :generics [T K V]
|
|
298
|
+
const arrayItems = (genericsValue as any).fields?.get("items") as ASTNode[];
|
|
299
|
+
if (Array.isArray(arrayItems)) {
|
|
300
|
+
const gen: string[] = [];
|
|
301
|
+
for (const item of arrayItems) {
|
|
302
|
+
if ((item as any).kind === "literal" && (item as any).type === "symbol") {
|
|
303
|
+
gen.push((item as any).value);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (gen.length > 0) {
|
|
307
|
+
generics = gen;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Phase 3: Extract type annotations from :params field (new syntax: [[$x int] [$y int]])
|
|
314
|
+
if (keyName === "params" && values.length === 1) {
|
|
315
|
+
const paramsValue = values[0];
|
|
316
|
+
// Check if it's an array (represented as Block with type="Array")
|
|
317
|
+
if ((paramsValue as any).kind === "literal" && (paramsValue as any).type === "symbol") {
|
|
318
|
+
// Old syntax: :params [$x $y] (no types)
|
|
319
|
+
// Keep backward compatibility - no type annotation
|
|
320
|
+
} else if ((paramsValue as any).kind === "block" && (paramsValue as any).type === "Array") {
|
|
321
|
+
// New syntax: :params [[$x int] [$y int]]
|
|
322
|
+
// Each item should be [name type] array
|
|
323
|
+
const arrayItems = (paramsValue as any).fields?.get("items") as ASTNode[];
|
|
324
|
+
if (Array.isArray(arrayItems)) {
|
|
325
|
+
const paramTypes: TypeAnnotation[] = [];
|
|
326
|
+
for (const item of arrayItems) {
|
|
327
|
+
if ((item as any).kind === "block" && (item as any).type === "Array") {
|
|
328
|
+
// This is a [name type] pair
|
|
329
|
+
const pairItems = (item as any).fields?.get("items") as ASTNode[];
|
|
330
|
+
if (Array.isArray(pairItems) && pairItems.length === 2) {
|
|
331
|
+
// pairItems[0] = name ($x), pairItems[1] = type (int)
|
|
332
|
+
const typeNode = pairItems[1];
|
|
333
|
+
if ((typeNode as any).kind === "literal" && (typeNode as any).type === "symbol") {
|
|
334
|
+
const typeName = (typeNode as any).value;
|
|
335
|
+
paramTypes.push(makeTypeAnnotation(typeName));
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
if (paramTypes.length > 0) {
|
|
341
|
+
// Store params as array of types
|
|
342
|
+
typeAnnotations.set("params", paramTypes as any);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
this.expect(T.RBracket);
|
|
350
|
+
|
|
351
|
+
// Phase 6: If this is a MODULE block, convert it to ModuleBlock
|
|
352
|
+
if (blockType === "MODULE") {
|
|
353
|
+
const block = makeBlock(blockType, blockName, fields, blockLine);
|
|
354
|
+
return this.convertBlockToModuleBlock(block);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Phase 5: If this is a TYPECLASS block, convert it to TypeClass
|
|
358
|
+
if (blockType === "TYPECLASS") {
|
|
359
|
+
const block = makeBlock(blockType, blockName, fields, blockLine);
|
|
360
|
+
return this.convertBlockToTypeClass(block);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Phase 5: If this is an INSTANCE block, convert it to TypeClassInstance
|
|
364
|
+
if (blockType === "INSTANCE") {
|
|
365
|
+
const block = makeBlock(blockType, blockName, fields, blockLine);
|
|
366
|
+
return this.convertBlockToInstance(block);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Phase 11: Parse all enterprise blocks
|
|
370
|
+
// Note: For PAGE/COMPONENT/FORM, we keep the original fields map intact
|
|
371
|
+
// so that interpreter can access :render, :state, etc. directly
|
|
372
|
+
const enterpriseBlocks = ["PAGE", "ROUTE", "COMPONENT", "FORM", "SERVICE", "CONTROLLER", "GUARD", "MODEL", "QUERY", "MIGRATION", "REPOSITORY", "DATABASE", "CACHE", "CACHED", "KAFKA", "PRODUCER", "CONSUMER", "QUEUE", "RABBITMQ", "JWT", "OAUTH", "DOCKERFILE", "DOCKER-COMPOSE", "K8S-DEPLOYMENT", "K8S-SERVICE", "K8S-INGRESS", "AWS", "AWS-S3", "AWS-LAMBDA", "AWS-RDS", "AWS-SQS", "GCP", "GCP-CLOUD-RUN", "GCP-BIGQUERY", "AZURE", "AZURE-FUNCTION", "AZURE-COSMOS"];
|
|
373
|
+
if (enterpriseBlocks.includes(blockType)) {
|
|
374
|
+
const block = makeBlock(blockType, blockName, fields, blockLine);
|
|
375
|
+
// Could call parsePage/parseRoute etc for validation, but keep fields as-is
|
|
376
|
+
return block;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const block = makeBlock(blockType, blockName, fields, blockLine);
|
|
380
|
+
// Phase 3: Always set typeAnnotations (even if empty) for consistent handling
|
|
381
|
+
// FUNC blocks without :return/:params annotations still need to be registered with default types
|
|
382
|
+
if (blockType === "FUNC" || typeAnnotations.size > 0) {
|
|
383
|
+
block.typeAnnotations = typeAnnotations;
|
|
384
|
+
}
|
|
385
|
+
// Phase 4: Set generics if provided
|
|
386
|
+
if (generics && generics.length > 0) {
|
|
387
|
+
block.generics = generics;
|
|
388
|
+
}
|
|
389
|
+
return block;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Phase 6: Convert Block to ModuleBlock
|
|
393
|
+
private convertBlockToModuleBlock(block: Block): ModuleBlock {
|
|
394
|
+
const exports: string[] = [];
|
|
395
|
+
const bodyNodes: ASTNode[] = [];
|
|
396
|
+
|
|
397
|
+
// Extract :exports field (should be an array of symbol names)
|
|
398
|
+
const exportsField = block.fields?.get("exports");
|
|
399
|
+
if (exportsField) {
|
|
400
|
+
if ((exportsField as any).kind === "block" && (exportsField as any).type === "Array") {
|
|
401
|
+
// :exports [name1 name2 ...]
|
|
402
|
+
const items = (exportsField as any).fields?.get("items") as ASTNode[];
|
|
403
|
+
if (Array.isArray(items)) {
|
|
404
|
+
items.forEach((item) => {
|
|
405
|
+
if ((item as any).kind === "literal" && (item as any).type === "symbol") {
|
|
406
|
+
exports.push((item as any).value);
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
} else if ((exportsField as any).kind === "literal" && (exportsField as any).type === "symbol") {
|
|
411
|
+
// Single export
|
|
412
|
+
exports.push((exportsField as any).value);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Extract :body field (should be an array of blocks)
|
|
417
|
+
const bodyField = block.fields?.get("body");
|
|
418
|
+
if (bodyField) {
|
|
419
|
+
if ((bodyField as any).kind === "block" && (bodyField as any).type === "Array") {
|
|
420
|
+
// :body [blocks...]
|
|
421
|
+
const items = (bodyField as any).fields?.get("items") as ASTNode[];
|
|
422
|
+
if (Array.isArray(items)) {
|
|
423
|
+
bodyNodes.push(...items);
|
|
424
|
+
}
|
|
425
|
+
} else if (Array.isArray(bodyField)) {
|
|
426
|
+
// Multiple body items
|
|
427
|
+
bodyNodes.push(...(bodyField as ASTNode[]));
|
|
428
|
+
} else {
|
|
429
|
+
// Single body item
|
|
430
|
+
bodyNodes.push(bodyField as ASTNode);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return {
|
|
435
|
+
kind: "module",
|
|
436
|
+
name: block.name,
|
|
437
|
+
exports,
|
|
438
|
+
body: bodyNodes,
|
|
439
|
+
path: undefined,
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Phase 5: Convert Block to TypeClass
|
|
444
|
+
private convertBlockToTypeClass(block: Block): TypeClass {
|
|
445
|
+
const methods = new Map<string, TypeClassMethod>();
|
|
446
|
+
let typeParams: string[] = [];
|
|
447
|
+
|
|
448
|
+
// Extract :typeParams field (optional, e.g., [M] or [F])
|
|
449
|
+
const typeParamsField = block.fields?.get("typeParams");
|
|
450
|
+
if (typeParamsField) {
|
|
451
|
+
if ((typeParamsField as any).kind === "block" && (typeParamsField as any).type === "Array") {
|
|
452
|
+
// :typeParams [M F ...] (Array block)
|
|
453
|
+
const items = (typeParamsField as any).fields?.get("items") as ASTNode[];
|
|
454
|
+
if (Array.isArray(items)) {
|
|
455
|
+
items.forEach((item) => {
|
|
456
|
+
if ((item as any).kind === "literal" && (item as any).type === "symbol") {
|
|
457
|
+
typeParams.push((item as any).value);
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
} else if ((typeParamsField as any).kind === "literal" && (typeParamsField as any).type === "symbol") {
|
|
462
|
+
// Single type parameter as literal
|
|
463
|
+
typeParams.push((typeParamsField as any).value);
|
|
464
|
+
} else if (Array.isArray(typeParamsField)) {
|
|
465
|
+
// JavaScript array (shouldn't happen, but keep for safety)
|
|
466
|
+
(typeParamsField as any[]).forEach((param) => {
|
|
467
|
+
if ((param as any).kind === "literal" && (param as any).type === "symbol") {
|
|
468
|
+
typeParams.push((param as any).value);
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Extract :methods field (should be a map of method names to function types)
|
|
475
|
+
const methodsField = block.fields?.get("methods");
|
|
476
|
+
if (methodsField) {
|
|
477
|
+
if ((methodsField as any).kind === "block" && (methodsField as any).type === "Array") {
|
|
478
|
+
// :methods [[:pure (fn [a] (M a))] [:bind (fn [m f] (M b))]]
|
|
479
|
+
const items = (methodsField as any).fields?.get("items") as ASTNode[];
|
|
480
|
+
if (Array.isArray(items)) {
|
|
481
|
+
items.forEach((item) => {
|
|
482
|
+
// Each item should be a 2-element array: [methodName, methodType]
|
|
483
|
+
if ((item as any).kind === "block" && (item as any).type === "Array") {
|
|
484
|
+
const subItems = (item as any).fields?.get("items") as ASTNode[];
|
|
485
|
+
if (Array.isArray(subItems) && subItems.length === 2) {
|
|
486
|
+
const nameNode = subItems[0];
|
|
487
|
+
const typeNode = subItems[1];
|
|
488
|
+
if ((nameNode as any).kind === "literal" && (nameNode as any).type === "symbol") {
|
|
489
|
+
const methodName = (nameNode as any).value;
|
|
490
|
+
methods.set(methodName, {
|
|
491
|
+
name: methodName,
|
|
492
|
+
type: typeNode,
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return {
|
|
503
|
+
kind: "type-class",
|
|
504
|
+
name: block.name,
|
|
505
|
+
typeParams,
|
|
506
|
+
methods,
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Phase 5: Convert Block to TypeClassInstance
|
|
511
|
+
private convertBlockToInstance(block: Block): TypeClassInstance {
|
|
512
|
+
let className: string = "";
|
|
513
|
+
const concreteType: string = block.name; // Block name is the concrete type (Result, Option, List, etc.)
|
|
514
|
+
const implementations = new Map<string, ASTNode>();
|
|
515
|
+
|
|
516
|
+
// Extract :typeclass field - the class name (Monad, Functor, etc.)
|
|
517
|
+
const typeClassField = block.fields?.get("typeclass");
|
|
518
|
+
if (typeClassField) {
|
|
519
|
+
if ((typeClassField as any).kind === "literal" && (typeClassField as any).type === "symbol") {
|
|
520
|
+
className = (typeClassField as any).value;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Extract method implementations from fields
|
|
525
|
+
// Each method name like :pure, :bind, :map, :fmap should have a function value
|
|
526
|
+
block.fields?.forEach((value, key) => {
|
|
527
|
+
// Skip special fields
|
|
528
|
+
if (key !== "typeclass") {
|
|
529
|
+
// This is a method implementation
|
|
530
|
+
implementations.set(key, value as ASTNode);
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
return {
|
|
535
|
+
kind: "type-class-instance",
|
|
536
|
+
className,
|
|
537
|
+
concreteType,
|
|
538
|
+
implementations,
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// Parse any value: literal, variable, block, S-expr, pattern-match, or array
|
|
543
|
+
private parseValue(): ASTNode {
|
|
544
|
+
// Check for S-expression or match expression: (op arg1 arg2 ...) or (match ...)
|
|
545
|
+
if (this.check(T.LParen)) {
|
|
546
|
+
return this.parseSExpr();
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Check for literal number
|
|
550
|
+
if (this.check(T.Number)) {
|
|
551
|
+
const token = this.advance();
|
|
552
|
+
return makeLiteral("number", parseFloat(token.value));
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Check for literal string (or template string with ${...})
|
|
556
|
+
if (this.check(T.String)) {
|
|
557
|
+
const token = this.advance();
|
|
558
|
+
if (token.value.includes("${")) {
|
|
559
|
+
return makeTemplateString(token.value);
|
|
560
|
+
}
|
|
561
|
+
return makeLiteral("string", token.value);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// Check for variable: $name
|
|
565
|
+
if (this.check(T.Variable)) {
|
|
566
|
+
const token = this.advance();
|
|
567
|
+
return makeVariable(token.value, token.line);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// Check for keyword: :name (can appear as value in S-expressions)
|
|
571
|
+
if (this.check(T.Keyword)) {
|
|
572
|
+
const token = this.advance();
|
|
573
|
+
return makeKeyword(token.value);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// Phase 8+: control-flow keywords used as values (e.g. [else ...] in cond)
|
|
577
|
+
if (this.check(T.Else) || this.check(T.Then) || this.check(T.When) ||
|
|
578
|
+
this.check(T.Repeat) || this.check(T.Until) || this.check(T.While)) {
|
|
579
|
+
const token = this.advance();
|
|
580
|
+
return makeLiteral("symbol", token.value);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// Phase 8+: :symbol (Colon + Symbol/Keyword) used as keyword/string value in .fl files
|
|
584
|
+
// e.g. (get $parser :pos) → "pos" (get $m :open) → "open"
|
|
585
|
+
if (this.check(T.Colon)) {
|
|
586
|
+
this.advance(); // consume ':'
|
|
587
|
+
const next = this.tokens[this.pos];
|
|
588
|
+
if (next) {
|
|
589
|
+
// :$set :$gt :$in → "$set" "$gt" "$in" (MongoDB operators — Variable 토큰, $ 복원)
|
|
590
|
+
// ※ 반드시 심볼 체크보다 먼저 (Variable token의 value는 $ 없이 저장됨)
|
|
591
|
+
if (next.type === T.Variable && next.value) {
|
|
592
|
+
this.advance();
|
|
593
|
+
return makeLiteral("string", "$" + next.value);
|
|
594
|
+
}
|
|
595
|
+
// :symbol, :keyword → "symbol"
|
|
596
|
+
if (next.value && /^[a-zA-Z_][a-zA-Z0-9_\-]*$/.test(next.value)) {
|
|
597
|
+
return makeLiteral("string", this.advance().value);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
// Bare colon
|
|
601
|
+
return makeLiteral("string", ":");
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// Check for map literal: {:key1 value1 :key2 value2}
|
|
605
|
+
if (this.check(T.LBrace)) {
|
|
606
|
+
return this.parseMap();
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// Check for block: [TYPE ...]
|
|
610
|
+
if (this.check(T.LBracket)) {
|
|
611
|
+
// Lookahead: is this a block or value array?
|
|
612
|
+
const nextIdx = this.pos + 1;
|
|
613
|
+
const knownBlockTypes = ["FUNC", "INTENT", "PROMPT", "PIPE", "AGENT", "LOAD", "RULE", "MODULE", "TYPECLASS", "INSTANCE", "SERVER", "ROUTE", "MIDDLEWARE", "WEBSOCKET", "ERROR-HANDLER", "PAGE", "COMPONENT", "FORM", "SERVICE", "CONTROLLER", "GUARD"];
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
if (nextIdx < this.tokens.length && this.tokens[nextIdx].type === T.Symbol) {
|
|
617
|
+
const potentialType = this.tokens[nextIdx].value;
|
|
618
|
+
// Check if it's a known block type (uppercase) or looks like a block name followed by a keyword
|
|
619
|
+
// 블록 타입은 반드시 UPPERCASE여야 함 (소문자 page/form/guard 등은 일반 변수)
|
|
620
|
+
const isKnownType = knownBlockTypes.includes(potentialType);
|
|
621
|
+
const nextNextIdx = nextIdx + 1;
|
|
622
|
+
const hasKeywordAfterName = nextNextIdx < this.tokens.length &&
|
|
623
|
+
(this.tokens[nextNextIdx].type === T.Keyword || this.tokens[nextNextIdx].type === T.Colon);
|
|
624
|
+
|
|
625
|
+
if (isKnownType || hasKeywordAfterName) {
|
|
626
|
+
// It's a block
|
|
627
|
+
return this.parseBlock();
|
|
628
|
+
} else {
|
|
629
|
+
// It's a value array: [val1 val2 ...]
|
|
630
|
+
return this.parseArray();
|
|
631
|
+
}
|
|
632
|
+
} else if (nextIdx < this.tokens.length && this.tokens[nextIdx].type === T.Module) {
|
|
633
|
+
// Phase 6: MODULE keyword token
|
|
634
|
+
const result = this.parseBlock();
|
|
635
|
+
// parseBlock() already converts MODULE to ModuleBlock
|
|
636
|
+
if (result.kind === "module") {
|
|
637
|
+
return result;
|
|
638
|
+
}
|
|
639
|
+
// Should not reach here
|
|
640
|
+
throw this.error(`Expected ModuleBlock from parseBlock with MODULE token`, this.peek());
|
|
641
|
+
} else if (nextIdx < this.tokens.length && (this.tokens[nextIdx].type === T.TypeClass || this.tokens[nextIdx].type === T.Instance)) {
|
|
642
|
+
// Phase 6: TYPECLASS/INSTANCE keyword tokens
|
|
643
|
+
return this.parseBlock();
|
|
644
|
+
} else if (nextIdx < this.tokens.length && (this.tokens[nextIdx].type === T.Page || this.tokens[nextIdx].type === T.Api || this.tokens[nextIdx].type === T.Component || this.tokens[nextIdx].type === T.Form || this.tokens[nextIdx].type === T.Route)) {
|
|
645
|
+
// Phase 11 v11: PAGE/API/COMPONENT/FORM/ROUTE keyword tokens
|
|
646
|
+
return this.parseBlock();
|
|
647
|
+
} else if (nextIdx < this.tokens.length && (this.tokens[nextIdx].type === T.Service || this.tokens[nextIdx].type === T.Controller || this.tokens[nextIdx].type === T.Guard || this.tokens[nextIdx].type === T.Model || this.tokens[nextIdx].type === T.Query || this.tokens[nextIdx].type === T.Migration || this.tokens[nextIdx].type === T.Repository || this.tokens[nextIdx].type === T.Database || this.tokens[nextIdx].type === T.Cached || this.tokens[nextIdx].type === T.Kafka || this.tokens[nextIdx].type === T.Producer || this.tokens[nextIdx].type === T.Consumer || this.tokens[nextIdx].type === T.Queue || this.tokens[nextIdx].type === T.RabbitMQ || this.tokens[nextIdx].type === T.JWT || this.tokens[nextIdx].type === T.OAuth || this.tokens[nextIdx].type === T.Dockerfile || this.tokens[nextIdx].type === T.DockerCompose || this.tokens[nextIdx].type === T.K8sDeployment || this.tokens[nextIdx].type === T.K8sService || this.tokens[nextIdx].type === T.K8sIngress || this.tokens[nextIdx].type === T.AWS || this.tokens[nextIdx].type === T.GCP || this.tokens[nextIdx].type === T.Azure)) {
|
|
648
|
+
// Phase 11 Enterprise blocks: SERVICE/CONTROLLER/GUARD/MODEL/...
|
|
649
|
+
return this.parseBlock();
|
|
650
|
+
} else {
|
|
651
|
+
// It's a value array: [val1 val2 ...]
|
|
652
|
+
return this.parseArray();
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Check for symbol (including keywords used as values)
|
|
657
|
+
if (this.check(T.Symbol)) {
|
|
658
|
+
const token = this.advance();
|
|
659
|
+
// Phase 9c: Special handling for boolean literals (true/false)
|
|
660
|
+
if (token.value === "true") {
|
|
661
|
+
return makeLiteral("boolean", true);
|
|
662
|
+
}
|
|
663
|
+
if (token.value === "false") {
|
|
664
|
+
return makeLiteral("boolean", false);
|
|
665
|
+
}
|
|
666
|
+
return makeLiteral("symbol", token.value);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
throw this.error(`Unexpected token: ${this.peek().type}`, this.peek());
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// Parse array: [val1 val2 val3 ...]
|
|
673
|
+
private parseArray(): ASTNode {
|
|
674
|
+
this.expect(T.LBracket);
|
|
675
|
+
const values: ASTNode[] = [];
|
|
676
|
+
while (!this.check(T.RBracket) && !this.isAtEnd()) {
|
|
677
|
+
values.push(this.parseValue());
|
|
678
|
+
}
|
|
679
|
+
this.expect(T.RBracket);
|
|
680
|
+
// Return as synthetic block-like structure
|
|
681
|
+
// Actually, arrays in v9 are represented as special blocks
|
|
682
|
+
const arrayFields = new Map<string, ASTNode | ASTNode[]>();
|
|
683
|
+
arrayFields.set("items", values);
|
|
684
|
+
return makeBlock("Array", "$array", arrayFields);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// Parse map literal: {:key1 value1 :key2 value2 ...} or {"key1" value1 "key2" value2 ...}
|
|
688
|
+
private parseMap(): ASTNode {
|
|
689
|
+
this.expect(T.LBrace);
|
|
690
|
+
const mapFields = new Map<string, ASTNode>();
|
|
691
|
+
|
|
692
|
+
while (!this.check(T.RBrace) && !this.isAtEnd()) {
|
|
693
|
+
let key: string;
|
|
694
|
+
|
|
695
|
+
// Accept both keyword keys (:name) and string keys ("name")
|
|
696
|
+
if (this.check(T.Colon)) {
|
|
697
|
+
// Keyword key: :name or :$operator (MongoDB)
|
|
698
|
+
this.advance(); // consume ':'
|
|
699
|
+
const keyTok = this.peek();
|
|
700
|
+
if (keyTok.type === T.RBrace || this.isAtEnd()) {
|
|
701
|
+
throw this.error(`Expected key after ':' in map literal`, keyTok);
|
|
702
|
+
}
|
|
703
|
+
// :$set :$gt :$in — Variable token → restore $ prefix
|
|
704
|
+
if (keyTok.type === T.Variable) {
|
|
705
|
+
this.advance();
|
|
706
|
+
key = "$" + keyTok.value;
|
|
707
|
+
} else {
|
|
708
|
+
key = this.advance().value;
|
|
709
|
+
}
|
|
710
|
+
} else if (this.check(T.String)) {
|
|
711
|
+
// String key: "name" (includes MongoDB operators like "$set")
|
|
712
|
+
key = this.advance().value;
|
|
713
|
+
} else {
|
|
714
|
+
throw this.error(`Map key must be :keyword or "string"`, this.peek());
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// Parse the value
|
|
718
|
+
const value = this.parseValue();
|
|
719
|
+
mapFields.set(key, value);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
this.expect(T.RBrace);
|
|
723
|
+
// Return as Map block
|
|
724
|
+
return makeBlock("Map", "$map", mapFields);
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// Check if next is an array literal (not generic function type args)
|
|
728
|
+
// Array literal: [$x ...] or [1 2 3] or [value ...]
|
|
729
|
+
// Generic syntax: [int string] (all symbols)
|
|
730
|
+
private isArrayLiteralStart(): boolean {
|
|
731
|
+
if (!this.check(T.LBracket)) return false;
|
|
732
|
+
const peekPos = this.pos + 1;
|
|
733
|
+
if (peekPos >= this.tokens.length) return false;
|
|
734
|
+
const nextToken = this.tokens[peekPos];
|
|
735
|
+
// Symbol(일반 변수)도 배열 리터럴 요소가 될 수 있음 — generic type은 대문자 시작 또는 단일 대문자 관례
|
|
736
|
+
// [a b c] 처럼 소문자 심볼로 시작하면 배열 리터럴로 처리
|
|
737
|
+
if (nextToken.type === T.Symbol) {
|
|
738
|
+
const v = nextToken.value;
|
|
739
|
+
// 소문자 시작 심볼 → 배열 리터럴 (변수명)
|
|
740
|
+
// 대문자 단일 글자 (T, E, K, V 등) → generic type parameter
|
|
741
|
+
const isGenericType = v.length <= 2 && v === v.toUpperCase() && /^[A-Z]/.test(v);
|
|
742
|
+
return !isGenericType;
|
|
743
|
+
}
|
|
744
|
+
return nextToken.type === T.Variable || nextToken.type === T.Number || nextToken.type === T.String || nextToken.type === T.RBracket || nextToken.type === T.LBracket || nextToken.type === T.LParen;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
// Parse S-expression: (op arg1 arg2 ...) or (op[T] arg1 arg2 ...) for generic functions
|
|
748
|
+
// Also handles match expressions: (match value (pattern body) ...)
|
|
749
|
+
// Phase 6: Also handles import and open expressions
|
|
750
|
+
// Phase 9a: Also handles search and fetch expressions
|
|
751
|
+
// Phase 9b: Also handles learn and recall expressions
|
|
752
|
+
private parseSExpr(): SExpr | PatternMatch | ImportBlock | OpenBlock | SearchBlock | LearnBlock | ReasoningBlock | ReasoningSequence | TryBlock | ThrowExpression {
|
|
753
|
+
this.expect(T.LParen);
|
|
754
|
+
|
|
755
|
+
let op: string;
|
|
756
|
+
const opToken = this.advance();
|
|
757
|
+
|
|
758
|
+
// Phase 6: Handle import/open keyword tokens
|
|
759
|
+
// Phase 9a: Handle search keyword token
|
|
760
|
+
// Phase 9b: Handle learn/recall keyword tokens
|
|
761
|
+
// NOTE: import는 이제 Symbol로 처리되므로 T.Import 체크 제거
|
|
762
|
+
if (opToken.type === T.Open) {
|
|
763
|
+
op = "open";
|
|
764
|
+
} else if (opToken.type === T.Search) {
|
|
765
|
+
op = "search";
|
|
766
|
+
} else if (opToken.type === T.Fetch) {
|
|
767
|
+
op = "fetch";
|
|
768
|
+
} else if (opToken.type === T.Learn) {
|
|
769
|
+
op = "learn";
|
|
770
|
+
} else if (opToken.type === T.Recall) {
|
|
771
|
+
op = "recall";
|
|
772
|
+
} else if (opToken.type === T.Remember) {
|
|
773
|
+
op = "remember";
|
|
774
|
+
} else if (opToken.type === T.Forget) {
|
|
775
|
+
op = "forget";
|
|
776
|
+
} else if (opToken.type === T.Observe) {
|
|
777
|
+
op = "observe";
|
|
778
|
+
} else if (opToken.type === T.Analyze) {
|
|
779
|
+
op = "analyze";
|
|
780
|
+
} else if (opToken.type === T.Decide) {
|
|
781
|
+
op = "decide";
|
|
782
|
+
} else if (opToken.type === T.Act) {
|
|
783
|
+
op = "act";
|
|
784
|
+
} else if (opToken.type === T.Verify) {
|
|
785
|
+
op = "verify";
|
|
786
|
+
} else if (opToken.type === T.If) {
|
|
787
|
+
op = "if";
|
|
788
|
+
} else if (opToken.type === T.When) {
|
|
789
|
+
op = "when";
|
|
790
|
+
} else if (opToken.type === T.Then) {
|
|
791
|
+
op = "then";
|
|
792
|
+
} else if (opToken.type === T.Else) {
|
|
793
|
+
op = "else";
|
|
794
|
+
} else if (opToken.type === T.Repeat) {
|
|
795
|
+
op = "repeat";
|
|
796
|
+
} else if (opToken.type === T.Until) {
|
|
797
|
+
op = "until";
|
|
798
|
+
} else if (opToken.type === T.While) {
|
|
799
|
+
op = "while";
|
|
800
|
+
} else if (opToken.type === T.LParen) {
|
|
801
|
+
// Implicit begin/do block: ( (expr1) (expr2) ... )
|
|
802
|
+
// Used in .fl self-hosting files where :body wraps multiple forms
|
|
803
|
+
this.pos--; // un-advance: treat the inner ( as a value, not operator
|
|
804
|
+
const exprs: ASTNode[] = [];
|
|
805
|
+
while (!this.check(T.RParen) && !this.isAtEnd()) {
|
|
806
|
+
exprs.push(this.parseValue());
|
|
807
|
+
}
|
|
808
|
+
this.expect(T.RParen);
|
|
809
|
+
if (exprs.length === 1) return exprs[0] as SExpr;
|
|
810
|
+
// Multiple exprs: wrap in "do" S-expr
|
|
811
|
+
return { kind: "sexpr" as const, op: "do", args: exprs } as SExpr;
|
|
812
|
+
} else if (opToken.type === T.Variable) {
|
|
813
|
+
// P0-2 후속 (2026-04-25): Variable도 sexpr op으로 허용 — 사용자 #3 함정 해소
|
|
814
|
+
// 예: [FUNC f :params [$pred] :body ($pred 5)]
|
|
815
|
+
// op = "$pred" → evalSExpr → callFunction → variables fallback (P0-2 fix가 처리)
|
|
816
|
+
op = opToken.value.startsWith("$") ? opToken.value : "$" + opToken.value;
|
|
817
|
+
} else if (opToken.type !== T.Symbol) {
|
|
818
|
+
throw this.error(`Expected operator (symbol or keyword) in S-expression, got ${opToken.type}`, opToken);
|
|
819
|
+
} else {
|
|
820
|
+
op = opToken.value;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// Special case: import expressions (Module System only - with :from clause)
|
|
824
|
+
// NOTE: import now parses module name next, then optionally :from etc.
|
|
825
|
+
if (op === "import") {
|
|
826
|
+
const importBlock = this.parseImportExpression();
|
|
827
|
+
this.expect(T.RParen);
|
|
828
|
+
return importBlock;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// Special case: open expressions
|
|
832
|
+
if (op === "open") {
|
|
833
|
+
const openBlock = this.parseOpenExpression();
|
|
834
|
+
this.expect(T.RParen);
|
|
835
|
+
return openBlock;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// Special case: search expressions (Phase 9a)
|
|
839
|
+
if (op === "search") {
|
|
840
|
+
const searchBlock = this.parseSearchExpression();
|
|
841
|
+
this.expect(T.RParen);
|
|
842
|
+
return searchBlock;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// Special case: fetch expressions (Phase 9a)
|
|
846
|
+
if (op === "fetch") {
|
|
847
|
+
const searchBlock = this.parseFetchExpression();
|
|
848
|
+
this.expect(T.RParen);
|
|
849
|
+
return searchBlock;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// Special case: learn expressions (Phase 9b)
|
|
853
|
+
if (op === "learn") {
|
|
854
|
+
const learnBlock = this.parseLearnExpression();
|
|
855
|
+
this.expect(T.RParen);
|
|
856
|
+
return learnBlock;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// Special case: recall expressions (Phase 9b)
|
|
860
|
+
if (op === "recall") {
|
|
861
|
+
const learnBlock = this.parseRecallExpression();
|
|
862
|
+
this.expect(T.RParen);
|
|
863
|
+
return learnBlock;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// Special case: reasoning expressions (Phase 9c)
|
|
867
|
+
if (op === "observe" || opToken.type === T.Observe) {
|
|
868
|
+
const reasoningBlock = this.parseReasoningExpression("observe");
|
|
869
|
+
this.expect(T.RParen);
|
|
870
|
+
return reasoningBlock;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
if (op === "analyze" || opToken.type === T.Analyze) {
|
|
874
|
+
const reasoningBlock = this.parseReasoningExpression("analyze");
|
|
875
|
+
this.expect(T.RParen);
|
|
876
|
+
return reasoningBlock;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
if (op === "decide" || opToken.type === T.Decide) {
|
|
880
|
+
const reasoningBlock = this.parseReasoningExpression("decide");
|
|
881
|
+
this.expect(T.RParen);
|
|
882
|
+
return reasoningBlock;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
if (op === "act" || opToken.type === T.Act) {
|
|
886
|
+
const reasoningBlock = this.parseReasoningExpression("act");
|
|
887
|
+
this.expect(T.RParen);
|
|
888
|
+
return reasoningBlock;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
if (op === "verify" || opToken.type === T.Verify) {
|
|
892
|
+
const reasoningBlock = this.parseReasoningExpression("verify");
|
|
893
|
+
this.expect(T.RParen);
|
|
894
|
+
return reasoningBlock;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
// Special case: reasoning-sequence expressions (Phase 9c Extension)
|
|
898
|
+
if (op === "reasoning-sequence") {
|
|
899
|
+
const reasoningSeq = this.parseReasoningSequenceExpression();
|
|
900
|
+
this.expect(T.RParen);
|
|
901
|
+
return reasoningSeq;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
// Special case: try expressions (Phase 11)
|
|
905
|
+
if (op === "try") {
|
|
906
|
+
const tryBlock = this.parseTryExpression();
|
|
907
|
+
this.expect(T.RParen);
|
|
908
|
+
return tryBlock;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// Special case: throw expressions (Phase 11)
|
|
912
|
+
if (op === "throw") {
|
|
913
|
+
const throwExpr = this.parseThrowExpression();
|
|
914
|
+
this.expect(T.RParen);
|
|
915
|
+
return throwExpr;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// Note: Loop handled as regular S-expr for now (Phase B-1 delayed)
|
|
919
|
+
// Parser sees: (loop [...] body) as normal function call
|
|
920
|
+
// Runtime: evalSExpr will route to evalLoop when op === "loop"
|
|
921
|
+
|
|
922
|
+
// Special case: match expressions
|
|
923
|
+
if (op === "match") {
|
|
924
|
+
const matchExpr = this.parsePatternMatch();
|
|
925
|
+
this.expect(T.RParen);
|
|
926
|
+
return matchExpr;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// Special case: certain operators always use array literal syntax
|
|
930
|
+
// Never generic function syntax: fn, let, if, cond, match, etc.
|
|
931
|
+
const specialFormsForbiddingGeneric = new Set([
|
|
932
|
+
"fn", "let", "if", "cond", "match", "do", "try", "catch",
|
|
933
|
+
"let*", "letrec", "define", "async", "await",
|
|
934
|
+
"loop", "recur", "import", "export"
|
|
935
|
+
]);
|
|
936
|
+
|
|
937
|
+
if (!specialFormsForbiddingGeneric.has(op) && this.check(T.LBracket) && !this.isArrayLiteralStart()) {
|
|
938
|
+
// Phase 4: Handle generic function syntax: (identity[int] ...) or (fn[T] ...)
|
|
939
|
+
// But not array literals: (fn [$x] ...) - those are parsed as regular values
|
|
940
|
+
this.advance(); // consume [
|
|
941
|
+
const typeArgs: string[] = [];
|
|
942
|
+
|
|
943
|
+
while (!this.check(T.RBracket) && !this.isAtEnd()) {
|
|
944
|
+
const typeToken = this.advance();
|
|
945
|
+
if (typeToken.type === T.Symbol) {
|
|
946
|
+
typeArgs.push(typeToken.value);
|
|
947
|
+
}
|
|
948
|
+
// Skip commas if present
|
|
949
|
+
if (this.check(T.Symbol) && this.peek().value === ",") {
|
|
950
|
+
this.advance();
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
this.expect(T.RBracket);
|
|
955
|
+
|
|
956
|
+
// Build generic function name: identity[int] or first-of-pair[int string]
|
|
957
|
+
if (typeArgs.length > 0) {
|
|
958
|
+
op = `${op}[${typeArgs.join(", ")}]`;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
const args: ASTNode[] = [];
|
|
963
|
+
while (!this.check(T.RParen) && !this.isAtEnd()) {
|
|
964
|
+
args.push(this.parseValue());
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
this.expect(T.RParen);
|
|
968
|
+
return makeSExpr(op, args, opToken.line);
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// Parse type annotation: int, string, bool, array<T>, map<K,V>, T?
|
|
972
|
+
private parseTypeAnnotation(): TypeAnnotation {
|
|
973
|
+
const token = this.advance();
|
|
974
|
+
if (token.type !== T.Symbol) {
|
|
975
|
+
throw this.error(`Expected type annotation (symbol), got ${token.type}`, token);
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
let typeName = token.value;
|
|
979
|
+
let generic: TypeAnnotation | undefined = undefined;
|
|
980
|
+
let optional = false;
|
|
981
|
+
|
|
982
|
+
// Handle optional type: T?
|
|
983
|
+
if (this.check(T.Symbol) && this.peek().value === "?") {
|
|
984
|
+
this.advance();
|
|
985
|
+
optional = true;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
// Handle generic types: array<T>, map<K,V>
|
|
989
|
+
if (typeName.includes("<") && typeName.includes(">")) {
|
|
990
|
+
// Simple parsing: extract inner type (e.g., "array<int>" → "int")
|
|
991
|
+
const match = typeName.match(/<(.+)>/);
|
|
992
|
+
if (match) {
|
|
993
|
+
const innerTypeName = match[1];
|
|
994
|
+
generic = makeTypeAnnotation(innerTypeName);
|
|
995
|
+
typeName = typeName.substring(0, typeName.indexOf("<"));
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
return makeTypeAnnotation(typeName, generic, undefined, optional);
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
// ===== Pattern Matching (Phase 4 Week 3-4) =====
|
|
1003
|
+
|
|
1004
|
+
// Parse pattern: literal, variable, wildcard, list, struct, or or-pattern
|
|
1005
|
+
private parsePattern(): Pattern {
|
|
1006
|
+
const firstPattern = this.parseAtomicPattern();
|
|
1007
|
+
|
|
1008
|
+
// Check for or-pattern: pat1 | pat2 | pat3
|
|
1009
|
+
if (this.check(T.Symbol) && this.peek().value === "|") {
|
|
1010
|
+
const alternatives: Pattern[] = [firstPattern];
|
|
1011
|
+
|
|
1012
|
+
while (this.check(T.Symbol) && this.peek().value === "|") {
|
|
1013
|
+
this.advance(); // consume |
|
|
1014
|
+
alternatives.push(this.parseAtomicPattern());
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
return makeOrPattern(alternatives);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
return firstPattern;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
// Parse atomic pattern (without or-alternatives)
|
|
1024
|
+
private parseAtomicPattern(): Pattern {
|
|
1025
|
+
// Wildcard pattern: _
|
|
1026
|
+
if (this.check(T.Symbol) && this.peek().value === "_") {
|
|
1027
|
+
this.advance();
|
|
1028
|
+
return makeWildcardPattern();
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
// Variable pattern: $x, $y (explicit $ syntax)
|
|
1032
|
+
if (this.check(T.Variable)) {
|
|
1033
|
+
const varToken = this.advance();
|
|
1034
|
+
return makeVariablePattern(varToken.value);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
// Variable pattern: x, y, name (bare symbol, but not |, &, etc)
|
|
1038
|
+
if (this.check(T.Symbol) && !["&", "|"].includes(this.peek().value)) {
|
|
1039
|
+
const nameToken = this.advance();
|
|
1040
|
+
return makeVariablePattern(nameToken.value);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
// Literal pattern: number, string, boolean
|
|
1044
|
+
if (this.check(T.Number)) {
|
|
1045
|
+
const token = this.advance();
|
|
1046
|
+
return makeLiteralPattern("number", parseFloat(token.value));
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
if (this.check(T.String)) {
|
|
1050
|
+
const token = this.advance();
|
|
1051
|
+
return makeLiteralPattern("string", token.value);
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
// Parenthesized pattern or range pattern: (pattern) / (range min max)
|
|
1055
|
+
if (this.check(T.LParen)) {
|
|
1056
|
+
this.advance(); // consume (
|
|
1057
|
+
// Check for range pattern: (range min max)
|
|
1058
|
+
if (this.check(T.Symbol) && this.peek().value === "range") {
|
|
1059
|
+
this.advance(); // consume 'range'
|
|
1060
|
+
const minToken = this.advance(); // min value
|
|
1061
|
+
const maxToken = this.advance(); // max value
|
|
1062
|
+
this.expect(T.RParen);
|
|
1063
|
+
return makeRangePattern(parseFloat(minToken.value), parseFloat(maxToken.value));
|
|
1064
|
+
}
|
|
1065
|
+
const pattern = this.parsePattern();
|
|
1066
|
+
this.expect(T.RParen);
|
|
1067
|
+
return pattern;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
// List pattern: [x y z] or [x & rest]
|
|
1071
|
+
if (this.check(T.LBracket)) {
|
|
1072
|
+
this.advance(); // consume [
|
|
1073
|
+
const elements: Pattern[] = [];
|
|
1074
|
+
let restElement: string | undefined;
|
|
1075
|
+
|
|
1076
|
+
while (!this.check(T.RBracket) && !this.isAtEnd()) {
|
|
1077
|
+
// Check for rest element: & name
|
|
1078
|
+
if (this.check(T.Symbol) && this.peek().value === "&") {
|
|
1079
|
+
this.advance(); // consume &
|
|
1080
|
+
if (this.check(T.Symbol)) {
|
|
1081
|
+
const nameToken = this.advance();
|
|
1082
|
+
restElement = nameToken.value;
|
|
1083
|
+
}
|
|
1084
|
+
break; // rest element must be last
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
elements.push(this.parsePattern());
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
this.expect(T.RBracket);
|
|
1091
|
+
return makeListPattern(elements, restElement);
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
// Struct pattern (brace form): {:key pattern :key pattern ...} (Phase 65)
|
|
1095
|
+
// Supports :as $varname inside {} or after closing brace
|
|
1096
|
+
if (this.check(T.LBrace)) {
|
|
1097
|
+
this.advance(); // consume '{'
|
|
1098
|
+
const fields = new Map<string, Pattern>();
|
|
1099
|
+
let asBinding: string | undefined;
|
|
1100
|
+
while (!this.check(T.RBrace) && !this.isAtEnd()) {
|
|
1101
|
+
let fieldName: string;
|
|
1102
|
+
if (this.check(T.Colon)) {
|
|
1103
|
+
this.advance(); // consume ':'
|
|
1104
|
+
if (!this.check(T.Symbol)) break;
|
|
1105
|
+
const keyToken = this.advance();
|
|
1106
|
+
fieldName = keyToken.value;
|
|
1107
|
+
} else if (this.check(T.Keyword)) {
|
|
1108
|
+
const keyToken = this.advance();
|
|
1109
|
+
fieldName = keyToken.value.startsWith(":") ? keyToken.value.slice(1) : keyToken.value;
|
|
1110
|
+
} else {
|
|
1111
|
+
break;
|
|
1112
|
+
}
|
|
1113
|
+
// :as $varname inside braces (Phase 65)
|
|
1114
|
+
if (fieldName === "as") {
|
|
1115
|
+
if (this.check(T.Variable)) {
|
|
1116
|
+
asBinding = this.advance().value;
|
|
1117
|
+
}
|
|
1118
|
+
continue; // don't parse as field
|
|
1119
|
+
}
|
|
1120
|
+
const pattern = this.parsePattern();
|
|
1121
|
+
fields.set(fieldName, pattern);
|
|
1122
|
+
}
|
|
1123
|
+
this.expect(T.RBrace);
|
|
1124
|
+
// Also check for :as $varname after closing brace (Phase 65)
|
|
1125
|
+
if (!asBinding && this.check(T.Colon)) {
|
|
1126
|
+
const savedPos = this.pos;
|
|
1127
|
+
this.advance(); // consume ':'
|
|
1128
|
+
if (this.check(T.Symbol) && this.peek().value === "as") {
|
|
1129
|
+
this.advance(); // consume 'as'
|
|
1130
|
+
if (this.check(T.Variable)) {
|
|
1131
|
+
asBinding = this.advance().value;
|
|
1132
|
+
}
|
|
1133
|
+
} else {
|
|
1134
|
+
this.pos = savedPos; // backtrack
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
return makeStructPattern(fields, asBinding);
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
// Struct pattern (colon form, legacy): :key pattern :key pattern ... (Phase 6: T.Colon + T.Symbol)
|
|
1141
|
+
// Only used outside match-case context (e.g., nested) — kept for backward compatibility
|
|
1142
|
+
if (this.check(T.Keyword)) {
|
|
1143
|
+
const fields = new Map<string, Pattern>();
|
|
1144
|
+
let asBinding: string | undefined;
|
|
1145
|
+
while (this.check(T.Keyword) && !this.isAtEnd()) {
|
|
1146
|
+
const keyToken = this.advance();
|
|
1147
|
+
const fieldName = keyToken.value.startsWith(":") ? keyToken.value.slice(1) : keyToken.value;
|
|
1148
|
+
if (fieldName === "as") {
|
|
1149
|
+
if (this.check(T.Variable)) {
|
|
1150
|
+
asBinding = this.advance().value;
|
|
1151
|
+
}
|
|
1152
|
+
break;
|
|
1153
|
+
}
|
|
1154
|
+
const pattern = this.parsePattern();
|
|
1155
|
+
fields.set(fieldName, pattern);
|
|
1156
|
+
}
|
|
1157
|
+
return makeStructPattern(fields, asBinding);
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
throw this.error(`Expected pattern, got ${this.peek().type}`, this.peek());
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
// Parse match expression: (match value (pattern body) (pattern body) ... [default])
|
|
1164
|
+
// Note: Opening paren '(' already consumed by parseSExpr
|
|
1165
|
+
private parsePatternMatch(): PatternMatch {
|
|
1166
|
+
// 'match' keyword already consumed by parseSExpr
|
|
1167
|
+
// Just parse the value to match and cases
|
|
1168
|
+
|
|
1169
|
+
// Parse value to match
|
|
1170
|
+
const value = this.parseValue();
|
|
1171
|
+
|
|
1172
|
+
// Parse cases: (pattern body) (pattern body) ...
|
|
1173
|
+
const cases: MatchCase[] = [];
|
|
1174
|
+
let defaultCase: ASTNode | undefined;
|
|
1175
|
+
|
|
1176
|
+
while (this.check(T.LParen) && !this.isAtEnd()) {
|
|
1177
|
+
this.advance(); // consume (
|
|
1178
|
+
|
|
1179
|
+
// Check for default case: (default body)
|
|
1180
|
+
if (this.check(T.Symbol) && this.peek().value === "default") {
|
|
1181
|
+
this.advance(); // consume 'default'
|
|
1182
|
+
defaultCase = this.parseValue();
|
|
1183
|
+
this.expect(T.RParen);
|
|
1184
|
+
break; // default must be last
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
// Parse pattern
|
|
1188
|
+
const pattern = this.parsePattern();
|
|
1189
|
+
|
|
1190
|
+
// Check for optional guard: (if condition) or (when condition)
|
|
1191
|
+
// If (if cond then else) with then/else, it's the body, not a guard
|
|
1192
|
+
let guard: ASTNode | undefined;
|
|
1193
|
+
if (this.check(T.LParen) && (this.peekNext()?.value === "if" || this.peekNext()?.value === "when")) {
|
|
1194
|
+
const savedPos = this.pos;
|
|
1195
|
+
this.advance(); // consume (
|
|
1196
|
+
const guardKeyword = this.advance().value; // consume 'if' or 'when'
|
|
1197
|
+
const possibleGuard = this.parseValue();
|
|
1198
|
+
if (this.check(T.RParen)) {
|
|
1199
|
+
// (if condition) or (when condition) with no extra args → it's a guard
|
|
1200
|
+
this.advance(); // consume )
|
|
1201
|
+
guard = possibleGuard;
|
|
1202
|
+
} else if (guardKeyword === "when") {
|
|
1203
|
+
// (when condition ...) — treat all remaining args as do-block
|
|
1204
|
+
// for safety: backtrack and treat as body
|
|
1205
|
+
this.pos = savedPos;
|
|
1206
|
+
} else {
|
|
1207
|
+
// (if condition then else) → it's the body, backtrack
|
|
1208
|
+
this.pos = savedPos;
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
// Parse body
|
|
1213
|
+
const body = this.parseValue();
|
|
1214
|
+
cases.push(makeMatchCase(pattern, body, guard));
|
|
1215
|
+
|
|
1216
|
+
this.expect(T.RParen);
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
// Note: Closing paren will be consumed by parseSExpr
|
|
1220
|
+
|
|
1221
|
+
return makePatternMatch(value, cases, defaultCase);
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
// ===== Utility Methods =====
|
|
1225
|
+
|
|
1226
|
+
private peek(): Token {
|
|
1227
|
+
if (this.pos >= this.tokens.length) {
|
|
1228
|
+
return this.tokens[this.tokens.length - 1]; // EOF
|
|
1229
|
+
}
|
|
1230
|
+
return this.tokens[this.pos];
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
private peekNext(): Token | null {
|
|
1234
|
+
if (this.pos + 1 >= this.tokens.length) return null;
|
|
1235
|
+
return this.tokens[this.pos + 1];
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
private advance(): Token {
|
|
1239
|
+
if (this.pos < this.tokens.length) this.pos++;
|
|
1240
|
+
return this.tokens[this.pos - 1];
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
private check(type: string): boolean {
|
|
1244
|
+
if (this.isAtEnd()) return false;
|
|
1245
|
+
return this.peek().type === type;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
private expect(type: string): Token {
|
|
1249
|
+
if (this.check(type)) {
|
|
1250
|
+
const tok = this.advance();
|
|
1251
|
+
// 자잘 #4 (2026-04-25): paren matching — opening 추적
|
|
1252
|
+
if (type === T.LParen || type === T.LBracket || type === T.LBrace) {
|
|
1253
|
+
this.parenStack.push({ type, line: tok.line, col: tok.col });
|
|
1254
|
+
} else if (type === T.RParen || type === T.RBracket || type === T.RBrace) {
|
|
1255
|
+
this.parenStack.pop();
|
|
1256
|
+
}
|
|
1257
|
+
return tok;
|
|
1258
|
+
}
|
|
1259
|
+
const token = this.peek();
|
|
1260
|
+
// 자잘 #4: paren matching info — RParen/RBracket/RBrace 누락 시 가장 최근 opening 위치 표시
|
|
1261
|
+
let extraHint = "";
|
|
1262
|
+
if ((type === T.RParen || type === T.RBracket || type === T.RBrace) && this.parenStack.length > 0) {
|
|
1263
|
+
const opening = this.parenStack[this.parenStack.length - 1];
|
|
1264
|
+
const openSym = opening.type === T.LParen ? "(" : opening.type === T.LBracket ? "[" : "{";
|
|
1265
|
+
const wantSym = type === T.RParen ? ")" : type === T.RBracket ? "]" : "}";
|
|
1266
|
+
extraHint = ` (가장 최근 opening '${openSym}' at line ${opening.line}:${opening.col} — '${wantSym}' 누락 또는 중첩 오류)`;
|
|
1267
|
+
}
|
|
1268
|
+
throw this.error(`Expected ${type}, got ${token.type}${extraHint}`, token);
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
private isAtEnd(): boolean {
|
|
1272
|
+
return this.pos >= this.tokens.length;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
private error(message: string, token: Token): ParserError {
|
|
1276
|
+
// 힌트 매칭: 메시지 앞부분으로 검색
|
|
1277
|
+
let hint = Object.entries(ERROR_HINTS).find(([k]) => message.includes(k))?.[1];
|
|
1278
|
+
|
|
1279
|
+
// Priority 4 (2026-04-28): 전체 열린 괄호 스택 표시 — AI가 자동 검증 가능하도록
|
|
1280
|
+
// EOF 또는 Expected R* 에러 시 parenStack 전체 정보 표시
|
|
1281
|
+
if (this.parenStack.length > 0 &&
|
|
1282
|
+
(token.type === T.EOF || message.includes("Expected R") || message.includes("Unexpected"))) {
|
|
1283
|
+
|
|
1284
|
+
// 1. 전체 스택 정보 (깊이, 심볼, 위치)
|
|
1285
|
+
const stackInfo = this.parenStack.map((opening, idx) => {
|
|
1286
|
+
const openSym = opening.type === T.LParen ? "(" : opening.type === T.LBracket ? "[" : "{";
|
|
1287
|
+
const depth = idx + 1;
|
|
1288
|
+
return ` [${depth}] '${openSym}' at line ${opening.line}:${opening.col}`;
|
|
1289
|
+
}).join("\n");
|
|
1290
|
+
|
|
1291
|
+
// 2. 필요한 닫는 괄호 시퀀스 (역순)
|
|
1292
|
+
const closeSeq = this.parenStack
|
|
1293
|
+
.map(o => o.type === T.LParen ? ")" : o.type === T.LBracket ? "]" : "}")
|
|
1294
|
+
.reverse()
|
|
1295
|
+
.join("");
|
|
1296
|
+
|
|
1297
|
+
// 3. 다음 예상 닫는 괄호 (가장 최근 열린 괄호)
|
|
1298
|
+
const nextOpening = this.parenStack[this.parenStack.length - 1];
|
|
1299
|
+
const nextExpected = nextOpening.type === T.LParen ? ")" : nextOpening.type === T.LBracket ? "]" : "}";
|
|
1300
|
+
|
|
1301
|
+
// 4. 힌트 메시지
|
|
1302
|
+
const parenHint = `미닫힘 괄호 스택 (깊이: ${this.parenStack.length}):\n${stackInfo}\n\n필요한 닫힘 괄호 시퀀스: ${closeSeq}\n\n다음 토큰을 예상: '${nextExpected}'`;
|
|
1303
|
+
hint = hint ? `${hint}\n\n${parenHint}` : parenHint;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
// 메시지 패턴에 따라 에러 코드 결정
|
|
1307
|
+
let code = "E_PARSE_SYNTAX_ERROR";
|
|
1308
|
+
if (message.includes("Expected") || message.includes("Unexpected")) {
|
|
1309
|
+
code = "E_PARSE_UNEXPECTED_TOKEN";
|
|
1310
|
+
} else if (message.includes("Unterminated")) {
|
|
1311
|
+
code = "E_PARSE_UNCLOSED_PAREN";
|
|
1312
|
+
} else if (message.includes("Expected block")) {
|
|
1313
|
+
code = "E_PARSE_SYNTAX_ERROR";
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
return new ParserError(message, token.line, token.col, hint, code, "parse");
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
// Synchronization for error recovery
|
|
1320
|
+
private synchronize(): void {
|
|
1321
|
+
this.advance();
|
|
1322
|
+
while (!this.isAtEnd()) {
|
|
1323
|
+
if (this.check(T.LBracket)) return; // Next block start
|
|
1324
|
+
this.advance();
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
// Phase 6: Parse import expression (import math :from "./math.fl" :as m :only [add])
|
|
1329
|
+
private parseImportExpression(): ImportBlock {
|
|
1330
|
+
// Module name is next (qualified identifier: math or a:b or a:b:c)
|
|
1331
|
+
const moduleName = this.parseQualifiedIdentifier();
|
|
1332
|
+
|
|
1333
|
+
// Parse optional clauses: :from, :only, :as
|
|
1334
|
+
let source: string | undefined;
|
|
1335
|
+
let selective: string[] | undefined;
|
|
1336
|
+
let alias: string | undefined;
|
|
1337
|
+
|
|
1338
|
+
while (!this.check(T.RParen) && !this.isAtEnd()) {
|
|
1339
|
+
if (this.check(T.Colon)) {
|
|
1340
|
+
this.advance(); // consume ':'
|
|
1341
|
+
if (!this.check(T.Symbol)) {
|
|
1342
|
+
throw this.error(
|
|
1343
|
+
`Expected symbol after ':', got ${this.peek().type}`,
|
|
1344
|
+
this.peek()
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
const clauseName = this.advance().value;
|
|
1349
|
+
|
|
1350
|
+
switch (clauseName) {
|
|
1351
|
+
case "from":
|
|
1352
|
+
// :from "path"
|
|
1353
|
+
if (!this.check(T.String)) {
|
|
1354
|
+
throw this.error(`Expected string after :from, got ${this.peek().type}`, this.peek());
|
|
1355
|
+
}
|
|
1356
|
+
source = this.advance().value;
|
|
1357
|
+
break;
|
|
1358
|
+
|
|
1359
|
+
case "only":
|
|
1360
|
+
// :only [func1 func2 ...]
|
|
1361
|
+
if (!this.check(T.LBracket)) {
|
|
1362
|
+
throw this.error(`Expected [ after :only, got ${this.peek().type}`, this.peek());
|
|
1363
|
+
}
|
|
1364
|
+
selective = this.parseSelectiveImport();
|
|
1365
|
+
break;
|
|
1366
|
+
|
|
1367
|
+
case "as":
|
|
1368
|
+
// :as alias
|
|
1369
|
+
if (!this.check(T.Symbol)) {
|
|
1370
|
+
throw this.error(`Expected symbol after :as, got ${this.peek().type}`, this.peek());
|
|
1371
|
+
}
|
|
1372
|
+
alias = this.advance().value;
|
|
1373
|
+
break;
|
|
1374
|
+
|
|
1375
|
+
default:
|
|
1376
|
+
throw this.error(`Unknown import clause: ${clauseName}`, this.peek());
|
|
1377
|
+
}
|
|
1378
|
+
} else {
|
|
1379
|
+
throw this.error(`Expected ':' in import expression, got ${this.peek().type}`, this.peek());
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
return makeImportBlock(moduleName, source, selective, alias);
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
// Phase 6: Parse open expression (open math :from "./math.fl")
|
|
1387
|
+
private parseOpenExpression(): OpenBlock {
|
|
1388
|
+
// Module name is next (qualified identifier)
|
|
1389
|
+
const moduleName = this.parseQualifiedIdentifier();
|
|
1390
|
+
|
|
1391
|
+
// Parse optional :from clause
|
|
1392
|
+
let source: string | undefined;
|
|
1393
|
+
|
|
1394
|
+
while (!this.check(T.RParen) && !this.isAtEnd()) {
|
|
1395
|
+
if (this.check(T.Colon)) {
|
|
1396
|
+
this.advance(); // consume ':'
|
|
1397
|
+
if (!this.check(T.Symbol)) {
|
|
1398
|
+
throw this.error(
|
|
1399
|
+
`Expected symbol after ':', got ${this.peek().type}`,
|
|
1400
|
+
this.peek()
|
|
1401
|
+
);
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
const clauseName = this.advance().value;
|
|
1405
|
+
|
|
1406
|
+
if (clauseName === "from") {
|
|
1407
|
+
if (!this.check(T.String)) {
|
|
1408
|
+
throw this.error(`Expected string after :from, got ${this.peek().type}`, this.peek());
|
|
1409
|
+
}
|
|
1410
|
+
source = this.advance().value;
|
|
1411
|
+
} else {
|
|
1412
|
+
throw this.error(`Unknown open clause: ${clauseName}`, this.peek());
|
|
1413
|
+
}
|
|
1414
|
+
} else {
|
|
1415
|
+
throw this.error(`Expected ':' in open expression, got ${this.peek().type}`, this.peek());
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
return makeOpenBlock(moduleName, source);
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
// Phase 9a: Parse search expression
|
|
1423
|
+
// (search query :source "web"|"api"|"kb" :cache true|false :limit 5)
|
|
1424
|
+
private parseSearchExpression(): SearchBlock {
|
|
1425
|
+
// First argument: query (string)
|
|
1426
|
+
let query = "";
|
|
1427
|
+
const queryNode = this.parseValue();
|
|
1428
|
+
if (queryNode.kind === "literal" && queryNode.type === "string") {
|
|
1429
|
+
query = queryNode.value as string;
|
|
1430
|
+
} else {
|
|
1431
|
+
throw this.error(`Expected string query in search expression`, this.peek());
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
// Parse optional keyword arguments
|
|
1435
|
+
let source: "web" | "api" | "kb" = "web";
|
|
1436
|
+
let cache = false;
|
|
1437
|
+
let limit = 10;
|
|
1438
|
+
let name: string | undefined;
|
|
1439
|
+
|
|
1440
|
+
while (!this.check(T.RParen) && !this.isAtEnd()) {
|
|
1441
|
+
if (this.check(T.Colon)) {
|
|
1442
|
+
this.advance(); // consume ':'
|
|
1443
|
+
if (!this.check(T.Symbol)) {
|
|
1444
|
+
throw this.error(`Expected symbol after ':', got ${this.peek().type}`, this.peek());
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
const clauseName = this.advance().value;
|
|
1448
|
+
|
|
1449
|
+
switch (clauseName) {
|
|
1450
|
+
case "source":
|
|
1451
|
+
if (!this.check(T.String)) {
|
|
1452
|
+
throw this.error(`Expected string after :source`, this.peek());
|
|
1453
|
+
}
|
|
1454
|
+
const sourceVal = this.advance().value;
|
|
1455
|
+
if (sourceVal === "web" || sourceVal === "api" || sourceVal === "kb") {
|
|
1456
|
+
source = sourceVal as "web" | "api" | "kb";
|
|
1457
|
+
} else {
|
|
1458
|
+
throw this.error(`Invalid source: ${sourceVal}`, this.peek());
|
|
1459
|
+
}
|
|
1460
|
+
break;
|
|
1461
|
+
|
|
1462
|
+
case "cache":
|
|
1463
|
+
const cacheVal = this.parseValue();
|
|
1464
|
+
if (cacheVal.kind === "literal") {
|
|
1465
|
+
cache = cacheVal.value === "true" || cacheVal.value === true;
|
|
1466
|
+
}
|
|
1467
|
+
break;
|
|
1468
|
+
|
|
1469
|
+
case "limit":
|
|
1470
|
+
const limitVal = this.parseValue();
|
|
1471
|
+
if (limitVal.kind === "literal" && limitVal.type === "number") {
|
|
1472
|
+
limit = limitVal.value as number;
|
|
1473
|
+
}
|
|
1474
|
+
break;
|
|
1475
|
+
|
|
1476
|
+
case "name":
|
|
1477
|
+
if (!this.check(T.Symbol)) {
|
|
1478
|
+
throw this.error(`Expected symbol after :name`, this.peek());
|
|
1479
|
+
}
|
|
1480
|
+
name = this.advance().value;
|
|
1481
|
+
break;
|
|
1482
|
+
|
|
1483
|
+
default:
|
|
1484
|
+
throw this.error(`Unknown search clause: ${clauseName}`, this.peek());
|
|
1485
|
+
}
|
|
1486
|
+
} else {
|
|
1487
|
+
throw this.error(`Expected ':' in search expression, got ${this.peek().type}`, this.peek());
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
return {
|
|
1492
|
+
kind: "search-block",
|
|
1493
|
+
query,
|
|
1494
|
+
source,
|
|
1495
|
+
cache,
|
|
1496
|
+
limit,
|
|
1497
|
+
name,
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
// Phase 9a: Parse fetch expression
|
|
1502
|
+
// (fetch url :cache true|false)
|
|
1503
|
+
private parseFetchExpression(): SearchBlock {
|
|
1504
|
+
// First argument: URL (string)
|
|
1505
|
+
let query = "";
|
|
1506
|
+
const urlNode = this.parseValue();
|
|
1507
|
+
if (urlNode.kind === "literal" && urlNode.type === "string") {
|
|
1508
|
+
query = urlNode.value as string;
|
|
1509
|
+
} else {
|
|
1510
|
+
throw this.error(`Expected string URL in fetch expression`, this.peek());
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
// Parse optional keyword arguments
|
|
1514
|
+
let cache = false;
|
|
1515
|
+
|
|
1516
|
+
while (!this.check(T.RParen) && !this.isAtEnd()) {
|
|
1517
|
+
if (this.check(T.Colon)) {
|
|
1518
|
+
this.advance(); // consume ':'
|
|
1519
|
+
if (!this.check(T.Symbol)) {
|
|
1520
|
+
throw this.error(`Expected symbol after ':', got ${this.peek().type}`, this.peek());
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
const clauseName = this.advance().value;
|
|
1524
|
+
|
|
1525
|
+
if (clauseName === "cache") {
|
|
1526
|
+
const cacheVal = this.parseValue();
|
|
1527
|
+
if (cacheVal.kind === "literal") {
|
|
1528
|
+
cache = cacheVal.value === "true" || cacheVal.value === true;
|
|
1529
|
+
}
|
|
1530
|
+
} else {
|
|
1531
|
+
throw this.error(`Unknown fetch clause: ${clauseName}`, this.peek());
|
|
1532
|
+
}
|
|
1533
|
+
} else {
|
|
1534
|
+
throw this.error(`Expected ':' in fetch expression, got ${this.peek().type}`, this.peek());
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
return {
|
|
1539
|
+
kind: "search-block",
|
|
1540
|
+
query,
|
|
1541
|
+
source: "api",
|
|
1542
|
+
cache,
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
// Phase 9b: Parse learn expression
|
|
1547
|
+
// (learn key data :source "search" :confidence 0.95)
|
|
1548
|
+
private parseLearnExpression(): LearnBlock {
|
|
1549
|
+
// First argument: key (symbol or string)
|
|
1550
|
+
let key = "";
|
|
1551
|
+
const keyNode = this.parseValue();
|
|
1552
|
+
if (keyNode.kind === "literal" && (keyNode.type === "string" || keyNode.type === "symbol")) {
|
|
1553
|
+
key = keyNode.value as string;
|
|
1554
|
+
} else if ((keyNode as any).kind === "variable") {
|
|
1555
|
+
key = (keyNode as Variable).name;
|
|
1556
|
+
} else {
|
|
1557
|
+
throw this.error(`Expected symbol/string key in learn expression`, this.peek());
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
// Second argument: data (any value)
|
|
1561
|
+
// For now, data is optional - if next token is :keyword, skip data parsing
|
|
1562
|
+
let data: any = null;
|
|
1563
|
+
if (!this.check(T.Colon) && !this.check(T.RParen) && !this.isAtEnd()) {
|
|
1564
|
+
data = this.parseValue();
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
// Parse optional keyword arguments
|
|
1568
|
+
let source: "search" | "feedback" | "analysis" = "search";
|
|
1569
|
+
let confidence: number | undefined;
|
|
1570
|
+
let timestamp: string | undefined;
|
|
1571
|
+
|
|
1572
|
+
while (!this.check(T.RParen) && !this.isAtEnd()) {
|
|
1573
|
+
if (this.check(T.Colon)) {
|
|
1574
|
+
this.advance(); // consume ':'
|
|
1575
|
+
if (!this.check(T.Symbol)) {
|
|
1576
|
+
throw this.error(`Expected symbol after ':', got ${this.peek().type}`, this.peek());
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
const clauseName = this.advance().value;
|
|
1580
|
+
|
|
1581
|
+
switch (clauseName) {
|
|
1582
|
+
case "source":
|
|
1583
|
+
const sourceVal = this.parseValue();
|
|
1584
|
+
if (sourceVal.kind === "literal" && sourceVal.type === "string") {
|
|
1585
|
+
const srcStr = sourceVal.value as string;
|
|
1586
|
+
if (srcStr === "search" || srcStr === "feedback" || srcStr === "analysis") {
|
|
1587
|
+
source = srcStr;
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
break;
|
|
1591
|
+
|
|
1592
|
+
case "confidence":
|
|
1593
|
+
const confVal = this.parseValue();
|
|
1594
|
+
if (confVal.kind === "literal" && confVal.type === "number") {
|
|
1595
|
+
confidence = confVal.value as number;
|
|
1596
|
+
}
|
|
1597
|
+
break;
|
|
1598
|
+
|
|
1599
|
+
case "timestamp":
|
|
1600
|
+
const timeVal = this.parseValue();
|
|
1601
|
+
if (timeVal.kind === "literal" && timeVal.type === "string") {
|
|
1602
|
+
timestamp = timeVal.value as string;
|
|
1603
|
+
}
|
|
1604
|
+
break;
|
|
1605
|
+
|
|
1606
|
+
default:
|
|
1607
|
+
throw this.error(`Unknown learn clause: ${clauseName}`, this.peek());
|
|
1608
|
+
}
|
|
1609
|
+
} else {
|
|
1610
|
+
throw this.error(`Expected ':' in learn expression, got ${this.peek().type}`, this.peek());
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
return {
|
|
1615
|
+
kind: "learn-block",
|
|
1616
|
+
key,
|
|
1617
|
+
data,
|
|
1618
|
+
source,
|
|
1619
|
+
confidence,
|
|
1620
|
+
timestamp,
|
|
1621
|
+
};
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
// Phase 9b: Parse recall expression
|
|
1625
|
+
// (recall key) - retrieves learned data by key
|
|
1626
|
+
private parseRecallExpression(): LearnBlock {
|
|
1627
|
+
// First argument: key (symbol or string)
|
|
1628
|
+
let key = "";
|
|
1629
|
+
const keyNode = this.parseValue();
|
|
1630
|
+
if (keyNode.kind === "literal" && (keyNode.type === "string" || keyNode.type === "symbol")) {
|
|
1631
|
+
key = keyNode.value as string;
|
|
1632
|
+
} else if ((keyNode as any).kind === "variable") {
|
|
1633
|
+
key = (keyNode as Variable).name;
|
|
1634
|
+
} else {
|
|
1635
|
+
throw this.error(`Expected symbol/string key in recall expression`, this.peek());
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
// Return a recall block (kind="learn-block" with data=null to indicate retrieval)
|
|
1639
|
+
return {
|
|
1640
|
+
kind: "learn-block",
|
|
1641
|
+
key,
|
|
1642
|
+
data: null, // null indicates this is a recall operation
|
|
1643
|
+
source: "search",
|
|
1644
|
+
};
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
// Phase 9c: Parse reasoning expression
|
|
1648
|
+
// (observe "facts" :confidence 0.9)
|
|
1649
|
+
// (analyze :angle1 "perf" :angle2 "security" :selected "angle1")
|
|
1650
|
+
// (decide :choice "angle2" :reason "best performance")
|
|
1651
|
+
// (act :action "implement" :parameters {...})
|
|
1652
|
+
// (verify :result success :evidence [...])
|
|
1653
|
+
private parseReasoningExpression(stage: "observe" | "analyze" | "decide" | "act" | "verify"): ReasoningBlock {
|
|
1654
|
+
const data = new Map<string, any>();
|
|
1655
|
+
const metadata: { startTime?: string; endTime?: string; confidence?: number; feedback?: string } = {
|
|
1656
|
+
startTime: new Date().toISOString(),
|
|
1657
|
+
};
|
|
1658
|
+
|
|
1659
|
+
let observations: any[] | undefined;
|
|
1660
|
+
let analysis: any[] | undefined;
|
|
1661
|
+
let decisions: any[] | undefined;
|
|
1662
|
+
let actions: any[] | undefined;
|
|
1663
|
+
let verifications: any[] | undefined;
|
|
1664
|
+
|
|
1665
|
+
// Parse stage-specific data
|
|
1666
|
+
switch (stage) {
|
|
1667
|
+
case "observe": {
|
|
1668
|
+
// Parse observation data and optional keyword arguments
|
|
1669
|
+
// (observe "fact" :confidence 0.9)
|
|
1670
|
+
if (!this.check(T.RParen) && !this.check(T.Colon)) {
|
|
1671
|
+
const obs = this.parseValue();
|
|
1672
|
+
observations = [obs];
|
|
1673
|
+
data.set("observation", obs);
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
// Parse keyword arguments
|
|
1677
|
+
while (!this.check(T.RParen) && !this.isAtEnd()) {
|
|
1678
|
+
if (this.check(T.Colon)) {
|
|
1679
|
+
this.advance();
|
|
1680
|
+
const clauseName = this.advance().value;
|
|
1681
|
+
|
|
1682
|
+
const clauseValue = this.parseValue();
|
|
1683
|
+
if (clauseName === "confidence") {
|
|
1684
|
+
if (clauseValue.kind === "literal" && clauseValue.type === "number") {
|
|
1685
|
+
data.set(clauseName, clauseValue.value);
|
|
1686
|
+
}
|
|
1687
|
+
} else {
|
|
1688
|
+
data.set(clauseName, clauseValue);
|
|
1689
|
+
}
|
|
1690
|
+
} else {
|
|
1691
|
+
throw this.error(`Expected ':' in observe expression, got ${this.peek().type}`, this.peek());
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
break;
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
case "analyze": {
|
|
1698
|
+
// Parse multiple angles: :angle1 "value1" :angle2 "value2" :selected "angle1"
|
|
1699
|
+
const angles = new Map<string, any>();
|
|
1700
|
+
while (!this.check(T.RParen) && !this.isAtEnd()) {
|
|
1701
|
+
if (this.check(T.Colon)) {
|
|
1702
|
+
this.advance();
|
|
1703
|
+
const clauseName = this.advance().value;
|
|
1704
|
+
|
|
1705
|
+
if (clauseName === "selected") {
|
|
1706
|
+
const selected = this.parseValue();
|
|
1707
|
+
data.set("selected", selected);
|
|
1708
|
+
} else {
|
|
1709
|
+
// It's an angle
|
|
1710
|
+
const angleValue = this.parseValue();
|
|
1711
|
+
angles.set(clauseName, angleValue);
|
|
1712
|
+
}
|
|
1713
|
+
} else {
|
|
1714
|
+
throw this.error(`Expected ':' in analyze expression, got ${this.peek().type}`, this.peek());
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
data.set("angles", angles);
|
|
1718
|
+
analysis = Array.from(angles.values());
|
|
1719
|
+
break;
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
case "decide": {
|
|
1723
|
+
// Parse decision: :choice "angle2" :reason "best performance"
|
|
1724
|
+
while (!this.check(T.RParen) && !this.isAtEnd()) {
|
|
1725
|
+
if (this.check(T.Colon)) {
|
|
1726
|
+
this.advance();
|
|
1727
|
+
const clauseName = this.advance().value;
|
|
1728
|
+
|
|
1729
|
+
const clauseValue = this.parseValue();
|
|
1730
|
+
data.set(clauseName, clauseValue);
|
|
1731
|
+
} else {
|
|
1732
|
+
throw this.error(`Expected ':' in decide expression, got ${this.peek().type}`, this.peek());
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
decisions = [data.get("choice")];
|
|
1736
|
+
break;
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
case "act": {
|
|
1740
|
+
// Parse action: :action "implement" :parameters {...}
|
|
1741
|
+
while (!this.check(T.RParen) && !this.isAtEnd()) {
|
|
1742
|
+
if (this.check(T.Colon)) {
|
|
1743
|
+
this.advance();
|
|
1744
|
+
const clauseName = this.advance().value;
|
|
1745
|
+
|
|
1746
|
+
const clauseValue = this.parseValue();
|
|
1747
|
+
data.set(clauseName, clauseValue);
|
|
1748
|
+
} else {
|
|
1749
|
+
throw this.error(`Expected ':' in act expression, got ${this.peek().type}`, this.peek());
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
actions = [data.get("action")];
|
|
1753
|
+
break;
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
case "verify": {
|
|
1757
|
+
// Parse verification: :result success :evidence [...]
|
|
1758
|
+
while (!this.check(T.RParen) && !this.isAtEnd()) {
|
|
1759
|
+
if (this.check(T.Colon)) {
|
|
1760
|
+
this.advance();
|
|
1761
|
+
const clauseName = this.advance().value;
|
|
1762
|
+
|
|
1763
|
+
const clauseValue = this.parseValue();
|
|
1764
|
+
if (clauseName === "confidence") {
|
|
1765
|
+
if (clauseValue.kind === "literal" && clauseValue.type === "number") {
|
|
1766
|
+
metadata.confidence = clauseValue.value as number;
|
|
1767
|
+
}
|
|
1768
|
+
} else {
|
|
1769
|
+
data.set(clauseName, clauseValue);
|
|
1770
|
+
}
|
|
1771
|
+
} else {
|
|
1772
|
+
throw this.error(`Expected ':' in verify expression, got ${this.peek().type}`, this.peek());
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
verifications = [data.get("result")];
|
|
1776
|
+
break;
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
metadata.endTime = new Date().toISOString();
|
|
1781
|
+
|
|
1782
|
+
return {
|
|
1783
|
+
kind: "reasoning-block",
|
|
1784
|
+
stage,
|
|
1785
|
+
data,
|
|
1786
|
+
observations,
|
|
1787
|
+
analysis,
|
|
1788
|
+
decisions,
|
|
1789
|
+
actions,
|
|
1790
|
+
verifications,
|
|
1791
|
+
metadata,
|
|
1792
|
+
};
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
// Phase 9c Extension: Parse reasoning-sequence expression
|
|
1796
|
+
// (reasoning-sequence (observe ...) (analyze ...) (decide ...) (act ...) (verify ...))
|
|
1797
|
+
// Phase 9a/9b: Support search and learn blocks in sequences
|
|
1798
|
+
private parseReasoningSequenceExpression(): ReasoningSequence {
|
|
1799
|
+
const stages: (ReasoningBlock | SearchBlock | LearnBlock)[] = [];
|
|
1800
|
+
const startTime = new Date().toISOString();
|
|
1801
|
+
|
|
1802
|
+
// Parse multiple reasoning blocks in sequence
|
|
1803
|
+
let feedbackLoop: any = undefined;
|
|
1804
|
+
|
|
1805
|
+
while (!this.check(T.RParen) && !this.isAtEnd()) {
|
|
1806
|
+
// Check for feedback loop configuration (Phase 9c Feedback)
|
|
1807
|
+
if (this.check(T.Colon)) {
|
|
1808
|
+
this.advance(); // consume ':'
|
|
1809
|
+
if (!this.check(T.Symbol)) {
|
|
1810
|
+
throw this.error(`Expected symbol after ':', got ${this.peek().type}`, this.peek());
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
const keyword = this.advance().value;
|
|
1814
|
+
|
|
1815
|
+
if (keyword === "feedback") {
|
|
1816
|
+
feedbackLoop = {
|
|
1817
|
+
enabled: false,
|
|
1818
|
+
fromStage: "verify",
|
|
1819
|
+
toStage: "analyze",
|
|
1820
|
+
maxIterations: 3,
|
|
1821
|
+
confidenceDamping: 0.1,
|
|
1822
|
+
};
|
|
1823
|
+
|
|
1824
|
+
// Parse feedback options
|
|
1825
|
+
while (!this.check(T.RParen) && !this.isAtEnd() && this.check(T.Colon)) {
|
|
1826
|
+
this.advance(); // consume ':'
|
|
1827
|
+
const feedbackKey = this.advance().value;
|
|
1828
|
+
|
|
1829
|
+
switch (feedbackKey) {
|
|
1830
|
+
case "enabled":
|
|
1831
|
+
const enabledVal = this.parseValue();
|
|
1832
|
+
feedbackLoop.enabled =
|
|
1833
|
+
enabledVal.kind === "literal" && enabledVal.value === true;
|
|
1834
|
+
break;
|
|
1835
|
+
|
|
1836
|
+
case "from":
|
|
1837
|
+
const fromVal = this.parseValue();
|
|
1838
|
+
if (fromVal.kind === "literal" && fromVal.type === "string") {
|
|
1839
|
+
feedbackLoop.fromStage = fromVal.value as "verify" | "act";
|
|
1840
|
+
}
|
|
1841
|
+
break;
|
|
1842
|
+
|
|
1843
|
+
case "to":
|
|
1844
|
+
const toVal = this.parseValue();
|
|
1845
|
+
if (toVal.kind === "literal" && toVal.type === "string") {
|
|
1846
|
+
feedbackLoop.toStage = toVal.value as "analyze" | "decide";
|
|
1847
|
+
}
|
|
1848
|
+
break;
|
|
1849
|
+
|
|
1850
|
+
case "max-iterations":
|
|
1851
|
+
const maxVal = this.parseValue();
|
|
1852
|
+
if (maxVal.kind === "literal" && maxVal.type === "number") {
|
|
1853
|
+
feedbackLoop.maxIterations = maxVal.value as number;
|
|
1854
|
+
}
|
|
1855
|
+
break;
|
|
1856
|
+
|
|
1857
|
+
case "damping":
|
|
1858
|
+
const dampVal = this.parseValue();
|
|
1859
|
+
if (dampVal.kind === "literal" && dampVal.type === "number") {
|
|
1860
|
+
feedbackLoop.confidenceDamping = dampVal.value as number;
|
|
1861
|
+
}
|
|
1862
|
+
break;
|
|
1863
|
+
|
|
1864
|
+
case "condition":
|
|
1865
|
+
feedbackLoop.condition = this.parseValue();
|
|
1866
|
+
break;
|
|
1867
|
+
|
|
1868
|
+
default:
|
|
1869
|
+
throw this.error(`Unknown feedback option: ${feedbackKey}`, this.peek());
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
} else {
|
|
1873
|
+
throw this.error(`Unknown option: ${keyword}`, this.peek());
|
|
1874
|
+
}
|
|
1875
|
+
} else if (this.check(T.LParen)) {
|
|
1876
|
+
// Phase 9c: Lookahead to check if this is (if ...), (when ...), or (repeat ...) / (while ...)
|
|
1877
|
+
if (this.pos + 1 < this.tokens.length) {
|
|
1878
|
+
const nextToken = this.tokens[this.pos + 1];
|
|
1879
|
+
if (nextToken.type === T.If) {
|
|
1880
|
+
// Don't consume '(', let parseConditionalReasoningBlock handle it
|
|
1881
|
+
const conditionalBlock = this.parseConditionalReasoningBlock();
|
|
1882
|
+
stages.push(conditionalBlock);
|
|
1883
|
+
continue;
|
|
1884
|
+
}
|
|
1885
|
+
if (nextToken.type === T.When) {
|
|
1886
|
+
// Don't consume '(', let parseWhenReasoningBlock handle it
|
|
1887
|
+
const whenBlock = this.parseWhenReasoningBlock();
|
|
1888
|
+
stages.push(whenBlock);
|
|
1889
|
+
continue;
|
|
1890
|
+
}
|
|
1891
|
+
if (nextToken.type === T.Repeat || nextToken.type === T.While) {
|
|
1892
|
+
// Don't consume '(', let parseLoopReasoningBlock handle it
|
|
1893
|
+
const loopBlock = this.parseLoopReasoningBlock();
|
|
1894
|
+
stages.push(loopBlock);
|
|
1895
|
+
continue;
|
|
1896
|
+
}
|
|
1897
|
+
if (nextToken.type === T.Search) {
|
|
1898
|
+
// Phase 9a: Don't consume '(', let parseSearchReasoningBlock handle it
|
|
1899
|
+
const searchBlock = this.parseSearchReasoningBlock();
|
|
1900
|
+
stages.push(searchBlock);
|
|
1901
|
+
continue;
|
|
1902
|
+
}
|
|
1903
|
+
if (nextToken.type === T.Learn) {
|
|
1904
|
+
// Phase 9b: Don't consume '(', let parseLearnReasoningBlock handle it
|
|
1905
|
+
const learnBlock = this.parseLearnReasoningBlock();
|
|
1906
|
+
stages.push(learnBlock);
|
|
1907
|
+
continue;
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
this.advance(); // consume '('
|
|
1912
|
+
|
|
1913
|
+
// Check for reasoning stage keywords
|
|
1914
|
+
const stageToken = this.peek();
|
|
1915
|
+
const isReasoningStage =
|
|
1916
|
+
stageToken.type === T.Observe ||
|
|
1917
|
+
stageToken.type === T.Analyze ||
|
|
1918
|
+
stageToken.type === T.Decide ||
|
|
1919
|
+
stageToken.type === T.Act ||
|
|
1920
|
+
stageToken.type === T.Verify ||
|
|
1921
|
+
(stageToken.type === T.Symbol &&
|
|
1922
|
+
(stageToken.value === "observe" ||
|
|
1923
|
+
stageToken.value === "analyze" ||
|
|
1924
|
+
stageToken.value === "decide" ||
|
|
1925
|
+
stageToken.value === "act" ||
|
|
1926
|
+
stageToken.value === "verify"));
|
|
1927
|
+
|
|
1928
|
+
if (!isReasoningStage) {
|
|
1929
|
+
throw this.error(
|
|
1930
|
+
`Expected reasoning stage (observe/analyze/decide/act/verify), got ${stageToken.value}`,
|
|
1931
|
+
stageToken
|
|
1932
|
+
);
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
const stageName =
|
|
1936
|
+
stageToken.type === T.Observe
|
|
1937
|
+
? "observe"
|
|
1938
|
+
: stageToken.type === T.Analyze
|
|
1939
|
+
? "analyze"
|
|
1940
|
+
: stageToken.type === T.Decide
|
|
1941
|
+
? "decide"
|
|
1942
|
+
: stageToken.type === T.Act
|
|
1943
|
+
? "act"
|
|
1944
|
+
: stageToken.type === T.Verify
|
|
1945
|
+
? "verify"
|
|
1946
|
+
: stageToken.value;
|
|
1947
|
+
|
|
1948
|
+
this.advance(); // consume stage name
|
|
1949
|
+
const reasoningBlock = this.parseReasoningExpressionInternal(
|
|
1950
|
+
stageName as "observe" | "analyze" | "decide" | "act" | "verify"
|
|
1951
|
+
);
|
|
1952
|
+
stages.push(reasoningBlock);
|
|
1953
|
+
this.expect(T.RParen);
|
|
1954
|
+
} else if (this.check(T.If)) {
|
|
1955
|
+
// Phase 9c: Parse if/then/else conditional
|
|
1956
|
+
const conditionalBlock = this.parseConditionalReasoningBlock();
|
|
1957
|
+
stages.push(conditionalBlock);
|
|
1958
|
+
} else if (this.check(T.When)) {
|
|
1959
|
+
// Phase 9c: Parse when guard clause
|
|
1960
|
+
const whenBlock = this.parseWhenReasoningBlock();
|
|
1961
|
+
stages.push(whenBlock);
|
|
1962
|
+
} else if (this.check(T.Repeat) || this.check(T.While)) {
|
|
1963
|
+
// Phase 9c: Parse loop control (repeat-until or repeat-while)
|
|
1964
|
+
const loopBlock = this.parseLoopReasoningBlock();
|
|
1965
|
+
stages.push(loopBlock);
|
|
1966
|
+
} else {
|
|
1967
|
+
throw this.error(
|
|
1968
|
+
`Expected '(' before reasoning block, 'if', 'when', 'repeat', 'while', or ':feedback', got ${this.peek().type}`,
|
|
1969
|
+
this.peek()
|
|
1970
|
+
);
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
const endTime = new Date().toISOString();
|
|
1975
|
+
|
|
1976
|
+
return {
|
|
1977
|
+
kind: "reasoning-sequence",
|
|
1978
|
+
stages,
|
|
1979
|
+
metadata: {
|
|
1980
|
+
startTime,
|
|
1981
|
+
endTime,
|
|
1982
|
+
// Phase 9a/9b: Support search/learn blocks in execution path
|
|
1983
|
+
executionPath: stages.map((s) => {
|
|
1984
|
+
if ("stage" in s) return (s as ReasoningBlock).stage;
|
|
1985
|
+
if ((s as any).kind === "search-block") return "search";
|
|
1986
|
+
if ((s as any).kind === "learn-block") return "learn";
|
|
1987
|
+
return "unknown";
|
|
1988
|
+
}),
|
|
1989
|
+
},
|
|
1990
|
+
feedbackLoop: feedbackLoop?.enabled ? feedbackLoop : undefined,
|
|
1991
|
+
};
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
// Phase 11: Parse try-catch-finally expressions
|
|
1995
|
+
// (try body (catch [pattern] handler) (finally cleanup))
|
|
1996
|
+
private parseTryExpression(): TryBlock {
|
|
1997
|
+
// 'try' keyword already consumed by parseSExpr
|
|
1998
|
+
// body: 여러 표현식 허용 — (catch/finally 절이 아닌 것들을 do 블록으로 묶음)
|
|
1999
|
+
const bodyExprs: ASTNode[] = [];
|
|
2000
|
+
while (this.check(T.LParen) || !this.check(T.RParen)) {
|
|
2001
|
+
// 다음 토큰이 (catch 또는 (finally 이면 body 수집 종료
|
|
2002
|
+
if (this.check(T.LParen) && this.pos + 1 < this.tokens.length) {
|
|
2003
|
+
const next = this.tokens[this.pos + 1];
|
|
2004
|
+
if (next.type === T.Symbol && (next.value === "catch" || next.value === "finally")) break;
|
|
2005
|
+
}
|
|
2006
|
+
if (this.isAtEnd() || this.check(T.RParen)) break;
|
|
2007
|
+
bodyExprs.push(this.parseValue());
|
|
2008
|
+
}
|
|
2009
|
+
const body = bodyExprs.length === 1 ? bodyExprs[0] : makeSExpr("do", bodyExprs);
|
|
2010
|
+
|
|
2011
|
+
const catchClauses: CatchClause[] = [];
|
|
2012
|
+
let finallyBlock: ASTNode | undefined;
|
|
2013
|
+
|
|
2014
|
+
while (this.check(T.LParen) && !this.isAtEnd()) {
|
|
2015
|
+
const clauseToken = this.peek();
|
|
2016
|
+
|
|
2017
|
+
// Check if this is a catch or finally clause
|
|
2018
|
+
if (this.pos + 1 < this.tokens.length) {
|
|
2019
|
+
const nextToken = this.tokens[this.pos + 1];
|
|
2020
|
+
|
|
2021
|
+
if (nextToken.type === T.Symbol && nextToken.value === "catch") {
|
|
2022
|
+
this.advance(); // consume '('
|
|
2023
|
+
this.advance(); // consume 'catch'
|
|
2024
|
+
|
|
2025
|
+
// Optional error pattern (can be empty)
|
|
2026
|
+
let pattern: Pattern | undefined;
|
|
2027
|
+
let variable: string | undefined;
|
|
2028
|
+
|
|
2029
|
+
if (this.check(T.LBracket)) {
|
|
2030
|
+
// Format 1: (catch [ErrorType] handler) or (catch [err] handler)
|
|
2031
|
+
this.advance(); // consume '['
|
|
2032
|
+
if (this.check(T.Symbol)) {
|
|
2033
|
+
const patternName = this.advance().value;
|
|
2034
|
+
// Simple pattern: bind to variable
|
|
2035
|
+
variable = patternName;
|
|
2036
|
+
pattern = makeVariablePattern(patternName);
|
|
2037
|
+
}
|
|
2038
|
+
this.expect(T.RBracket);
|
|
2039
|
+
} else if (this.check(T.Symbol) || this.check(T.Variable)) {
|
|
2040
|
+
// Format 2 (self-hosted): (catch param body) - bare param without brackets
|
|
2041
|
+
const paramToken = this.advance();
|
|
2042
|
+
variable = paramToken.value;
|
|
2043
|
+
pattern = makeVariablePattern(paramToken.value);
|
|
2044
|
+
}
|
|
2045
|
+
|
|
2046
|
+
// Parse catch handler (can be multiple expressions)
|
|
2047
|
+
const handlerExprs: ASTNode[] = [];
|
|
2048
|
+
while (!this.check(T.RParen) && !this.isAtEnd()) {
|
|
2049
|
+
handlerExprs.push(this.parseValue());
|
|
2050
|
+
}
|
|
2051
|
+
const handler = handlerExprs.length === 1 ? handlerExprs[0] : makeSExpr("do", handlerExprs);
|
|
2052
|
+
catchClauses.push(makeCatchClause(handler, pattern, variable));
|
|
2053
|
+
this.expect(T.RParen);
|
|
2054
|
+
} else if (nextToken.type === T.Symbol && nextToken.value === "finally") {
|
|
2055
|
+
this.advance(); // consume '('
|
|
2056
|
+
this.advance(); // consume 'finally'
|
|
2057
|
+
const finallyExprs: ASTNode[] = [];
|
|
2058
|
+
while (!this.check(T.RParen) && !this.isAtEnd()) {
|
|
2059
|
+
finallyExprs.push(this.parseValue());
|
|
2060
|
+
}
|
|
2061
|
+
finallyBlock = finallyExprs.length === 1 ? finallyExprs[0] : makeSExpr("do", finallyExprs);
|
|
2062
|
+
this.expect(T.RParen);
|
|
2063
|
+
break; // finally must be last
|
|
2064
|
+
} else {
|
|
2065
|
+
break;
|
|
2066
|
+
}
|
|
2067
|
+
} else {
|
|
2068
|
+
break;
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
return makeTryBlock(body, catchClauses.length > 0 ? catchClauses : undefined, finallyBlock);
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
// Phase 11: Parse throw expressions
|
|
2076
|
+
// (throw error-value)
|
|
2077
|
+
private parseThrowExpression(): ThrowExpression {
|
|
2078
|
+
// 'throw' keyword already consumed by parseSExpr
|
|
2079
|
+
const argument = this.parseValue();
|
|
2080
|
+
return makeThrowExpression(argument);
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
// Phase B-1: Parse loop expressions
|
|
2084
|
+
// (loop [($var init) condition update] body)
|
|
2085
|
+
private parseLoopExpression(): any {
|
|
2086
|
+
// 'loop' keyword already consumed by parseSExpr
|
|
2087
|
+
const startLine = this.peek().line || 0;
|
|
2088
|
+
|
|
2089
|
+
// Parse loop header: [($var val) condition update]
|
|
2090
|
+
this.expect(T.LBracket);
|
|
2091
|
+
|
|
2092
|
+
// Parse init: ($var val)
|
|
2093
|
+
const init = this.parseValue();
|
|
2094
|
+
|
|
2095
|
+
// Parse condition
|
|
2096
|
+
const condition = this.parseValue();
|
|
2097
|
+
|
|
2098
|
+
// Parse update
|
|
2099
|
+
const update = this.parseValue();
|
|
2100
|
+
|
|
2101
|
+
this.expect(T.RBracket);
|
|
2102
|
+
|
|
2103
|
+
// Parse body (one or more expressions)
|
|
2104
|
+
const bodyExprs: ASTNode[] = [];
|
|
2105
|
+
while (!this.check(T.RParen) && !this.isAtEnd()) {
|
|
2106
|
+
bodyExprs.push(this.parseValue());
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
const body = bodyExprs.length === 1 ? bodyExprs[0] : makeSExpr("do", bodyExprs);
|
|
2110
|
+
|
|
2111
|
+
return {
|
|
2112
|
+
kind: "loop",
|
|
2113
|
+
init,
|
|
2114
|
+
condition,
|
|
2115
|
+
update,
|
|
2116
|
+
body,
|
|
2117
|
+
line: startLine,
|
|
2118
|
+
};
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
// Phase 9c: Internal helper for parsing reasoning expressions (used by parseReasoningExpression and parseReasoningSequenceExpression)
|
|
2122
|
+
private parseReasoningExpressionInternal(
|
|
2123
|
+
stage: "observe" | "analyze" | "decide" | "act" | "verify"
|
|
2124
|
+
): ReasoningBlock {
|
|
2125
|
+
const data = new Map<string, any>();
|
|
2126
|
+
let observations: any[] = [];
|
|
2127
|
+
let analysis: any[] = [];
|
|
2128
|
+
let decisions: any[] = [];
|
|
2129
|
+
let actions: any[] = [];
|
|
2130
|
+
let verifications: any[] = [];
|
|
2131
|
+
const metadata: any = { startTime: new Date().toISOString() };
|
|
2132
|
+
|
|
2133
|
+
switch (stage) {
|
|
2134
|
+
case "observe": {
|
|
2135
|
+
// Parse observation: "message" or :data "message" :confidence 0.9
|
|
2136
|
+
if (!this.check(T.RParen)) {
|
|
2137
|
+
const firstArg = this.parseValue();
|
|
2138
|
+
if (firstArg.kind === "literal" && firstArg.type === "string") {
|
|
2139
|
+
data.set("observation", firstArg.value);
|
|
2140
|
+
observations.push(firstArg.value);
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
// Parse keyword arguments: :confidence, :feedback, etc.
|
|
2145
|
+
while (!this.check(T.RParen) && !this.isAtEnd()) {
|
|
2146
|
+
if (this.check(T.Colon)) {
|
|
2147
|
+
this.advance(); // consume ':'
|
|
2148
|
+
if (!this.check(T.Symbol)) {
|
|
2149
|
+
throw this.error(
|
|
2150
|
+
`Expected symbol after ':', got ${this.peek().type}`,
|
|
2151
|
+
this.peek()
|
|
2152
|
+
);
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
const clauseName = this.advance().value;
|
|
2156
|
+
const clauseValue = this.parseValue();
|
|
2157
|
+
|
|
2158
|
+
if (clauseName === "confidence") {
|
|
2159
|
+
if (clauseValue.kind === "literal" && clauseValue.type === "number") {
|
|
2160
|
+
metadata.confidence = clauseValue.value as number;
|
|
2161
|
+
}
|
|
2162
|
+
} else {
|
|
2163
|
+
data.set(clauseName, clauseValue);
|
|
2164
|
+
}
|
|
2165
|
+
} else {
|
|
2166
|
+
throw this.error(
|
|
2167
|
+
`Expected ':' in observe expression, got ${this.peek().type}`,
|
|
2168
|
+
this.peek()
|
|
2169
|
+
);
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
break;
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
case "analyze": {
|
|
2176
|
+
// Parse analysis: :angle1 "..." :angle2 "..." :selected "..."
|
|
2177
|
+
while (!this.check(T.RParen) && !this.isAtEnd()) {
|
|
2178
|
+
if (this.check(T.Colon)) {
|
|
2179
|
+
this.advance();
|
|
2180
|
+
const clauseName = this.advance().value;
|
|
2181
|
+
|
|
2182
|
+
const clauseValue = this.parseValue();
|
|
2183
|
+
data.set(clauseName, clauseValue);
|
|
2184
|
+
|
|
2185
|
+
if (clauseName === "selected") {
|
|
2186
|
+
analysis.push(clauseValue);
|
|
2187
|
+
}
|
|
2188
|
+
} else {
|
|
2189
|
+
throw this.error(
|
|
2190
|
+
`Expected ':' in analyze expression, got ${this.peek().type}`,
|
|
2191
|
+
this.peek()
|
|
2192
|
+
);
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
break;
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
case "decide": {
|
|
2199
|
+
// Parse decision: :choice "..." :reason "..."
|
|
2200
|
+
while (!this.check(T.RParen) && !this.isAtEnd()) {
|
|
2201
|
+
if (this.check(T.Colon)) {
|
|
2202
|
+
this.advance();
|
|
2203
|
+
const clauseName = this.advance().value;
|
|
2204
|
+
|
|
2205
|
+
const clauseValue = this.parseValue();
|
|
2206
|
+
data.set(clauseName, clauseValue);
|
|
2207
|
+
} else {
|
|
2208
|
+
throw this.error(
|
|
2209
|
+
`Expected ':' in decide expression, got ${this.peek().type}`,
|
|
2210
|
+
this.peek()
|
|
2211
|
+
);
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2214
|
+
decisions = [data.get("choice")];
|
|
2215
|
+
break;
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
case "act": {
|
|
2219
|
+
// Parse action: :action "..." :parameters {...}
|
|
2220
|
+
while (!this.check(T.RParen) && !this.isAtEnd()) {
|
|
2221
|
+
if (this.check(T.Colon)) {
|
|
2222
|
+
this.advance();
|
|
2223
|
+
const clauseName = this.advance().value;
|
|
2224
|
+
|
|
2225
|
+
const clauseValue = this.parseValue();
|
|
2226
|
+
data.set(clauseName, clauseValue);
|
|
2227
|
+
} else {
|
|
2228
|
+
throw this.error(
|
|
2229
|
+
`Expected ':' in act expression, got ${this.peek().type}`,
|
|
2230
|
+
this.peek()
|
|
2231
|
+
);
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
actions = [data.get("action")];
|
|
2235
|
+
break;
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
case "verify": {
|
|
2239
|
+
// Parse verification: :result success :evidence [...]
|
|
2240
|
+
while (!this.check(T.RParen) && !this.isAtEnd()) {
|
|
2241
|
+
if (this.check(T.Colon)) {
|
|
2242
|
+
this.advance();
|
|
2243
|
+
const clauseName = this.advance().value;
|
|
2244
|
+
|
|
2245
|
+
const clauseValue = this.parseValue();
|
|
2246
|
+
if (clauseName === "confidence") {
|
|
2247
|
+
if (clauseValue.kind === "literal" && clauseValue.type === "number") {
|
|
2248
|
+
metadata.confidence = clauseValue.value as number;
|
|
2249
|
+
}
|
|
2250
|
+
} else {
|
|
2251
|
+
data.set(clauseName, clauseValue);
|
|
2252
|
+
}
|
|
2253
|
+
} else {
|
|
2254
|
+
throw this.error(
|
|
2255
|
+
`Expected ':' in verify expression, got ${this.peek().type}`,
|
|
2256
|
+
this.peek()
|
|
2257
|
+
);
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
verifications = [data.get("result")];
|
|
2261
|
+
break;
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
|
|
2265
|
+
metadata.endTime = new Date().toISOString();
|
|
2266
|
+
|
|
2267
|
+
return {
|
|
2268
|
+
kind: "reasoning-block",
|
|
2269
|
+
stage,
|
|
2270
|
+
data,
|
|
2271
|
+
observations,
|
|
2272
|
+
analysis,
|
|
2273
|
+
decisions,
|
|
2274
|
+
actions,
|
|
2275
|
+
verifications,
|
|
2276
|
+
metadata,
|
|
2277
|
+
};
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2280
|
+
// Phase 6: Parse qualified identifier (math or math:add or utils:double:helper)
|
|
2281
|
+
// IMPORTANT: Stop when encountering keyword colons like :from, :as, :only
|
|
2282
|
+
private parseQualifiedIdentifier(): string {
|
|
2283
|
+
if (!this.check(T.Symbol)) {
|
|
2284
|
+
throw this.error(`Expected symbol, got ${this.peek().type}`, this.peek());
|
|
2285
|
+
}
|
|
2286
|
+
|
|
2287
|
+
const parts: string[] = [];
|
|
2288
|
+
parts.push(this.advance().value);
|
|
2289
|
+
|
|
2290
|
+
// Keyword colons that should NOT be consumed as part of qualified identifiers
|
|
2291
|
+
const keywordColons = new Set(["from", "as", "only", "to", "body", "params", "exports"]);
|
|
2292
|
+
|
|
2293
|
+
// Check for additional parts separated by colons
|
|
2294
|
+
while (this.check(T.Colon)) {
|
|
2295
|
+
// Peek ahead to see if the next symbol is a keyword
|
|
2296
|
+
const peekPos = this.pos + 1;
|
|
2297
|
+
if (peekPos >= this.tokens.length) break;
|
|
2298
|
+
const nextToken = this.tokens[peekPos];
|
|
2299
|
+
|
|
2300
|
+
// If next token is a keyword, stop parsing qualified identifier
|
|
2301
|
+
if (nextToken.type === T.Symbol && keywordColons.has(nextToken.value)) {
|
|
2302
|
+
break;
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
this.advance(); // consume ':'
|
|
2306
|
+
if (!this.check(T.Symbol)) {
|
|
2307
|
+
throw this.error(`Expected symbol after ':', got ${this.peek().type}`, this.peek());
|
|
2308
|
+
}
|
|
2309
|
+
parts.push(this.advance().value);
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2312
|
+
return parts.join(":");
|
|
2313
|
+
}
|
|
2314
|
+
|
|
2315
|
+
// Phase 6: Parse selective import list: [func1 func2 ...]
|
|
2316
|
+
private parseSelectiveImport(): string[] {
|
|
2317
|
+
this.expect(T.LBracket);
|
|
2318
|
+
const names: string[] = [];
|
|
2319
|
+
|
|
2320
|
+
while (!this.check(T.RBracket) && !this.isAtEnd()) {
|
|
2321
|
+
if (!this.check(T.Symbol)) {
|
|
2322
|
+
throw this.error(`Expected symbol in import list, got ${this.peek().type}`, this.peek());
|
|
2323
|
+
}
|
|
2324
|
+
names.push(this.advance().value);
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2327
|
+
this.expect(T.RBracket);
|
|
2328
|
+
return names;
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
// Phase 9c: Parse if/then/else conditional reasoning block
|
|
2332
|
+
// Format: (if condition (thenBlock) (elseBlock)?)
|
|
2333
|
+
private parseConditionalReasoningBlock(): ReasoningBlock {
|
|
2334
|
+
this.expect(T.LParen); // consume '('
|
|
2335
|
+
this.expect(T.If); // consume 'if'
|
|
2336
|
+
|
|
2337
|
+
// Parse condition expression
|
|
2338
|
+
const condition = this.parseValue();
|
|
2339
|
+
|
|
2340
|
+
// Parse then block
|
|
2341
|
+
if (!this.check(T.LParen)) {
|
|
2342
|
+
throw this.error(`Expected '(' for then block, got ${this.peek().type}`, this.peek());
|
|
2343
|
+
}
|
|
2344
|
+
this.advance(); // consume '('
|
|
2345
|
+
|
|
2346
|
+
const thenStageToken = this.peek();
|
|
2347
|
+
const thenStageName = this.getReasoningStageName(thenStageToken);
|
|
2348
|
+
if (!thenStageName) {
|
|
2349
|
+
throw this.error(
|
|
2350
|
+
`Expected reasoning stage in then block, got ${thenStageToken.value}`,
|
|
2351
|
+
thenStageToken
|
|
2352
|
+
);
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
this.advance(); // consume stage name
|
|
2356
|
+
const thenBlock = this.parseReasoningExpressionInternal(
|
|
2357
|
+
thenStageName as "observe" | "analyze" | "decide" | "act" | "verify"
|
|
2358
|
+
);
|
|
2359
|
+
this.expect(T.RParen);
|
|
2360
|
+
|
|
2361
|
+
// Parse optional else block
|
|
2362
|
+
let elseBlock: ReasoningBlock | undefined = undefined;
|
|
2363
|
+
if (this.check(T.LParen)) {
|
|
2364
|
+
// Lookahead to check if this is an else block or something else
|
|
2365
|
+
const nextIdx = this.pos + 1;
|
|
2366
|
+
if (nextIdx < this.tokens.length) {
|
|
2367
|
+
const nextToken = this.tokens[nextIdx];
|
|
2368
|
+
const elseStageName = this.getReasoningStageName(nextToken);
|
|
2369
|
+
if (elseStageName) {
|
|
2370
|
+
this.advance(); // consume '('
|
|
2371
|
+
this.advance(); // consume stage name
|
|
2372
|
+
elseBlock = this.parseReasoningExpressionInternal(
|
|
2373
|
+
elseStageName as "observe" | "analyze" | "decide" | "act" | "verify"
|
|
2374
|
+
);
|
|
2375
|
+
this.expect(T.RParen); // close (block)
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
|
|
2380
|
+
this.expect(T.RParen); // close outer (if ...)
|
|
2381
|
+
|
|
2382
|
+
// Return then block with conditional info
|
|
2383
|
+
return {
|
|
2384
|
+
...thenBlock,
|
|
2385
|
+
conditional: { condition, thenBlock, elseBlock },
|
|
2386
|
+
};
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
// Phase 9c: Parse when guard clause
|
|
2390
|
+
// Format: (when condition (block))
|
|
2391
|
+
private parseWhenReasoningBlock(): ReasoningBlock {
|
|
2392
|
+
this.expect(T.LParen); // consume '('
|
|
2393
|
+
this.expect(T.When); // consume 'when'
|
|
2394
|
+
|
|
2395
|
+
// Parse guard condition
|
|
2396
|
+
const condition = this.parseValue();
|
|
2397
|
+
|
|
2398
|
+
// Parse block
|
|
2399
|
+
if (!this.check(T.LParen)) {
|
|
2400
|
+
throw this.error(`Expected '(' for when block, got ${this.peek().type}`, this.peek());
|
|
2401
|
+
}
|
|
2402
|
+
this.advance(); // consume '('
|
|
2403
|
+
|
|
2404
|
+
const stageToken = this.peek();
|
|
2405
|
+
const stageName = this.getReasoningStageName(stageToken);
|
|
2406
|
+
if (!stageName) {
|
|
2407
|
+
throw this.error(`Expected reasoning stage in when block, got ${stageToken.value}`, stageToken);
|
|
2408
|
+
}
|
|
2409
|
+
|
|
2410
|
+
this.advance(); // consume stage name
|
|
2411
|
+
const block = this.parseReasoningExpressionInternal(
|
|
2412
|
+
stageName as "observe" | "analyze" | "decide" | "act" | "verify"
|
|
2413
|
+
);
|
|
2414
|
+
this.expect(T.RParen); // close (block)
|
|
2415
|
+
|
|
2416
|
+
this.expect(T.RParen); // close outer (when ...)
|
|
2417
|
+
|
|
2418
|
+
// Return block with when guard
|
|
2419
|
+
return {
|
|
2420
|
+
...block,
|
|
2421
|
+
whenGuard: condition,
|
|
2422
|
+
};
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2425
|
+
// Phase 9c: Parse loop control (repeat-until or repeat-while)
|
|
2426
|
+
// Format: (repeat-until condition (block))
|
|
2427
|
+
// (repeat-while condition (block))
|
|
2428
|
+
// Phase 9a: Parse search block in reasoning sequence
|
|
2429
|
+
// (search query :source "web"|"api"|"kb" :cache true|false :limit 5)
|
|
2430
|
+
private parseSearchReasoningBlock(): SearchBlock {
|
|
2431
|
+
this.expect(T.LParen); // consume '('
|
|
2432
|
+
this.expect(T.Search); // consume 'search'
|
|
2433
|
+
const searchBlock = this.parseSearchExpression();
|
|
2434
|
+
this.expect(T.RParen); // consume ')'
|
|
2435
|
+
return searchBlock;
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
// Phase 9b: Parse learn block in reasoning sequence
|
|
2439
|
+
// (learn key data :source "search"|"feedback"|"analysis" :confidence 0.95)
|
|
2440
|
+
private parseLearnReasoningBlock(): LearnBlock {
|
|
2441
|
+
this.expect(T.LParen); // consume '('
|
|
2442
|
+
this.expect(T.Learn); // consume 'learn'
|
|
2443
|
+
const learnBlock = this.parseLearnExpression();
|
|
2444
|
+
this.expect(T.RParen); // consume ')'
|
|
2445
|
+
return learnBlock;
|
|
2446
|
+
}
|
|
2447
|
+
|
|
2448
|
+
private parseLoopReasoningBlock(): ReasoningBlock {
|
|
2449
|
+
this.expect(T.LParen); // consume '('
|
|
2450
|
+
|
|
2451
|
+
// Check if it's repeat or while
|
|
2452
|
+
const loopTypeToken = this.peek();
|
|
2453
|
+
const isRepeat = loopTypeToken.type === T.Repeat;
|
|
2454
|
+
const isWhile = loopTypeToken.type === T.While;
|
|
2455
|
+
|
|
2456
|
+
if (!isRepeat && !isWhile) {
|
|
2457
|
+
throw this.error(
|
|
2458
|
+
`Expected 'repeat' or 'while' in loop, got ${loopTypeToken.type}`,
|
|
2459
|
+
loopTypeToken
|
|
2460
|
+
);
|
|
2461
|
+
}
|
|
2462
|
+
|
|
2463
|
+
this.advance(); // consume 'repeat' or 'while'
|
|
2464
|
+
|
|
2465
|
+
// For repeat-until and repeat-while, expect an 'until' or 'while' keyword
|
|
2466
|
+
let loopType: "repeat-until" | "repeat-while";
|
|
2467
|
+
|
|
2468
|
+
if (isRepeat) {
|
|
2469
|
+
this.expect(T.Until); // consume 'until'
|
|
2470
|
+
loopType = "repeat-until";
|
|
2471
|
+
} else {
|
|
2472
|
+
// already consumed 'while'
|
|
2473
|
+
loopType = "repeat-while";
|
|
2474
|
+
}
|
|
2475
|
+
|
|
2476
|
+
// Parse loop condition
|
|
2477
|
+
const condition = this.parseValue();
|
|
2478
|
+
|
|
2479
|
+
// Parse block (single reasoning stage block)
|
|
2480
|
+
if (!this.check(T.LParen)) {
|
|
2481
|
+
throw this.error(`Expected '(' for loop block, got ${this.peek().type}`, this.peek());
|
|
2482
|
+
}
|
|
2483
|
+
|
|
2484
|
+
this.advance(); // consume '('
|
|
2485
|
+
|
|
2486
|
+
const stageToken = this.peek();
|
|
2487
|
+
const stageName = this.getReasoningStageName(stageToken);
|
|
2488
|
+
if (!stageName) {
|
|
2489
|
+
throw this.error(`Expected reasoning stage in loop block, got ${stageToken.value}`, stageToken);
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2492
|
+
this.advance(); // consume stage name
|
|
2493
|
+
const block = this.parseReasoningExpressionInternal(
|
|
2494
|
+
stageName as "observe" | "analyze" | "decide" | "act" | "verify"
|
|
2495
|
+
);
|
|
2496
|
+
this.expect(T.RParen); // close (block)
|
|
2497
|
+
|
|
2498
|
+
this.expect(T.RParen); // close outer (repeat/while ...)
|
|
2499
|
+
|
|
2500
|
+
// Return block with loop control info
|
|
2501
|
+
return {
|
|
2502
|
+
...block,
|
|
2503
|
+
loopControl: {
|
|
2504
|
+
type: loopType,
|
|
2505
|
+
condition,
|
|
2506
|
+
},
|
|
2507
|
+
};
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2510
|
+
// Phase 11: Parse PAGE block - [PAGE name :path "/" :render "<h1>...</h1>" :title "..." :component ComponentName]
|
|
2511
|
+
private parsePage(name: string, fields: Map<string, ASTNode | ASTNode[]>, line: number): PageNode {
|
|
2512
|
+
const path = this.extractStringField(fields, "path");
|
|
2513
|
+
const title = this.extractStringField(fields, "title");
|
|
2514
|
+
const render = this.extractStringField(fields, "render");
|
|
2515
|
+
const component = this.extractSymbolField(fields, "component");
|
|
2516
|
+
const metadata = this.extractMapField(fields, "metadata");
|
|
2517
|
+
|
|
2518
|
+
// makePageNode의 route 파라미터로 path 사용
|
|
2519
|
+
const pageNode = makePageNode(name, title, path, component, metadata, line) as any;
|
|
2520
|
+
// render 필드 추가 (interpreter가 사용함)
|
|
2521
|
+
if (render) {
|
|
2522
|
+
pageNode.fields = pageNode.fields || new Map();
|
|
2523
|
+
pageNode.fields.set("render", { kind: "literal", type: "string", value: render });
|
|
2524
|
+
}
|
|
2525
|
+
return pageNode;
|
|
2526
|
+
}
|
|
2527
|
+
|
|
2528
|
+
// Phase 11: Parse ROUTE block - [ROUTE name :path "/api/users/:id" :method "GET" :handler handlerName]
|
|
2529
|
+
private parseRoute(name: string, fields: Map<string, ASTNode | ASTNode[]>, line: number): RouteNode {
|
|
2530
|
+
const path = this.extractStringField(fields, "path");
|
|
2531
|
+
const method = this.extractStringField(fields, "method");
|
|
2532
|
+
const handler = this.extractSymbolField(fields, "handler");
|
|
2533
|
+
const middleware = this.extractSymbolArrayField(fields, "middleware");
|
|
2534
|
+
const validation = this.extractSymbolField(fields, "validation");
|
|
2535
|
+
|
|
2536
|
+
return makeRouteNode(name, path, method, handler, middleware, validation, line);
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
// Phase 11: Parse COMPONENT block - [COMPONENT name :render renderFn :state stateName :methods [...]]
|
|
2540
|
+
private parseComponent(name: string, fields: Map<string, ASTNode | ASTNode[]>, line: number): ComponentNode {
|
|
2541
|
+
const render = this.extractSymbolField(fields, "render");
|
|
2542
|
+
const state = this.extractSymbolField(fields, "state");
|
|
2543
|
+
const computed = this.extractSymbolArrayField(fields, "computed");
|
|
2544
|
+
const watch = this.extractWatchField(fields, "watch");
|
|
2545
|
+
const methods = this.extractMethodsField(fields, "methods");
|
|
2546
|
+
const slots = this.extractSymbolArrayField(fields, "slots");
|
|
2547
|
+
|
|
2548
|
+
return makeComponentNode(name, render, state, computed, watch, methods, slots, line);
|
|
2549
|
+
}
|
|
2550
|
+
|
|
2551
|
+
// Phase 11: Parse FORM block - [FORM name :fields [...] :validation validationFn :submit submitFn]
|
|
2552
|
+
private parseForm(name: string, fields: Map<string, ASTNode | ASTNode[]>, line: number): FormNode {
|
|
2553
|
+
const formFields = this.extractMapField(fields, "fields");
|
|
2554
|
+
const validation = this.extractSymbolField(fields, "validation");
|
|
2555
|
+
const submit = this.extractSymbolField(fields, "submit");
|
|
2556
|
+
const handlers = this.extractHandlersField(fields, "handlers");
|
|
2557
|
+
|
|
2558
|
+
return makeFormNode(name, formFields, validation, submit, handlers, line);
|
|
2559
|
+
}
|
|
2560
|
+
|
|
2561
|
+
// Helper: Extract string field from block fields
|
|
2562
|
+
private extractStringField(fields: Map<string, ASTNode | ASTNode[]>, key: string): string | undefined {
|
|
2563
|
+
const value = fields.get(key);
|
|
2564
|
+
if (!value) return undefined;
|
|
2565
|
+
|
|
2566
|
+
if (Array.isArray(value)) {
|
|
2567
|
+
const first = value[0];
|
|
2568
|
+
if ((first as any).kind === "literal" && (first as any).type === "string") {
|
|
2569
|
+
return (first as any).value as string;
|
|
2570
|
+
}
|
|
2571
|
+
} else if ((value as any).kind === "literal" && (value as any).type === "string") {
|
|
2572
|
+
return (value as any).value as string;
|
|
2573
|
+
}
|
|
2574
|
+
return undefined;
|
|
2575
|
+
}
|
|
2576
|
+
|
|
2577
|
+
// Helper: Extract symbol field from block fields
|
|
2578
|
+
private extractSymbolField(fields: Map<string, ASTNode | ASTNode[]>, key: string): string | undefined {
|
|
2579
|
+
const value = fields.get(key);
|
|
2580
|
+
if (!value) return undefined;
|
|
2581
|
+
|
|
2582
|
+
if (Array.isArray(value)) {
|
|
2583
|
+
const first = value[0];
|
|
2584
|
+
if ((first as any).kind === "literal" && (first as any).type === "symbol") {
|
|
2585
|
+
return (first as any).value as string;
|
|
2586
|
+
}
|
|
2587
|
+
} else if ((value as any).kind === "literal" && (value as any).type === "symbol") {
|
|
2588
|
+
return (value as any).value as string;
|
|
2589
|
+
}
|
|
2590
|
+
return undefined;
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2593
|
+
// Helper: Extract array of symbols from block fields
|
|
2594
|
+
private extractSymbolArrayField(fields: Map<string, ASTNode | ASTNode[]>, key: string): string[] | undefined {
|
|
2595
|
+
const value = fields.get(key);
|
|
2596
|
+
if (!value) return undefined;
|
|
2597
|
+
|
|
2598
|
+
const items: string[] = [];
|
|
2599
|
+
let arrayItems: ASTNode[] = [];
|
|
2600
|
+
|
|
2601
|
+
if (Array.isArray(value)) {
|
|
2602
|
+
arrayItems = value;
|
|
2603
|
+
} else if ((value as any).kind === "block" && (value as any).type === "Array") {
|
|
2604
|
+
const innerItems = (value as any).fields?.get("items");
|
|
2605
|
+
if (Array.isArray(innerItems)) {
|
|
2606
|
+
arrayItems = innerItems;
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
|
|
2610
|
+
for (const item of arrayItems) {
|
|
2611
|
+
if ((item as any).kind === "literal" && (item as any).type === "symbol") {
|
|
2612
|
+
items.push((item as any).value as string);
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
|
|
2616
|
+
return items.length > 0 ? items : undefined;
|
|
2617
|
+
}
|
|
2618
|
+
|
|
2619
|
+
// Helper: Extract map field from block fields
|
|
2620
|
+
private extractMapField(fields: Map<string, ASTNode | ASTNode[]>, key: string): Map<string, any> | undefined {
|
|
2621
|
+
const value = fields.get(key);
|
|
2622
|
+
if (!value) return undefined;
|
|
2623
|
+
|
|
2624
|
+
const result = new Map<string, any>();
|
|
2625
|
+
|
|
2626
|
+
if ((value as any).kind === "block" && (value as any).type === "Map") {
|
|
2627
|
+
const innerFields = (value as any).fields;
|
|
2628
|
+
if (innerFields) {
|
|
2629
|
+
for (const [k, v] of innerFields) {
|
|
2630
|
+
result.set(k, v);
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
|
|
2635
|
+
return result.size > 0 ? result : undefined;
|
|
2636
|
+
}
|
|
2637
|
+
|
|
2638
|
+
// Helper: Extract watch field from block fields (map of property names to handler names)
|
|
2639
|
+
private extractWatchField(fields: Map<string, ASTNode | ASTNode[]>, key: string): Map<string, string> | undefined {
|
|
2640
|
+
const value = fields.get(key);
|
|
2641
|
+
if (!value) return undefined;
|
|
2642
|
+
|
|
2643
|
+
const result = new Map<string, string>();
|
|
2644
|
+
|
|
2645
|
+
if ((value as any).kind === "block" && (value as any).type === "Map") {
|
|
2646
|
+
const innerFields = (value as any).fields;
|
|
2647
|
+
if (innerFields) {
|
|
2648
|
+
for (const [k, v] of innerFields) {
|
|
2649
|
+
if ((v as any).kind === "literal" && (v as any).type === "symbol") {
|
|
2650
|
+
result.set(k, (v as any).value as string);
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
|
|
2656
|
+
return result.size > 0 ? result : undefined;
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2659
|
+
// Helper: Extract methods field from block fields (map of method names to function names)
|
|
2660
|
+
private extractMethodsField(fields: Map<string, ASTNode | ASTNode[]>, key: string): Map<string, string> | undefined {
|
|
2661
|
+
const value = fields.get(key);
|
|
2662
|
+
if (!value) return undefined;
|
|
2663
|
+
|
|
2664
|
+
const result = new Map<string, string>();
|
|
2665
|
+
|
|
2666
|
+
if ((value as any).kind === "block" && (value as any).type === "Map") {
|
|
2667
|
+
const innerFields = (value as any).fields;
|
|
2668
|
+
if (innerFields) {
|
|
2669
|
+
for (const [k, v] of innerFields) {
|
|
2670
|
+
if ((v as any).kind === "literal" && (v as any).type === "symbol") {
|
|
2671
|
+
result.set(k, (v as any).value as string);
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
|
|
2677
|
+
return result.size > 0 ? result : undefined;
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2680
|
+
// Helper: Extract handlers field from block fields (map of event names to handler names)
|
|
2681
|
+
private extractHandlersField(fields: Map<string, ASTNode | ASTNode[]>, key: string): Map<string, string> | undefined {
|
|
2682
|
+
const value = fields.get(key);
|
|
2683
|
+
if (!value) return undefined;
|
|
2684
|
+
|
|
2685
|
+
const result = new Map<string, string>();
|
|
2686
|
+
|
|
2687
|
+
if ((value as any).kind === "block" && (value as any).type === "Map") {
|
|
2688
|
+
const innerFields = (value as any).fields;
|
|
2689
|
+
if (innerFields) {
|
|
2690
|
+
for (const [k, v] of innerFields) {
|
|
2691
|
+
if ((v as any).kind === "literal" && (v as any).type === "symbol") {
|
|
2692
|
+
result.set(k, (v as any).value as string);
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
|
|
2698
|
+
return result.size > 0 ? result : undefined;
|
|
2699
|
+
}
|
|
2700
|
+
|
|
2701
|
+
// Helper: Get reasoning stage name from token
|
|
2702
|
+
private getReasoningStageName(token: Token): string | null {
|
|
2703
|
+
if (token.type === T.Observe) return "observe";
|
|
2704
|
+
if (token.type === T.Analyze) return "analyze";
|
|
2705
|
+
if (token.type === T.Decide) return "decide";
|
|
2706
|
+
if (token.type === T.Act) return "act";
|
|
2707
|
+
if (token.type === T.Verify) return "verify";
|
|
2708
|
+
if (token.type === T.Symbol) {
|
|
2709
|
+
if (token.value === "observe") return "observe";
|
|
2710
|
+
if (token.value === "analyze") return "analyze";
|
|
2711
|
+
if (token.value === "decide") return "decide";
|
|
2712
|
+
if (token.value === "act") return "act";
|
|
2713
|
+
if (token.value === "verify") return "verify";
|
|
2714
|
+
}
|
|
2715
|
+
return null;
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2719
|
+
export function parse(tokens: Token[]): ASTNode[] {
|
|
2720
|
+
const parser = new Parser(tokens);
|
|
2721
|
+
return parser.parse();
|
|
2722
|
+
}
|