more grammar and some interpreter
This commit is contained in:
parent
c40a6ef937
commit
40bf37dd77
13 changed files with 1765 additions and 734 deletions
|
@ -1,79 +0,0 @@
|
||||||
YourLang {
|
|
||||||
// Entry point
|
|
||||||
Program = Statement*
|
|
||||||
|
|
||||||
// Statements
|
|
||||||
Statement = LiveDecl
|
|
||||||
| ComputedDecl
|
|
||||||
| TrackStmt
|
|
||||||
| VarDecl
|
|
||||||
| ExprStatement
|
|
||||||
| Block
|
|
||||||
|
|
||||||
// Reactive declarations
|
|
||||||
LiveDecl = "live" identifier "=" Expr
|
|
||||||
|
|
||||||
ComputedDecl = "computed" identifier Block
|
|
||||||
|
|
||||||
TrackStmt = "track" ListOf<identifier, ","> LambdaParams? Block
|
|
||||||
|
|
||||||
// Regular variable declarations
|
|
||||||
VarDecl = VarType identifier "=" Expr
|
|
||||||
VarType = "const" | "var" | "let"
|
|
||||||
|
|
||||||
// Expression statement (for function calls, etc)
|
|
||||||
ExprStatement = Expr
|
|
||||||
|
|
||||||
// Blocks and lambdas
|
|
||||||
Block = "{" Statement* "}"
|
|
||||||
|
|
||||||
LambdaParams = "|" ListOf<identifier, ","> "|"
|
|
||||||
|
|
||||||
// Expressions (precedence from loose to tight)
|
|
||||||
Expr = AssignExpr
|
|
||||||
|
|
||||||
AssignExpr = CompareExpr ("=" CompareExpr)?
|
|
||||||
|
|
||||||
CompareExpr = AddExpr (CompareOp AddExpr)?
|
|
||||||
CompareOp = "==" | "!=" | "<=" | ">=" | "<" | ">"
|
|
||||||
|
|
||||||
AddExpr = MulExpr (("+"|"-") MulExpr)*
|
|
||||||
|
|
||||||
MulExpr = UnaryExpr (("*"|"/"|"%") UnaryExpr)*
|
|
||||||
|
|
||||||
UnaryExpr = "!" UnaryExpr
|
|
||||||
| "-" UnaryExpr
|
|
||||||
| CallExpr
|
|
||||||
|
|
||||||
CallExpr = MemberExpr ("(" ListOf<Expr, ","> ")")?
|
|
||||||
|
|
||||||
MemberExpr = PrimaryExpr ("." identifier)*
|
|
||||||
|
|
||||||
PrimaryExpr = number
|
|
||||||
| string
|
|
||||||
| boolean
|
|
||||||
| identifier
|
|
||||||
| "(" Expr ")"
|
|
||||||
| Block // Block expressions
|
|
||||||
|
|
||||||
// Literals
|
|
||||||
number = digit+ ("." digit+)?
|
|
||||||
|
|
||||||
string = "\"" (~"\"" any)* "\""
|
|
||||||
| "'" (~"'" any)* "'"
|
|
||||||
| "`" templateChar* "`"
|
|
||||||
|
|
||||||
templateChar = ~"`" any
|
|
||||||
|
|
||||||
boolean = "true" | "false"
|
|
||||||
|
|
||||||
// Identifiers and keywords
|
|
||||||
identifier = ~keyword letter (letter | digit | "_")*
|
|
||||||
|
|
||||||
keyword = "live" | "computed" | "track" | "const" | "var" | "let"
|
|
||||||
| "true" | "false" | "if" | "else" | "for" | "while"
|
|
||||||
|
|
||||||
// Lexical rules
|
|
||||||
space += "//" (~"\n" any)* // Single-line comments
|
|
||||||
| "/*" (~"*/" any)* "*/" // Multi-line comments
|
|
||||||
}
|
|
|
@ -17,10 +17,11 @@
|
||||||
"url": "https://git.koehr.ing/n/ohm-grammar-solace/issues"
|
"url": "https://git.koehr.ing/n/ohm-grammar-solace/issues"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "vitest"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.5.2",
|
"packageManager": "pnpm@10.5.2",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"ohm-js": "^17.1.0"
|
"ohm-js": "^17.1.0",
|
||||||
|
"vitest": "^3.2.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
917
pnpm-lock.yaml
generated
917
pnpm-lock.yaml
generated
|
@ -11,13 +11,930 @@ importers:
|
||||||
ohm-js:
|
ohm-js:
|
||||||
specifier: ^17.1.0
|
specifier: ^17.1.0
|
||||||
version: 17.1.0
|
version: 17.1.0
|
||||||
|
vitest:
|
||||||
|
specifier: ^3.2.3
|
||||||
|
version: 3.2.3
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
|
'@esbuild/aix-ppc64@0.25.5':
|
||||||
|
resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [aix]
|
||||||
|
|
||||||
|
'@esbuild/android-arm64@0.25.5':
|
||||||
|
resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@esbuild/android-arm@0.25.5':
|
||||||
|
resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@esbuild/android-x64@0.25.5':
|
||||||
|
resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@esbuild/darwin-arm64@0.25.5':
|
||||||
|
resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@esbuild/darwin-x64@0.25.5':
|
||||||
|
resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@esbuild/freebsd-arm64@0.25.5':
|
||||||
|
resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [freebsd]
|
||||||
|
|
||||||
|
'@esbuild/freebsd-x64@0.25.5':
|
||||||
|
resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [freebsd]
|
||||||
|
|
||||||
|
'@esbuild/linux-arm64@0.25.5':
|
||||||
|
resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-arm@0.25.5':
|
||||||
|
resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-ia32@0.25.5':
|
||||||
|
resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-loong64@0.25.5':
|
||||||
|
resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [loong64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-mips64el@0.25.5':
|
||||||
|
resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [mips64el]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-ppc64@0.25.5':
|
||||||
|
resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-riscv64@0.25.5':
|
||||||
|
resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [riscv64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-s390x@0.25.5':
|
||||||
|
resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [s390x]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/linux-x64@0.25.5':
|
||||||
|
resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@esbuild/netbsd-arm64@0.25.5':
|
||||||
|
resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [netbsd]
|
||||||
|
|
||||||
|
'@esbuild/netbsd-x64@0.25.5':
|
||||||
|
resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [netbsd]
|
||||||
|
|
||||||
|
'@esbuild/openbsd-arm64@0.25.5':
|
||||||
|
resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [openbsd]
|
||||||
|
|
||||||
|
'@esbuild/openbsd-x64@0.25.5':
|
||||||
|
resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [openbsd]
|
||||||
|
|
||||||
|
'@esbuild/sunos-x64@0.25.5':
|
||||||
|
resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [sunos]
|
||||||
|
|
||||||
|
'@esbuild/win32-arm64@0.25.5':
|
||||||
|
resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@esbuild/win32-ia32@0.25.5':
|
||||||
|
resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@esbuild/win32-x64@0.25.5':
|
||||||
|
resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@jridgewell/sourcemap-codec@1.5.0':
|
||||||
|
resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==}
|
||||||
|
|
||||||
|
'@rollup/rollup-android-arm-eabi@4.42.0':
|
||||||
|
resolution: {integrity: sha512-gldmAyS9hpj+H6LpRNlcjQWbuKUtb94lodB9uCz71Jm+7BxK1VIOo7y62tZZwxhA7j1ylv/yQz080L5WkS+LoQ==}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@rollup/rollup-android-arm64@4.42.0':
|
||||||
|
resolution: {integrity: sha512-bpRipfTgmGFdCZDFLRvIkSNO1/3RGS74aWkJJTFJBH7h3MRV4UijkaEUeOMbi9wxtxYmtAbVcnMtHTPBhLEkaw==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@rollup/rollup-darwin-arm64@4.42.0':
|
||||||
|
resolution: {integrity: sha512-JxHtA081izPBVCHLKnl6GEA0w3920mlJPLh89NojpU2GsBSB6ypu4erFg/Wx1qbpUbepn0jY4dVWMGZM8gplgA==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@rollup/rollup-darwin-x64@4.42.0':
|
||||||
|
resolution: {integrity: sha512-rv5UZaWVIJTDMyQ3dCEK+m0SAn6G7H3PRc2AZmExvbDvtaDc+qXkei0knQWcI3+c9tEs7iL/4I4pTQoPbNL2SA==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@rollup/rollup-freebsd-arm64@4.42.0':
|
||||||
|
resolution: {integrity: sha512-fJcN4uSGPWdpVmvLuMtALUFwCHgb2XiQjuECkHT3lWLZhSQ3MBQ9pq+WoWeJq2PrNxr9rPM1Qx+IjyGj8/c6zQ==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [freebsd]
|
||||||
|
|
||||||
|
'@rollup/rollup-freebsd-x64@4.42.0':
|
||||||
|
resolution: {integrity: sha512-CziHfyzpp8hJpCVE/ZdTizw58gr+m7Y2Xq5VOuCSrZR++th2xWAz4Nqk52MoIIrV3JHtVBhbBsJcAxs6NammOQ==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [freebsd]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm-gnueabihf@4.42.0':
|
||||||
|
resolution: {integrity: sha512-UsQD5fyLWm2Fe5CDM7VPYAo+UC7+2Px4Y+N3AcPh/LdZu23YcuGPegQly++XEVaC8XUTFVPscl5y5Cl1twEI4A==}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm-musleabihf@4.42.0':
|
||||||
|
resolution: {integrity: sha512-/i8NIrlgc/+4n1lnoWl1zgH7Uo0XK5xK3EDqVTf38KvyYgCU/Rm04+o1VvvzJZnVS5/cWSd07owkzcVasgfIkQ==}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm64-gnu@4.42.0':
|
||||||
|
resolution: {integrity: sha512-eoujJFOvoIBjZEi9hJnXAbWg+Vo1Ov8n/0IKZZcPZ7JhBzxh2A+2NFyeMZIRkY9iwBvSjloKgcvnjTbGKHE44Q==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm64-musl@4.42.0':
|
||||||
|
resolution: {integrity: sha512-/3NrcOWFSR7RQUQIuZQChLND36aTU9IYE4j+TB40VU78S+RA0IiqHR30oSh6P1S9f9/wVOenHQnacs/Byb824g==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-loongarch64-gnu@4.42.0':
|
||||||
|
resolution: {integrity: sha512-O8AplvIeavK5ABmZlKBq9/STdZlnQo7Sle0LLhVA7QT+CiGpNVe197/t8Aph9bhJqbDVGCHpY2i7QyfEDDStDg==}
|
||||||
|
cpu: [loong64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-powerpc64le-gnu@4.42.0':
|
||||||
|
resolution: {integrity: sha512-6Qb66tbKVN7VyQrekhEzbHRxXXFFD8QKiFAwX5v9Xt6FiJ3BnCVBuyBxa2fkFGqxOCSGGYNejxd8ht+q5SnmtA==}
|
||||||
|
cpu: [ppc64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-riscv64-gnu@4.42.0':
|
||||||
|
resolution: {integrity: sha512-KQETDSEBamQFvg/d8jajtRwLNBlGc3aKpaGiP/LvEbnmVUKlFta1vqJqTrvPtsYsfbE/DLg5CC9zyXRX3fnBiA==}
|
||||||
|
cpu: [riscv64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-riscv64-musl@4.42.0':
|
||||||
|
resolution: {integrity: sha512-qMvnyjcU37sCo/tuC+JqeDKSuukGAd+pVlRl/oyDbkvPJ3awk6G6ua7tyum02O3lI+fio+eM5wsVd66X0jQtxw==}
|
||||||
|
cpu: [riscv64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-s390x-gnu@4.42.0':
|
||||||
|
resolution: {integrity: sha512-I2Y1ZUgTgU2RLddUHXTIgyrdOwljjkmcZ/VilvaEumtS3Fkuhbw4p4hgHc39Ypwvo2o7sBFNl2MquNvGCa55Iw==}
|
||||||
|
cpu: [s390x]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-x64-gnu@4.42.0':
|
||||||
|
resolution: {integrity: sha512-Gfm6cV6mj3hCUY8TqWa63DB8Mx3NADoFwiJrMpoZ1uESbK8FQV3LXkhfry+8bOniq9pqY1OdsjFWNsSbfjPugw==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-x64-musl@4.42.0':
|
||||||
|
resolution: {integrity: sha512-g86PF8YZ9GRqkdi0VoGlcDUb4rYtQKyTD1IVtxxN4Hpe7YqLBShA7oHMKU6oKTCi3uxwW4VkIGnOaH/El8de3w==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@rollup/rollup-win32-arm64-msvc@4.42.0':
|
||||||
|
resolution: {integrity: sha512-+axkdyDGSp6hjyzQ5m1pgcvQScfHnMCcsXkx8pTgy/6qBmWVhtRVlgxjWwDp67wEXXUr0x+vD6tp5W4x6V7u1A==}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@rollup/rollup-win32-ia32-msvc@4.42.0':
|
||||||
|
resolution: {integrity: sha512-F+5J9pelstXKwRSDq92J0TEBXn2nfUrQGg+HK1+Tk7VOL09e0gBqUHugZv7SW4MGrYj41oNCUe3IKCDGVlis2g==}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@rollup/rollup-win32-x64-msvc@4.42.0':
|
||||||
|
resolution: {integrity: sha512-LpHiJRwkaVz/LqjHjK8LCi8osq7elmpwujwbXKNW88bM8eeGxavJIKKjkjpMHAh/2xfnrt1ZSnhTv41WYUHYmA==}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@types/chai@5.2.2':
|
||||||
|
resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==}
|
||||||
|
|
||||||
|
'@types/deep-eql@4.0.2':
|
||||||
|
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
|
||||||
|
|
||||||
|
'@types/estree@1.0.7':
|
||||||
|
resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
|
||||||
|
|
||||||
|
'@types/estree@1.0.8':
|
||||||
|
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
|
||||||
|
|
||||||
|
'@vitest/expect@3.2.3':
|
||||||
|
resolution: {integrity: sha512-W2RH2TPWVHA1o7UmaFKISPvdicFJH+mjykctJFoAkUw+SPTJTGjUNdKscFBrqM7IPnCVu6zihtKYa7TkZS1dkQ==}
|
||||||
|
|
||||||
|
'@vitest/mocker@3.2.3':
|
||||||
|
resolution: {integrity: sha512-cP6fIun+Zx8he4rbWvi+Oya6goKQDZK+Yq4hhlggwQBbrlOQ4qtZ+G4nxB6ZnzI9lyIb+JnvyiJnPC2AGbKSPA==}
|
||||||
|
peerDependencies:
|
||||||
|
msw: ^2.4.9
|
||||||
|
vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
msw:
|
||||||
|
optional: true
|
||||||
|
vite:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@vitest/pretty-format@3.2.3':
|
||||||
|
resolution: {integrity: sha512-yFglXGkr9hW/yEXngO+IKMhP0jxyFw2/qys/CK4fFUZnSltD+MU7dVYGrH8rvPcK/O6feXQA+EU33gjaBBbAng==}
|
||||||
|
|
||||||
|
'@vitest/runner@3.2.3':
|
||||||
|
resolution: {integrity: sha512-83HWYisT3IpMaU9LN+VN+/nLHVBCSIUKJzGxC5RWUOsK1h3USg7ojL+UXQR3b4o4UBIWCYdD2fxuzM7PQQ1u8w==}
|
||||||
|
|
||||||
|
'@vitest/snapshot@3.2.3':
|
||||||
|
resolution: {integrity: sha512-9gIVWx2+tysDqUmmM1L0hwadyumqssOL1r8KJipwLx5JVYyxvVRfxvMq7DaWbZZsCqZnu/dZedaZQh4iYTtneA==}
|
||||||
|
|
||||||
|
'@vitest/spy@3.2.3':
|
||||||
|
resolution: {integrity: sha512-JHu9Wl+7bf6FEejTCREy+DmgWe+rQKbK+y32C/k5f4TBIAlijhJbRBIRIOCEpVevgRsCQR2iHRUH2/qKVM/plw==}
|
||||||
|
|
||||||
|
'@vitest/utils@3.2.3':
|
||||||
|
resolution: {integrity: sha512-4zFBCU5Pf+4Z6v+rwnZ1HU1yzOKKvDkMXZrymE2PBlbjKJRlrOxbvpfPSvJTGRIwGoahaOGvp+kbCoxifhzJ1Q==}
|
||||||
|
|
||||||
|
assertion-error@2.0.1:
|
||||||
|
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
cac@6.7.14:
|
||||||
|
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
chai@5.2.0:
|
||||||
|
resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
check-error@2.1.1:
|
||||||
|
resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==}
|
||||||
|
engines: {node: '>= 16'}
|
||||||
|
|
||||||
|
debug@4.4.1:
|
||||||
|
resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
|
||||||
|
engines: {node: '>=6.0'}
|
||||||
|
peerDependencies:
|
||||||
|
supports-color: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
supports-color:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
deep-eql@5.0.2:
|
||||||
|
resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
es-module-lexer@1.7.0:
|
||||||
|
resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==}
|
||||||
|
|
||||||
|
esbuild@0.25.5:
|
||||||
|
resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
estree-walker@3.0.3:
|
||||||
|
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
|
||||||
|
|
||||||
|
expect-type@1.2.1:
|
||||||
|
resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==}
|
||||||
|
engines: {node: '>=12.0.0'}
|
||||||
|
|
||||||
|
fdir@6.4.6:
|
||||||
|
resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==}
|
||||||
|
peerDependencies:
|
||||||
|
picomatch: ^3 || ^4
|
||||||
|
peerDependenciesMeta:
|
||||||
|
picomatch:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
fsevents@2.3.3:
|
||||||
|
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||||
|
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
js-tokens@9.0.1:
|
||||||
|
resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
|
||||||
|
|
||||||
|
loupe@3.1.3:
|
||||||
|
resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==}
|
||||||
|
|
||||||
|
magic-string@0.30.17:
|
||||||
|
resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
|
||||||
|
|
||||||
|
ms@2.1.3:
|
||||||
|
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||||
|
|
||||||
|
nanoid@3.3.11:
|
||||||
|
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
|
||||||
|
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
ohm-js@17.1.0:
|
ohm-js@17.1.0:
|
||||||
resolution: {integrity: sha512-xc3B5dgAjTBQGHaH7B58M2Pmv6WvzrJ/3/7LeUzXNg0/sY3jQPdSd/S2SstppaleO77rifR1tyhdfFGNIwxf2Q==}
|
resolution: {integrity: sha512-xc3B5dgAjTBQGHaH7B58M2Pmv6WvzrJ/3/7LeUzXNg0/sY3jQPdSd/S2SstppaleO77rifR1tyhdfFGNIwxf2Q==}
|
||||||
engines: {node: '>=0.12.1'}
|
engines: {node: '>=0.12.1'}
|
||||||
|
|
||||||
|
pathe@2.0.3:
|
||||||
|
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
||||||
|
|
||||||
|
pathval@2.0.0:
|
||||||
|
resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==}
|
||||||
|
engines: {node: '>= 14.16'}
|
||||||
|
|
||||||
|
picocolors@1.1.1:
|
||||||
|
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||||
|
|
||||||
|
picomatch@4.0.2:
|
||||||
|
resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
postcss@8.5.4:
|
||||||
|
resolution: {integrity: sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==}
|
||||||
|
engines: {node: ^10 || ^12 || >=14}
|
||||||
|
|
||||||
|
rollup@4.42.0:
|
||||||
|
resolution: {integrity: sha512-LW+Vse3BJPyGJGAJt1j8pWDKPd73QM8cRXYK1IxOBgL2AGLu7Xd2YOW0M2sLUBCkF5MshXXtMApyEAEzMVMsnw==}
|
||||||
|
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
siginfo@2.0.0:
|
||||||
|
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
|
||||||
|
|
||||||
|
source-map-js@1.2.1:
|
||||||
|
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
stackback@0.0.2:
|
||||||
|
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
|
||||||
|
|
||||||
|
std-env@3.9.0:
|
||||||
|
resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==}
|
||||||
|
|
||||||
|
strip-literal@3.0.0:
|
||||||
|
resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==}
|
||||||
|
|
||||||
|
tinybench@2.9.0:
|
||||||
|
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
|
||||||
|
|
||||||
|
tinyexec@0.3.2:
|
||||||
|
resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==}
|
||||||
|
|
||||||
|
tinyglobby@0.2.14:
|
||||||
|
resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==}
|
||||||
|
engines: {node: '>=12.0.0'}
|
||||||
|
|
||||||
|
tinypool@1.1.0:
|
||||||
|
resolution: {integrity: sha512-7CotroY9a8DKsKprEy/a14aCCm8jYVmR7aFy4fpkZM8sdpNJbKkixuNjgM50yCmip2ezc8z4N7k3oe2+rfRJCQ==}
|
||||||
|
engines: {node: ^18.0.0 || >=20.0.0}
|
||||||
|
|
||||||
|
tinyrainbow@2.0.0:
|
||||||
|
resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
||||||
|
tinyspy@4.0.3:
|
||||||
|
resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
||||||
|
vite-node@3.2.3:
|
||||||
|
resolution: {integrity: sha512-gc8aAifGuDIpZHrPjuHyP4dpQmYXqWw7D1GmDnWeNWP654UEXzVfQ5IHPSK5HaHkwB/+p1atpYpSdw/2kOv8iQ==}
|
||||||
|
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
vite@6.3.5:
|
||||||
|
resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==}
|
||||||
|
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
|
||||||
|
jiti: '>=1.21.0'
|
||||||
|
less: '*'
|
||||||
|
lightningcss: ^1.21.0
|
||||||
|
sass: '*'
|
||||||
|
sass-embedded: '*'
|
||||||
|
stylus: '*'
|
||||||
|
sugarss: '*'
|
||||||
|
terser: ^5.16.0
|
||||||
|
tsx: ^4.8.1
|
||||||
|
yaml: ^2.4.2
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
jiti:
|
||||||
|
optional: true
|
||||||
|
less:
|
||||||
|
optional: true
|
||||||
|
lightningcss:
|
||||||
|
optional: true
|
||||||
|
sass:
|
||||||
|
optional: true
|
||||||
|
sass-embedded:
|
||||||
|
optional: true
|
||||||
|
stylus:
|
||||||
|
optional: true
|
||||||
|
sugarss:
|
||||||
|
optional: true
|
||||||
|
terser:
|
||||||
|
optional: true
|
||||||
|
tsx:
|
||||||
|
optional: true
|
||||||
|
yaml:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
vitest@3.2.3:
|
||||||
|
resolution: {integrity: sha512-E6U2ZFXe3N/t4f5BwUaVCKRLHqUpk1CBWeMh78UT4VaTPH/2dyvH6ALl29JTovEPu9dVKr/K/J4PkXgrMbw4Ww==}
|
||||||
|
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
'@edge-runtime/vm': '*'
|
||||||
|
'@types/debug': ^4.1.12
|
||||||
|
'@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
|
||||||
|
'@vitest/browser': 3.2.3
|
||||||
|
'@vitest/ui': 3.2.3
|
||||||
|
happy-dom: '*'
|
||||||
|
jsdom: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@edge-runtime/vm':
|
||||||
|
optional: true
|
||||||
|
'@types/debug':
|
||||||
|
optional: true
|
||||||
|
'@types/node':
|
||||||
|
optional: true
|
||||||
|
'@vitest/browser':
|
||||||
|
optional: true
|
||||||
|
'@vitest/ui':
|
||||||
|
optional: true
|
||||||
|
happy-dom:
|
||||||
|
optional: true
|
||||||
|
jsdom:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
why-is-node-running@2.3.0:
|
||||||
|
resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
|
|
||||||
|
'@esbuild/aix-ppc64@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/android-arm64@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/android-arm@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/android-x64@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/darwin-arm64@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/darwin-x64@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/freebsd-arm64@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/freebsd-x64@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-arm64@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-arm@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-ia32@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-loong64@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-mips64el@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-ppc64@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-riscv64@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-s390x@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/linux-x64@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/netbsd-arm64@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/netbsd-x64@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/openbsd-arm64@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/openbsd-x64@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/sunos-x64@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/win32-arm64@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/win32-ia32@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@esbuild/win32-x64@0.25.5':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@jridgewell/sourcemap-codec@1.5.0': {}
|
||||||
|
|
||||||
|
'@rollup/rollup-android-arm-eabi@4.42.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-android-arm64@4.42.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-darwin-arm64@4.42.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-darwin-x64@4.42.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-freebsd-arm64@4.42.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-freebsd-x64@4.42.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm-gnueabihf@4.42.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm-musleabihf@4.42.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm64-gnu@4.42.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-arm64-musl@4.42.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-loongarch64-gnu@4.42.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-powerpc64le-gnu@4.42.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-riscv64-gnu@4.42.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-riscv64-musl@4.42.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-s390x-gnu@4.42.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-x64-gnu@4.42.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-linux-x64-musl@4.42.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-win32-arm64-msvc@4.42.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-win32-ia32-msvc@4.42.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@rollup/rollup-win32-x64-msvc@4.42.0':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@types/chai@5.2.2':
|
||||||
|
dependencies:
|
||||||
|
'@types/deep-eql': 4.0.2
|
||||||
|
|
||||||
|
'@types/deep-eql@4.0.2': {}
|
||||||
|
|
||||||
|
'@types/estree@1.0.7': {}
|
||||||
|
|
||||||
|
'@types/estree@1.0.8': {}
|
||||||
|
|
||||||
|
'@vitest/expect@3.2.3':
|
||||||
|
dependencies:
|
||||||
|
'@types/chai': 5.2.2
|
||||||
|
'@vitest/spy': 3.2.3
|
||||||
|
'@vitest/utils': 3.2.3
|
||||||
|
chai: 5.2.0
|
||||||
|
tinyrainbow: 2.0.0
|
||||||
|
|
||||||
|
'@vitest/mocker@3.2.3(vite@6.3.5)':
|
||||||
|
dependencies:
|
||||||
|
'@vitest/spy': 3.2.3
|
||||||
|
estree-walker: 3.0.3
|
||||||
|
magic-string: 0.30.17
|
||||||
|
optionalDependencies:
|
||||||
|
vite: 6.3.5
|
||||||
|
|
||||||
|
'@vitest/pretty-format@3.2.3':
|
||||||
|
dependencies:
|
||||||
|
tinyrainbow: 2.0.0
|
||||||
|
|
||||||
|
'@vitest/runner@3.2.3':
|
||||||
|
dependencies:
|
||||||
|
'@vitest/utils': 3.2.3
|
||||||
|
pathe: 2.0.3
|
||||||
|
strip-literal: 3.0.0
|
||||||
|
|
||||||
|
'@vitest/snapshot@3.2.3':
|
||||||
|
dependencies:
|
||||||
|
'@vitest/pretty-format': 3.2.3
|
||||||
|
magic-string: 0.30.17
|
||||||
|
pathe: 2.0.3
|
||||||
|
|
||||||
|
'@vitest/spy@3.2.3':
|
||||||
|
dependencies:
|
||||||
|
tinyspy: 4.0.3
|
||||||
|
|
||||||
|
'@vitest/utils@3.2.3':
|
||||||
|
dependencies:
|
||||||
|
'@vitest/pretty-format': 3.2.3
|
||||||
|
loupe: 3.1.3
|
||||||
|
tinyrainbow: 2.0.0
|
||||||
|
|
||||||
|
assertion-error@2.0.1: {}
|
||||||
|
|
||||||
|
cac@6.7.14: {}
|
||||||
|
|
||||||
|
chai@5.2.0:
|
||||||
|
dependencies:
|
||||||
|
assertion-error: 2.0.1
|
||||||
|
check-error: 2.1.1
|
||||||
|
deep-eql: 5.0.2
|
||||||
|
loupe: 3.1.3
|
||||||
|
pathval: 2.0.0
|
||||||
|
|
||||||
|
check-error@2.1.1: {}
|
||||||
|
|
||||||
|
debug@4.4.1:
|
||||||
|
dependencies:
|
||||||
|
ms: 2.1.3
|
||||||
|
|
||||||
|
deep-eql@5.0.2: {}
|
||||||
|
|
||||||
|
es-module-lexer@1.7.0: {}
|
||||||
|
|
||||||
|
esbuild@0.25.5:
|
||||||
|
optionalDependencies:
|
||||||
|
'@esbuild/aix-ppc64': 0.25.5
|
||||||
|
'@esbuild/android-arm': 0.25.5
|
||||||
|
'@esbuild/android-arm64': 0.25.5
|
||||||
|
'@esbuild/android-x64': 0.25.5
|
||||||
|
'@esbuild/darwin-arm64': 0.25.5
|
||||||
|
'@esbuild/darwin-x64': 0.25.5
|
||||||
|
'@esbuild/freebsd-arm64': 0.25.5
|
||||||
|
'@esbuild/freebsd-x64': 0.25.5
|
||||||
|
'@esbuild/linux-arm': 0.25.5
|
||||||
|
'@esbuild/linux-arm64': 0.25.5
|
||||||
|
'@esbuild/linux-ia32': 0.25.5
|
||||||
|
'@esbuild/linux-loong64': 0.25.5
|
||||||
|
'@esbuild/linux-mips64el': 0.25.5
|
||||||
|
'@esbuild/linux-ppc64': 0.25.5
|
||||||
|
'@esbuild/linux-riscv64': 0.25.5
|
||||||
|
'@esbuild/linux-s390x': 0.25.5
|
||||||
|
'@esbuild/linux-x64': 0.25.5
|
||||||
|
'@esbuild/netbsd-arm64': 0.25.5
|
||||||
|
'@esbuild/netbsd-x64': 0.25.5
|
||||||
|
'@esbuild/openbsd-arm64': 0.25.5
|
||||||
|
'@esbuild/openbsd-x64': 0.25.5
|
||||||
|
'@esbuild/sunos-x64': 0.25.5
|
||||||
|
'@esbuild/win32-arm64': 0.25.5
|
||||||
|
'@esbuild/win32-ia32': 0.25.5
|
||||||
|
'@esbuild/win32-x64': 0.25.5
|
||||||
|
|
||||||
|
estree-walker@3.0.3:
|
||||||
|
dependencies:
|
||||||
|
'@types/estree': 1.0.8
|
||||||
|
|
||||||
|
expect-type@1.2.1: {}
|
||||||
|
|
||||||
|
fdir@6.4.6(picomatch@4.0.2):
|
||||||
|
optionalDependencies:
|
||||||
|
picomatch: 4.0.2
|
||||||
|
|
||||||
|
fsevents@2.3.3:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
js-tokens@9.0.1: {}
|
||||||
|
|
||||||
|
loupe@3.1.3: {}
|
||||||
|
|
||||||
|
magic-string@0.30.17:
|
||||||
|
dependencies:
|
||||||
|
'@jridgewell/sourcemap-codec': 1.5.0
|
||||||
|
|
||||||
|
ms@2.1.3: {}
|
||||||
|
|
||||||
|
nanoid@3.3.11: {}
|
||||||
|
|
||||||
ohm-js@17.1.0: {}
|
ohm-js@17.1.0: {}
|
||||||
|
|
||||||
|
pathe@2.0.3: {}
|
||||||
|
|
||||||
|
pathval@2.0.0: {}
|
||||||
|
|
||||||
|
picocolors@1.1.1: {}
|
||||||
|
|
||||||
|
picomatch@4.0.2: {}
|
||||||
|
|
||||||
|
postcss@8.5.4:
|
||||||
|
dependencies:
|
||||||
|
nanoid: 3.3.11
|
||||||
|
picocolors: 1.1.1
|
||||||
|
source-map-js: 1.2.1
|
||||||
|
|
||||||
|
rollup@4.42.0:
|
||||||
|
dependencies:
|
||||||
|
'@types/estree': 1.0.7
|
||||||
|
optionalDependencies:
|
||||||
|
'@rollup/rollup-android-arm-eabi': 4.42.0
|
||||||
|
'@rollup/rollup-android-arm64': 4.42.0
|
||||||
|
'@rollup/rollup-darwin-arm64': 4.42.0
|
||||||
|
'@rollup/rollup-darwin-x64': 4.42.0
|
||||||
|
'@rollup/rollup-freebsd-arm64': 4.42.0
|
||||||
|
'@rollup/rollup-freebsd-x64': 4.42.0
|
||||||
|
'@rollup/rollup-linux-arm-gnueabihf': 4.42.0
|
||||||
|
'@rollup/rollup-linux-arm-musleabihf': 4.42.0
|
||||||
|
'@rollup/rollup-linux-arm64-gnu': 4.42.0
|
||||||
|
'@rollup/rollup-linux-arm64-musl': 4.42.0
|
||||||
|
'@rollup/rollup-linux-loongarch64-gnu': 4.42.0
|
||||||
|
'@rollup/rollup-linux-powerpc64le-gnu': 4.42.0
|
||||||
|
'@rollup/rollup-linux-riscv64-gnu': 4.42.0
|
||||||
|
'@rollup/rollup-linux-riscv64-musl': 4.42.0
|
||||||
|
'@rollup/rollup-linux-s390x-gnu': 4.42.0
|
||||||
|
'@rollup/rollup-linux-x64-gnu': 4.42.0
|
||||||
|
'@rollup/rollup-linux-x64-musl': 4.42.0
|
||||||
|
'@rollup/rollup-win32-arm64-msvc': 4.42.0
|
||||||
|
'@rollup/rollup-win32-ia32-msvc': 4.42.0
|
||||||
|
'@rollup/rollup-win32-x64-msvc': 4.42.0
|
||||||
|
fsevents: 2.3.3
|
||||||
|
|
||||||
|
siginfo@2.0.0: {}
|
||||||
|
|
||||||
|
source-map-js@1.2.1: {}
|
||||||
|
|
||||||
|
stackback@0.0.2: {}
|
||||||
|
|
||||||
|
std-env@3.9.0: {}
|
||||||
|
|
||||||
|
strip-literal@3.0.0:
|
||||||
|
dependencies:
|
||||||
|
js-tokens: 9.0.1
|
||||||
|
|
||||||
|
tinybench@2.9.0: {}
|
||||||
|
|
||||||
|
tinyexec@0.3.2: {}
|
||||||
|
|
||||||
|
tinyglobby@0.2.14:
|
||||||
|
dependencies:
|
||||||
|
fdir: 6.4.6(picomatch@4.0.2)
|
||||||
|
picomatch: 4.0.2
|
||||||
|
|
||||||
|
tinypool@1.1.0: {}
|
||||||
|
|
||||||
|
tinyrainbow@2.0.0: {}
|
||||||
|
|
||||||
|
tinyspy@4.0.3: {}
|
||||||
|
|
||||||
|
vite-node@3.2.3:
|
||||||
|
dependencies:
|
||||||
|
cac: 6.7.14
|
||||||
|
debug: 4.4.1
|
||||||
|
es-module-lexer: 1.7.0
|
||||||
|
pathe: 2.0.3
|
||||||
|
vite: 6.3.5
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@types/node'
|
||||||
|
- jiti
|
||||||
|
- less
|
||||||
|
- lightningcss
|
||||||
|
- sass
|
||||||
|
- sass-embedded
|
||||||
|
- stylus
|
||||||
|
- sugarss
|
||||||
|
- supports-color
|
||||||
|
- terser
|
||||||
|
- tsx
|
||||||
|
- yaml
|
||||||
|
|
||||||
|
vite@6.3.5:
|
||||||
|
dependencies:
|
||||||
|
esbuild: 0.25.5
|
||||||
|
fdir: 6.4.6(picomatch@4.0.2)
|
||||||
|
picomatch: 4.0.2
|
||||||
|
postcss: 8.5.4
|
||||||
|
rollup: 4.42.0
|
||||||
|
tinyglobby: 0.2.14
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents: 2.3.3
|
||||||
|
|
||||||
|
vitest@3.2.3:
|
||||||
|
dependencies:
|
||||||
|
'@types/chai': 5.2.2
|
||||||
|
'@vitest/expect': 3.2.3
|
||||||
|
'@vitest/mocker': 3.2.3(vite@6.3.5)
|
||||||
|
'@vitest/pretty-format': 3.2.3
|
||||||
|
'@vitest/runner': 3.2.3
|
||||||
|
'@vitest/snapshot': 3.2.3
|
||||||
|
'@vitest/spy': 3.2.3
|
||||||
|
'@vitest/utils': 3.2.3
|
||||||
|
chai: 5.2.0
|
||||||
|
debug: 4.4.1
|
||||||
|
expect-type: 1.2.1
|
||||||
|
magic-string: 0.30.17
|
||||||
|
pathe: 2.0.3
|
||||||
|
picomatch: 4.0.2
|
||||||
|
std-env: 3.9.0
|
||||||
|
tinybench: 2.9.0
|
||||||
|
tinyexec: 0.3.2
|
||||||
|
tinyglobby: 0.2.14
|
||||||
|
tinypool: 1.1.0
|
||||||
|
tinyrainbow: 2.0.0
|
||||||
|
vite: 6.3.5
|
||||||
|
vite-node: 3.2.3
|
||||||
|
why-is-node-running: 2.3.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- jiti
|
||||||
|
- less
|
||||||
|
- lightningcss
|
||||||
|
- msw
|
||||||
|
- sass
|
||||||
|
- sass-embedded
|
||||||
|
- stylus
|
||||||
|
- sugarss
|
||||||
|
- supports-color
|
||||||
|
- terser
|
||||||
|
- tsx
|
||||||
|
- yaml
|
||||||
|
|
||||||
|
why-is-node-running@2.3.0:
|
||||||
|
dependencies:
|
||||||
|
siginfo: 2.0.0
|
||||||
|
stackback: 0.0.2
|
||||||
|
|
88
src/compile.mjs
Normal file
88
src/compile.mjs
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
/* eslint-env node */
|
||||||
|
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
// Imports
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
import { readFileSync } from 'node:fs'
|
||||||
|
import * as solace from './interpreter.mjs'
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
// Helpers
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
function removeShebang(source) {
|
||||||
|
return source.slice(0, 2) === '#!' ? source.replace('#!', '//') : source
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseArgs(args) {
|
||||||
|
const filenames = []
|
||||||
|
let benchmark = false
|
||||||
|
let verbose = false
|
||||||
|
|
||||||
|
for (let i = 0; i < args.length; ++i) {
|
||||||
|
const arg = args[i]
|
||||||
|
|
||||||
|
if (arg.startsWith('-')) { // flag, please?
|
||||||
|
if (arg === '-b' || arg === '--benchmark') benchmark = true
|
||||||
|
if (arg === '-v' || arg === '--verbose') verbose = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
filenames.push(arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return { filenames, benchmark, verbose }
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
// Main
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
/* eslint-disable no-console */
|
||||||
|
|
||||||
|
function compile(args) {
|
||||||
|
const { filenames, benchmark, verbose } = parseArgs(args)
|
||||||
|
|
||||||
|
const files = filenames.map(name => {
|
||||||
|
// If benchmarking, read in all the files at once, so that we can just time the matching.
|
||||||
|
return [name, benchmark ? removeShebang(readFileSync(name).toString()) : null]
|
||||||
|
})
|
||||||
|
|
||||||
|
const matchStartTime = Date.now()
|
||||||
|
|
||||||
|
// Parsing -- bails out when the first error is encountered.
|
||||||
|
const results = []
|
||||||
|
const success = files.every(arr => {
|
||||||
|
const source = arr[1] ?? removeShebang(readFileSync(arr[0]).toString())
|
||||||
|
if (verbose) { console.error(arr[0]) }
|
||||||
|
|
||||||
|
const result = solace.grammar.match(source, 'Program')
|
||||||
|
if (!result.succeeded()) {
|
||||||
|
console.error(arr[0] + ':\n' + result.message)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
results.push(result)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!success) return null
|
||||||
|
if (benchmark) console.error('Matching:', (Date.now() - matchStartTime) + 'ms')
|
||||||
|
|
||||||
|
// Codegen
|
||||||
|
const codegenStartTime = Date.now()
|
||||||
|
const code = results.map(r => solace.semantics(r).toES6()).join('\n\n')
|
||||||
|
if (benchmark) console.error('Codegen:', (Date.now() - codegenStartTime) + 'ms')
|
||||||
|
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run as program
|
||||||
|
if (import.meta && process.argv[1]) {
|
||||||
|
console.log(compile(process.argv.slice(2)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Imported as module
|
||||||
|
export default compile
|
169
src/grammar.ohm
Normal file
169
src/grammar.ohm
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
Solace {
|
||||||
|
Program = Statement+
|
||||||
|
|
||||||
|
sourceCharacter = any
|
||||||
|
|
||||||
|
space := whitespace | lineTerminator | comment
|
||||||
|
|
||||||
|
whitespace = "\t"
|
||||||
|
| "\x0B" -- verticalTab
|
||||||
|
| "\x0C" -- formFeed
|
||||||
|
| " "
|
||||||
|
| "\u00A0" -- noBreakSpace
|
||||||
|
| "\uFEFF" -- byteOrderMark
|
||||||
|
| unicodeSpaceSeparator
|
||||||
|
|
||||||
|
lineTerminator = "\n" | "\r" | "\u2028" | "\u2029"
|
||||||
|
lineTerminatorSequence = "\n" | "\r" ~"\n" | "\u2028" | "\u2029" | "\r\n"
|
||||||
|
unicodeSpaceSeparator = "\u2000".."\u200B" | "\u3000"
|
||||||
|
|
||||||
|
comment = multiLineComment | singleLineComment
|
||||||
|
|
||||||
|
multiLineComment = "/*" (~"*/" sourceCharacter)* "*/"
|
||||||
|
singleLineComment = "//" (~lineTerminator sourceCharacter)*
|
||||||
|
|
||||||
|
Statement = FunctionDeclaration
|
||||||
|
| VariableDeclaration
|
||||||
|
| IfStatement
|
||||||
|
| ReturnStatement
|
||||||
|
| Block
|
||||||
|
| Expression
|
||||||
|
|
||||||
|
ReturnStatement = return Expression?
|
||||||
|
|
||||||
|
FunctionDeclaration = fn identifier "(" ListOf<Parameter, ","> ")" Block
|
||||||
|
|
||||||
|
FunctionExpression = fn? "(" ListOf<Parameter, ","> ")" "=>" Block -- arrow
|
||||||
|
| fn "(" ListOf<Parameter, ","> ")" Block -- anon
|
||||||
|
|
||||||
|
Parameter = identifier
|
||||||
|
|
||||||
|
Block = "{" Statement* "}"
|
||||||
|
|
||||||
|
IfStatement = if Expression Block (else (IfStatement | Block))?
|
||||||
|
|
||||||
|
VariableDeclaration = VarType identifier "=" Expression
|
||||||
|
|
||||||
|
VarType = const | var | live
|
||||||
|
|
||||||
|
Expression = AssignmentExpression
|
||||||
|
|
||||||
|
AssignmentExpression = LeftHandSide "=" AssignmentExpression -- assign
|
||||||
|
| ConditionalExpression
|
||||||
|
| FunctionExpression
|
||||||
|
|
||||||
|
ConditionalExpression = IfExpression
|
||||||
|
| LogicalORExpression
|
||||||
|
|
||||||
|
BitwiseANDExpression
|
||||||
|
= BitwiseANDExpression "&" ComparisonExpression -- band
|
||||||
|
| EqualityExpression
|
||||||
|
|
||||||
|
BitwiseXORExpression
|
||||||
|
= BitwiseXORExpression "^" BitwiseANDExpression -- bxor
|
||||||
|
| BitwiseANDExpression
|
||||||
|
|
||||||
|
BitwiseORExpression
|
||||||
|
= BitwiseORExpression "|" BitwiseXORExpression -- bor
|
||||||
|
| BitwiseXORExpression
|
||||||
|
|
||||||
|
LogicalANDExpression
|
||||||
|
= LogicalANDExpression "&&" BitwiseORExpression -- land
|
||||||
|
| BitwiseORExpression
|
||||||
|
|
||||||
|
LogicalORExpression
|
||||||
|
= LogicalORExpression "||" LogicalANDExpression -- lor
|
||||||
|
| LogicalANDExpression
|
||||||
|
|
||||||
|
TernaryExpression
|
||||||
|
= LogicalORExpression "?" AssignmentExpression ":" AssignmentExpression -- ternary
|
||||||
|
| LogicalORExpression
|
||||||
|
|
||||||
|
EqualityExpression = EqualityExpression "==" EqualityExpression -- eq
|
||||||
|
| EqualityExpression "===" EqualityExpression -- eeq
|
||||||
|
| EqualityExpression "is" EqualityExpression -- is
|
||||||
|
| EqualityExpression "!=" EqualityExpression -- ne
|
||||||
|
| ComparisonExpression
|
||||||
|
|
||||||
|
ComparisonExpression = AddExpression ">" AddExpression -- gt
|
||||||
|
| AddExpression "<" AddExpression -- lt
|
||||||
|
| AddExpression "<=" AddExpression -- le
|
||||||
|
| AddExpression ">=" AddExpression -- ge
|
||||||
|
| AddExpression
|
||||||
|
|
||||||
|
AddExpression = AddExpression "+" MultiplyExpression -- add
|
||||||
|
| AddExpression "-" MultiplyExpression -- subtract
|
||||||
|
| MultiplyExpression
|
||||||
|
|
||||||
|
MultiplyExpression = MultiplyExpression "*" PrimaryExpression -- multiply
|
||||||
|
| MultiplyExpression "/" PrimaryExpression -- divide
|
||||||
|
| MultiplyExpression "**" PrimaryExpression -- power
|
||||||
|
| MultiplyExpression "%" PrimaryExpression -- modulo
|
||||||
|
| UnaryExpression
|
||||||
|
|
||||||
|
UnaryExpression = "!" UnaryExpression -- not
|
||||||
|
| "-" UnaryExpression -- negate
|
||||||
|
| "+" UnaryExpression -- positive
|
||||||
|
| PostfixExpression
|
||||||
|
|
||||||
|
PostfixExpression = PostfixExpression "[" Expression "]" -- index
|
||||||
|
| PostfixExpression "." identifier -- property
|
||||||
|
| PostfixExpression "(" Arguments ")" -- call
|
||||||
|
| PrimaryExpression
|
||||||
|
|
||||||
|
PrimaryExpression = literal
|
||||||
|
| identifier
|
||||||
|
| ArrayLiteral
|
||||||
|
| StructLiteral
|
||||||
|
| DictLiteral
|
||||||
|
| "(" Expression ")" -- parentheses
|
||||||
|
|
||||||
|
LeftHandSide = PostfixExpression
|
||||||
|
|
||||||
|
IfExpression = if Expression Block else (IfExpression | Block)
|
||||||
|
|
||||||
|
Arguments = ListOf<Argument, ",">
|
||||||
|
Argument = Expression
|
||||||
|
|
||||||
|
ArrayLiteral = "[" ListOf<Expression, ","> "]"
|
||||||
|
|
||||||
|
StructLiteral = struct "{" "}"
|
||||||
|
DictLiteral = dict "{" "}"
|
||||||
|
|
||||||
|
literal = nullLiteral | undefined | booleanLiteral | numericLiteral | stringLiteral //TODO: | regExpLiteral
|
||||||
|
|
||||||
|
numericLiteral = ("-"|"+")? digit+ ("." digit+)? exponent?
|
||||||
|
|
||||||
|
exponent = ("e"|"E") ("-"|"+")? digit+
|
||||||
|
|
||||||
|
stringLiteral = "\"" (~"\"" sourceCharacter)* "\"" // "string"
|
||||||
|
| "'" (~"'" sourceCharacter)* "'" // 'string'
|
||||||
|
|
||||||
|
fn = "fn" ~identifierPart
|
||||||
|
return = "return" ~identifierPart
|
||||||
|
if = "if" ~identifierPart
|
||||||
|
else = "else" ~identifierPart
|
||||||
|
const = "const" ~identifierPart
|
||||||
|
var = "var" ~identifierPart
|
||||||
|
live = "live" ~identifierPart
|
||||||
|
true = "true" ~identifierPart
|
||||||
|
false = "false" ~identifierPart
|
||||||
|
null = "none" ~identifierPart
|
||||||
|
undefined = "undefined" ~identifierPart
|
||||||
|
struct = "struct" ~identifierPart
|
||||||
|
dict = "dict" ~identifierPart
|
||||||
|
|
||||||
|
identifier = ~reservedWord identifierName
|
||||||
|
identifierName = letter identifierPart*
|
||||||
|
identifierPart = letter | digit | "_" | "$"
|
||||||
|
|
||||||
|
reservedWord = keyword | reservedWord | nullLiteral | booleanLiteral | undefinedLiteral
|
||||||
|
|
||||||
|
keyword = fn | return | if | else
|
||||||
|
| const | var | live
|
||||||
|
| struct | dict
|
||||||
|
|
||||||
|
booleanLiteral = true | false
|
||||||
|
nullLiteral = null
|
||||||
|
undefinedLiteral = undefined
|
||||||
|
}
|
230
src/grammar.test.js
Normal file
230
src/grammar.test.js
Normal file
|
@ -0,0 +1,230 @@
|
||||||
|
import fs from 'fs'
|
||||||
|
import { describe, it, expect, beforeAll } from 'vitest'
|
||||||
|
import { grammar as createGrammar } from 'ohm-js'
|
||||||
|
|
||||||
|
describe('Solace Grammar', () => {
|
||||||
|
let grammar
|
||||||
|
|
||||||
|
function check(input, shouldSucceed = true) {
|
||||||
|
const match = grammar.match(input)
|
||||||
|
const success = match.succeeded()
|
||||||
|
|
||||||
|
if (shouldSucceed && !success) {
|
||||||
|
console.log(`Failed to parse «${input}», error:`, match.message)
|
||||||
|
} else if (!shouldSucceed && success) {
|
||||||
|
console.log(`Invalid input successfully parsed: «${input}»`, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(success).toBe(shouldSucceed)
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
const grammarText = fs.readFileSync('./src/grammar.ohm', 'utf-8')
|
||||||
|
grammar = createGrammar(grammarText)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Basic statements and expressions', () => {
|
||||||
|
it('literals', () => {
|
||||||
|
check('true')
|
||||||
|
check('false')
|
||||||
|
check('none')
|
||||||
|
check('undefined')
|
||||||
|
})
|
||||||
|
it('integers', () => {
|
||||||
|
check('42')
|
||||||
|
check('0')
|
||||||
|
check('1')
|
||||||
|
check('023')
|
||||||
|
check('9999')
|
||||||
|
})
|
||||||
|
it('integers with prefix', () => {
|
||||||
|
check('-1')
|
||||||
|
check('-23')
|
||||||
|
check('-0')
|
||||||
|
check('+0')
|
||||||
|
check('+999')
|
||||||
|
})
|
||||||
|
it('exponents', () => {
|
||||||
|
check('42e2') // 4200
|
||||||
|
check('1e+3') // 1000
|
||||||
|
check('2e-3') // 0.002
|
||||||
|
})
|
||||||
|
it('floats', () => {
|
||||||
|
check('1.0')
|
||||||
|
check('23.744')
|
||||||
|
check('-0.1')
|
||||||
|
check('+1.23')
|
||||||
|
check('999.000001')
|
||||||
|
})
|
||||||
|
it('strings in doublequotes', () => {
|
||||||
|
check('"Foo"')
|
||||||
|
check('"23"')
|
||||||
|
check('"Hello World"')
|
||||||
|
})
|
||||||
|
it('strings in singlequotes', () => {
|
||||||
|
check("'Foo'")
|
||||||
|
check("'23'")
|
||||||
|
check("'Hello World'")
|
||||||
|
})
|
||||||
|
|
||||||
|
it('arithmetic expressions', () => {
|
||||||
|
check("42 + 23")
|
||||||
|
check("42 - 23")
|
||||||
|
check("foo + 23 - 42e-2")
|
||||||
|
check("foo + bar")
|
||||||
|
check("42 - foo")
|
||||||
|
check("bar - foo")
|
||||||
|
check("bar-foo")
|
||||||
|
check("bar-42")
|
||||||
|
check("(42 - 23E2) + foo")
|
||||||
|
check("42**2 * foo")
|
||||||
|
check("(42 - 23) ** 2")
|
||||||
|
check("(42 % 23) / bar*2")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Statements and Declarations", () => {
|
||||||
|
it('if statements', () => {
|
||||||
|
check(`if x > 5 { 10 }`)
|
||||||
|
check(`if foo < bar { 1 } else { 2 }`)
|
||||||
|
check(`
|
||||||
|
if foo < 23 {
|
||||||
|
"small"
|
||||||
|
} else if foo > 42 {
|
||||||
|
"large"
|
||||||
|
} else {
|
||||||
|
"medium"
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('incomplete if statements', () => {
|
||||||
|
check(`
|
||||||
|
if > 23 {
|
||||||
|
"small"
|
||||||
|
}
|
||||||
|
`, false)
|
||||||
|
check(`
|
||||||
|
if foo < 23 {
|
||||||
|
"small"
|
||||||
|
} else if {
|
||||||
|
"wrong"
|
||||||
|
}
|
||||||
|
`, false)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
it('basic functions and function calls', () => {
|
||||||
|
check(`
|
||||||
|
fn add(x,y) {
|
||||||
|
return x + y
|
||||||
|
}
|
||||||
|
fn foo() {
|
||||||
|
log(23)
|
||||||
|
}
|
||||||
|
|
||||||
|
add(2,3)
|
||||||
|
foo()
|
||||||
|
|
||||||
|
add(if answer is true { 19 } else { 0 }, 23)
|
||||||
|
`)
|
||||||
|
|
||||||
|
check(`fn () { return "wrong" }`) // anonymous function
|
||||||
|
})
|
||||||
|
|
||||||
|
it('variable declarations', () => {
|
||||||
|
check(`
|
||||||
|
var foo = 42
|
||||||
|
const bar = 'Hello'
|
||||||
|
live baz = (foo-1)*2
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('arrays', () => {
|
||||||
|
check(`
|
||||||
|
const arr = ["Pirates", "know", "how", "to", "arr!"]
|
||||||
|
var ary = []
|
||||||
|
ary.push(23)
|
||||||
|
ary.push(5)
|
||||||
|
ary[1] = 42
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Expressions", () => {
|
||||||
|
it('comparison', () => {
|
||||||
|
check(`x == 5`)
|
||||||
|
check(`x != 5`)
|
||||||
|
check(`x is true`)
|
||||||
|
check(`x <= 23`)
|
||||||
|
check(`x >= 23`)
|
||||||
|
check(`x > 23`)
|
||||||
|
check(`x < 23`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('logic', () => {
|
||||||
|
check(`x > 5 && !true && !(ary[0] > ary[1] || ary[0] == 5)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('bitwise', () => {
|
||||||
|
check(`x & y | (2 ^ 5)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('if expressions', () => {
|
||||||
|
check(`
|
||||||
|
const y = if x > 5 {
|
||||||
|
10
|
||||||
|
} else {
|
||||||
|
23
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
check(`const y = if true { 10 } else { 23 }`)
|
||||||
|
|
||||||
|
// if expressions always need an else block
|
||||||
|
check(`
|
||||||
|
const y = if x > 5 {
|
||||||
|
10
|
||||||
|
}
|
||||||
|
`, false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('nested if expressions', () => {
|
||||||
|
check(`
|
||||||
|
const y = if x > 5 {
|
||||||
|
10
|
||||||
|
} else if x < 2 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
2
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
// if expressions always need an else block
|
||||||
|
check(`
|
||||||
|
const y = if x > 5 {
|
||||||
|
10
|
||||||
|
} else if x < 2 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
`, false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('function expressions', () => {
|
||||||
|
check(`const foo = fn () { return 42 }`) // anonymous function with fn
|
||||||
|
check(`const bar = fn () => { return 3e2 }`) // anon func with fn and arrow
|
||||||
|
check(`const baz = () => { return 3e2 }`) // anon arrow func
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Example Program", () => {
|
||||||
|
it('hello world', () => {
|
||||||
|
check(`
|
||||||
|
fn main() {
|
||||||
|
const ary = ["foo", "bar"]
|
||||||
|
ary[1] = 'World'
|
||||||
|
console.log('Hello', ary[1])
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
165
src/interpreter.mjs
Normal file
165
src/interpreter.mjs
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
import fs from 'fs'
|
||||||
|
import { grammar as createGrammar } from 'ohm-js'
|
||||||
|
|
||||||
|
function flattenIterNodes(nodes) {
|
||||||
|
const result = []
|
||||||
|
for (let i = 0; i < nodes.length; ++i) {
|
||||||
|
if (nodes[i]._node.ctorName === '_iter') {
|
||||||
|
result.push(...flattenIterNodes(nodes[i].children))
|
||||||
|
} else {
|
||||||
|
result.push(nodes[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparison function for sorting nodes based on their interval's start index.
|
||||||
|
function compareByInterval(node, otherNode) {
|
||||||
|
return node.source.startIdx - otherNode.source.startIdx
|
||||||
|
}
|
||||||
|
|
||||||
|
function nodeToES6(node, children) {
|
||||||
|
const flatChildren = flattenIterNodes(children).sort(compareByInterval)
|
||||||
|
|
||||||
|
// Keeps track of where the previous sibling ended, so that we can re-insert discarded
|
||||||
|
// whitespace into the final output.
|
||||||
|
let prevEndIdx = node.source.startIdx
|
||||||
|
let code = ''
|
||||||
|
|
||||||
|
for (let i = 0; i < flatChildren.length; ++i) {
|
||||||
|
const child = flatChildren[i]
|
||||||
|
|
||||||
|
// Restore any discarded whitespace between this node and the previous one.
|
||||||
|
if (child.source.startIdx > prevEndIdx) {
|
||||||
|
code += node.source.sourceString.slice(prevEndIdx, child.source.startIdx)
|
||||||
|
}
|
||||||
|
code += child.toES6()
|
||||||
|
prevEndIdx = child.source.endIdx
|
||||||
|
}
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
|
||||||
|
const grammarPath = `${import.meta.dirname}/grammar.ohm`
|
||||||
|
const grammarText = fs.readFileSync(grammarPath, 'utf-8')
|
||||||
|
const grammar = createGrammar(grammarText)
|
||||||
|
const semantics = grammar.createSemantics()
|
||||||
|
|
||||||
|
semantics.addOperation('toES6', {
|
||||||
|
Program(sourceElements) {
|
||||||
|
const { sourceString, startIdx, endIdx } = this.source
|
||||||
|
return (
|
||||||
|
sourceString.slice(0, startIdx) +
|
||||||
|
nodeToES6(this, [sourceElements]) +
|
||||||
|
sourceString.slice(endIdx)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
VariableDeclaration(varType, ident, _equals, expr) {
|
||||||
|
const keyword = varType.sourceString
|
||||||
|
const name = ident.toES6()
|
||||||
|
const value = expr.toES6()
|
||||||
|
|
||||||
|
switch (keyword) {
|
||||||
|
case 'var': return `let ${name} = ${value}`
|
||||||
|
case 'live': return `let ${name} = ${value} /* TODO: reactivity not yet implemented */`
|
||||||
|
default: return `${keyword} ${name} = ${value}`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
IfStatement(_ifKw, expr, thenBlock, _elseKw, elseBlock) {
|
||||||
|
const ifStmt = `if (${expr.toES6()}) ${thenBlock.toES6()}`
|
||||||
|
const elseStmt = `${elseBlock.toES6()}`.trim()
|
||||||
|
|
||||||
|
let output = ifStmt
|
||||||
|
if (elseStmt.length) output += ` else ${elseStmt}`
|
||||||
|
|
||||||
|
return output
|
||||||
|
},
|
||||||
|
EqualityExpression_eq(leftExpr, _eq, rightExpr) {
|
||||||
|
return `${leftExpr.toES6()} === ${rightExpr.toES6()}`
|
||||||
|
},
|
||||||
|
IfExpression(_ifKw, expr, thenBlock, _elseKw, elseBlock) {
|
||||||
|
const thenValue = thenBlock.blockAsExpression()
|
||||||
|
const elseValue = elseBlock.blockAsExpression()
|
||||||
|
return `(${expr.toES6()} ? ${thenValue} : ${elseValue})`
|
||||||
|
},
|
||||||
|
FunctionDeclaration(_fnKw, ident, _leftParen, params, _rightParen, block) {
|
||||||
|
return `const ${ident.toES6()} = (${params.toES6()}) => ${block.toES6()}`
|
||||||
|
},
|
||||||
|
FunctionExpression_arrow(_maybeFnKw, _leftParen, params, _rightParen, _arrow, block) {
|
||||||
|
return `(${params.toES6()}) => ${block.toES6()}`
|
||||||
|
},
|
||||||
|
FunctionExpression_anon(_fnKw, _leftParen, params, _rightParen, block) {
|
||||||
|
return `(${params.toES6()}) => ${block.toES6()}`
|
||||||
|
},
|
||||||
|
_nonterminal(...children) {
|
||||||
|
return nodeToES6(this, children)
|
||||||
|
},
|
||||||
|
_terminal() {
|
||||||
|
return this.sourceString
|
||||||
|
},
|
||||||
|
_iter(...children) {
|
||||||
|
return children.map(c => c.toES6()).join('')
|
||||||
|
},
|
||||||
|
_default(...children) {
|
||||||
|
// Reconstruct source from children
|
||||||
|
return children.map(child => {
|
||||||
|
if (typeof child.toES6 === 'function') return child.toES6()
|
||||||
|
return '' // Skip iteration nodes
|
||||||
|
}).join(' ')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// Blocks can be simple statements (if condition {/*block*/})
|
||||||
|
// or expressions (const foo = condition ? /*block*/ : /*block*/)
|
||||||
|
// which needs to return a value, so it is transpiled to an IIFE
|
||||||
|
// like (const foo = cond ? (() => { const x = 5; return x; })() : ... )
|
||||||
|
semantics.addOperation('blockAsExpression', {
|
||||||
|
Block(_open, statementList, _close) {
|
||||||
|
const statements = statementList.children
|
||||||
|
if (statements.length === 0) return ''
|
||||||
|
if (statements.length === 1) return statements[0].toES6()
|
||||||
|
|
||||||
|
const transpiled = statements.map(stmt => stmt.toES6())
|
||||||
|
const last = transpiled[transpiled.length - 1]
|
||||||
|
const init = transpiled.slice(0, -1).join('; ')
|
||||||
|
|
||||||
|
return `(() => { ${init}; return ${last}; })()`
|
||||||
|
},
|
||||||
|
IfExpression(_ifKw, expr, thenBlock, _elseKw, elseBlock) {
|
||||||
|
const thenValue = thenBlock.blockAsExpression()
|
||||||
|
const elseValue = elseBlock.blockAsExpression()
|
||||||
|
return `(${expr.toES6()} ? ${thenValue} : ${elseValue})`
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
semantics.addOperation('hoistDeclarations', {
|
||||||
|
FunctionDeclaration(_fnKw, ident, _leftParen, _params, _rightParen, _block) {
|
||||||
|
return new Map([[ident.sourceString, [ident.source]]])
|
||||||
|
},
|
||||||
|
FunctionExpression(_) {
|
||||||
|
return new Map()
|
||||||
|
},
|
||||||
|
_iter: mergeBindings,
|
||||||
|
_nonterminal: mergeBindings,
|
||||||
|
_terminal() {
|
||||||
|
return new Map()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// Merge the bindings from the given `nodes` into a single map, where the value
|
||||||
|
// is an array of source locations that name is bound.
|
||||||
|
function mergeBindings(...nodes) {
|
||||||
|
const bindings = new Map()
|
||||||
|
for (const child of nodes.filter(c => !c.isLexical())) {
|
||||||
|
child.hoistDeclarations().forEach((sources, ident) => {
|
||||||
|
if (bindings.has(ident)) {
|
||||||
|
bindings.get(ident).push(...sources) // Shadowed binding.
|
||||||
|
} else {
|
||||||
|
bindings.set(ident, sources) // Not shadowed at this level.
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return bindings
|
||||||
|
}
|
||||||
|
|
||||||
|
export { grammar, semantics }
|
150
src/interpreter.test.js
Normal file
150
src/interpreter.test.js
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
import { describe, it, expect, beforeAll } from 'vitest'
|
||||||
|
import { grammar, semantics } from './interpreter.mjs'
|
||||||
|
|
||||||
|
function compile(source) {
|
||||||
|
const match = grammar.match(source, 'Program')
|
||||||
|
if (!match.succeeded()) {
|
||||||
|
console.error(`Failed to parse «${source}», error:`, match.message)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return semantics(match).toES6()
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Solace Interpreter', () => {
|
||||||
|
describe('Expressions', () => {
|
||||||
|
it('addition/substraction', () => {
|
||||||
|
expect(compile('a + b - c')).toBe('a + b - c')
|
||||||
|
})
|
||||||
|
it('multiplication/division', () => {
|
||||||
|
expect(compile('a * b / c')).toBe('a * b / c')
|
||||||
|
})
|
||||||
|
it('mixed', () => {
|
||||||
|
expect(compile('a + b * c')).toBe('a + b * c')
|
||||||
|
})
|
||||||
|
it('others', () => {
|
||||||
|
expect(compile('a ** b % c')).toBe('a ** b % c')
|
||||||
|
})
|
||||||
|
it('negative', () => {
|
||||||
|
expect(compile('-2 * c')).toBe('-2 * c')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('Functions', () => {
|
||||||
|
it('one-liner declaration', () => {
|
||||||
|
const input = `fn foo() {}`
|
||||||
|
const output = compile(input)
|
||||||
|
expect(output).toBe('const foo = () => {}')
|
||||||
|
})
|
||||||
|
it('one-liner expression with fn', () => {
|
||||||
|
const input = `const foo = fn () {}`
|
||||||
|
const output = compile(input)
|
||||||
|
expect(output).toBe('const foo = () => {}')
|
||||||
|
})
|
||||||
|
it('one-liner expression with arrow', () => {
|
||||||
|
const input = `const foo = () => {}`
|
||||||
|
const output = compile(input)
|
||||||
|
expect(output).toBe('const foo = () => {}')
|
||||||
|
})
|
||||||
|
it('one-liner expression with fn and arrow', () => {
|
||||||
|
const input = `const foo = fn () => {}`
|
||||||
|
const output = compile(input)
|
||||||
|
expect(output).toBe('const foo = () => {}')
|
||||||
|
})
|
||||||
|
it('multi-line declaration', () => {
|
||||||
|
const input = `fn foo() {
|
||||||
|
const x = 5
|
||||||
|
return x
|
||||||
|
}`
|
||||||
|
const output = compile(input)
|
||||||
|
expect(output).toBe(`const foo = () => {
|
||||||
|
const x = 5
|
||||||
|
return x
|
||||||
|
}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Assignments', () => {
|
||||||
|
it('var', () => {
|
||||||
|
const input = 'var foo = 23'
|
||||||
|
const output = compile(input)
|
||||||
|
expect(output).toBe('let foo = 23')
|
||||||
|
})
|
||||||
|
it('let', () => {
|
||||||
|
const input = 'let foo = 23'
|
||||||
|
const output = compile(input)
|
||||||
|
expect(output).toBe('let foo = 23')
|
||||||
|
})
|
||||||
|
it('const', () => {
|
||||||
|
const input = 'const foo = 23'
|
||||||
|
const output = compile(input)
|
||||||
|
expect(output).toBe('const foo = 23')
|
||||||
|
})
|
||||||
|
it('array declaration', () => {
|
||||||
|
const input = 'const foo = [1,2,3]'
|
||||||
|
const output = compile(input)
|
||||||
|
expect(output).toBe('const foo = [1,2,3]')
|
||||||
|
})
|
||||||
|
it('array usage', () => {
|
||||||
|
const input = 'const foo = ary[2]'
|
||||||
|
const output = compile(input)
|
||||||
|
expect(output).toBe('const foo = ary[2]')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Conditions and Comparisons', () => {
|
||||||
|
it('if', () => {
|
||||||
|
const input = 'if x < 5 { console.log(x) }'
|
||||||
|
const output = compile(input)
|
||||||
|
expect(output).toBe('if (x < 5) { console.log(x) }')
|
||||||
|
})
|
||||||
|
it('if-else', () => {
|
||||||
|
const input = 'if x < 5 { console.log(x) } else { console.log("no") }'
|
||||||
|
const output = compile(input)
|
||||||
|
expect(output).toBe('if (x < 5) { console.log(x) } else { console.log("no") }')
|
||||||
|
})
|
||||||
|
it('nested if', () => {
|
||||||
|
const input = `if x < 5 {
|
||||||
|
console.log('small')
|
||||||
|
} else if x < 23 {
|
||||||
|
console.log('medium')
|
||||||
|
} else {
|
||||||
|
console.log('large')
|
||||||
|
}
|
||||||
|
`
|
||||||
|
const output = compile(input)
|
||||||
|
expect(output).toBe(`if (x < 5) {
|
||||||
|
console.log('small')
|
||||||
|
} else if (x < 23) {
|
||||||
|
console.log('medium')
|
||||||
|
} else {
|
||||||
|
console.log('large')
|
||||||
|
}`)
|
||||||
|
})
|
||||||
|
it('if expressions with single statements', () => {
|
||||||
|
const input = 'const foo = if x < 5 { "small" } else { "medium" }'
|
||||||
|
const output = compile(input)
|
||||||
|
expect(output).toBe('const foo = (x < 5 ? "small" : "medium")')
|
||||||
|
})
|
||||||
|
it('if expressions with multiple statements', () => {
|
||||||
|
const input = `const foo = if x < 5 {
|
||||||
|
const y = x*3
|
||||||
|
y
|
||||||
|
} else {
|
||||||
|
const y = x/3
|
||||||
|
console.log(y)
|
||||||
|
y
|
||||||
|
}`
|
||||||
|
const output = compile(input)
|
||||||
|
expect(output).toBe(`const foo = (x < 5 ? (() => { const y = x*3; return y; })() : (() => { const y = x/3; console.log(y); return y; })())`)
|
||||||
|
})
|
||||||
|
it('if expressions with mixed statements', () => {
|
||||||
|
const input = `const foo = if x < 5 {
|
||||||
|
const y = x*3
|
||||||
|
y
|
||||||
|
} else {
|
||||||
|
42
|
||||||
|
}`
|
||||||
|
const output = compile(input)
|
||||||
|
expect(output).toBe(`const foo = (x < 5 ? (() => { const y = x*3; return y; })() : 42)`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
43
src/test.solace
Normal file
43
src/test.solace
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
fn relief(array, index) {
|
||||||
|
if array.length > index {
|
||||||
|
const word = 'Solace'
|
||||||
|
array[index] = word
|
||||||
|
}
|
||||||
|
return array
|
||||||
|
}
|
||||||
|
|
||||||
|
fn doStuff(v) {
|
||||||
|
const result = if v === 5 {
|
||||||
|
const x = 5
|
||||||
|
x
|
||||||
|
} else if v == 23 {
|
||||||
|
const word = "World"
|
||||||
|
word
|
||||||
|
} else {
|
||||||
|
"Whatever"
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loop constructs are not supported, yet
|
||||||
|
*
|
||||||
|
* for (var i = 0; i < 23; i++) {
|
||||||
|
* console.log('i is now', i, 'so that we test loops')
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// function expressions
|
||||||
|
const func1 = fn(a,b) {}
|
||||||
|
const func2 = fn(a,b) { console.log(a,b) }
|
||||||
|
const func3 = fn(a,b) => { console.log(a,b) }
|
||||||
|
const func4 = () => {}
|
||||||
|
const func5 = (a,b) => { console.log(a,b) }
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
const ary = ["Hello", "World"]
|
||||||
|
const fooFun = fn () { "foo" }
|
||||||
|
live index = 1
|
||||||
|
relief(ary, index)
|
||||||
|
console.log(ary.join(' '))
|
||||||
|
}
|
123
stwl.js
123
stwl.js
|
@ -1,123 +0,0 @@
|
||||||
/* eslint-env node */
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
|
||||||
// Imports
|
|
||||||
// --------------------------------------------------------------------
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
const ohm = require('ohm-js');
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
|
||||||
// Helpers
|
|
||||||
// --------------------------------------------------------------------
|
|
||||||
|
|
||||||
// Take an Array of nodes, and whenever an _iter node is encountered, splice in its
|
|
||||||
// recursively-flattened children instead.
|
|
||||||
function flattenIterNodes(nodes) {
|
|
||||||
const result = [];
|
|
||||||
for (let i = 0; i < nodes.length; ++i) {
|
|
||||||
if (nodes[i]._node.ctorName === '_iter') {
|
|
||||||
result.push(...flattenIterNodes(nodes[i].children));
|
|
||||||
} else {
|
|
||||||
result.push(nodes[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comparison function for sorting nodes based on their interval's start index.
|
|
||||||
function compareByInterval(node, otherNode) {
|
|
||||||
return node.source.startIdx - otherNode.source.startIdx;
|
|
||||||
}
|
|
||||||
|
|
||||||
function nodeToES5(node, children) {
|
|
||||||
const flatChildren = flattenIterNodes(children).sort(compareByInterval);
|
|
||||||
|
|
||||||
// Keeps track of where the previous sibling ended, so that we can re-insert discarded
|
|
||||||
// whitespace into the final output.
|
|
||||||
let prevEndIdx = node.source.startIdx;
|
|
||||||
|
|
||||||
let code = '';
|
|
||||||
for (let i = 0; i < flatChildren.length; ++i) {
|
|
||||||
const child = flatChildren[i];
|
|
||||||
|
|
||||||
// Restore any discarded whitespace between this node and the previous one.
|
|
||||||
if (child.source.startIdx > prevEndIdx) {
|
|
||||||
code += node.source.sourceString.slice(prevEndIdx, child.source.startIdx);
|
|
||||||
}
|
|
||||||
code += child.toES5();
|
|
||||||
prevEndIdx = child.source.endIdx;
|
|
||||||
}
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instantiate the ES5 grammar.
|
|
||||||
const contents = fs.readFileSync(path.join(__dirname, 'es5.ohm'));
|
|
||||||
const g = ohm.grammars(contents).ES5;
|
|
||||||
const semantics = g.createSemantics();
|
|
||||||
|
|
||||||
semantics.addOperation('toES5()', {
|
|
||||||
Program(_, sourceElements) {
|
|
||||||
// Top-level leading and trailing whitespace is not handled by nodeToES5(), so do it here.
|
|
||||||
const {sourceString} = this.source;
|
|
||||||
return (
|
|
||||||
sourceString.slice(0, this.source.startIdx) +
|
|
||||||
nodeToES5(this, [sourceElements]) +
|
|
||||||
sourceString.slice(this.source.endIdx)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
_nonterminal(...children) {
|
|
||||||
return nodeToES5(this, children);
|
|
||||||
},
|
|
||||||
_terminal() {
|
|
||||||
return this.sourceString;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Implements hoisting of variable and function declarations.
|
|
||||||
// See https://developer.mozilla.org/en-US/docs/Glossary/Hoisting
|
|
||||||
// Note that on its own, this operation doesn't create nested lexical environments,
|
|
||||||
// but it should be possible to use it as a helper for another operation that would.
|
|
||||||
semantics.addOperation('hoistDeclarations()', {
|
|
||||||
FunctionDeclaration(_, ident, _1, _2, _3, _4, _5, _6) {
|
|
||||||
// Don't hoist from the function body, only return this function's identifier.
|
|
||||||
return new Map([[ident.sourceString, [ident.source]]]);
|
|
||||||
},
|
|
||||||
FunctionExpression(_) {
|
|
||||||
return new Map();
|
|
||||||
},
|
|
||||||
VariableDeclaration(ident, _) {
|
|
||||||
return new Map([[ident.sourceString, [ident.source]]]);
|
|
||||||
},
|
|
||||||
_iter: mergeBindings,
|
|
||||||
_nonterminal: mergeBindings,
|
|
||||||
_terminal() {
|
|
||||||
return new Map();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Merge the bindings from the given `nodes` into a single map, where the value
|
|
||||||
// is an array of source locations that name is bound.
|
|
||||||
function mergeBindings(...nodes) {
|
|
||||||
const bindings = new Map();
|
|
||||||
for (const child of nodes.filter(c => !c.isLexical())) {
|
|
||||||
child.hoistDeclarations().forEach((sources, ident) => {
|
|
||||||
if (bindings.has(ident)) {
|
|
||||||
bindings.get(ident).push(...sources); // Shadowed binding.
|
|
||||||
} else {
|
|
||||||
bindings.set(ident, sources); // Not shadowed at this level.
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return bindings;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
grammar: g,
|
|
||||||
semantics,
|
|
||||||
};
|
|
||||||
|
|
424
stwl.ohm
424
stwl.ohm
|
@ -1,424 +0,0 @@
|
||||||
STWL {
|
|
||||||
Program = &(Directive*) SourceElement*
|
|
||||||
/*
|
|
||||||
STWL - Strongly Typed Web Language is just a working title
|
|
||||||
|
|
||||||
This grammar is based on the ES5 and ES6 grammars and extends/cuts it where
|
|
||||||
necessary to clear STWL from many, in my opinion unnecessary, features
|
|
||||||
while extending it with a clearer syntax
|
|
||||||
*/
|
|
||||||
|
|
||||||
sourceCharacter = any
|
|
||||||
|
|
||||||
// Override Ohm's built-in definition of space.
|
|
||||||
space := whitespace | lineTerminator | comment
|
|
||||||
|
|
||||||
whitespace = "\t"
|
|
||||||
| "\x0B" -- verticalTab
|
|
||||||
| "\x0C" -- formFeed
|
|
||||||
| " "
|
|
||||||
| "\u00A0" -- noBreakSpace
|
|
||||||
| "\uFEFF" -- byteOrderMark
|
|
||||||
| unicodeSpaceSeparator
|
|
||||||
|
|
||||||
lineTerminator = "\n" | "\r" | "\u2028" | "\u2029"
|
|
||||||
lineTerminatorSequence = "\n" | "\r" ~"\n" | "\u2028" | "\u2029" | "\r\n"
|
|
||||||
|
|
||||||
comment = multiLineComment | singleLineComment
|
|
||||||
|
|
||||||
multiLineComment = "/*" (~"*/" sourceCharacter)* "*/"
|
|
||||||
singleLineComment = "//" (~lineTerminator sourceCharacter)*
|
|
||||||
|
|
||||||
identifier (an identifier) = ~reservedWord identifierName
|
|
||||||
identifierName = identifierStart identifierPart*
|
|
||||||
|
|
||||||
identifierStart = letter | "$" | "_"
|
|
||||||
identifierPart = identifierStart | unicodeDigit
|
|
||||||
letter += unicodeCategoryNl
|
|
||||||
unicodeCategoryNl
|
|
||||||
= "\u2160".."\u2182" | "\u3007" | "\u3021".."\u3029"
|
|
||||||
unicodeDigit (a digit)
|
|
||||||
= "\u0030".."\u0039" | "\u0660".."\u0669" | "\u06F0".."\u06F9" | "\u0966".."\u096F" | "\u09E6".."\u09EF" | "\u0A66".."\u0A6F" | "\u0AE6".."\u0AEF" | "\u0B66".."\u0B6F" | "\u0BE7".."\u0BEF" | "\u0C66".."\u0C6F" | "\u0CE6".."\u0CEF" | "\u0D66".."\u0D6F" | "\u0E50".."\u0E59" | "\u0ED0".."\u0ED9" | "\u0F20".."\u0F29" | "\uFF10".."\uFF19"
|
|
||||||
|
|
||||||
unicodeSpaceSeparator = "\u2000".."\u200B" | "\u3000"
|
|
||||||
|
|
||||||
reservedWord = keyword | futureReservedWord | nullLiteral | booleanLiteral
|
|
||||||
|
|
||||||
// Note: keywords that are the complete prefix of another keyword should
|
|
||||||
// be prioritized (e.g. 'in' should come before 'instanceof')
|
|
||||||
keyword = for | while | do | break | continue | match
|
|
||||||
| if | else | const | var | live | computed
|
|
||||||
| catch | fn | return | void | this
|
|
||||||
| has | enum | interface | extends | implements | struct
|
|
||||||
| export | import | defer
|
|
||||||
|
|
||||||
futureReservedWord = fallthrough
|
|
||||||
|
|
||||||
literal = nullLiteral | booleanLiteral | numericLiteral
|
|
||||||
| stringLiteral | regularExpressionLiteral
|
|
||||||
|
|
||||||
// Optionals, which can be either Some<T> or None, are translated to T | null
|
|
||||||
nullLiteral = "None" ~identifierPart
|
|
||||||
booleanLiteral = ("true" | "false") ~identifierPart
|
|
||||||
|
|
||||||
// For semantics on how decimal literals are constructed, see section 7.8.3
|
|
||||||
|
|
||||||
// Note that the ordering of hexIntegerLiteral and decimalLiteral is reversed w.r.t. the spec
|
|
||||||
// This is intentional: the order decimalLiteral | hexIntegerLiteral will parse
|
|
||||||
// "0x..." as a decimal literal "0" followed by "x..."
|
|
||||||
numericLiteral = octalIntegerLiteral | hexIntegerLiteral | decimalLiteral
|
|
||||||
|
|
||||||
decimalLiteral = decimalIntegerLiteral "." decimalDigit* exponentPart -- bothParts
|
|
||||||
| "." decimalDigit+ exponentPart -- decimalsOnly
|
|
||||||
| decimalIntegerLiteral exponentPart -- integerOnly
|
|
||||||
|
|
||||||
decimalIntegerLiteral = nonZeroDigit decimalDigit* -- nonZero
|
|
||||||
| "0" -- zero
|
|
||||||
decimalDigit = "0".."9"
|
|
||||||
nonZeroDigit = "1".."9"
|
|
||||||
|
|
||||||
exponentPart = exponentIndicator signedInteger -- present
|
|
||||||
| -- absent
|
|
||||||
exponentIndicator = "e" | "E"
|
|
||||||
signedInteger = "+" decimalDigit* -- positive
|
|
||||||
| "-" decimalDigit* -- negative
|
|
||||||
| decimalDigit+ -- noSign
|
|
||||||
|
|
||||||
hexIntegerLiteral = "0x" hexDigit+
|
|
||||||
| "0X" hexDigit+
|
|
||||||
|
|
||||||
// hexDigit defined in Ohm's built-in rules (otherwise: hexDigit = "0".."9" | "a".."f" | "A".."F")
|
|
||||||
|
|
||||||
octalIntegerLiteral = "0" octalDigit+
|
|
||||||
|
|
||||||
octalDigit = "0".."7"
|
|
||||||
zeroToThree = "0".."3"
|
|
||||||
fourToSeven = "4".."7"
|
|
||||||
|
|
||||||
// For semantics on how string literals are constructed, see section 7.8.4
|
|
||||||
stringLiteral = "\"" doubleStringCharacter* "\""
|
|
||||||
| "'" singleStringCharacter* "'"
|
|
||||||
doubleStringCharacter = ~("\"" | "\\" | lineTerminator) sourceCharacter -- nonEscaped
|
|
||||||
| "\\" escapeSequence -- escaped
|
|
||||||
| lineContinuation -- lineContinuation
|
|
||||||
singleStringCharacter = ~("'" | "\\" | lineTerminator) sourceCharacter -- nonEscaped
|
|
||||||
| "\\" escapeSequence -- escaped
|
|
||||||
| lineContinuation -- lineContinuation
|
|
||||||
lineContinuation = "\\" lineTerminatorSequence
|
|
||||||
escapeSequence = unicodeEscapeSequence
|
|
||||||
| hexEscapeSequence
|
|
||||||
| octalEscapeSequence
|
|
||||||
| characterEscapeSequence // Must come last.
|
|
||||||
characterEscapeSequence = singleEscapeCharacter
|
|
||||||
| nonEscapeCharacter
|
|
||||||
singleEscapeCharacter = "'" | "\"" | "\\" | "b" | "f" | "n" | "r" | "t" | "v"
|
|
||||||
nonEscapeCharacter = ~(escapeCharacter | lineTerminator) sourceCharacter
|
|
||||||
escapeCharacter = singleEscapeCharacter | decimalDigit | "x" | "u"
|
|
||||||
octalEscapeSequence = zeroToThree octalDigit octalDigit -- whole
|
|
||||||
| fourToSeven octalDigit -- eightTimesfourToSeven
|
|
||||||
| zeroToThree octalDigit ~decimalDigit -- eightTimesZeroToThree
|
|
||||||
| octalDigit ~decimalDigit -- octal
|
|
||||||
hexEscapeSequence = "x" hexDigit hexDigit
|
|
||||||
unicodeEscapeSequence = "u" hexDigit hexDigit hexDigit hexDigit
|
|
||||||
|
|
||||||
// §7.8.5 Regular Expression Literals -- https://es5.github.io/#x7.8.5
|
|
||||||
|
|
||||||
regularExpressionLiteral = "/" regularExpressionBody "/" regularExpressionFlags
|
|
||||||
regularExpressionBody = regularExpressionFirstChar regularExpressionChar*
|
|
||||||
regularExpressionFirstChar = ~("*" | "\\" | "/" | "[") regularExpressionNonTerminator
|
|
||||||
| regularExpressionBackslashSequence
|
|
||||||
| regularExpressionClass
|
|
||||||
regularExpressionChar = ~("\\" | "/" | "[") regularExpressionNonTerminator
|
|
||||||
| regularExpressionBackslashSequence
|
|
||||||
| regularExpressionClass
|
|
||||||
regularExpressionBackslashSequence = "\\" regularExpressionNonTerminator
|
|
||||||
regularExpressionNonTerminator = ~(lineTerminator) sourceCharacter
|
|
||||||
regularExpressionClass = "[" regularExpressionClassChar* "]"
|
|
||||||
regularExpressionClassChar = ~("]" | "\\") regularExpressionNonTerminator
|
|
||||||
| regularExpressionBackslashSequence
|
|
||||||
regularExpressionFlags = identifierPart*
|
|
||||||
|
|
||||||
// === Implementation-level rules (not part of the spec) ===
|
|
||||||
|
|
||||||
multiLineCommentNoNL = "/*" (~("*/" | lineTerminator) sourceCharacter)* "*/"
|
|
||||||
|
|
||||||
// does not accept lineTerminators, not even implicit ones in a multiLineComment (cf. section 7.4)
|
|
||||||
spacesNoNL = (whitespace | singleLineComment | multiLineCommentNoNL)*
|
|
||||||
|
|
||||||
// A semicolon is "automatically inserted" if a newline or the end of the input stream is
|
|
||||||
// reached, or the offending token is "}".
|
|
||||||
// See https://es5.github.io/#x7.9 for more information.
|
|
||||||
// NOTE: Applications of this rule *must* appear in a lexical context -- either in the body of a
|
|
||||||
// lexical rule, or inside `#()`.
|
|
||||||
sc = space* (";" | end)
|
|
||||||
| spacesNoNL (lineTerminator | ~multiLineCommentNoNL multiLineComment | &"}")
|
|
||||||
|
|
||||||
// Convenience rules for parsing keyword tokens.
|
|
||||||
for = "for" ~identifierPart
|
|
||||||
while = "while" ~identifierPart
|
|
||||||
do = "do" ~identifierPart // will we support do-while loops?
|
|
||||||
break = "break" ~identifierPart
|
|
||||||
continue = "continue" ~identifierPart
|
|
||||||
if = "if" ~identifierPart
|
|
||||||
else = "else" ~identifierPart
|
|
||||||
const = "const" ~identifierPart
|
|
||||||
var = "var" ~identifierPart
|
|
||||||
live = "live" ~identifierPart
|
|
||||||
track = "track" ~identifierPart
|
|
||||||
computed = "computed" ~identifierPart
|
|
||||||
catch = "catch" ~identifierPart
|
|
||||||
fn = "fn" ~identifierPart
|
|
||||||
return = "return" ~identifierPart
|
|
||||||
void = "void" ~identifierPart
|
|
||||||
this = "_" ~identifierPart
|
|
||||||
in = "in" ~identifierPart -- value in ArrayLike
|
|
||||||
has = "has" ~identifierPart -- struct has Property
|
|
||||||
enum = "enum" ~identifierPart
|
|
||||||
interface = "interface" ~identifierPart
|
|
||||||
extends = "extends" ~identifierPart
|
|
||||||
implements = "implements" ~identifierPart
|
|
||||||
export = "export" ~identifierPart
|
|
||||||
import = "import" ~identifierPart
|
|
||||||
struct = "struct" ~identifierPart
|
|
||||||
defer = "defer" ~identifierPart
|
|
||||||
match = "match" ~identifierPart
|
|
||||||
fallthrough = "fallthrough" ~identifierPart
|
|
||||||
|
|
||||||
// end of lexical rules
|
|
||||||
|
|
||||||
noIn = ~in
|
|
||||||
withIn =
|
|
||||||
|
|
||||||
noHas = ~has
|
|
||||||
withHas =
|
|
||||||
|
|
||||||
noInHas = ~(in | has)
|
|
||||||
withInHas =
|
|
||||||
|
|
||||||
// §A.3 Expressions -- https://es5.github.io/#A.3
|
|
||||||
|
|
||||||
PrimaryExpression = this
|
|
||||||
| identifier
|
|
||||||
| literal
|
|
||||||
// ( litToken.type === "regexp"
|
|
||||||
// ? this.ast(_fromIdx, "RegExpExpr",{body: litToken.value.body
|
|
||||||
// flags: litToken.value.flags}, [])
|
|
||||||
// : this.ast(_fromIdx, "LiteralExpr",{type: litToken.type
|
|
||||||
// value: litToken.value}, []) )
|
|
||||||
| ArrayLiteral
|
|
||||||
| ObjectLiteral
|
|
||||||
| "(" Expression<withIn> ")" -- parenExpr
|
|
||||||
|
|
||||||
ArrayLiteral = "[" ListOf<AssignmentExpressionOrElision, ","> "]"
|
|
||||||
AssignmentExpressionOrElision = AssignmentExpression<withIn>
|
|
||||||
| -- elision
|
|
||||||
|
|
||||||
ObjectLiteral = "{" ListOf<PropertyAssignment, ","> "}" -- noTrailingComma
|
|
||||||
| "{" NonemptyListOf<PropertyAssignment, ","> "," "}" -- trailingComma
|
|
||||||
|
|
||||||
PropertyAssignment = get PropertyName "(" ")" "{" FunctionBody "}" -- getter
|
|
||||||
| set PropertyName "(" FormalParameter ")" "{" FunctionBody "}" -- setter
|
|
||||||
| PropertyName ":" AssignmentExpression<withIn> -- simple
|
|
||||||
|
|
||||||
PropertyName = identifierName
|
|
||||||
| stringLiteral
|
|
||||||
| numericLiteral
|
|
||||||
|
|
||||||
MemberExpression = MemberExpression "[" Expression<withIn> "]" -- arrayRefExp
|
|
||||||
| MemberExpression "." identifierName -- propRefExp
|
|
||||||
| new MemberExpression Arguments -- newExp
|
|
||||||
| FunctionExpression
|
|
||||||
| PrimaryExpression
|
|
||||||
|
|
||||||
NewExpression = MemberExpression
|
|
||||||
| new NewExpression -- newExp
|
|
||||||
|
|
||||||
CallExpression = CallExpression "[" Expression<withIn> "]" -- arrayRefExp
|
|
||||||
| CallExpression "." identifierName -- propRefExp
|
|
||||||
| CallExpression Arguments -- callExpExp
|
|
||||||
| MemberExpression Arguments -- memberExpExp
|
|
||||||
|
|
||||||
Arguments = "(" ListOf<AssignmentExpression<withIn>, ","> ")"
|
|
||||||
|
|
||||||
LeftHandSideExpression = CallExpression
|
|
||||||
| NewExpression
|
|
||||||
|
|
||||||
PostfixExpression = LeftHandSideExpression #(spacesNoNL "++") -- postIncrement
|
|
||||||
| LeftHandSideExpression #(spacesNoNL "--") -- postDecrement
|
|
||||||
| LeftHandSideExpression
|
|
||||||
|
|
||||||
UnaryExpression = void UnaryExpression -- voidExp
|
|
||||||
| "++" UnaryExpression -- preIncrement
|
|
||||||
| "--" UnaryExpression -- preDecrement
|
|
||||||
| "+" UnaryExpression -- unaryPlus
|
|
||||||
| "-" UnaryExpression -- unaryMinus
|
|
||||||
| "~" UnaryExpression -- bnot
|
|
||||||
| "!" UnaryExpression -- lnot
|
|
||||||
| PostfixExpression
|
|
||||||
|
|
||||||
MultiplicativeExpression = MultiplicativeExpression "*" UnaryExpression -- mul
|
|
||||||
| MultiplicativeExpression "/" UnaryExpression -- div
|
|
||||||
| MultiplicativeExpression "%" UnaryExpression -- mod
|
|
||||||
| UnaryExpression
|
|
||||||
|
|
||||||
AdditiveExpression = AdditiveExpression "+" MultiplicativeExpression -- add
|
|
||||||
| AdditiveExpression "-" MultiplicativeExpression -- sub
|
|
||||||
| MultiplicativeExpression
|
|
||||||
|
|
||||||
ShiftExpression = ShiftExpression "<<" AdditiveExpression -- lsl
|
|
||||||
| ShiftExpression ">>>" AdditiveExpression -- lsr
|
|
||||||
| ShiftExpression ">>" AdditiveExpression -- asr
|
|
||||||
| AdditiveExpression
|
|
||||||
|
|
||||||
RelationalExpression<guardIn>
|
|
||||||
= RelationalExpression<guardIn> "<" ShiftExpression -- lt
|
|
||||||
| RelationalExpression<guardIn> ">" ShiftExpression -- gt
|
|
||||||
| RelationalExpression<guardIn> "<=" ShiftExpression -- le
|
|
||||||
| RelationalExpression<guardIn> ">=" ShiftExpression -- ge
|
|
||||||
| RelationalExpression<guardIn> guardIn "in" ShiftExpression -- inExp
|
|
||||||
| ShiftExpression
|
|
||||||
|
|
||||||
EqualityExpression<guardIn>
|
|
||||||
= EqualityExpression<guardIn> "==" RelationalExpression<guardIn> -- equal
|
|
||||||
| EqualityExpression<guardIn> "!=" RelationalExpression<guardIn> -- notEqual
|
|
||||||
| EqualityExpression<guardIn> "===" RelationalExpression<guardIn> -- eq
|
|
||||||
| EqualityExpression<guardIn> "!==" RelationalExpression<guardIn> -- notEq
|
|
||||||
| RelationalExpression<guardIn>
|
|
||||||
|
|
||||||
BitwiseANDExpression<guardIn>
|
|
||||||
= BitwiseANDExpression<guardIn> "&" EqualityExpression<guardIn> -- band
|
|
||||||
| EqualityExpression<guardIn>
|
|
||||||
|
|
||||||
BitwiseXORExpression<guardIn>
|
|
||||||
= BitwiseXORExpression<guardIn> "^" BitwiseANDExpression<guardIn> -- bxor
|
|
||||||
| BitwiseANDExpression<guardIn>
|
|
||||||
|
|
||||||
BitwiseORExpression<guardIn>
|
|
||||||
= BitwiseORExpression<guardIn> "|" BitwiseXORExpression<guardIn> -- bor
|
|
||||||
| BitwiseXORExpression<guardIn>
|
|
||||||
|
|
||||||
LogicalANDExpression<guardIn>
|
|
||||||
= LogicalANDExpression<guardIn> "&&" BitwiseORExpression<guardIn> -- land
|
|
||||||
| BitwiseORExpression<guardIn>
|
|
||||||
|
|
||||||
LogicalORExpression<guardIn>
|
|
||||||
= LogicalORExpression<guardIn> "||" LogicalANDExpression<guardIn> -- lor
|
|
||||||
| LogicalANDExpression<guardIn>
|
|
||||||
|
|
||||||
ConditionalExpression<guardIn>
|
|
||||||
= LogicalORExpression<guardIn> "?" AssignmentExpression<withIn> ":" AssignmentExpression<guardIn> -- conditional
|
|
||||||
| LogicalORExpression<guardIn>
|
|
||||||
|
|
||||||
AssignmentExpression<guardIn>
|
|
||||||
= LeftHandSideExpression assignmentOperator AssignmentExpression<guardIn> -- assignment
|
|
||||||
| ConditionalExpression<guardIn>
|
|
||||||
|
|
||||||
Expression<guardIn> (an expression)
|
|
||||||
= Expression<guardIn> "," AssignmentExpression<guardIn> -- commaExp
|
|
||||||
| AssignmentExpression<guardIn>
|
|
||||||
|
|
||||||
assignmentOperator = "=" | ">>>=" | "<<=" | ">>="
|
|
||||||
| "*=" | "/=" | "%=" | "+=" | "-=" | "&=" | "^=" | "|="
|
|
||||||
|
|
||||||
// Statements -- (extends https://es5.github.io/#A.4)
|
|
||||||
|
|
||||||
Statement
|
|
||||||
= Block
|
|
||||||
| VariableStatement
|
|
||||||
| EmptyStatement
|
|
||||||
| ExpressionStatement
|
|
||||||
| IfStatement
|
|
||||||
| IterationStatement
|
|
||||||
| ContinueStatement
|
|
||||||
| BreakStatement
|
|
||||||
| ReturnStatement
|
|
||||||
|
|
||||||
Block = "{" StatementList "}"
|
|
||||||
LambdaParameters = "|" ListOf<identifier, ","> "|"
|
|
||||||
|
|
||||||
StatementList = Statement*
|
|
||||||
|
|
||||||
ComputedStatement = computed identifier Block
|
|
||||||
|
|
||||||
TrackStatement = track ListOf<identifier, ","> LambdaParameters? Block
|
|
||||||
|
|
||||||
VariableStatement = VariableAssignment VariableDeclarationList<withIn> #sc
|
|
||||||
|
|
||||||
VariableAssignment = var | const | live
|
|
||||||
|
|
||||||
VariableDeclarationList<guardIn> = NonemptyListOf<VariableDeclaration<guardIn>, ",">
|
|
||||||
|
|
||||||
VariableDeclaration<guardIn> = identifier Initialiser<guardIn>?
|
|
||||||
|
|
||||||
Initialiser<guardIn> = "=" AssignmentExpression<guardIn>
|
|
||||||
|
|
||||||
EmptyStatement = ";" // note: this semicolon eats newlines
|
|
||||||
|
|
||||||
ExpressionStatement = ~("{" | function) Expression<withIn> #sc
|
|
||||||
|
|
||||||
IfStatement = if "(" Expression<withIn> ")" Statement (else Statement)?
|
|
||||||
|
|
||||||
IterationStatement = do Statement while "(" Expression<withIn> ")" #sc -- doWhile
|
|
||||||
| while "(" Expression<withIn> ")" Statement -- whileDo
|
|
||||||
| for "(" Expression<noIn>? ";"
|
|
||||||
Expression<withIn>? ";"
|
|
||||||
Expression<withIn>? ")" Statement -- for3
|
|
||||||
| for "(" var VariableDeclarationList<noIn> ";"
|
|
||||||
Expression<withIn>? ";"
|
|
||||||
Expression<withIn>? ")" Statement -- for3var
|
|
||||||
| for "(" LeftHandSideExpression in
|
|
||||||
Expression<withIn> ")" Statement -- forIn
|
|
||||||
| for "(" var VariableDeclaration<noIn> in
|
|
||||||
Expression<withIn> ")" Statement -- forInVar
|
|
||||||
|
|
||||||
ContinueStatement = continue #((spacesNoNL identifier)? sc)
|
|
||||||
|
|
||||||
BreakStatement = break #((spacesNoNL identifier)? sc)
|
|
||||||
|
|
||||||
ReturnStatement = return (#(spacesNoNL ~space) Expression<withIn>)? #sc
|
|
||||||
|
|
||||||
Catch = catch "(" FormalParameter ")" Block
|
|
||||||
|
|
||||||
Defer = defer (when Expression<withIn>)? Block
|
|
||||||
|
|
||||||
// Pattern Matching
|
|
||||||
|
|
||||||
MatchExpr = match Expression<withIn> (if Pattern)? "{" MatchArm+ "}" (else Block)?
|
|
||||||
MatchArm = Pattern "=>" (Expression<withIn> | Block)
|
|
||||||
Pattern = identifier
|
|
||||||
| literal
|
|
||||||
| GuardPattern
|
|
||||||
GuardPattern = Expression<withIn> // Just an expression! No special syntax needed
|
|
||||||
|
|
||||||
|
|
||||||
// §A.5 Functions and Programs -- https://es5.github.io/#A.5
|
|
||||||
|
|
||||||
FunctionDeclaration
|
|
||||||
= function identifier "(" FormalParameterList ")" "{" FunctionBody "}"
|
|
||||||
|
|
||||||
FunctionExpression
|
|
||||||
= function identifier "(" FormalParameterList ")" "{" FunctionBody "}" -- named
|
|
||||||
| function "(" FormalParameterList ")" "{" FunctionBody "}" -- anonymous
|
|
||||||
|
|
||||||
FormalParameterList = ListOf<FormalParameter, ",">
|
|
||||||
|
|
||||||
FormalParameter = identifier
|
|
||||||
|
|
||||||
/*
|
|
||||||
Note: The Directive Prologue is the longest sequence of ExpressionStatement
|
|
||||||
productions occurring as the initial SourceElement (see https://es5.github.io/#x14.1)
|
|
||||||
*/
|
|
||||||
FunctionBody = &(Directive*) SourceElement*
|
|
||||||
|
|
||||||
SourceElement = Declaration | Statement
|
|
||||||
|
|
||||||
// Broken out so es6 can override to include ConstDecl and LetDecl
|
|
||||||
Declaration = FunctionDeclaration
|
|
||||||
|
|
||||||
Directive = stringLiteral #sc
|
|
||||||
}
|
|
||||||
|
|
||||||
ES5Lax <: ES5 {
|
|
||||||
futureReservedWord := futureReservedWordLax
|
|
||||||
}
|
|
||||||
|
|
106
stwl.ohm.bak
106
stwl.ohm.bak
|
@ -1,106 +0,0 @@
|
||||||
StronglyTypedWebLanguage {
|
|
||||||
Program = Statement*
|
|
||||||
|
|
||||||
Statement = ConstDec
|
|
||||||
| VarDec
|
|
||||||
| LiveDec
|
|
||||||
| ComputedDec
|
|
||||||
| TrackStmt
|
|
||||||
| ExprStmt
|
|
||||||
| Block
|
|
||||||
|
|
||||||
ConstDec = "const" identifier "=" Expr
|
|
||||||
VarDec = "var" identifier "=" Expr
|
|
||||||
LiveDec = "live" identifier "=" Expr
|
|
||||||
ComputedDec = "computed" identifier Block
|
|
||||||
|
|
||||||
TrackStmt = "track" ListOf<identifier, ","> LambdaParams? Block
|
|
||||||
ExprStmt = Expr // necessary?
|
|
||||||
|
|
||||||
Block = "{" Statement* "}"
|
|
||||||
LambdaParams = "|" ListOf<identifier, ","> "|"
|
|
||||||
|
|
||||||
ParExpr = "(" Expr ")"
|
|
||||||
Expr = AssignExpr
|
|
||||||
AssignExpr = CompareExpr ("=" CompareExpr)?
|
|
||||||
|
|
||||||
CompareExpr = AddExpr (CompareOp AddExpr)?
|
|
||||||
CompareOp = "==" | "!=" | "<=" | ">=" | "<" | ">"
|
|
||||||
|
|
||||||
AddExpr = MulExpr (("+"|"-") MulExpr)*
|
|
||||||
MulExpr = UnaryExpr (("*"|"/"|"%") UnaryExpr)*
|
|
||||||
|
|
||||||
UnaryExpr = "void" UnaryExpr -- voidExp
|
|
||||||
| "++" UnaryExpr -- preIncrement
|
|
||||||
| "--" UnaryExpr -- preDecrement
|
|
||||||
| "+" UnaryExpr -- unaryPlus
|
|
||||||
| "-" UnaryExpr -- unaryMinus
|
|
||||||
| "~" UnaryExpr -- bnot
|
|
||||||
| "!" UnaryExpr -- lnot
|
|
||||||
| PostfixExpr
|
|
||||||
| FunctionExpr
|
|
||||||
|
|
||||||
PostfixExpr = CallExpr "++" -- postIncrement
|
|
||||||
| CallExpr "--" -- postDecrement
|
|
||||||
| CallExpr
|
|
||||||
|
|
||||||
CallExpr = MemberExpr ("(" ListOf<Expr, ","> ")")?
|
|
||||||
MemberExpr = Literal ("." identifier)*
|
|
||||||
|
|
||||||
FunctionExpr = "fn" identifier "(" ListOf<identifier, ",">* ")" Block
|
|
||||||
|
|
||||||
Literal = numberLiteral
|
|
||||||
| stringLiteral
|
|
||||||
| booleanLiteral
|
|
||||||
| nullLiteral
|
|
||||||
| identifier
|
|
||||||
| ParExpr
|
|
||||||
| Block
|
|
||||||
|
|
||||||
// 123 or 12.34
|
|
||||||
numberLiteral = digit+ ("." digit+)?
|
|
||||||
|
|
||||||
stringLiteral = "\"" (~"\"" any)* "\""
|
|
||||||
| "'" (~"'" any)* "'"
|
|
||||||
| "`" (~"`" any)* "`"
|
|
||||||
|
|
||||||
identifier = ~reservedWord identifierStart (letter | digit | "$" | "_")*
|
|
||||||
identifierStart = letter | "$" | "_"
|
|
||||||
reservedWord = keyword | futureReservedWord | nullLiteral | booleanLiteral
|
|
||||||
|
|
||||||
nullLiteral = "null"
|
|
||||||
booleanLiteral = "true" | "false"
|
|
||||||
|
|
||||||
keyword = "const" | "var" | "live" | "computed" | "track" | "when" | "while"
|
|
||||||
| "if" | "else" | "for" | "unreachable" | "catch" | "async" | "await"
|
|
||||||
| "interface" | "struct" | "private" | "public" | "defer" | "fn"
|
|
||||||
| "break" | "void" | "return" | "import" | "export" | "switch"
|
|
||||||
| "continue"
|
|
||||||
|
|
||||||
futureReservedWord = "new" | "class" | "enum" | "extends" | "super"
|
|
||||||
| "implements" | "yield"
|
|
||||||
|
|
||||||
|
|
||||||
sourceCharacter = any
|
|
||||||
|
|
||||||
// Override Ohm's built-in definition of space.
|
|
||||||
space := whitespace | lineTerminator | comment
|
|
||||||
|
|
||||||
whitespace = "\t"
|
|
||||||
| "\x0B" -- verticalTab
|
|
||||||
| "\x0C" -- formFeed
|
|
||||||
| " "
|
|
||||||
| "\u00A0" -- noBreakSpace
|
|
||||||
| "\uFEFF" -- byteOrderMark
|
|
||||||
| unicodeSpaceSeparator
|
|
||||||
|
|
||||||
unicodeSpaceSeparator = "\u2000".."\u200B" | "\u3000"
|
|
||||||
|
|
||||||
lineTerminator = "\n" | "\r" | "\u2028" | "\u2029"
|
|
||||||
lineTerminatorSequence = "\n" | "\r" ~"\n" | "\u2028" | "\u2029" | "\r\n"
|
|
||||||
|
|
||||||
comment = multiLineComment | singleLineComment
|
|
||||||
|
|
||||||
multiLineComment = "/*" (~"*/" sourceCharacter)* "*/"
|
|
||||||
singleLineComment = "//" (~lineTerminator sourceCharacter)*
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue