@lokascript/semantic 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +686 -0
- package/dist/browser-ar.ar.global.js +2 -0
- package/dist/browser-core.core.global.js +2 -0
- package/dist/browser-de.de.global.js +2 -0
- package/dist/browser-east-asian.east-asian.global.js +2 -0
- package/dist/browser-en-tr.en-tr.global.js +2 -0
- package/dist/browser-en.en.global.js +2 -0
- package/dist/browser-es-en.es-en.global.js +2 -0
- package/dist/browser-es.es.global.js +2 -0
- package/dist/browser-fr.fr.global.js +2 -0
- package/dist/browser-id.id.global.js +2 -0
- package/dist/browser-ja.ja.global.js +2 -0
- package/dist/browser-ko.ko.global.js +2 -0
- package/dist/browser-lazy.lazy.global.js +2 -0
- package/dist/browser-priority.priority.global.js +2 -0
- package/dist/browser-pt.pt.global.js +2 -0
- package/dist/browser-qu.qu.global.js +2 -0
- package/dist/browser-sw.sw.global.js +2 -0
- package/dist/browser-tr.tr.global.js +2 -0
- package/dist/browser-western.western.global.js +2 -0
- package/dist/browser-zh.zh.global.js +2 -0
- package/dist/browser.global.js +3 -0
- package/dist/browser.global.js.map +1 -0
- package/dist/index.cjs +35051 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +3426 -0
- package/dist/index.d.ts +3426 -0
- package/dist/index.js +34890 -0
- package/dist/index.js.map +1 -0
- package/dist/languages/ar.d.ts +78 -0
- package/dist/languages/ar.js +1622 -0
- package/dist/languages/ar.js.map +1 -0
- package/dist/languages/de.d.ts +38 -0
- package/dist/languages/de.js +1168 -0
- package/dist/languages/de.js.map +1 -0
- package/dist/languages/en.d.ts +44 -0
- package/dist/languages/en.js +3491 -0
- package/dist/languages/en.js.map +1 -0
- package/dist/languages/es.d.ts +52 -0
- package/dist/languages/es.js +1493 -0
- package/dist/languages/es.js.map +1 -0
- package/dist/languages/fr.d.ts +37 -0
- package/dist/languages/fr.js +1159 -0
- package/dist/languages/fr.js.map +1 -0
- package/dist/languages/id.d.ts +35 -0
- package/dist/languages/id.js +1152 -0
- package/dist/languages/id.js.map +1 -0
- package/dist/languages/ja.d.ts +53 -0
- package/dist/languages/ja.js +1430 -0
- package/dist/languages/ja.js.map +1 -0
- package/dist/languages/ko.d.ts +51 -0
- package/dist/languages/ko.js +1729 -0
- package/dist/languages/ko.js.map +1 -0
- package/dist/languages/pt.d.ts +37 -0
- package/dist/languages/pt.js +1127 -0
- package/dist/languages/pt.js.map +1 -0
- package/dist/languages/qu.d.ts +36 -0
- package/dist/languages/qu.js +1143 -0
- package/dist/languages/qu.js.map +1 -0
- package/dist/languages/sw.d.ts +35 -0
- package/dist/languages/sw.js +1147 -0
- package/dist/languages/sw.js.map +1 -0
- package/dist/languages/tr.d.ts +45 -0
- package/dist/languages/tr.js +1529 -0
- package/dist/languages/tr.js.map +1 -0
- package/dist/languages/zh.d.ts +58 -0
- package/dist/languages/zh.js +1257 -0
- package/dist/languages/zh.js.map +1 -0
- package/dist/types-C4dcj53L.d.ts +600 -0
- package/package.json +202 -0
- package/src/__test-utils__/index.ts +7 -0
- package/src/__test-utils__/test-helpers.ts +8 -0
- package/src/__types__/test-helpers.ts +122 -0
- package/src/analysis/index.ts +479 -0
- package/src/ast-builder/command-mappers.ts +1133 -0
- package/src/ast-builder/expression-parser/index.ts +41 -0
- package/src/ast-builder/expression-parser/parser.ts +563 -0
- package/src/ast-builder/expression-parser/tokenizer.ts +394 -0
- package/src/ast-builder/expression-parser/types.ts +208 -0
- package/src/ast-builder/index.ts +536 -0
- package/src/ast-builder/value-converters.ts +172 -0
- package/src/bridge.ts +275 -0
- package/src/browser-ar.ts +162 -0
- package/src/browser-core.ts +231 -0
- package/src/browser-de.ts +162 -0
- package/src/browser-east-asian.ts +173 -0
- package/src/browser-en-tr.ts +165 -0
- package/src/browser-en.ts +157 -0
- package/src/browser-es-en.ts +200 -0
- package/src/browser-es.ts +170 -0
- package/src/browser-fr.ts +162 -0
- package/src/browser-id.ts +162 -0
- package/src/browser-ja.ts +162 -0
- package/src/browser-ko.ts +162 -0
- package/src/browser-lazy.ts +189 -0
- package/src/browser-priority.ts +214 -0
- package/src/browser-pt.ts +162 -0
- package/src/browser-qu.ts +162 -0
- package/src/browser-sw.ts +162 -0
- package/src/browser-tr.ts +162 -0
- package/src/browser-western.ts +181 -0
- package/src/browser-zh.ts +162 -0
- package/src/browser.ts +268 -0
- package/src/cache/index.ts +14 -0
- package/src/cache/semantic-cache.ts +344 -0
- package/src/core-bridge.ts +372 -0
- package/src/explicit/converter.ts +258 -0
- package/src/explicit/index.ts +18 -0
- package/src/explicit/parser.ts +236 -0
- package/src/explicit/renderer.ts +424 -0
- package/src/generators/command-schemas.ts +1636 -0
- package/src/generators/event-handler-generator.ts +109 -0
- package/src/generators/index.ts +117 -0
- package/src/generators/language-profiles.ts +139 -0
- package/src/generators/pattern-generator.ts +537 -0
- package/src/generators/profiles/arabic.ts +131 -0
- package/src/generators/profiles/bengali.ts +132 -0
- package/src/generators/profiles/chinese.ts +124 -0
- package/src/generators/profiles/english.ts +113 -0
- package/src/generators/profiles/french.ts +125 -0
- package/src/generators/profiles/german.ts +126 -0
- package/src/generators/profiles/hindi.ts +146 -0
- package/src/generators/profiles/index.ts +46 -0
- package/src/generators/profiles/indonesian.ts +125 -0
- package/src/generators/profiles/italian.ts +139 -0
- package/src/generators/profiles/japanese.ts +149 -0
- package/src/generators/profiles/korean.ts +127 -0
- package/src/generators/profiles/marker-templates.ts +288 -0
- package/src/generators/profiles/ms.ts +130 -0
- package/src/generators/profiles/polish.ts +249 -0
- package/src/generators/profiles/portuguese.ts +115 -0
- package/src/generators/profiles/quechua.ts +113 -0
- package/src/generators/profiles/russian.ts +260 -0
- package/src/generators/profiles/spanish.ts +130 -0
- package/src/generators/profiles/swahili.ts +129 -0
- package/src/generators/profiles/thai.ts +132 -0
- package/src/generators/profiles/tl.ts +128 -0
- package/src/generators/profiles/turkish.ts +124 -0
- package/src/generators/profiles/types.ts +165 -0
- package/src/generators/profiles/ukrainian.ts +270 -0
- package/src/generators/profiles/vietnamese.ts +133 -0
- package/src/generators/schema-error-codes.ts +160 -0
- package/src/generators/schema-validator.ts +391 -0
- package/src/index.ts +429 -0
- package/src/language-building-schema.ts +3170 -0
- package/src/language-loader.ts +394 -0
- package/src/languages/_all.ts +65 -0
- package/src/languages/ar.ts +15 -0
- package/src/languages/bn.ts +16 -0
- package/src/languages/de.ts +15 -0
- package/src/languages/en.ts +29 -0
- package/src/languages/es.ts +15 -0
- package/src/languages/fr.ts +15 -0
- package/src/languages/hi.ts +26 -0
- package/src/languages/id.ts +15 -0
- package/src/languages/index.ts +18 -0
- package/src/languages/it.ts +15 -0
- package/src/languages/ja.ts +15 -0
- package/src/languages/ko.ts +15 -0
- package/src/languages/ms.ts +16 -0
- package/src/languages/pl.ts +18 -0
- package/src/languages/pt.ts +15 -0
- package/src/languages/qu.ts +15 -0
- package/src/languages/ru.ts +26 -0
- package/src/languages/sw.ts +15 -0
- package/src/languages/th.ts +16 -0
- package/src/languages/tl.ts +16 -0
- package/src/languages/tr.ts +15 -0
- package/src/languages/uk.ts +26 -0
- package/src/languages/vi.ts +16 -0
- package/src/languages/zh.ts +15 -0
- package/src/parser/index.ts +15 -0
- package/src/parser/pattern-matcher.ts +1181 -0
- package/src/parser/semantic-parser.ts +573 -0
- package/src/parser/utils/index.ts +35 -0
- package/src/parser/utils/marker-resolution.ts +111 -0
- package/src/parser/utils/possessive-keywords.ts +43 -0
- package/src/parser/utils/role-positioning.ts +70 -0
- package/src/parser/utils/type-validation.ts +134 -0
- package/src/patterns/add/ar.ts +71 -0
- package/src/patterns/add/bn.ts +70 -0
- package/src/patterns/add/hi.ts +69 -0
- package/src/patterns/add/index.ts +87 -0
- package/src/patterns/add/it.ts +61 -0
- package/src/patterns/add/ja.ts +93 -0
- package/src/patterns/add/ko.ts +74 -0
- package/src/patterns/add/ms.ts +30 -0
- package/src/patterns/add/pl.ts +62 -0
- package/src/patterns/add/ru.ts +62 -0
- package/src/patterns/add/th.ts +49 -0
- package/src/patterns/add/tl.ts +30 -0
- package/src/patterns/add/tr.ts +71 -0
- package/src/patterns/add/uk.ts +62 -0
- package/src/patterns/add/vi.ts +61 -0
- package/src/patterns/add/zh.ts +71 -0
- package/src/patterns/builders.ts +207 -0
- package/src/patterns/decrement/bn.ts +70 -0
- package/src/patterns/decrement/de.ts +42 -0
- package/src/patterns/decrement/hi.ts +68 -0
- package/src/patterns/decrement/index.ts +79 -0
- package/src/patterns/decrement/it.ts +69 -0
- package/src/patterns/decrement/ms.ts +30 -0
- package/src/patterns/decrement/pl.ts +58 -0
- package/src/patterns/decrement/ru.ts +58 -0
- package/src/patterns/decrement/th.ts +49 -0
- package/src/patterns/decrement/tl.ts +30 -0
- package/src/patterns/decrement/tr.ts +48 -0
- package/src/patterns/decrement/uk.ts +58 -0
- package/src/patterns/decrement/vi.ts +61 -0
- package/src/patterns/decrement/zh.ts +32 -0
- package/src/patterns/en.ts +302 -0
- package/src/patterns/event-handler/ar.ts +151 -0
- package/src/patterns/event-handler/bn.ts +72 -0
- package/src/patterns/event-handler/de.ts +117 -0
- package/src/patterns/event-handler/en.ts +117 -0
- package/src/patterns/event-handler/es.ts +136 -0
- package/src/patterns/event-handler/fr.ts +117 -0
- package/src/patterns/event-handler/hi.ts +64 -0
- package/src/patterns/event-handler/id.ts +117 -0
- package/src/patterns/event-handler/index.ts +119 -0
- package/src/patterns/event-handler/it.ts +54 -0
- package/src/patterns/event-handler/ja.ts +118 -0
- package/src/patterns/event-handler/ko.ts +133 -0
- package/src/patterns/event-handler/ms.ts +30 -0
- package/src/patterns/event-handler/pl.ts +62 -0
- package/src/patterns/event-handler/pt.ts +117 -0
- package/src/patterns/event-handler/qu.ts +66 -0
- package/src/patterns/event-handler/ru.ts +62 -0
- package/src/patterns/event-handler/shared.ts +270 -0
- package/src/patterns/event-handler/sw.ts +117 -0
- package/src/patterns/event-handler/th.ts +53 -0
- package/src/patterns/event-handler/tl.ts +30 -0
- package/src/patterns/event-handler/tr.ts +170 -0
- package/src/patterns/event-handler/uk.ts +62 -0
- package/src/patterns/event-handler/vi.ts +61 -0
- package/src/patterns/event-handler/zh.ts +150 -0
- package/src/patterns/get/ar.ts +49 -0
- package/src/patterns/get/bn.ts +47 -0
- package/src/patterns/get/de.ts +32 -0
- package/src/patterns/get/hi.ts +52 -0
- package/src/patterns/get/index.ts +83 -0
- package/src/patterns/get/it.ts +56 -0
- package/src/patterns/get/ja.ts +53 -0
- package/src/patterns/get/ko.ts +53 -0
- package/src/patterns/get/ms.ts +30 -0
- package/src/patterns/get/pl.ts +57 -0
- package/src/patterns/get/ru.ts +57 -0
- package/src/patterns/get/th.ts +29 -0
- package/src/patterns/get/tl.ts +30 -0
- package/src/patterns/get/uk.ts +57 -0
- package/src/patterns/get/vi.ts +48 -0
- package/src/patterns/grammar-transformed/index.ts +39 -0
- package/src/patterns/grammar-transformed/ja.ts +1713 -0
- package/src/patterns/grammar-transformed/ko.ts +1311 -0
- package/src/patterns/grammar-transformed/tr.ts +1067 -0
- package/src/patterns/hide/ar.ts +67 -0
- package/src/patterns/hide/bn.ts +47 -0
- package/src/patterns/hide/de.ts +36 -0
- package/src/patterns/hide/hi.ts +61 -0
- package/src/patterns/hide/index.ts +91 -0
- package/src/patterns/hide/it.ts +56 -0
- package/src/patterns/hide/ja.ts +69 -0
- package/src/patterns/hide/ko.ts +69 -0
- package/src/patterns/hide/ms.ts +30 -0
- package/src/patterns/hide/pl.ts +57 -0
- package/src/patterns/hide/ru.ts +57 -0
- package/src/patterns/hide/th.ts +29 -0
- package/src/patterns/hide/tl.ts +30 -0
- package/src/patterns/hide/tr.ts +65 -0
- package/src/patterns/hide/uk.ts +57 -0
- package/src/patterns/hide/vi.ts +56 -0
- package/src/patterns/hide/zh.ts +68 -0
- package/src/patterns/increment/bn.ts +70 -0
- package/src/patterns/increment/de.ts +36 -0
- package/src/patterns/increment/hi.ts +68 -0
- package/src/patterns/increment/index.ts +79 -0
- package/src/patterns/increment/it.ts +69 -0
- package/src/patterns/increment/ms.ts +30 -0
- package/src/patterns/increment/pl.ts +58 -0
- package/src/patterns/increment/ru.ts +58 -0
- package/src/patterns/increment/th.ts +49 -0
- package/src/patterns/increment/tl.ts +30 -0
- package/src/patterns/increment/tr.ts +52 -0
- package/src/patterns/increment/uk.ts +58 -0
- package/src/patterns/increment/vi.ts +61 -0
- package/src/patterns/increment/zh.ts +32 -0
- package/src/patterns/index.ts +84 -0
- package/src/patterns/languages/en/control-flow.ts +93 -0
- package/src/patterns/languages/en/fetch.ts +62 -0
- package/src/patterns/languages/en/index.ts +42 -0
- package/src/patterns/languages/en/repeat.ts +67 -0
- package/src/patterns/languages/en/set.ts +48 -0
- package/src/patterns/languages/en/swap.ts +38 -0
- package/src/patterns/languages/en/temporal.ts +57 -0
- package/src/patterns/put/ar.ts +74 -0
- package/src/patterns/put/bn.ts +53 -0
- package/src/patterns/put/en.ts +74 -0
- package/src/patterns/put/es.ts +74 -0
- package/src/patterns/put/hi.ts +69 -0
- package/src/patterns/put/id.ts +96 -0
- package/src/patterns/put/index.ts +99 -0
- package/src/patterns/put/it.ts +56 -0
- package/src/patterns/put/ja.ts +75 -0
- package/src/patterns/put/ko.ts +67 -0
- package/src/patterns/put/ms.ts +30 -0
- package/src/patterns/put/pl.ts +81 -0
- package/src/patterns/put/ru.ts +85 -0
- package/src/patterns/put/th.ts +32 -0
- package/src/patterns/put/tl.ts +30 -0
- package/src/patterns/put/tr.ts +67 -0
- package/src/patterns/put/uk.ts +85 -0
- package/src/patterns/put/vi.ts +72 -0
- package/src/patterns/put/zh.ts +62 -0
- package/src/patterns/registry.ts +163 -0
- package/src/patterns/remove/ar.ts +71 -0
- package/src/patterns/remove/bn.ts +68 -0
- package/src/patterns/remove/hi.ts +69 -0
- package/src/patterns/remove/index.ts +87 -0
- package/src/patterns/remove/it.ts +69 -0
- package/src/patterns/remove/ja.ts +74 -0
- package/src/patterns/remove/ko.ts +78 -0
- package/src/patterns/remove/ms.ts +30 -0
- package/src/patterns/remove/pl.ts +62 -0
- package/src/patterns/remove/ru.ts +62 -0
- package/src/patterns/remove/th.ts +49 -0
- package/src/patterns/remove/tl.ts +30 -0
- package/src/patterns/remove/tr.ts +78 -0
- package/src/patterns/remove/uk.ts +62 -0
- package/src/patterns/remove/vi.ts +61 -0
- package/src/patterns/remove/zh.ts +72 -0
- package/src/patterns/set/ar.ts +84 -0
- package/src/patterns/set/bn.ts +53 -0
- package/src/patterns/set/de.ts +84 -0
- package/src/patterns/set/es.ts +92 -0
- package/src/patterns/set/fr.ts +88 -0
- package/src/patterns/set/hi.ts +56 -0
- package/src/patterns/set/id.ts +84 -0
- package/src/patterns/set/index.ts +107 -0
- package/src/patterns/set/it.ts +56 -0
- package/src/patterns/set/ja.ts +86 -0
- package/src/patterns/set/ko.ts +85 -0
- package/src/patterns/set/ms.ts +30 -0
- package/src/patterns/set/pl.ts +57 -0
- package/src/patterns/set/pt.ts +84 -0
- package/src/patterns/set/ru.ts +57 -0
- package/src/patterns/set/th.ts +31 -0
- package/src/patterns/set/tl.ts +30 -0
- package/src/patterns/set/tr.ts +107 -0
- package/src/patterns/set/uk.ts +57 -0
- package/src/patterns/set/vi.ts +53 -0
- package/src/patterns/set/zh.ts +84 -0
- package/src/patterns/show/ar.ts +67 -0
- package/src/patterns/show/bn.ts +47 -0
- package/src/patterns/show/de.ts +32 -0
- package/src/patterns/show/fr.ts +32 -0
- package/src/patterns/show/hi.ts +61 -0
- package/src/patterns/show/index.ts +95 -0
- package/src/patterns/show/it.ts +56 -0
- package/src/patterns/show/ja.ts +69 -0
- package/src/patterns/show/ko.ts +73 -0
- package/src/patterns/show/ms.ts +30 -0
- package/src/patterns/show/pl.ts +57 -0
- package/src/patterns/show/ru.ts +57 -0
- package/src/patterns/show/th.ts +29 -0
- package/src/patterns/show/tl.ts +30 -0
- package/src/patterns/show/tr.ts +65 -0
- package/src/patterns/show/uk.ts +57 -0
- package/src/patterns/show/vi.ts +56 -0
- package/src/patterns/show/zh.ts +68 -0
- package/src/patterns/take/ar.ts +51 -0
- package/src/patterns/take/index.ts +31 -0
- package/src/patterns/toggle/ar.ts +61 -0
- package/src/patterns/toggle/bn.ts +70 -0
- package/src/patterns/toggle/en.ts +61 -0
- package/src/patterns/toggle/es.ts +61 -0
- package/src/patterns/toggle/hi.ts +80 -0
- package/src/patterns/toggle/index.ts +95 -0
- package/src/patterns/toggle/it.ts +69 -0
- package/src/patterns/toggle/ja.ts +156 -0
- package/src/patterns/toggle/ko.ts +113 -0
- package/src/patterns/toggle/ms.ts +30 -0
- package/src/patterns/toggle/pl.ts +62 -0
- package/src/patterns/toggle/ru.ts +62 -0
- package/src/patterns/toggle/th.ts +50 -0
- package/src/patterns/toggle/tl.ts +30 -0
- package/src/patterns/toggle/tr.ts +88 -0
- package/src/patterns/toggle/uk.ts +62 -0
- package/src/patterns/toggle/vi.ts +61 -0
- package/src/patterns/toggle/zh.ts +99 -0
- package/src/public-api.ts +286 -0
- package/src/registry.ts +441 -0
- package/src/tokenizers/arabic.ts +723 -0
- package/src/tokenizers/base.ts +1300 -0
- package/src/tokenizers/bengali.ts +289 -0
- package/src/tokenizers/chinese.ts +481 -0
- package/src/tokenizers/english.ts +416 -0
- package/src/tokenizers/french.ts +326 -0
- package/src/tokenizers/german.ts +324 -0
- package/src/tokenizers/hindi.ts +319 -0
- package/src/tokenizers/index.ts +127 -0
- package/src/tokenizers/indonesian.ts +306 -0
- package/src/tokenizers/italian.ts +458 -0
- package/src/tokenizers/japanese.ts +447 -0
- package/src/tokenizers/korean.ts +642 -0
- package/src/tokenizers/morphology/arabic-normalizer.ts +242 -0
- package/src/tokenizers/morphology/french-normalizer.ts +268 -0
- package/src/tokenizers/morphology/german-normalizer.ts +256 -0
- package/src/tokenizers/morphology/index.ts +46 -0
- package/src/tokenizers/morphology/italian-normalizer.ts +329 -0
- package/src/tokenizers/morphology/japanese-normalizer.ts +288 -0
- package/src/tokenizers/morphology/korean-normalizer.ts +428 -0
- package/src/tokenizers/morphology/polish-normalizer.ts +264 -0
- package/src/tokenizers/morphology/portuguese-normalizer.ts +310 -0
- package/src/tokenizers/morphology/spanish-normalizer.ts +327 -0
- package/src/tokenizers/morphology/turkish-normalizer.ts +412 -0
- package/src/tokenizers/morphology/types.ts +211 -0
- package/src/tokenizers/ms.ts +198 -0
- package/src/tokenizers/polish.ts +354 -0
- package/src/tokenizers/portuguese.ts +304 -0
- package/src/tokenizers/quechua.ts +339 -0
- package/src/tokenizers/russian.ts +375 -0
- package/src/tokenizers/spanish.ts +403 -0
- package/src/tokenizers/swahili.ts +303 -0
- package/src/tokenizers/thai.ts +236 -0
- package/src/tokenizers/tl.ts +198 -0
- package/src/tokenizers/turkish.ts +411 -0
- package/src/tokenizers/ukrainian.ts +369 -0
- package/src/tokenizers/vietnamese.ts +410 -0
- package/src/types/grammar-types.ts +617 -0
- package/src/types/unified-profile.ts +267 -0
- package/src/types.ts +709 -0
- package/src/utils/confidence-calculator.ts +147 -0
- package/src/validators/command-validator.ts +380 -0
- package/src/validators/index.ts +15 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Explicit Mode Parser
|
|
3
|
+
*
|
|
4
|
+
* Parses the explicit [command role:value ...] syntax.
|
|
5
|
+
* This syntax is universal across all languages and makes
|
|
6
|
+
* semantic roles visible for learning and debugging.
|
|
7
|
+
*
|
|
8
|
+
* Syntax:
|
|
9
|
+
* [command role1:value1 role2:value2 ...]
|
|
10
|
+
*
|
|
11
|
+
* Examples:
|
|
12
|
+
* [toggle class:.active target:#button]
|
|
13
|
+
* [put content:"hello" destination:#output]
|
|
14
|
+
* [on event:click body:[toggle class:.active]]
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { SemanticNode, SemanticValue, SemanticRole, ActionType } from '../types';
|
|
18
|
+
import {
|
|
19
|
+
createCommandNode,
|
|
20
|
+
createEventHandler,
|
|
21
|
+
createSelector,
|
|
22
|
+
createLiteral,
|
|
23
|
+
createReference,
|
|
24
|
+
} from '../types';
|
|
25
|
+
|
|
26
|
+
// =============================================================================
|
|
27
|
+
// Explicit Syntax Parser
|
|
28
|
+
// =============================================================================
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Parse explicit syntax into a semantic node.
|
|
32
|
+
*/
|
|
33
|
+
export function parseExplicit(input: string): SemanticNode {
|
|
34
|
+
const trimmed = input.trim();
|
|
35
|
+
|
|
36
|
+
if (!trimmed.startsWith('[') || !trimmed.endsWith(']')) {
|
|
37
|
+
throw new Error('Explicit syntax must be wrapped in brackets: [command role:value ...]');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const content = trimmed.slice(1, -1).trim();
|
|
41
|
+
if (!content) {
|
|
42
|
+
throw new Error('Empty explicit statement');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const tokens = tokenizeExplicit(content);
|
|
46
|
+
if (tokens.length === 0) {
|
|
47
|
+
throw new Error('No command specified in explicit statement');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const command = tokens[0].toLowerCase() as ActionType;
|
|
51
|
+
const roles = new Map<SemanticRole, SemanticValue>();
|
|
52
|
+
|
|
53
|
+
// Parse role:value pairs
|
|
54
|
+
for (let i = 1; i < tokens.length; i++) {
|
|
55
|
+
const token = tokens[i];
|
|
56
|
+
const colonIndex = token.indexOf(':');
|
|
57
|
+
|
|
58
|
+
if (colonIndex === -1) {
|
|
59
|
+
throw new Error(`Invalid role format: "${token}". Expected role:value`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const role = token.slice(0, colonIndex) as SemanticRole;
|
|
63
|
+
const valueStr = token.slice(colonIndex + 1);
|
|
64
|
+
|
|
65
|
+
// Handle nested explicit syntax for body
|
|
66
|
+
if (role === ('body' as SemanticRole) && valueStr.startsWith('[')) {
|
|
67
|
+
// Find matching bracket
|
|
68
|
+
const nestedEnd = findMatchingBracket(token, colonIndex + 1);
|
|
69
|
+
const nestedSyntax = token.slice(colonIndex + 1, nestedEnd + 1);
|
|
70
|
+
roles.set(role, { type: 'expression', raw: nestedSyntax });
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const value = parseExplicitValue(valueStr);
|
|
75
|
+
roles.set(role, value);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Build appropriate node type
|
|
79
|
+
if (command === 'on') {
|
|
80
|
+
const eventValue = roles.get('event');
|
|
81
|
+
if (!eventValue) {
|
|
82
|
+
throw new Error('Event handler requires event role: [on event:click ...]');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Parse body if present
|
|
86
|
+
const bodyValue = roles.get('body' as SemanticRole);
|
|
87
|
+
const body: SemanticNode[] = [];
|
|
88
|
+
if (bodyValue && bodyValue.type === 'expression') {
|
|
89
|
+
body.push(parseExplicit(bodyValue.raw));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
roles.delete('body' as SemanticRole);
|
|
93
|
+
|
|
94
|
+
return createEventHandler(eventValue, body, undefined, {
|
|
95
|
+
sourceLanguage: 'explicit',
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Regular command
|
|
100
|
+
const rolesObj: Record<string, SemanticValue> = {};
|
|
101
|
+
for (const [role, value] of roles) {
|
|
102
|
+
rolesObj[role] = value;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return createCommandNode(command, rolesObj, {
|
|
106
|
+
sourceLanguage: 'explicit',
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Tokenize explicit syntax content (space-separated, respecting quotes and brackets).
|
|
112
|
+
*/
|
|
113
|
+
function tokenizeExplicit(content: string): string[] {
|
|
114
|
+
const tokens: string[] = [];
|
|
115
|
+
let current = '';
|
|
116
|
+
let inString = false;
|
|
117
|
+
let stringChar = '';
|
|
118
|
+
let bracketDepth = 0;
|
|
119
|
+
|
|
120
|
+
for (let i = 0; i < content.length; i++) {
|
|
121
|
+
const char = content[i];
|
|
122
|
+
|
|
123
|
+
if (inString) {
|
|
124
|
+
current += char;
|
|
125
|
+
if (char === stringChar && content[i - 1] !== '\\') {
|
|
126
|
+
inString = false;
|
|
127
|
+
}
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (char === '"' || char === "'") {
|
|
132
|
+
inString = true;
|
|
133
|
+
stringChar = char;
|
|
134
|
+
current += char;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (char === '[') {
|
|
139
|
+
bracketDepth++;
|
|
140
|
+
current += char;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (char === ']') {
|
|
145
|
+
bracketDepth--;
|
|
146
|
+
current += char;
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (char === ' ' && bracketDepth === 0) {
|
|
151
|
+
if (current) {
|
|
152
|
+
tokens.push(current);
|
|
153
|
+
current = '';
|
|
154
|
+
}
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
current += char;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (current) {
|
|
162
|
+
tokens.push(current);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return tokens;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Parse a value string into a SemanticValue.
|
|
170
|
+
*/
|
|
171
|
+
function parseExplicitValue(valueStr: string): SemanticValue {
|
|
172
|
+
// CSS selector
|
|
173
|
+
if (
|
|
174
|
+
valueStr.startsWith('#') ||
|
|
175
|
+
valueStr.startsWith('.') ||
|
|
176
|
+
valueStr.startsWith('[') ||
|
|
177
|
+
valueStr.startsWith('@') ||
|
|
178
|
+
valueStr.startsWith('*')
|
|
179
|
+
) {
|
|
180
|
+
return createSelector(valueStr);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// String literal
|
|
184
|
+
if (valueStr.startsWith('"') || valueStr.startsWith("'")) {
|
|
185
|
+
const inner = valueStr.slice(1, -1);
|
|
186
|
+
return createLiteral(inner, 'string');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Boolean
|
|
190
|
+
if (valueStr === 'true') return createLiteral(true, 'boolean');
|
|
191
|
+
if (valueStr === 'false') return createLiteral(false, 'boolean');
|
|
192
|
+
|
|
193
|
+
// Reference
|
|
194
|
+
if (['me', 'you', 'it', 'result', 'event', 'target', 'body'].includes(valueStr.toLowerCase())) {
|
|
195
|
+
return createReference(valueStr.toLowerCase() as any);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Number (possibly with duration suffix)
|
|
199
|
+
const numMatch = valueStr.match(/^(-?\d+(?:\.\d+)?)(ms|s|m|h)?$/);
|
|
200
|
+
if (numMatch) {
|
|
201
|
+
const num = parseFloat(numMatch[1]);
|
|
202
|
+
const suffix = numMatch[2];
|
|
203
|
+
if (suffix) {
|
|
204
|
+
return createLiteral(valueStr, 'duration');
|
|
205
|
+
}
|
|
206
|
+
return createLiteral(num, 'number');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Default to string
|
|
210
|
+
return createLiteral(valueStr, 'string');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Find the matching closing bracket.
|
|
215
|
+
*/
|
|
216
|
+
function findMatchingBracket(str: string, start: number): number {
|
|
217
|
+
let depth = 0;
|
|
218
|
+
|
|
219
|
+
for (let i = start; i < str.length; i++) {
|
|
220
|
+
if (str[i] === '[') depth++;
|
|
221
|
+
else if (str[i] === ']') {
|
|
222
|
+
depth--;
|
|
223
|
+
if (depth === 0) return i;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return str.length - 1;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Check if input is explicit syntax.
|
|
232
|
+
*/
|
|
233
|
+
export function isExplicitSyntax(input: string): boolean {
|
|
234
|
+
const trimmed = input.trim();
|
|
235
|
+
return trimmed.startsWith('[') && trimmed.endsWith(']');
|
|
236
|
+
}
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Explicit Mode Renderer
|
|
3
|
+
*
|
|
4
|
+
* Renders semantic nodes to explicit [command role:value] syntax.
|
|
5
|
+
* Also renders to natural language syntax for any supported language.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
SemanticNode,
|
|
10
|
+
EventHandlerSemanticNode,
|
|
11
|
+
CompoundSemanticNode,
|
|
12
|
+
SemanticValue,
|
|
13
|
+
SemanticRenderer as ISemanticRenderer,
|
|
14
|
+
LanguagePattern,
|
|
15
|
+
ReferenceValue,
|
|
16
|
+
PropertyPathValue,
|
|
17
|
+
} from '../types';
|
|
18
|
+
// Import from registry for tree-shaking (registry uses directly-registered patterns first)
|
|
19
|
+
import { getPatternsForLanguageAndCommand, tryGetProfile } from '../registry';
|
|
20
|
+
import { getSupportedLanguages as getTokenizerLanguages } from '../tokenizers';
|
|
21
|
+
|
|
22
|
+
// =============================================================================
|
|
23
|
+
// Semantic Renderer Implementation
|
|
24
|
+
// =============================================================================
|
|
25
|
+
|
|
26
|
+
export class SemanticRendererImpl implements ISemanticRenderer {
|
|
27
|
+
/**
|
|
28
|
+
* Render a semantic node in the specified language.
|
|
29
|
+
*/
|
|
30
|
+
render(node: SemanticNode, language: string): string {
|
|
31
|
+
// Handle compound nodes specially (e.g., "cmd1 then cmd2")
|
|
32
|
+
if (node.kind === 'compound') {
|
|
33
|
+
return this.renderCompound(node as CompoundSemanticNode, language);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const patterns = getPatternsForLanguageAndCommand(language, node.action);
|
|
37
|
+
|
|
38
|
+
if (patterns.length === 0) {
|
|
39
|
+
// Fall back to explicit syntax if no patterns
|
|
40
|
+
return this.renderExplicit(node);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Find the best pattern for rendering (prefer patterns that match our roles)
|
|
44
|
+
const bestPattern = this.findBestPattern(node, patterns);
|
|
45
|
+
|
|
46
|
+
if (!bestPattern) {
|
|
47
|
+
return this.renderExplicit(node);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return this.renderWithPattern(node, bestPattern);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Render a compound node (multiple statements chained with then/and).
|
|
55
|
+
*/
|
|
56
|
+
private renderCompound(node: CompoundSemanticNode, language: string): string {
|
|
57
|
+
const renderedStatements = node.statements.map(stmt => this.render(stmt, language));
|
|
58
|
+
const chainWord = this.getChainWord(node.chainType, language);
|
|
59
|
+
return renderedStatements.join(` ${chainWord} `);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get the translated chain word for the given language.
|
|
64
|
+
*/
|
|
65
|
+
private getChainWord(chainType: 'then' | 'and' | 'async', language: string): string {
|
|
66
|
+
const profile = tryGetProfile(language);
|
|
67
|
+
if (!profile?.keywords) {
|
|
68
|
+
// Fall back to English
|
|
69
|
+
return chainType;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Map chain types to keyword lookup
|
|
73
|
+
const keyword = profile.keywords[chainType];
|
|
74
|
+
return keyword?.primary ?? chainType;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Render a semantic node in explicit mode.
|
|
79
|
+
*/
|
|
80
|
+
renderExplicit(node: SemanticNode): string {
|
|
81
|
+
// Handle compound nodes
|
|
82
|
+
if (node.kind === 'compound') {
|
|
83
|
+
const compoundNode = node as CompoundSemanticNode;
|
|
84
|
+
const renderedStatements = compoundNode.statements.map(stmt => this.renderExplicit(stmt));
|
|
85
|
+
return renderedStatements.join(` ${compoundNode.chainType} `);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const parts: string[] = [node.action];
|
|
89
|
+
|
|
90
|
+
// Add roles
|
|
91
|
+
for (const [role, value] of node.roles) {
|
|
92
|
+
parts.push(`${role}:${this.valueToString(value)}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Handle event handler body
|
|
96
|
+
if (node.kind === 'event-handler') {
|
|
97
|
+
const eventNode = node as EventHandlerSemanticNode;
|
|
98
|
+
if (eventNode.body && eventNode.body.length > 0) {
|
|
99
|
+
const bodyParts = eventNode.body.map(n => this.renderExplicit(n));
|
|
100
|
+
parts.push(`body:${bodyParts.join(' ')}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return `[${parts.join(' ')}]`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Get all supported languages.
|
|
109
|
+
*/
|
|
110
|
+
supportedLanguages(): string[] {
|
|
111
|
+
return getTokenizerLanguages();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Find the best pattern for rendering a semantic node.
|
|
116
|
+
*
|
|
117
|
+
* For rendering, we prefer "standard" patterns (e.g., "on click") over
|
|
118
|
+
* native idiom patterns (e.g., "when clicked") because standard patterns
|
|
119
|
+
* are more recognizable and closer to the original hyperscript syntax.
|
|
120
|
+
*/
|
|
121
|
+
private findBestPattern(node: SemanticNode, patterns: LanguagePattern[]): LanguagePattern | null {
|
|
122
|
+
// Score patterns by how well they match our roles
|
|
123
|
+
const scored = patterns.map(pattern => {
|
|
124
|
+
let score = pattern.priority;
|
|
125
|
+
|
|
126
|
+
// Check each role token in the pattern
|
|
127
|
+
for (const token of pattern.template.tokens) {
|
|
128
|
+
if (token.type === 'role') {
|
|
129
|
+
if (node.roles.has(token.role)) {
|
|
130
|
+
// Bonus for patterns that use roles we have
|
|
131
|
+
score += 10;
|
|
132
|
+
} else if (!(token as any).optional) {
|
|
133
|
+
// Heavy penalty for patterns that require roles we DON'T have
|
|
134
|
+
// This prevents selecting "source" patterns when there's no source
|
|
135
|
+
score -= 50;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// For English rendering, prefer "standard" patterns over "native idiom" patterns
|
|
141
|
+
// This ensures "on click" is preferred over "when clicked" for English output
|
|
142
|
+
// Only apply this boost for English - other languages should use their native idioms
|
|
143
|
+
if (pattern.language === 'en') {
|
|
144
|
+
if (pattern.id.includes('standard') || pattern.id.includes('en-source')) {
|
|
145
|
+
score += 20; // Boost standard patterns for English rendering
|
|
146
|
+
}
|
|
147
|
+
// Penalize English "when", "if", "upon" variants (good for parsing, not output)
|
|
148
|
+
if (
|
|
149
|
+
pattern.id.includes('-when') ||
|
|
150
|
+
pattern.id.includes('-if') ||
|
|
151
|
+
pattern.id.includes('-upon')
|
|
152
|
+
) {
|
|
153
|
+
score -= 15;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return { pattern, score };
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
scored.sort((a, b) => b.score - a.score);
|
|
161
|
+
|
|
162
|
+
return scored.length > 0 ? scored[0].pattern : null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Render a semantic node using a specific pattern.
|
|
167
|
+
*/
|
|
168
|
+
private renderWithPattern(node: SemanticNode, pattern: LanguagePattern): string {
|
|
169
|
+
const parts: string[] = [];
|
|
170
|
+
const language = pattern.language;
|
|
171
|
+
|
|
172
|
+
for (const token of pattern.template.tokens) {
|
|
173
|
+
const rendered = this.renderPatternToken(token, node, language);
|
|
174
|
+
if (rendered !== null) {
|
|
175
|
+
parts.push(rendered);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Handle event handler body (render separately after pattern)
|
|
180
|
+
if (node.kind === 'event-handler') {
|
|
181
|
+
const eventNode = node as EventHandlerSemanticNode;
|
|
182
|
+
if (eventNode.body && eventNode.body.length > 0) {
|
|
183
|
+
const bodyParts = eventNode.body.map(n => this.render(n, language));
|
|
184
|
+
parts.push(bodyParts.join(' '));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return parts.join(' ');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Render a single pattern token.
|
|
193
|
+
*/
|
|
194
|
+
private renderPatternToken(token: any, node: SemanticNode, language: string): string | null {
|
|
195
|
+
switch (token.type) {
|
|
196
|
+
case 'literal':
|
|
197
|
+
return token.value;
|
|
198
|
+
|
|
199
|
+
case 'role': {
|
|
200
|
+
const value = node.roles.get(token.role);
|
|
201
|
+
if (!value) {
|
|
202
|
+
if (token.optional) return null;
|
|
203
|
+
// Use default if available
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
return this.valueToNaturalString(value, language);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
case 'group': {
|
|
210
|
+
// Check if we have all required roles in the group
|
|
211
|
+
const hasRequired = token.tokens
|
|
212
|
+
.filter((t: any) => t.type === 'role' && !t.optional)
|
|
213
|
+
.every((t: any) => node.roles.has(t.role));
|
|
214
|
+
|
|
215
|
+
if (!hasRequired && token.optional) {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// For optional groups with destination role, skip if destination is "me" (the default)
|
|
220
|
+
// This avoids rendering "on me" / "en yo" when it's implicit
|
|
221
|
+
if (token.optional) {
|
|
222
|
+
const destToken = token.tokens.find(
|
|
223
|
+
(t: any) => t.type === 'role' && t.role === 'destination'
|
|
224
|
+
);
|
|
225
|
+
if (destToken) {
|
|
226
|
+
const destValue = node.roles.get('destination');
|
|
227
|
+
if (destValue?.type === 'reference' && destValue.value === 'me') {
|
|
228
|
+
return null; // Skip rendering default "me" destination
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const groupParts: string[] = [];
|
|
234
|
+
for (const subToken of token.tokens) {
|
|
235
|
+
const rendered = this.renderPatternToken(subToken, node, language);
|
|
236
|
+
if (rendered !== null) {
|
|
237
|
+
groupParts.push(rendered);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return groupParts.length > 0 ? groupParts.join(' ') : null;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
default:
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Convert a semantic value to a string for explicit syntax.
|
|
251
|
+
*/
|
|
252
|
+
private valueToString(value: SemanticValue): string {
|
|
253
|
+
switch (value.type) {
|
|
254
|
+
case 'literal':
|
|
255
|
+
if (typeof value.value === 'string') {
|
|
256
|
+
// Check if it needs quoting
|
|
257
|
+
if (value.dataType === 'string' || /\s/.test(value.value)) {
|
|
258
|
+
return `"${value.value}"`;
|
|
259
|
+
}
|
|
260
|
+
return value.value;
|
|
261
|
+
}
|
|
262
|
+
return String(value.value);
|
|
263
|
+
|
|
264
|
+
case 'selector':
|
|
265
|
+
return value.value;
|
|
266
|
+
|
|
267
|
+
case 'reference':
|
|
268
|
+
return value.value;
|
|
269
|
+
|
|
270
|
+
case 'property-path':
|
|
271
|
+
return `${this.valueToString(value.object)}'s ${value.property}`;
|
|
272
|
+
|
|
273
|
+
case 'expression':
|
|
274
|
+
return value.raw;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Convert a semantic value to natural language string.
|
|
280
|
+
* Uses language-specific possessive rendering when language is provided.
|
|
281
|
+
*/
|
|
282
|
+
private valueToNaturalString(value: SemanticValue, language: string = 'en'): string {
|
|
283
|
+
switch (value.type) {
|
|
284
|
+
case 'literal':
|
|
285
|
+
if (typeof value.value === 'string' && value.dataType === 'string') {
|
|
286
|
+
return `"${value.value}"`;
|
|
287
|
+
}
|
|
288
|
+
return String(value.value);
|
|
289
|
+
|
|
290
|
+
case 'selector':
|
|
291
|
+
return value.value;
|
|
292
|
+
|
|
293
|
+
case 'reference':
|
|
294
|
+
return this.renderReference(value, language);
|
|
295
|
+
|
|
296
|
+
case 'property-path':
|
|
297
|
+
return this.renderPropertyPath(value, language);
|
|
298
|
+
|
|
299
|
+
case 'expression':
|
|
300
|
+
return value.raw;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Render a reference value in the target language.
|
|
306
|
+
*/
|
|
307
|
+
private renderReference(value: ReferenceValue, language: string): string {
|
|
308
|
+
const profile = tryGetProfile(language);
|
|
309
|
+
if (!profile?.references) {
|
|
310
|
+
return value.value; // Fall back to English reference
|
|
311
|
+
}
|
|
312
|
+
return profile.references[value.value] ?? value.value;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Render a property-path value (possessive expression) in the target language.
|
|
317
|
+
*
|
|
318
|
+
* Examples by language:
|
|
319
|
+
* - English: "my value", "its opacity", "#el's value"
|
|
320
|
+
* - Japanese: "自分の value", "それの opacity"
|
|
321
|
+
* - Korean: "내 value", "그것의 opacity"
|
|
322
|
+
* - Spanish: "mi value", "su opacity"
|
|
323
|
+
* - Chinese: "我的 value", "它的 opacity"
|
|
324
|
+
*/
|
|
325
|
+
private renderPropertyPath(value: PropertyPathValue, language: string): string {
|
|
326
|
+
const profile = tryGetProfile(language);
|
|
327
|
+
const property = value.property;
|
|
328
|
+
|
|
329
|
+
// Get the object reference
|
|
330
|
+
const objectRef = value.object.type === 'reference' ? value.object.value : null;
|
|
331
|
+
|
|
332
|
+
// Check for special possessive forms (e.g., me → my, it → its)
|
|
333
|
+
if (profile?.possessive?.specialForms && objectRef) {
|
|
334
|
+
const specialForm = profile.possessive.specialForms[objectRef];
|
|
335
|
+
if (specialForm) {
|
|
336
|
+
const { markerPosition, usePossessiveAdjectives } = profile.possessive;
|
|
337
|
+
|
|
338
|
+
// Handle different word orders based on marker position
|
|
339
|
+
if (usePossessiveAdjectives && markerPosition === 'after-object') {
|
|
340
|
+
// Languages like Arabic, Indonesian: "value لي", "value saya"
|
|
341
|
+
// Possessive pronoun comes after the property
|
|
342
|
+
return `${property} ${specialForm}`;
|
|
343
|
+
}
|
|
344
|
+
// Languages like Spanish, German, French, Korean: "mi value", "mein value", "내 value"
|
|
345
|
+
// Possessive pronoun comes before the property
|
|
346
|
+
return `${specialForm} ${property}`;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Get the rendered object string
|
|
351
|
+
const objectStr = this.valueToNaturalString(value.object, language);
|
|
352
|
+
|
|
353
|
+
// Use language-specific possessive construction
|
|
354
|
+
if (profile?.possessive) {
|
|
355
|
+
const { marker, markerPosition, usePossessiveAdjectives } = profile.possessive;
|
|
356
|
+
|
|
357
|
+
// Languages that use possessive adjectives without explicit object reference
|
|
358
|
+
if (usePossessiveAdjectives && objectRef) {
|
|
359
|
+
// Fall back to generic construction if no special form
|
|
360
|
+
// e.g., Indonesian: "value saya" (property + possessor)
|
|
361
|
+
if (markerPosition === 'after-object') {
|
|
362
|
+
return `${property} ${objectStr}`;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Particle/marker-based languages
|
|
367
|
+
if (marker) {
|
|
368
|
+
switch (markerPosition) {
|
|
369
|
+
case 'between':
|
|
370
|
+
// Japanese: "自分の value", Chinese: "我的 value", Korean: "나의 value"
|
|
371
|
+
return profile.usesSpaces
|
|
372
|
+
? `${objectStr}${marker} ${property}`
|
|
373
|
+
: `${objectStr}${marker}${property}`;
|
|
374
|
+
|
|
375
|
+
case 'after-object':
|
|
376
|
+
// Quechua: "ñuqapa value"
|
|
377
|
+
return `${objectStr}${marker} ${property}`;
|
|
378
|
+
|
|
379
|
+
case 'before-property':
|
|
380
|
+
// Spanish (with de): "value de yo" (rarely used, usually special forms)
|
|
381
|
+
return `${objectStr} ${marker} ${property}`;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Default: English-style possessive "'s"
|
|
387
|
+
// Handle special English cases
|
|
388
|
+
if (language === 'en' || !profile?.possessive) {
|
|
389
|
+
if (objectStr === 'me') {
|
|
390
|
+
return `my ${property}`;
|
|
391
|
+
}
|
|
392
|
+
if (objectStr === 'it') {
|
|
393
|
+
return `its ${property}`;
|
|
394
|
+
}
|
|
395
|
+
return `${objectStr}'s ${property}`;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Generic fallback
|
|
399
|
+
return `${objectStr} ${property}`;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// =============================================================================
|
|
404
|
+
// Convenience Functions
|
|
405
|
+
// =============================================================================
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Singleton renderer instance.
|
|
409
|
+
*/
|
|
410
|
+
export const semanticRenderer = new SemanticRendererImpl();
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Render a semantic node in the specified language.
|
|
414
|
+
*/
|
|
415
|
+
export function render(node: SemanticNode, language: string): string {
|
|
416
|
+
return semanticRenderer.render(node, language);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Render a semantic node in explicit mode.
|
|
421
|
+
*/
|
|
422
|
+
export function renderExplicit(node: SemanticNode): string {
|
|
423
|
+
return semanticRenderer.renderExplicit(node);
|
|
424
|
+
}
|