@hyperfixi/core 2.9.1 → 2.9.3
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/dist/bundle-generator/index.js +6 -0
- package/dist/bundle-generator/index.mjs +6 -0
- package/dist/bundle-generator/parser-templates.d.ts +1 -1
- package/dist/chunks/{bridge-BYhqTOnL.js → bridge-DwtNLKK3.js} +2 -2
- package/dist/chunks/browser-modular-6HT1r2AH.js +2 -0
- package/dist/chunks/{index-DXgJaDj-.js → index-B8jwvuj_.js} +2 -2
- package/dist/commands/index.js +133 -74
- package/dist/commands/index.mjs +133 -74
- package/dist/hyperfixi-browser-classic-i18n.js +1 -1
- package/dist/hyperfixi-browser-minimal.js +1 -1
- package/dist/hyperfixi-browser-standard.js +1 -1
- package/dist/hyperfixi-browser.js +1 -1
- package/dist/hyperfixi-classic-i18n.js +1 -1
- package/dist/hyperfixi-hx-v4.js +1 -1
- package/dist/hyperfixi-hx.js +1 -1
- package/dist/hyperfixi-hybrid-complete.js +1 -1
- package/dist/hyperfixi-hybrid-hx.js +1 -1
- package/dist/hyperfixi-minimal.js +1 -1
- package/dist/hyperfixi-multilingual.js +1 -1
- package/dist/hyperfixi-standard.js +1 -1
- package/dist/hyperfixi.js +1 -1
- package/dist/hyperfixi.mjs +1 -1
- package/dist/index.js +233 -79
- package/dist/index.min.js +1 -1
- package/dist/index.mjs +233 -79
- package/dist/lokascript-browser-classic-i18n.js +1 -1
- package/dist/lokascript-browser-minimal.js +1 -1
- package/dist/lokascript-browser-standard.js +1 -1
- package/dist/lokascript-browser.js +1 -1
- package/dist/lokascript-hybrid-complete.js +1 -1
- package/dist/lokascript-hybrid-hx.js +1 -1
- package/dist/lokascript-multilingual.js +1 -1
- package/dist/parser/command-parsers/utility-commands.d.ts +1 -1
- package/dist/parser/full-parser.js +133 -74
- package/dist/parser/full-parser.mjs +133 -74
- package/dist/parser/hybrid/index.js +3 -0
- package/dist/parser/hybrid/index.mjs +3 -0
- package/dist/parser/hybrid/parser-core.js +3 -0
- package/dist/parser/hybrid/parser-core.mjs +3 -0
- package/dist/parser/hybrid-parser.js +3 -0
- package/dist/parser/hybrid-parser.mjs +3 -0
- package/dist/reference/index.js +1 -0
- package/dist/reference/index.mjs +1 -0
- package/dist/registry/index.js +133 -74
- package/dist/registry/index.mjs +133 -74
- package/dist/runtime/runtime-base.d.ts +2 -1
- package/dist/types/core.d.ts +1 -0
- package/package.json +6 -6
- package/dist/chunks/browser-modular-D2BHzZFB.js +0 -2
|
@@ -1744,6 +1744,12 @@ class HybridParser {
|
|
|
1744
1744
|
const normalized = normalizeCommand(this.peek().value);
|
|
1745
1745
|
if (cmdMap[normalized]) return cmdMap[normalized]();
|
|
1746
1746
|
|
|
1747
|
+
// Mirrors parser/hybrid/parser-core.ts: absorbing catch/finally into the
|
|
1748
|
+
// body ran the error path on every success. Reject loudly instead.
|
|
1749
|
+
if (this.match('catch', 'finally')) {
|
|
1750
|
+
throw new Error("'" + this.peek().value + "' needs the full parser (use hyperfixi.js)");
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1747
1753
|
if (!this.isAtEnd() && !this.match('then', 'and', 'end', 'else')) this.advance();
|
|
1748
1754
|
return null;
|
|
1749
1755
|
}
|
|
@@ -1742,6 +1742,12 @@ class HybridParser {
|
|
|
1742
1742
|
const normalized = normalizeCommand(this.peek().value);
|
|
1743
1743
|
if (cmdMap[normalized]) return cmdMap[normalized]();
|
|
1744
1744
|
|
|
1745
|
+
// Mirrors parser/hybrid/parser-core.ts: absorbing catch/finally into the
|
|
1746
|
+
// body ran the error path on every success. Reject loudly instead.
|
|
1747
|
+
if (this.match('catch', 'finally')) {
|
|
1748
|
+
throw new Error("'" + this.peek().value + "' needs the full parser (use hyperfixi.js)");
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1745
1751
|
if (!this.isAtEnd() && !this.match('then', 'and', 'end', 'else')) this.advance();
|
|
1746
1752
|
return null;
|
|
1747
1753
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const LITE_PARSER_TEMPLATE = "\n// Lite Parser - Regex-based for minimal bundle size\n\nfunction parseLite(code) {\n const trimmed = code.trim();\n\n // Handle event handlers: \"on click toggle .active\"\n const onMatch = trimmed.match(/^on\\s+(\\w+)(?:\\s+from\\s+([^\\s]+))?\\s+(.+)$/i);\n if (onMatch) {\n return {\n type: 'event',\n event: onMatch[1],\n filter: onMatch[2] ? { type: 'selector', value: onMatch[2] } : undefined,\n modifiers: {},\n body: parseCommands(onMatch[3]),\n };\n }\n\n // Handle \"every Nms\" event pattern\n const everyMatch = trimmed.match(/^every\\s+(\\d+)(ms|s)?\\s+(.+)$/i);\n if (everyMatch) {\n const ms = everyMatch[2] === 's' ? parseInt(everyMatch[1]) * 1000 : parseInt(everyMatch[1]);\n return {\n type: 'event',\n event: 'interval:' + ms,\n modifiers: {},\n body: parseCommands(everyMatch[3]),\n };\n }\n\n // Handle \"init\" pattern\n const initMatch = trimmed.match(/^init\\s+(.+)$/i);\n if (initMatch) {\n return {\n type: 'event',\n event: 'init',\n modifiers: {},\n body: parseCommands(initMatch[1]),\n };\n }\n\n return { type: 'sequence', commands: parseCommands(trimmed) };\n}\n\nfunction parseCommands(code) {\n const parts = code.split(/\\s+(?:then|and)\\s+/i);\n return parts.map(parseCommand).filter(Boolean);\n}\n\nfunction parseCommand(code) {\n const trimmed = code.trim();\n if (!trimmed) return null;\n\n let match;\n\n // toggle .class [on target]\n match = trimmed.match(/^toggle\\s+(\\.\\w+|\\w+)(?:\\s+on\\s+(.+))?$/i);\n if (match) {\n return {\n type: 'command',\n name: 'toggle',\n args: [{ type: 'selector', value: match[1] }],\n target: match[2] ? parseTarget(match[2]) : undefined,\n };\n }\n\n // add .class [to target]\n match = trimmed.match(/^add\\s+(\\.\\w+|\\w+)(?:\\s+to\\s+(.+))?$/i);\n if (match) {\n return {\n type: 'command',\n name: 'add',\n args: [{ type: 'selector', value: match[1] }],\n target: match[2] ? parseTarget(match[2]) : undefined,\n };\n }\n\n // remove .class [from target] | remove [target]\n match = trimmed.match(/^remove\\s+(\\.\\w+)(?:\\s+from\\s+(.+))?$/i);\n if (match) {\n return {\n type: 'command',\n name: 'removeClass',\n args: [{ type: 'selector', value: match[1] }],\n target: match[2] ? parseTarget(match[2]) : undefined,\n };\n }\n match = trimmed.match(/^remove\\s+(.+)$/i);\n if (match) {\n return {\n type: 'command',\n name: 'remove',\n args: [],\n target: parseTarget(match[1]),\n };\n }\n\n // put \"content\" into target\n match = trimmed.match(/^put\\s+(?:\"([^\"]+)\"|'([^']+)'|(\\S+))\\s+(into|before|after)\\s+(.+)$/i);\n if (match) {\n const content = match[1] || match[2] || match[3];\n return {\n type: 'command',\n name: 'put',\n args: [{ type: 'literal', value: content }],\n modifier: match[4],\n target: parseTarget(match[5]),\n };\n }\n\n // set target to value | set :var to value\n match = trimmed.match(/^set\\s+(.+?)\\s+to\\s+(.+)$/i);\n if (match) {\n return {\n type: 'command',\n name: 'set',\n args: [parseTarget(match[1]), parseLiteValue(match[2])],\n };\n }\n\n // log message\n match = trimmed.match(/^log\\s+(.+)$/i);\n if (match) {\n return {\n type: 'command',\n name: 'log',\n args: [parseLiteValue(match[1])],\n };\n }\n\n // send event [to target]\n match = trimmed.match(/^send\\s+(\\w+)(?:\\s+to\\s+(.+))?$/i);\n if (match) {\n return {\n type: 'command',\n name: 'send',\n args: [{ type: 'literal', value: match[1] }],\n target: match[2] ? parseTarget(match[2]) : undefined,\n };\n }\n\n // wait Nms | wait Ns\n match = trimmed.match(/^wait\\s+(\\d+)(ms|s)?$/i);\n if (match) {\n const ms = match[2] === 's' ? parseInt(match[1]) * 1000 : parseInt(match[1]);\n return {\n type: 'command',\n name: 'wait',\n args: [{ type: 'literal', value: ms }],\n };\n }\n\n // show/hide shortcuts\n match = trimmed.match(/^(show|hide)(?:\\s+(.+))?$/i);\n if (match) {\n return {\n type: 'command',\n name: match[1].toLowerCase(),\n args: [],\n target: match[2] ? parseTarget(match[2]) : undefined,\n };\n }\n\n // Unknown command - try generic parsing\n const parts = trimmed.split(/\\s+/);\n if (parts.length > 0) {\n return {\n type: 'command',\n name: parts[0],\n args: parts.slice(1).map(p => ({ type: 'literal', value: p })),\n };\n }\n\n return null;\n}\n\nfunction parseTarget(str) {\n const s = str.trim();\n if (s === 'me') return { type: 'identifier', value: 'me' };\n if (s === 'body') return { type: 'identifier', value: 'body' };\n if (s.startsWith('#') || s.startsWith('.') || s.startsWith('[')) {\n return { type: 'selector', value: s };\n }\n if (s.startsWith(':')) {\n return { type: 'variable', name: s, scope: 'local' };\n }\n return { type: 'identifier', value: s };\n}\n\nfunction parseLiteValue(str) {\n const s = str.trim();\n if ((s.startsWith('\"') && s.endsWith('\"')) || (s.startsWith(\"'\") && s.endsWith(\"'\"))) {\n return { type: 'literal', value: s.slice(1, -1) };\n }\n if (/^-?\\d+(\\.\\d+)?$/.test(s)) {\n return { type: 'literal', value: parseFloat(s) };\n }\n if (s === 'true') return { type: 'literal', value: true };\n if (s === 'false') return { type: 'literal', value: false };\n if (s === 'null') return { type: 'literal', value: null };\n if (s.startsWith(':')) return { type: 'variable', name: s, scope: 'local' };\n if (s === 'me') return { type: 'identifier', value: 'me' };\n return { type: 'identifier', value: s };\n}\n";
|
|
2
|
-
export declare const HYBRID_PARSER_TEMPLATE = "\n// Hybrid Parser - Full AST with operator precedence\n\n// Tokenizer\nconst KEYWORDS = new Set([\n 'on', 'from', 'to', 'into', 'before', 'after', 'in', 'of', 'at', 'with',\n 'if', 'else', 'unless', 'end', 'then', 'and', 'or', 'not',\n 'repeat', 'times', 'for', 'each', 'while', 'until',\n 'toggle', 'add', 'remove', 'put', 'set', 'get', 'call', 'return', 'append',\n 'log', 'send', 'trigger', 'wait', 'settle', 'fetch', 'as',\n 'show', 'hide', 'take', 'increment', 'decrement', 'focus', 'blur', 'empty', 'go', 'transition', 'over',\n 'the', 'a', 'an', 'my', 'its', 'me', 'it', 'you',\n 'first', 'last', 'next', 'previous', 'closest', 'parent',\n 'true', 'false', 'null', 'undefined',\n 'is', 'matches', 'contains', 'includes', 'exists', 'has', 'init', 'every', 'by',\n]);\n\nconst COMMAND_ALIASES = {\n flip: 'toggle', switch: 'toggle', display: 'show', reveal: 'show',\n conceal: 'hide', increase: 'increment', decrease: 'decrement',\n fire: 'trigger', dispatch: 'send', navigate: 'go', goto: 'go',\n};\n\nconst EVENT_ALIASES = {\n clicked: 'click', pressed: 'keydown', changed: 'change',\n submitted: 'submit', loaded: 'load',\n};\n\nfunction normalizeCommand(name) {\n const lower = name.toLowerCase();\n return COMMAND_ALIASES[lower] || lower;\n}\n\nfunction normalizeEvent(name) {\n const lower = name.toLowerCase();\n return EVENT_ALIASES[lower] || lower;\n}\n\nfunction tokenize(code) {\n const tokens = [];\n let pos = 0;\n\n while (pos < code.length) {\n if (/\\s/.test(code[pos])) { pos++; continue; }\n if (code.slice(pos, pos + 2) === '--') {\n while (pos < code.length && code[pos] !== '\\n') pos++;\n continue;\n }\n\n const start = pos;\n\n // HTML selector <tag/>\n if (code[pos] === '<' && /[a-zA-Z]/.test(code[pos + 1] || '')) {\n pos++;\n while (pos < code.length && code[pos] !== '>') pos++;\n if (code[pos] === '>') pos++;\n const val = code.slice(start, pos);\n if (val.endsWith('/>') || val.endsWith('>')) {\n const normalized = val.slice(1).replace(/\\/?>$/, '');\n tokens.push({ type: 'selector', value: normalized, pos: start });\n continue;\n }\n }\n\n // Possessive 's\n if (code.slice(pos, pos + 2) === \"'s\" && !/[a-zA-Z]/.test(code[pos + 2] || '')) {\n tokens.push({ type: 'operator', value: \"'s\", pos: start });\n pos += 2;\n continue;\n }\n\n // String literals\n if (code[pos] === '\"' || code[pos] === \"'\") {\n const quote = code[pos++];\n while (pos < code.length && code[pos] !== quote) {\n if (code[pos] === '\\\\') pos++;\n pos++;\n }\n pos++;\n tokens.push({ type: 'string', value: code.slice(start, pos), pos: start });\n continue;\n }\n\n // Numbers with units\n if (/\\d/.test(code[pos]) || (code[pos] === '-' && /\\d/.test(code[pos + 1] || ''))) {\n if (code[pos] === '-') pos++;\n while (pos < code.length && /[\\d.]/.test(code[pos])) pos++;\n if (code.slice(pos, pos + 2) === 'ms') pos += 2;\n else if (code[pos] === 's' && !/[a-zA-Z]/.test(code[pos + 1] || '')) pos++;\n else if (code.slice(pos, pos + 2) === 'px') pos += 2;\n tokens.push({ type: 'number', value: code.slice(start, pos), pos: start });\n continue;\n }\n\n // Local variable :name\n if (code[pos] === ':') {\n pos++;\n while (pos < code.length && /[\\w]/.test(code[pos])) pos++;\n tokens.push({ type: 'localVar', value: code.slice(start, pos), pos: start });\n continue;\n }\n\n // Global variable $name\n if (code[pos] === '$') {\n pos++;\n while (pos < code.length && /[\\w]/.test(code[pos])) pos++;\n tokens.push({ type: 'globalVar', value: code.slice(start, pos), pos: start });\n continue;\n }\n\n // CSS selectors: #id, .class\n if (code[pos] === '#' || code[pos] === '.') {\n if (code[pos] === '.') {\n const afterDot = code.slice(pos + 1).match(/^(once|prevent|stop|debounce|throttle)\\b/i);\n if (afterDot) {\n tokens.push({ type: 'symbol', value: '.', pos: start });\n pos++;\n continue;\n }\n }\n pos++;\n while (pos < code.length && /[\\w-]/.test(code[pos])) pos++;\n tokens.push({ type: 'selector', value: code.slice(start, pos), pos: start });\n continue;\n }\n\n // Array literal vs Attribute selector\n if (code[pos] === '[') {\n let lookahead = pos + 1;\n while (lookahead < code.length && /\\s/.test(code[lookahead])) lookahead++;\n const nextChar = code[lookahead] || '';\n const isArrayLiteral = /['\"\\d\\[\\]:\\$\\-]/.test(nextChar) || nextChar === '';\n if (isArrayLiteral) {\n tokens.push({ type: 'symbol', value: '[', pos: start });\n pos++;\n continue;\n } else {\n pos++;\n let depth = 1;\n while (pos < code.length && depth > 0) {\n if (code[pos] === '[') depth++;\n if (code[pos] === ']') depth--;\n pos++;\n }\n tokens.push({ type: 'selector', value: code.slice(start, pos), pos: start });\n continue;\n }\n }\n\n if (code[pos] === ']') {\n tokens.push({ type: 'symbol', value: ']', pos: start });\n pos++;\n continue;\n }\n\n // Multi-char operators\n const twoChar = code.slice(pos, pos + 2);\n if (['==', '!=', '<=', '>=', '&&', '||'].includes(twoChar)) {\n tokens.push({ type: 'operator', value: twoChar, pos: start });\n pos += 2;\n continue;\n }\n\n // Style property *opacity\n if (code[pos] === '*' && /[a-zA-Z]/.test(code[pos + 1] || '')) {\n pos++;\n while (pos < code.length && /[\\w-]/.test(code[pos])) pos++;\n tokens.push({ type: 'styleProperty', value: code.slice(start, pos), pos: start });\n continue;\n }\n\n if ('+-*/%<>!'.includes(code[pos])) {\n tokens.push({ type: 'operator', value: code[pos], pos: start });\n pos++;\n continue;\n }\n\n if ('()[]{},.'.includes(code[pos])) {\n tokens.push({ type: 'symbol', value: code[pos], pos: start });\n pos++;\n continue;\n }\n\n if (/[a-zA-Z_]/.test(code[pos])) {\n while (pos < code.length && /[\\w-]/.test(code[pos])) pos++;\n const value = code.slice(start, pos);\n const type = KEYWORDS.has(value.toLowerCase()) ? 'keyword' : 'identifier';\n tokens.push({ type, value, pos: start });\n continue;\n }\n\n pos++;\n }\n\n tokens.push({ type: 'eof', value: '', pos: code.length });\n return tokens;\n}\n\n// Parser\nclass HybridParser {\n constructor(code) {\n this.tokens = tokenize(code);\n this.pos = 0;\n }\n\n peek(offset = 0) {\n return this.tokens[Math.min(this.pos + offset, this.tokens.length - 1)];\n }\n\n advance() {\n return this.tokens[this.pos++];\n }\n\n match(...values) {\n const token = this.peek();\n return values.some(v => token.value.toLowerCase() === v.toLowerCase());\n }\n\n matchType(...types) {\n return types.includes(this.peek().type);\n }\n\n expect(value) {\n if (!this.match(value) && normalizeCommand(this.peek().value) !== value) {\n throw new Error(\"Expected '\" + value + \"', got '\" + this.peek().value + \"'\");\n }\n return this.advance();\n }\n\n isAtEnd() {\n return this.peek().type === 'eof';\n }\n\n parse() {\n if (this.match('on')) return this.parseEventHandler();\n if (this.match('init')) {\n this.advance();\n return { type: 'event', event: 'init', modifiers: {}, body: this.parseCommandSequence() };\n }\n if (this.match('every')) return this.parseEveryHandler();\n return { type: 'sequence', commands: this.parseCommandSequence() };\n }\n\n parseEventHandler() {\n this.expect('on');\n const eventName = this.advance().value;\n const modifiers = {};\n let filter;\n\n while (this.peek().value === '.') {\n this.advance();\n const mod = this.advance().value.toLowerCase();\n if (mod === 'once') modifiers.once = true;\n else if (mod === 'prevent') modifiers.prevent = true;\n else if (mod === 'stop') modifiers.stop = true;\n else if (mod === 'debounce' || mod === 'throttle') {\n if (this.peek().value === '(') {\n this.advance();\n const num = this.advance().value;\n this.expect(')');\n if (mod === 'debounce') modifiers.debounce = parseInt(num) || 100;\n else modifiers.throttle = parseInt(num) || 100;\n }\n }\n }\n\n if (this.match('from')) {\n this.advance();\n filter = this.parseExpression();\n }\n\n return { type: 'event', event: normalizeEvent(eventName), filter, modifiers, body: this.parseCommandSequence() };\n }\n\n parseEveryHandler() {\n this.expect('every');\n const interval = this.advance().value;\n return { type: 'event', event: 'interval:' + interval, modifiers: {}, body: this.parseCommandSequence() };\n }\n\n parseCommandSequence() {\n const commands = [];\n while (!this.isAtEnd() && !this.match('end', 'else')) {\n const cmd = this.parseCommand();\n if (cmd) commands.push(cmd);\n if (this.match('then', 'and')) this.advance();\n }\n return commands;\n }\n\n parseCommand() {\n if (this.match('if', 'unless')) return this.parseIf();\n if (this.match('repeat')) return this.parseRepeat();\n if (this.match('for')) return this.parseFor();\n if (this.match('while')) return this.parseWhile();\n if (this.match('fetch')) return this.parseFetchBlock();\n\n const cmdMap = {\n toggle: () => this.parseToggle(),\n add: () => this.parseAdd(),\n remove: () => this.parseRemove(),\n put: () => this.parsePut(),\n append: () => this.parseAppend(),\n set: () => this.parseSet(),\n get: () => this.parseGet(),\n call: () => this.parseCall(),\n log: () => this.parseLog(),\n send: () => this.parseSend(),\n trigger: () => this.parseSend(),\n wait: () => this.parseWait(),\n show: () => this.parseShow(),\n hide: () => this.parseHide(),\n take: () => this.parseTake(),\n increment: () => this.parseIncDec('increment'),\n decrement: () => this.parseIncDec('decrement'),\n focus: () => this.parseFocusBlur('focus'),\n blur: () => this.parseFocusBlur('blur'),\n empty: () => this.parseEmpty(),\n go: () => this.parseGo(),\n return: () => this.parseReturn(),\n transition: () => this.parseTransition(),\n };\n\n const normalized = normalizeCommand(this.peek().value);\n if (cmdMap[normalized]) return cmdMap[normalized]();\n\n if (!this.isAtEnd() && !this.match('then', 'and', 'end', 'else')) this.advance();\n return null;\n }\n\n parseIf() {\n const isUnless = this.match('unless');\n this.advance();\n const condition = this.parseExpression();\n const body = this.parseCommandSequence();\n let elseBody;\n\n if (this.match('else')) {\n this.advance();\n elseBody = this.parseCommandSequence();\n }\n if (this.match('end')) this.advance();\n\n return {\n type: 'if',\n condition: isUnless ? { type: 'unary', operator: 'not', operand: condition } : condition,\n body,\n elseBody,\n };\n }\n\n parseRepeat() {\n this.expect('repeat');\n let count;\n if (!this.match('until', 'while', 'forever')) {\n count = this.parseExpression();\n if (this.match('times')) this.advance();\n }\n const body = this.parseCommandSequence();\n if (this.match('end')) this.advance();\n return { type: 'repeat', condition: count, body };\n }\n\n parseFor() {\n this.expect('for');\n if (this.match('each')) this.advance();\n const variable = this.advance().value;\n this.expect('in');\n const iterable = this.parseExpression();\n const body = this.parseCommandSequence();\n if (this.match('end')) this.advance();\n return { type: 'for', condition: { type: 'forCondition', variable, iterable }, body };\n }\n\n parseWhile() {\n this.expect('while');\n const condition = this.parseExpression();\n const body = this.parseCommandSequence();\n if (this.match('end')) this.advance();\n return { type: 'while', condition, body };\n }\n\n parseFetchBlock() {\n this.expect('fetch');\n const url = this.parseExpression();\n let responseType = { type: 'literal', value: 'text' };\n if (this.match('as')) {\n this.advance();\n responseType = this.parseExpression();\n }\n if (this.match('then')) this.advance();\n const body = this.parseCommandSequence();\n return { type: 'fetch', condition: { type: 'fetchConfig', url, responseType }, body };\n }\n\n parseToggle() {\n this.expect('toggle');\n const what = this.parseExpression();\n let target;\n if (this.match('on')) {\n this.advance();\n target = this.parseExpression();\n }\n return { type: 'command', name: 'toggle', args: [what], target };\n }\n\n parseAdd() {\n this.expect('add');\n const what = this.parseExpression();\n let target;\n if (this.match('to')) {\n this.advance();\n target = this.parseExpression();\n }\n return { type: 'command', name: 'add', args: [what], target };\n }\n\n parseRemove() {\n this.expect('remove');\n if (this.matchType('selector')) {\n const what = this.parseExpression();\n let target;\n if (this.match('from')) {\n this.advance();\n target = this.parseExpression();\n }\n return { type: 'command', name: 'removeClass', args: [what], target };\n }\n const target = this.parseExpression();\n return { type: 'command', name: 'remove', args: [], target };\n }\n\n parsePut() {\n this.expect('put');\n const content = this.parseExpression();\n let modifier = 'into';\n if (this.match('into', 'before', 'after', 'at')) {\n modifier = this.advance().value;\n if (modifier === 'at') {\n const pos = this.advance().value;\n this.expect('of');\n modifier = 'at ' + pos + ' of';\n }\n }\n const target = this.parseExpression();\n return { type: 'command', name: 'put', args: [content], target, modifier };\n }\n\n parseAppend() {\n this.expect('append');\n const content = this.parseExpression();\n let target;\n if (this.match('to')) {\n this.advance();\n target = this.parseExpression();\n }\n return { type: 'command', name: 'append', args: [content], target };\n }\n\n parseSet() {\n this.expect('set');\n const target = this.parseExpression();\n if (this.match('to')) {\n this.advance();\n const value = this.parseExpression();\n return { type: 'command', name: 'set', args: [target, value] };\n }\n return { type: 'command', name: 'set', args: [target] };\n }\n\n parseGet() {\n this.expect('get');\n return { type: 'command', name: 'get', args: [this.parseExpression()] };\n }\n\n parseCall() {\n this.expect('call');\n return { type: 'command', name: 'call', args: [this.parseExpression()] };\n }\n\n parseLog() {\n this.expect('log');\n const args = [];\n while (!this.isAtEnd() && !this.match('then', 'and', 'end', 'else')) {\n args.push(this.parseExpression());\n if (this.match(',')) this.advance();\n else break;\n }\n return { type: 'command', name: 'log', args };\n }\n\n parseSend() {\n this.advance();\n const event = this.advance().value;\n let target;\n if (this.match('to')) {\n this.advance();\n target = this.parseExpression();\n }\n return { type: 'command', name: 'send', args: [{ type: 'literal', value: event }], target };\n }\n\n parseWait() {\n this.expect('wait');\n if (this.match('for')) {\n this.advance();\n const event = this.advance().value;\n let target;\n if (this.match('from')) {\n this.advance();\n target = this.parseExpression();\n }\n return { type: 'command', name: 'waitFor', args: [{ type: 'literal', value: event }], target };\n }\n return { type: 'command', name: 'wait', args: [this.parseExpression()] };\n }\n\n parseShow() {\n this.expect('show');\n let target;\n const modifiers = {};\n if (!this.isAtEnd() && !this.match('then', 'and', 'end', 'else', 'when', 'where')) {\n target = this.parseExpression();\n }\n if (!this.isAtEnd() && this.match('when', 'where')) {\n const keyword = this.advance().value;\n modifiers[keyword] = this.parseExpression();\n }\n return { type: 'command', name: 'show', args: [], target, modifiers };\n }\n\n parseHide() {\n this.expect('hide');\n let target;\n const modifiers = {};\n if (!this.isAtEnd() && !this.match('then', 'and', 'end', 'else', 'when', 'where')) {\n target = this.parseExpression();\n }\n if (!this.isAtEnd() && this.match('when', 'where')) {\n const keyword = this.advance().value;\n modifiers[keyword] = this.parseExpression();\n }\n return { type: 'command', name: 'hide', args: [], target, modifiers };\n }\n\n parseTake() {\n this.expect('take');\n const what = this.parseExpression();\n let from;\n if (this.match('from')) {\n this.advance();\n from = this.parseExpression();\n }\n return { type: 'command', name: 'take', args: [what], target: from };\n }\n\n parseIncDec(name) {\n this.advance();\n const target = this.parseExpression();\n let amount = { type: 'literal', value: 1 };\n if (this.match('by')) {\n this.advance();\n amount = this.parseExpression();\n }\n return { type: 'command', name, args: [target, amount] };\n }\n\n parseFocusBlur(name) {\n this.advance();\n let target;\n if (!this.isAtEnd() && !this.match('then', 'and', 'end', 'else')) {\n target = this.parseExpression();\n }\n return { type: 'command', name, args: [], target };\n }\n\n parseEmpty() {\n this.expect('empty');\n let target;\n if (!this.isAtEnd() && !this.match('then', 'and', 'end', 'else')) {\n target = this.parseExpression();\n }\n return { type: 'command', name: 'empty', args: [], target };\n }\n\n parseGo() {\n this.expect('go');\n if (this.match('to')) this.advance();\n if (this.match('url')) this.advance();\n const dest = this.parseExpression();\n return { type: 'command', name: 'go', args: [dest] };\n }\n\n parseReturn() {\n this.expect('return');\n let value;\n if (!this.isAtEnd() && !this.match('then', 'and', 'end', 'else')) {\n value = this.parseExpression();\n }\n return { type: 'command', name: 'return', args: value ? [value] : [] };\n }\n\n parseTransition() {\n this.expect('transition');\n let target;\n if (this.match('my', 'its')) {\n const ref = this.advance().value;\n target = { type: 'identifier', value: ref === 'my' ? 'me' : 'it' };\n } else if (this.matchType('selector')) {\n const expr = this.parseExpression();\n if (expr.type === 'possessive') {\n return this.parseTransitionRest(expr.object, expr.property);\n }\n target = expr;\n }\n\n const propToken = this.peek();\n let property;\n if (propToken.type === 'styleProperty') {\n property = this.advance().value;\n } else if (propToken.type === 'identifier' || propToken.type === 'keyword') {\n property = this.advance().value;\n } else {\n property = 'opacity';\n }\n\n return this.parseTransitionRest(target, property);\n }\n\n parseTransitionRest(target, property) {\n let toValue = { type: 'literal', value: 1 };\n if (this.match('to')) {\n this.advance();\n toValue = this.parseExpression();\n }\n let duration = { type: 'literal', value: 300 };\n if (this.match('over')) {\n this.advance();\n duration = this.parseExpression();\n }\n return { type: 'command', name: 'transition', args: [{ type: 'literal', value: property }, toValue, duration], target };\n }\n\n parseExpression() { return this.parseOr(); }\n\n parseOr() {\n let left = this.parseAnd();\n while (this.match('or', '||')) {\n this.advance();\n left = { type: 'binary', operator: 'or', left, right: this.parseAnd() };\n }\n return left;\n }\n\n parseAnd() {\n let left = this.parseEquality();\n while (this.match('and', '&&') && !this.isCommandKeyword(this.peek(1))) {\n this.advance();\n left = { type: 'binary', operator: 'and', left, right: this.parseEquality() };\n }\n return left;\n }\n\n isCommandKeyword(token) {\n const cmds = ['toggle', 'add', 'remove', 'set', 'put', 'log', 'send', 'wait', 'show', 'hide', 'increment', 'decrement', 'focus', 'blur', 'empty', 'go'];\n return cmds.includes(normalizeCommand(token.value));\n }\n\n parseEquality() {\n let left = this.parseComparison();\n while (this.match('==', '!=', 'is', 'matches', 'contains', 'includes', 'has')) {\n const op = this.advance().value;\n if (op.toLowerCase() === 'is' && this.match('not')) {\n this.advance();\n left = { type: 'binary', operator: 'is not', left, right: this.parseComparison() };\n } else {\n left = { type: 'binary', operator: op, left, right: this.parseComparison() };\n }\n }\n return left;\n }\n\n parseComparison() {\n let left = this.parseAdditive();\n while (this.match('<', '>', '<=', '>=')) {\n const op = this.advance().value;\n left = { type: 'binary', operator: op, left, right: this.parseAdditive() };\n }\n return left;\n }\n\n parseAdditive() {\n let left = this.parseMultiplicative();\n while (this.match('+', '-')) {\n const op = this.advance().value;\n left = { type: 'binary', operator: op, left, right: this.parseMultiplicative() };\n }\n return left;\n }\n\n parseMultiplicative() {\n let left = this.parseUnary();\n while (this.match('*', '/', '%')) {\n const op = this.advance().value;\n left = { type: 'binary', operator: op, left, right: this.parseUnary() };\n }\n return left;\n }\n\n parseUnary() {\n if (this.match('not', '!')) {\n this.advance();\n return { type: 'unary', operator: 'not', operand: this.parseUnary() };\n }\n if (this.match('-') && this.peek(1).type === 'number') {\n this.advance();\n const num = this.advance();\n return { type: 'literal', value: -parseFloat(num.value) };\n }\n return this.parsePostfix();\n }\n\n parsePostfix() {\n let left = this.parsePrimary();\n while (true) {\n if (this.match(\"'s\")) {\n this.advance();\n const next = this.peek();\n const prop = next.type === 'styleProperty' ? this.advance().value : this.advance().value;\n left = { type: 'possessive', object: left, property: prop };\n } else if (this.peek().type === 'styleProperty') {\n const prop = this.advance().value;\n left = { type: 'possessive', object: left, property: prop };\n } else if (this.peek().value === '.') {\n this.advance();\n const prop = this.advance().value;\n left = { type: 'member', object: left, property: prop };\n } else if (this.peek().type === 'selector' && this.peek().value.startsWith('.')) {\n const prop = this.advance().value.slice(1);\n left = { type: 'member', object: left, property: prop };\n } else if (this.peek().value === '(') {\n this.advance();\n const args = [];\n while (!this.match(')')) {\n args.push(this.parseExpression());\n if (this.match(',')) this.advance();\n }\n this.expect(')');\n left = { type: 'call', callee: left, args };\n } else if (this.peek().value === '[' && left.type !== 'selector') {\n this.advance();\n const index = this.parseExpression();\n this.expect(']');\n left = { type: 'member', object: left, property: index, computed: true };\n } else {\n break;\n }\n }\n return left;\n }\n\n parsePrimary() {\n const token = this.peek();\n\n if (token.value === '(') {\n this.advance();\n const expr = this.parseExpression();\n this.expect(')');\n return expr;\n }\n\n if (token.value === '{') return this.parseObjectLiteral();\n if (token.value === '[') return this.parseArrayLiteral();\n\n if (token.type === 'number') {\n this.advance();\n const val = token.value;\n if (val.endsWith('ms')) return { type: 'literal', value: parseInt(val), unit: 'ms' };\n if (val.endsWith('s')) return { type: 'literal', value: parseFloat(val) * 1000, unit: 'ms' };\n return { type: 'literal', value: parseFloat(val) };\n }\n\n if (token.type === 'string') {\n this.advance();\n return { type: 'literal', value: token.value.slice(1, -1) };\n }\n\n if (this.match('true')) { this.advance(); return { type: 'literal', value: true }; }\n if (this.match('false')) { this.advance(); return { type: 'literal', value: false }; }\n if (this.match('null')) { this.advance(); return { type: 'literal', value: null }; }\n if (this.match('undefined')) { this.advance(); return { type: 'literal', value: undefined }; }\n\n if (token.type === 'localVar') {\n this.advance();\n return { type: 'variable', name: token.value, scope: 'local' };\n }\n if (token.type === 'globalVar') {\n this.advance();\n return { type: 'variable', name: token.value, scope: 'global' };\n }\n if (token.type === 'selector') {\n this.advance();\n return { type: 'selector', value: token.value };\n }\n\n if (this.match('my')) {\n this.advance();\n const next = this.peek();\n if ((next.type === 'identifier' || next.type === 'keyword') && !this.isCommandKeyword(next)) {\n const prop = this.advance().value;\n return { type: 'possessive', object: { type: 'identifier', value: 'me' }, property: prop };\n }\n return { type: 'identifier', value: 'me' };\n }\n if (this.match('its')) {\n this.advance();\n const next = this.peek();\n if ((next.type === 'identifier' || next.type === 'keyword') && !this.isCommandKeyword(next)) {\n const prop = this.advance().value;\n return { type: 'possessive', object: { type: 'identifier', value: 'it' }, property: prop };\n }\n return { type: 'identifier', value: 'it' };\n }\n if (this.match('me')) { this.advance(); return { type: 'identifier', value: 'me' }; }\n if (this.match('it')) { this.advance(); return { type: 'identifier', value: 'it' }; }\n if (this.match('you')) { this.advance(); return { type: 'identifier', value: 'you' }; }\n\n if (this.match('the', 'a', 'an')) {\n this.advance();\n if (this.match('first', 'last', 'next', 'previous', 'closest', 'parent')) {\n const position = this.advance().value;\n const target = this.parsePositionalTarget();\n return { type: 'positional', position, target };\n }\n return this.parsePrimary();\n }\n\n if (this.match('first', 'last', 'next', 'previous', 'closest', 'parent')) {\n const position = this.advance().value;\n const target = this.parsePositionalTarget();\n return { type: 'positional', position, target };\n }\n\n if (token.type === 'identifier' || token.type === 'keyword') {\n this.advance();\n return { type: 'identifier', value: token.value };\n }\n\n this.advance();\n return { type: 'identifier', value: token.value };\n }\n\n parseObjectLiteral() {\n this.expect('{');\n const properties = [];\n while (!this.match('}')) {\n const key = this.advance().value;\n this.expect(':');\n const value = this.parseExpression();\n properties.push({ key, value });\n if (this.match(',')) this.advance();\n }\n this.expect('}');\n return { type: 'object', properties };\n }\n\n parseArrayLiteral() {\n this.expect('[');\n const elements = [];\n while (!this.match(']')) {\n elements.push(this.parseExpression());\n if (this.match(',')) this.advance();\n }\n this.expect(']');\n return { type: 'array', elements };\n }\n\n parsePositionalTarget() {\n const token = this.peek();\n if (token.type === 'selector') {\n return { type: 'selector', value: this.advance().value };\n }\n if (token.type === 'identifier' || token.type === 'keyword') {\n return { type: 'identifier', value: this.advance().value };\n }\n return this.parseExpression();\n }\n}\n";
|
|
2
|
+
export declare const HYBRID_PARSER_TEMPLATE = "\n// Hybrid Parser - Full AST with operator precedence\n\n// Tokenizer\nconst KEYWORDS = new Set([\n 'on', 'from', 'to', 'into', 'before', 'after', 'in', 'of', 'at', 'with',\n 'if', 'else', 'unless', 'end', 'then', 'and', 'or', 'not',\n 'repeat', 'times', 'for', 'each', 'while', 'until',\n 'toggle', 'add', 'remove', 'put', 'set', 'get', 'call', 'return', 'append',\n 'log', 'send', 'trigger', 'wait', 'settle', 'fetch', 'as',\n 'show', 'hide', 'take', 'increment', 'decrement', 'focus', 'blur', 'empty', 'go', 'transition', 'over',\n 'the', 'a', 'an', 'my', 'its', 'me', 'it', 'you',\n 'first', 'last', 'next', 'previous', 'closest', 'parent',\n 'true', 'false', 'null', 'undefined',\n 'is', 'matches', 'contains', 'includes', 'exists', 'has', 'init', 'every', 'by',\n]);\n\nconst COMMAND_ALIASES = {\n flip: 'toggle', switch: 'toggle', display: 'show', reveal: 'show',\n conceal: 'hide', increase: 'increment', decrease: 'decrement',\n fire: 'trigger', dispatch: 'send', navigate: 'go', goto: 'go',\n};\n\nconst EVENT_ALIASES = {\n clicked: 'click', pressed: 'keydown', changed: 'change',\n submitted: 'submit', loaded: 'load',\n};\n\nfunction normalizeCommand(name) {\n const lower = name.toLowerCase();\n return COMMAND_ALIASES[lower] || lower;\n}\n\nfunction normalizeEvent(name) {\n const lower = name.toLowerCase();\n return EVENT_ALIASES[lower] || lower;\n}\n\nfunction tokenize(code) {\n const tokens = [];\n let pos = 0;\n\n while (pos < code.length) {\n if (/\\s/.test(code[pos])) { pos++; continue; }\n if (code.slice(pos, pos + 2) === '--') {\n while (pos < code.length && code[pos] !== '\\n') pos++;\n continue;\n }\n\n const start = pos;\n\n // HTML selector <tag/>\n if (code[pos] === '<' && /[a-zA-Z]/.test(code[pos + 1] || '')) {\n pos++;\n while (pos < code.length && code[pos] !== '>') pos++;\n if (code[pos] === '>') pos++;\n const val = code.slice(start, pos);\n if (val.endsWith('/>') || val.endsWith('>')) {\n const normalized = val.slice(1).replace(/\\/?>$/, '');\n tokens.push({ type: 'selector', value: normalized, pos: start });\n continue;\n }\n }\n\n // Possessive 's\n if (code.slice(pos, pos + 2) === \"'s\" && !/[a-zA-Z]/.test(code[pos + 2] || '')) {\n tokens.push({ type: 'operator', value: \"'s\", pos: start });\n pos += 2;\n continue;\n }\n\n // String literals\n if (code[pos] === '\"' || code[pos] === \"'\") {\n const quote = code[pos++];\n while (pos < code.length && code[pos] !== quote) {\n if (code[pos] === '\\\\') pos++;\n pos++;\n }\n pos++;\n tokens.push({ type: 'string', value: code.slice(start, pos), pos: start });\n continue;\n }\n\n // Numbers with units\n if (/\\d/.test(code[pos]) || (code[pos] === '-' && /\\d/.test(code[pos + 1] || ''))) {\n if (code[pos] === '-') pos++;\n while (pos < code.length && /[\\d.]/.test(code[pos])) pos++;\n if (code.slice(pos, pos + 2) === 'ms') pos += 2;\n else if (code[pos] === 's' && !/[a-zA-Z]/.test(code[pos + 1] || '')) pos++;\n else if (code.slice(pos, pos + 2) === 'px') pos += 2;\n tokens.push({ type: 'number', value: code.slice(start, pos), pos: start });\n continue;\n }\n\n // Local variable :name\n if (code[pos] === ':') {\n pos++;\n while (pos < code.length && /[\\w]/.test(code[pos])) pos++;\n tokens.push({ type: 'localVar', value: code.slice(start, pos), pos: start });\n continue;\n }\n\n // Global variable $name\n if (code[pos] === '$') {\n pos++;\n while (pos < code.length && /[\\w]/.test(code[pos])) pos++;\n tokens.push({ type: 'globalVar', value: code.slice(start, pos), pos: start });\n continue;\n }\n\n // CSS selectors: #id, .class\n if (code[pos] === '#' || code[pos] === '.') {\n if (code[pos] === '.') {\n const afterDot = code.slice(pos + 1).match(/^(once|prevent|stop|debounce|throttle)\\b/i);\n if (afterDot) {\n tokens.push({ type: 'symbol', value: '.', pos: start });\n pos++;\n continue;\n }\n }\n pos++;\n while (pos < code.length && /[\\w-]/.test(code[pos])) pos++;\n tokens.push({ type: 'selector', value: code.slice(start, pos), pos: start });\n continue;\n }\n\n // Array literal vs Attribute selector\n if (code[pos] === '[') {\n let lookahead = pos + 1;\n while (lookahead < code.length && /\\s/.test(code[lookahead])) lookahead++;\n const nextChar = code[lookahead] || '';\n const isArrayLiteral = /['\"\\d\\[\\]:\\$\\-]/.test(nextChar) || nextChar === '';\n if (isArrayLiteral) {\n tokens.push({ type: 'symbol', value: '[', pos: start });\n pos++;\n continue;\n } else {\n pos++;\n let depth = 1;\n while (pos < code.length && depth > 0) {\n if (code[pos] === '[') depth++;\n if (code[pos] === ']') depth--;\n pos++;\n }\n tokens.push({ type: 'selector', value: code.slice(start, pos), pos: start });\n continue;\n }\n }\n\n if (code[pos] === ']') {\n tokens.push({ type: 'symbol', value: ']', pos: start });\n pos++;\n continue;\n }\n\n // Multi-char operators\n const twoChar = code.slice(pos, pos + 2);\n if (['==', '!=', '<=', '>=', '&&', '||'].includes(twoChar)) {\n tokens.push({ type: 'operator', value: twoChar, pos: start });\n pos += 2;\n continue;\n }\n\n // Style property *opacity\n if (code[pos] === '*' && /[a-zA-Z]/.test(code[pos + 1] || '')) {\n pos++;\n while (pos < code.length && /[\\w-]/.test(code[pos])) pos++;\n tokens.push({ type: 'styleProperty', value: code.slice(start, pos), pos: start });\n continue;\n }\n\n if ('+-*/%<>!'.includes(code[pos])) {\n tokens.push({ type: 'operator', value: code[pos], pos: start });\n pos++;\n continue;\n }\n\n if ('()[]{},.'.includes(code[pos])) {\n tokens.push({ type: 'symbol', value: code[pos], pos: start });\n pos++;\n continue;\n }\n\n if (/[a-zA-Z_]/.test(code[pos])) {\n while (pos < code.length && /[\\w-]/.test(code[pos])) pos++;\n const value = code.slice(start, pos);\n const type = KEYWORDS.has(value.toLowerCase()) ? 'keyword' : 'identifier';\n tokens.push({ type, value, pos: start });\n continue;\n }\n\n pos++;\n }\n\n tokens.push({ type: 'eof', value: '', pos: code.length });\n return tokens;\n}\n\n// Parser\nclass HybridParser {\n constructor(code) {\n this.tokens = tokenize(code);\n this.pos = 0;\n }\n\n peek(offset = 0) {\n return this.tokens[Math.min(this.pos + offset, this.tokens.length - 1)];\n }\n\n advance() {\n return this.tokens[this.pos++];\n }\n\n match(...values) {\n const token = this.peek();\n return values.some(v => token.value.toLowerCase() === v.toLowerCase());\n }\n\n matchType(...types) {\n return types.includes(this.peek().type);\n }\n\n expect(value) {\n if (!this.match(value) && normalizeCommand(this.peek().value) !== value) {\n throw new Error(\"Expected '\" + value + \"', got '\" + this.peek().value + \"'\");\n }\n return this.advance();\n }\n\n isAtEnd() {\n return this.peek().type === 'eof';\n }\n\n parse() {\n if (this.match('on')) return this.parseEventHandler();\n if (this.match('init')) {\n this.advance();\n return { type: 'event', event: 'init', modifiers: {}, body: this.parseCommandSequence() };\n }\n if (this.match('every')) return this.parseEveryHandler();\n return { type: 'sequence', commands: this.parseCommandSequence() };\n }\n\n parseEventHandler() {\n this.expect('on');\n const eventName = this.advance().value;\n const modifiers = {};\n let filter;\n\n while (this.peek().value === '.') {\n this.advance();\n const mod = this.advance().value.toLowerCase();\n if (mod === 'once') modifiers.once = true;\n else if (mod === 'prevent') modifiers.prevent = true;\n else if (mod === 'stop') modifiers.stop = true;\n else if (mod === 'debounce' || mod === 'throttle') {\n if (this.peek().value === '(') {\n this.advance();\n const num = this.advance().value;\n this.expect(')');\n if (mod === 'debounce') modifiers.debounce = parseInt(num) || 100;\n else modifiers.throttle = parseInt(num) || 100;\n }\n }\n }\n\n if (this.match('from')) {\n this.advance();\n filter = this.parseExpression();\n }\n\n return { type: 'event', event: normalizeEvent(eventName), filter, modifiers, body: this.parseCommandSequence() };\n }\n\n parseEveryHandler() {\n this.expect('every');\n const interval = this.advance().value;\n return { type: 'event', event: 'interval:' + interval, modifiers: {}, body: this.parseCommandSequence() };\n }\n\n parseCommandSequence() {\n const commands = [];\n while (!this.isAtEnd() && !this.match('end', 'else')) {\n const cmd = this.parseCommand();\n if (cmd) commands.push(cmd);\n if (this.match('then', 'and')) this.advance();\n }\n return commands;\n }\n\n parseCommand() {\n if (this.match('if', 'unless')) return this.parseIf();\n if (this.match('repeat')) return this.parseRepeat();\n if (this.match('for')) return this.parseFor();\n if (this.match('while')) return this.parseWhile();\n if (this.match('fetch')) return this.parseFetchBlock();\n\n const cmdMap = {\n toggle: () => this.parseToggle(),\n add: () => this.parseAdd(),\n remove: () => this.parseRemove(),\n put: () => this.parsePut(),\n append: () => this.parseAppend(),\n set: () => this.parseSet(),\n get: () => this.parseGet(),\n call: () => this.parseCall(),\n log: () => this.parseLog(),\n send: () => this.parseSend(),\n trigger: () => this.parseSend(),\n wait: () => this.parseWait(),\n show: () => this.parseShow(),\n hide: () => this.parseHide(),\n take: () => this.parseTake(),\n increment: () => this.parseIncDec('increment'),\n decrement: () => this.parseIncDec('decrement'),\n focus: () => this.parseFocusBlur('focus'),\n blur: () => this.parseFocusBlur('blur'),\n empty: () => this.parseEmpty(),\n go: () => this.parseGo(),\n return: () => this.parseReturn(),\n transition: () => this.parseTransition(),\n };\n\n const normalized = normalizeCommand(this.peek().value);\n if (cmdMap[normalized]) return cmdMap[normalized]();\n\n // Mirrors parser/hybrid/parser-core.ts: absorbing catch/finally into the\n // body ran the error path on every success. Reject loudly instead.\n if (this.match('catch', 'finally')) {\n throw new Error(\"'\" + this.peek().value + \"' needs the full parser (use hyperfixi.js)\");\n }\n\n if (!this.isAtEnd() && !this.match('then', 'and', 'end', 'else')) this.advance();\n return null;\n }\n\n parseIf() {\n const isUnless = this.match('unless');\n this.advance();\n const condition = this.parseExpression();\n const body = this.parseCommandSequence();\n let elseBody;\n\n if (this.match('else')) {\n this.advance();\n elseBody = this.parseCommandSequence();\n }\n if (this.match('end')) this.advance();\n\n return {\n type: 'if',\n condition: isUnless ? { type: 'unary', operator: 'not', operand: condition } : condition,\n body,\n elseBody,\n };\n }\n\n parseRepeat() {\n this.expect('repeat');\n let count;\n if (!this.match('until', 'while', 'forever')) {\n count = this.parseExpression();\n if (this.match('times')) this.advance();\n }\n const body = this.parseCommandSequence();\n if (this.match('end')) this.advance();\n return { type: 'repeat', condition: count, body };\n }\n\n parseFor() {\n this.expect('for');\n if (this.match('each')) this.advance();\n const variable = this.advance().value;\n this.expect('in');\n const iterable = this.parseExpression();\n const body = this.parseCommandSequence();\n if (this.match('end')) this.advance();\n return { type: 'for', condition: { type: 'forCondition', variable, iterable }, body };\n }\n\n parseWhile() {\n this.expect('while');\n const condition = this.parseExpression();\n const body = this.parseCommandSequence();\n if (this.match('end')) this.advance();\n return { type: 'while', condition, body };\n }\n\n parseFetchBlock() {\n this.expect('fetch');\n const url = this.parseExpression();\n let responseType = { type: 'literal', value: 'text' };\n if (this.match('as')) {\n this.advance();\n responseType = this.parseExpression();\n }\n if (this.match('then')) this.advance();\n const body = this.parseCommandSequence();\n return { type: 'fetch', condition: { type: 'fetchConfig', url, responseType }, body };\n }\n\n parseToggle() {\n this.expect('toggle');\n const what = this.parseExpression();\n let target;\n if (this.match('on')) {\n this.advance();\n target = this.parseExpression();\n }\n return { type: 'command', name: 'toggle', args: [what], target };\n }\n\n parseAdd() {\n this.expect('add');\n const what = this.parseExpression();\n let target;\n if (this.match('to')) {\n this.advance();\n target = this.parseExpression();\n }\n return { type: 'command', name: 'add', args: [what], target };\n }\n\n parseRemove() {\n this.expect('remove');\n if (this.matchType('selector')) {\n const what = this.parseExpression();\n let target;\n if (this.match('from')) {\n this.advance();\n target = this.parseExpression();\n }\n return { type: 'command', name: 'removeClass', args: [what], target };\n }\n const target = this.parseExpression();\n return { type: 'command', name: 'remove', args: [], target };\n }\n\n parsePut() {\n this.expect('put');\n const content = this.parseExpression();\n let modifier = 'into';\n if (this.match('into', 'before', 'after', 'at')) {\n modifier = this.advance().value;\n if (modifier === 'at') {\n const pos = this.advance().value;\n this.expect('of');\n modifier = 'at ' + pos + ' of';\n }\n }\n const target = this.parseExpression();\n return { type: 'command', name: 'put', args: [content], target, modifier };\n }\n\n parseAppend() {\n this.expect('append');\n const content = this.parseExpression();\n let target;\n if (this.match('to')) {\n this.advance();\n target = this.parseExpression();\n }\n return { type: 'command', name: 'append', args: [content], target };\n }\n\n parseSet() {\n this.expect('set');\n const target = this.parseExpression();\n if (this.match('to')) {\n this.advance();\n const value = this.parseExpression();\n return { type: 'command', name: 'set', args: [target, value] };\n }\n return { type: 'command', name: 'set', args: [target] };\n }\n\n parseGet() {\n this.expect('get');\n return { type: 'command', name: 'get', args: [this.parseExpression()] };\n }\n\n parseCall() {\n this.expect('call');\n return { type: 'command', name: 'call', args: [this.parseExpression()] };\n }\n\n parseLog() {\n this.expect('log');\n const args = [];\n while (!this.isAtEnd() && !this.match('then', 'and', 'end', 'else')) {\n args.push(this.parseExpression());\n if (this.match(',')) this.advance();\n else break;\n }\n return { type: 'command', name: 'log', args };\n }\n\n parseSend() {\n this.advance();\n const event = this.advance().value;\n let target;\n if (this.match('to')) {\n this.advance();\n target = this.parseExpression();\n }\n return { type: 'command', name: 'send', args: [{ type: 'literal', value: event }], target };\n }\n\n parseWait() {\n this.expect('wait');\n if (this.match('for')) {\n this.advance();\n const event = this.advance().value;\n let target;\n if (this.match('from')) {\n this.advance();\n target = this.parseExpression();\n }\n return { type: 'command', name: 'waitFor', args: [{ type: 'literal', value: event }], target };\n }\n return { type: 'command', name: 'wait', args: [this.parseExpression()] };\n }\n\n parseShow() {\n this.expect('show');\n let target;\n const modifiers = {};\n if (!this.isAtEnd() && !this.match('then', 'and', 'end', 'else', 'when', 'where')) {\n target = this.parseExpression();\n }\n if (!this.isAtEnd() && this.match('when', 'where')) {\n const keyword = this.advance().value;\n modifiers[keyword] = this.parseExpression();\n }\n return { type: 'command', name: 'show', args: [], target, modifiers };\n }\n\n parseHide() {\n this.expect('hide');\n let target;\n const modifiers = {};\n if (!this.isAtEnd() && !this.match('then', 'and', 'end', 'else', 'when', 'where')) {\n target = this.parseExpression();\n }\n if (!this.isAtEnd() && this.match('when', 'where')) {\n const keyword = this.advance().value;\n modifiers[keyword] = this.parseExpression();\n }\n return { type: 'command', name: 'hide', args: [], target, modifiers };\n }\n\n parseTake() {\n this.expect('take');\n const what = this.parseExpression();\n let from;\n if (this.match('from')) {\n this.advance();\n from = this.parseExpression();\n }\n return { type: 'command', name: 'take', args: [what], target: from };\n }\n\n parseIncDec(name) {\n this.advance();\n const target = this.parseExpression();\n let amount = { type: 'literal', value: 1 };\n if (this.match('by')) {\n this.advance();\n amount = this.parseExpression();\n }\n return { type: 'command', name, args: [target, amount] };\n }\n\n parseFocusBlur(name) {\n this.advance();\n let target;\n if (!this.isAtEnd() && !this.match('then', 'and', 'end', 'else')) {\n target = this.parseExpression();\n }\n return { type: 'command', name, args: [], target };\n }\n\n parseEmpty() {\n this.expect('empty');\n let target;\n if (!this.isAtEnd() && !this.match('then', 'and', 'end', 'else')) {\n target = this.parseExpression();\n }\n return { type: 'command', name: 'empty', args: [], target };\n }\n\n parseGo() {\n this.expect('go');\n if (this.match('to')) this.advance();\n if (this.match('url')) this.advance();\n const dest = this.parseExpression();\n return { type: 'command', name: 'go', args: [dest] };\n }\n\n parseReturn() {\n this.expect('return');\n let value;\n if (!this.isAtEnd() && !this.match('then', 'and', 'end', 'else')) {\n value = this.parseExpression();\n }\n return { type: 'command', name: 'return', args: value ? [value] : [] };\n }\n\n parseTransition() {\n this.expect('transition');\n let target;\n if (this.match('my', 'its')) {\n const ref = this.advance().value;\n target = { type: 'identifier', value: ref === 'my' ? 'me' : 'it' };\n } else if (this.matchType('selector')) {\n const expr = this.parseExpression();\n if (expr.type === 'possessive') {\n return this.parseTransitionRest(expr.object, expr.property);\n }\n target = expr;\n }\n\n const propToken = this.peek();\n let property;\n if (propToken.type === 'styleProperty') {\n property = this.advance().value;\n } else if (propToken.type === 'identifier' || propToken.type === 'keyword') {\n property = this.advance().value;\n } else {\n property = 'opacity';\n }\n\n return this.parseTransitionRest(target, property);\n }\n\n parseTransitionRest(target, property) {\n let toValue = { type: 'literal', value: 1 };\n if (this.match('to')) {\n this.advance();\n toValue = this.parseExpression();\n }\n let duration = { type: 'literal', value: 300 };\n if (this.match('over')) {\n this.advance();\n duration = this.parseExpression();\n }\n return { type: 'command', name: 'transition', args: [{ type: 'literal', value: property }, toValue, duration], target };\n }\n\n parseExpression() { return this.parseOr(); }\n\n parseOr() {\n let left = this.parseAnd();\n while (this.match('or', '||')) {\n this.advance();\n left = { type: 'binary', operator: 'or', left, right: this.parseAnd() };\n }\n return left;\n }\n\n parseAnd() {\n let left = this.parseEquality();\n while (this.match('and', '&&') && !this.isCommandKeyword(this.peek(1))) {\n this.advance();\n left = { type: 'binary', operator: 'and', left, right: this.parseEquality() };\n }\n return left;\n }\n\n isCommandKeyword(token) {\n const cmds = ['toggle', 'add', 'remove', 'set', 'put', 'log', 'send', 'wait', 'show', 'hide', 'increment', 'decrement', 'focus', 'blur', 'empty', 'go'];\n return cmds.includes(normalizeCommand(token.value));\n }\n\n parseEquality() {\n let left = this.parseComparison();\n while (this.match('==', '!=', 'is', 'matches', 'contains', 'includes', 'has')) {\n const op = this.advance().value;\n if (op.toLowerCase() === 'is' && this.match('not')) {\n this.advance();\n left = { type: 'binary', operator: 'is not', left, right: this.parseComparison() };\n } else {\n left = { type: 'binary', operator: op, left, right: this.parseComparison() };\n }\n }\n return left;\n }\n\n parseComparison() {\n let left = this.parseAdditive();\n while (this.match('<', '>', '<=', '>=')) {\n const op = this.advance().value;\n left = { type: 'binary', operator: op, left, right: this.parseAdditive() };\n }\n return left;\n }\n\n parseAdditive() {\n let left = this.parseMultiplicative();\n while (this.match('+', '-')) {\n const op = this.advance().value;\n left = { type: 'binary', operator: op, left, right: this.parseMultiplicative() };\n }\n return left;\n }\n\n parseMultiplicative() {\n let left = this.parseUnary();\n while (this.match('*', '/', '%')) {\n const op = this.advance().value;\n left = { type: 'binary', operator: op, left, right: this.parseUnary() };\n }\n return left;\n }\n\n parseUnary() {\n if (this.match('not', '!')) {\n this.advance();\n return { type: 'unary', operator: 'not', operand: this.parseUnary() };\n }\n if (this.match('-') && this.peek(1).type === 'number') {\n this.advance();\n const num = this.advance();\n return { type: 'literal', value: -parseFloat(num.value) };\n }\n return this.parsePostfix();\n }\n\n parsePostfix() {\n let left = this.parsePrimary();\n while (true) {\n if (this.match(\"'s\")) {\n this.advance();\n const next = this.peek();\n const prop = next.type === 'styleProperty' ? this.advance().value : this.advance().value;\n left = { type: 'possessive', object: left, property: prop };\n } else if (this.peek().type === 'styleProperty') {\n const prop = this.advance().value;\n left = { type: 'possessive', object: left, property: prop };\n } else if (this.peek().value === '.') {\n this.advance();\n const prop = this.advance().value;\n left = { type: 'member', object: left, property: prop };\n } else if (this.peek().type === 'selector' && this.peek().value.startsWith('.')) {\n const prop = this.advance().value.slice(1);\n left = { type: 'member', object: left, property: prop };\n } else if (this.peek().value === '(') {\n this.advance();\n const args = [];\n while (!this.match(')')) {\n args.push(this.parseExpression());\n if (this.match(',')) this.advance();\n }\n this.expect(')');\n left = { type: 'call', callee: left, args };\n } else if (this.peek().value === '[' && left.type !== 'selector') {\n this.advance();\n const index = this.parseExpression();\n this.expect(']');\n left = { type: 'member', object: left, property: index, computed: true };\n } else {\n break;\n }\n }\n return left;\n }\n\n parsePrimary() {\n const token = this.peek();\n\n if (token.value === '(') {\n this.advance();\n const expr = this.parseExpression();\n this.expect(')');\n return expr;\n }\n\n if (token.value === '{') return this.parseObjectLiteral();\n if (token.value === '[') return this.parseArrayLiteral();\n\n if (token.type === 'number') {\n this.advance();\n const val = token.value;\n if (val.endsWith('ms')) return { type: 'literal', value: parseInt(val), unit: 'ms' };\n if (val.endsWith('s')) return { type: 'literal', value: parseFloat(val) * 1000, unit: 'ms' };\n return { type: 'literal', value: parseFloat(val) };\n }\n\n if (token.type === 'string') {\n this.advance();\n return { type: 'literal', value: token.value.slice(1, -1) };\n }\n\n if (this.match('true')) { this.advance(); return { type: 'literal', value: true }; }\n if (this.match('false')) { this.advance(); return { type: 'literal', value: false }; }\n if (this.match('null')) { this.advance(); return { type: 'literal', value: null }; }\n if (this.match('undefined')) { this.advance(); return { type: 'literal', value: undefined }; }\n\n if (token.type === 'localVar') {\n this.advance();\n return { type: 'variable', name: token.value, scope: 'local' };\n }\n if (token.type === 'globalVar') {\n this.advance();\n return { type: 'variable', name: token.value, scope: 'global' };\n }\n if (token.type === 'selector') {\n this.advance();\n return { type: 'selector', value: token.value };\n }\n\n if (this.match('my')) {\n this.advance();\n const next = this.peek();\n if ((next.type === 'identifier' || next.type === 'keyword') && !this.isCommandKeyword(next)) {\n const prop = this.advance().value;\n return { type: 'possessive', object: { type: 'identifier', value: 'me' }, property: prop };\n }\n return { type: 'identifier', value: 'me' };\n }\n if (this.match('its')) {\n this.advance();\n const next = this.peek();\n if ((next.type === 'identifier' || next.type === 'keyword') && !this.isCommandKeyword(next)) {\n const prop = this.advance().value;\n return { type: 'possessive', object: { type: 'identifier', value: 'it' }, property: prop };\n }\n return { type: 'identifier', value: 'it' };\n }\n if (this.match('me')) { this.advance(); return { type: 'identifier', value: 'me' }; }\n if (this.match('it')) { this.advance(); return { type: 'identifier', value: 'it' }; }\n if (this.match('you')) { this.advance(); return { type: 'identifier', value: 'you' }; }\n\n if (this.match('the', 'a', 'an')) {\n this.advance();\n if (this.match('first', 'last', 'next', 'previous', 'closest', 'parent')) {\n const position = this.advance().value;\n const target = this.parsePositionalTarget();\n return { type: 'positional', position, target };\n }\n return this.parsePrimary();\n }\n\n if (this.match('first', 'last', 'next', 'previous', 'closest', 'parent')) {\n const position = this.advance().value;\n const target = this.parsePositionalTarget();\n return { type: 'positional', position, target };\n }\n\n if (token.type === 'identifier' || token.type === 'keyword') {\n this.advance();\n return { type: 'identifier', value: token.value };\n }\n\n this.advance();\n return { type: 'identifier', value: token.value };\n }\n\n parseObjectLiteral() {\n this.expect('{');\n const properties = [];\n while (!this.match('}')) {\n const key = this.advance().value;\n this.expect(':');\n const value = this.parseExpression();\n properties.push({ key, value });\n if (this.match(',')) this.advance();\n }\n this.expect('}');\n return { type: 'object', properties };\n }\n\n parseArrayLiteral() {\n this.expect('[');\n const elements = [];\n while (!this.match(']')) {\n elements.push(this.parseExpression());\n if (this.match(',')) this.advance();\n }\n this.expect(']');\n return { type: 'array', elements };\n }\n\n parsePositionalTarget() {\n const token = this.peek();\n if (token.type === 'selector') {\n return { type: 'selector', value: this.advance().value };\n }\n if (token.type === 'identifier' || token.type === 'keyword') {\n return { type: 'identifier', value: this.advance().value };\n }\n return this.parseExpression();\n }\n}\n";
|
|
3
3
|
export declare function getParserTemplate(type: 'lite' | 'hybrid'): string;
|
|
4
4
|
export declare const LITE_PARSER_COMMANDS: readonly ["toggle", "add", "remove", "put", "set", "log", "send", "wait", "show", "hide"];
|
|
5
5
|
export declare function canUseLiteParser(commands: string[], blocks: string[], positional: boolean): boolean;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{D as n,d as i}from"./browser-modular-
|
|
2
|
-
//# sourceMappingURL=bridge-
|
|
1
|
+
import{D as n,d as i}from"./browser-modular-6HT1r2AH.js";import"./feature-sockets-CcTxD5sT.js";function e(n){const i=n.diagnostics;return i?.length?i.filter(n=>"unconsumed-input"===n.code).map(n=>n.message):[]}let t=null;async function a(){return t||(t=await import("./browser-modular-6HT1r2AH.js").then(function(n){return n.n})),t}class c{constructor(i={}){this._initialized=!1,this.config={confidenceThreshold:i.confidenceThreshold??n,fallbackOnLowConfidence:i.fallbackOnLowConfidence??!0}}async initialize(){await a(),this._initialized=!0}isInitialized(){return this._initialized}async transform(n,i,e){if(this.isInitialized()||await this.initialize(),i===e)return{output:n,usedSemantic:!1,confidence:1,sourceLang:i,targetLang:e};const t=await a();try{const a=t.translate(n,i,e);if(a!==n)return{output:a,usedSemantic:!0,confidence:.9,sourceLang:i,targetLang:e}}catch{}return{output:n,usedSemantic:!1,confidence:0,sourceLang:i,targetLang:e}}async parse(n,i){this.isInitialized()||await this.initialize();return(await a()).parseSemantic(n,i).node??null}async render(n,i){return(await a()).render(n,i)}async parseToAST(n,i){this.isInitialized()||await this.initialize();const e=await a(),t=e.parseSemantic(n,i);if(t.confidence>=this.config.confidenceThreshold&&t.node)try{return e.buildAST(t.node).ast}catch(n){}return null}async parseToASTWithDetails(n,i){this.isInitialized()||await this.initialize();const t=await a(),c=t.parseSemantic(n,i);if(c.confidence>=this.config.confidenceThreshold&&c.node)try{const n=t.buildAST(c.node);return{ast:n.ast,usedDirectPath:!0,confidence:c.confidence,lang:i,fallbackText:null,warnings:[...n.warnings??[],...e(c.node)]}}catch{}if(c.node&&this.config.fallbackOnLowConfidence){const n=t.render(c.node,"en");return{ast:null,usedDirectPath:!1,confidence:c.confidence,lang:i,fallbackText:n}}return{ast:null,usedDirectPath:!1,confidence:c.confidence,lang:i,fallbackText:null}}async getAllTranslations(n,i){const e=(await a()).getSupportedLanguages(),t={};for(const a of e)t[a]=await this.transform(n,i,a);return t}}export{c as SemanticGrammarBridge};
|
|
2
|
+
//# sourceMappingURL=bridge-DwtNLKK3.js.map
|