Compare commits
3 Commits
7a4affd01e
...
34c1177636
| Author | SHA1 | Date | |
|---|---|---|---|
| 34c1177636 | |||
| 339c09eb8c | |||
| 7da4c14962 |
1
packages/ReefVM
Submodule
1
packages/ReefVM
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 97b6722a113417398a1c47d583bfe07a906f87a0
|
||||
|
|
@ -468,6 +468,13 @@ export class Compiler {
|
|||
return instructions
|
||||
}
|
||||
|
||||
case terms.Array: {
|
||||
const children = getAllChildren(node)
|
||||
const instructions: ProgramItem[] = children.map((x) => this.#compileNode(x, input)).flat()
|
||||
instructions.push(['MAKE_ARRAY', children.length])
|
||||
return instructions
|
||||
}
|
||||
|
||||
default:
|
||||
throw new CompilerError(
|
||||
`Compiler doesn't know how to handle a "${node.type.name}" node.`,
|
||||
|
|
|
|||
96
src/compiler/tests/literals.test.ts
Normal file
96
src/compiler/tests/literals.test.ts
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
import { describe } from 'bun:test'
|
||||
import { expect, test } from 'bun:test'
|
||||
|
||||
describe('array literals', () => {
|
||||
test('work with numbers', () => {
|
||||
expect('[1 2 3]').toEvaluateTo([1, 2, 3])
|
||||
})
|
||||
|
||||
test('work with strings', () => {
|
||||
expect("['one' 'two' 'three']").toEvaluateTo(['one', 'two', 'three'])
|
||||
})
|
||||
|
||||
test('work with identifiers', () => {
|
||||
expect('[one two three]').toEvaluateTo(['one', 'two', 'three'])
|
||||
})
|
||||
|
||||
test('can be nested', () => {
|
||||
expect('[one [two [three]]]').toEvaluateTo(['one', ['two', ['three']]])
|
||||
})
|
||||
|
||||
test('can span multiple lines', () => {
|
||||
expect(`[
|
||||
1
|
||||
2
|
||||
3
|
||||
]`).toEvaluateTo([1, 2, 3])
|
||||
})
|
||||
|
||||
test('can span multiple w/o calling functions', () => {
|
||||
expect(`[
|
||||
one
|
||||
two
|
||||
three
|
||||
]`).toEvaluateTo(['one', 'two', 'three'])
|
||||
})
|
||||
|
||||
test('empty arrays', () => {
|
||||
expect('[]').toEvaluateTo([])
|
||||
})
|
||||
|
||||
test('mixed types', () => {
|
||||
expect("[1 'two' three true null]").toEvaluateTo([1, 'two', 'three', true, null])
|
||||
})
|
||||
|
||||
test('semicolons as separators', () => {
|
||||
expect('[1; 2; 3]').toEvaluateTo([1, 2, 3])
|
||||
})
|
||||
|
||||
test('expressions in arrays', () => {
|
||||
expect('[(1 + 2) (3 * 4)]').toEvaluateTo([3, 12])
|
||||
})
|
||||
|
||||
test('mixed separators - spaces and newlines', () => {
|
||||
expect(`[1 2
|
||||
3 4]`).toEvaluateTo([1, 2, 3, 4])
|
||||
})
|
||||
|
||||
test('mixed separators - spaces and semicolons', () => {
|
||||
expect('[1 2; 3 4]').toEvaluateTo([1, 2, 3, 4])
|
||||
})
|
||||
|
||||
test('empty lines within arrays', () => {
|
||||
expect(`[1
|
||||
|
||||
2]`).toEvaluateTo([1, 2])
|
||||
})
|
||||
|
||||
test('comments within arrays', () => {
|
||||
expect(`[1 # first
|
||||
2 # second
|
||||
]`).toEvaluateTo([1, 2])
|
||||
})
|
||||
|
||||
test('complex nested multiline', () => {
|
||||
expect(`[
|
||||
[1 2]
|
||||
[3 4]
|
||||
[5 6]
|
||||
]`).toEvaluateTo([[1, 2], [3, 4], [5, 6]])
|
||||
})
|
||||
|
||||
test('boolean and null literals', () => {
|
||||
expect('[true false null]').toEvaluateTo([true, false, null])
|
||||
})
|
||||
|
||||
test('regex literals', () => {
|
||||
expect('[//[0-9]+//]').toEvaluateTo([/[0-9]+/])
|
||||
})
|
||||
|
||||
test('trailing newlines', () => {
|
||||
expect(`[
|
||||
1
|
||||
2
|
||||
]`).toEvaluateTo([1, 2])
|
||||
})
|
||||
})
|
||||
|
|
@ -191,6 +191,10 @@ EscapeSeq {
|
|||
"\\" ("$" | "n" | "t" | "r" | "\\" | "'")
|
||||
}
|
||||
|
||||
Array {
|
||||
"[" (newlineOrSemicolon | expression)* "]"
|
||||
}
|
||||
|
||||
// We need expressionWithoutIdentifier to avoid conflicts in consumeToTerminator.
|
||||
// Without this, when parsing "my-var" at statement level, the parser can't decide:
|
||||
// - ambiguousFunctionCall → FunctionCallOrIdentifier → Identifier
|
||||
|
|
@ -200,7 +204,7 @@ EscapeSeq {
|
|||
// to go through ambiguousFunctionCall (which is what we want semantically).
|
||||
// Yes, it is annoying and I gave up trying to use GLR to fix it.
|
||||
expressionWithoutIdentifier {
|
||||
ParenExpr | Word | String | Number | Boolean | Regex | @specialize[@name=Null]<Identifier, "null">
|
||||
ParenExpr | Word | String | Number | Boolean | Regex | Array | @specialize[@name=Null]<Identifier, "null">
|
||||
}
|
||||
|
||||
block {
|
||||
|
|
|
|||
|
|
@ -31,19 +31,20 @@ export const
|
|||
EscapeSeq = 29,
|
||||
Boolean = 30,
|
||||
Regex = 31,
|
||||
Null = 32,
|
||||
ConditionalOp = 33,
|
||||
FunctionDef = 34,
|
||||
Params = 35,
|
||||
colon = 36,
|
||||
keyword = 50,
|
||||
PositionalArg = 38,
|
||||
Underscore = 39,
|
||||
NamedArg = 40,
|
||||
NamedArgPrefix = 41,
|
||||
IfExpr = 43,
|
||||
SingleLineThenBlock = 45,
|
||||
ThenBlock = 46,
|
||||
ElseIfExpr = 47,
|
||||
ElseExpr = 49,
|
||||
Assign = 51
|
||||
Array = 32,
|
||||
Null = 33,
|
||||
ConditionalOp = 34,
|
||||
FunctionDef = 35,
|
||||
Params = 36,
|
||||
colon = 37,
|
||||
keyword = 51,
|
||||
PositionalArg = 39,
|
||||
Underscore = 40,
|
||||
NamedArg = 41,
|
||||
NamedArgPrefix = 42,
|
||||
IfExpr = 44,
|
||||
SingleLineThenBlock = 46,
|
||||
ThenBlock = 47,
|
||||
ElseIfExpr = 48,
|
||||
ElseExpr = 50,
|
||||
Assign = 52
|
||||
|
|
|
|||
|
|
@ -4,24 +4,24 @@ import {operatorTokenizer} from "./operatorTokenizer"
|
|||
import {tokenizer, specializeKeyword} from "./tokenizer"
|
||||
import {trackScope} from "./scopeTracker"
|
||||
import {highlighting} from "./highlight"
|
||||
const spec_Identifier = {__proto__:null,null:64, end:74, if:88, elseif:96, else:100}
|
||||
const spec_Identifier = {__proto__:null,null:66, end:76, if:90, elseif:98, else:102}
|
||||
export const parser = LRParser.deserialize({
|
||||
version: 14,
|
||||
states: "/SQYQbOOO!TOpO'#CqO#aQcO'#CtO$ZOSO'#CvO%aQcO'#DsOOQa'#Ds'#DsO&gQcO'#DrO'OQRO'#CuO'^QcO'#DnO'uQbO'#D{OOQ`'#DO'#DOO'}QbO'#CsOOQ`'#Do'#DoO(oQbO'#DnO(}QbO'#EROOQ`'#DX'#DXO)lQRO'#DaOOQ`'#Dn'#DnO)qQQO'#DmOOQ`'#Dm'#DmOOQ`'#Db'#DbQYQbOOO)yObO,59]OOQa'#Dr'#DrOOQ`'#DS'#DSO*RQbO'#DUOOQ`'#EQ'#EQOOQ`'#Df'#DfO*]QbO,59[O*pQbO'#CxO*xQWO'#CyOOOO'#Du'#DuOOOO'#Dc'#DcO+^OSO,59bOOQa,59b,59bO(}QbO,59aO(}QbO,59aOOQ`'#Dd'#DdO+lQbO'#DPO+tQQO,5:gO+yQRO,59_O-`QRO'#CuO-pQRO,59_O-|QQO,59_O.RQQO,59_O.ZQbO'#DgO.fQbO,59ZO.wQRO,5:mO/OQQO,5:mO/TQbO,59{OOQ`,5:X,5:XOOQ`-E7`-E7`OOQa1G.w1G.wOOQ`,59p,59pOOQ`-E7d-E7dOOOO,59d,59dOOOO,59e,59eOOOO-E7a-E7aOOQa1G.|1G.|OOQa1G.{1G.{O/_QcO1G.{OOQ`-E7b-E7bO/yQbO1G0ROOQa1G.y1G.yO(}QbO,59iO(}QbO,59iO!YQbO'#CtO$iQbO'#CpOOQ`,5:R,5:ROOQ`-E7e-E7eO0WQbO1G0XOOQ`1G/g1G/gO0eQbO7+%mO0jQbO7+%nOOQO1G/T1G/TO0zQRO1G/TOOQ`'#DZ'#DZO1UQbO7+%sO1ZQbO7+%tOOQ`<<IX<<IXOOQ`'#De'#DeO1qQQO'#DeO1vQbO'#EOO2^QbO<<IYOOQ`<<I_<<I_OOQ`'#D['#D[O2cQbO<<I`OOQ`,5:P,5:POOQ`-E7c-E7cOOQ`AN>tAN>tO(}QbO'#D]OOQ`'#Dh'#DhO2nQbOAN>zO2yQQO'#D_OOQ`AN>zAN>zO3OQbOAN>zO3TQRO,59wO3[QQO,59wOOQ`-E7f-E7fOOQ`G24fG24fO3aQbOG24fO3fQQO,59yO3kQQO1G/cOOQ`LD*QLD*QO0jQbO1G/eO1ZQbO7+$}OOQ`7+%P7+%POOQ`<<Hi<<Hi",
|
||||
stateData: "3s~O!_OS!`OS~O]QO^`O_TO`POaXOfTOnTOoTOpTO|^O!eZO!hRO!qcO~O!dfO~O]gO_TO`POaXOfTOnTOoTOpTOwhOyiO!eZO!hROzhX!qhX!whX!shXuhX~OP!fXQ!fXR!fXS!fXT!fXU!fXV!fXW!fXX!fXY!fXZ!fX[!fX~P!YOkoO!hrO!jmO!knO~O]gO_TO`POaXOfTOnTOoTOpTOwhOyiO!eZO!hRO~OP!gXQ!gXR!gXS!gX!q!gX!w!gXT!gXU!gXV!gXW!gXX!gXY!gXZ!gX[!gX!s!gXu!gX~P$iOP!fXQ!fXR!fXS!fX!q!bX!w!bXu!bX~OPsOQsORtOStO~OPsOQsORtOStO!q!bX!w!bXu!bX~O]uOtsP~O]QO_TO`POaXOfTOnTOoTOpTO!eZO!hRO~Oz}O!q!bX!w!bXu!bX~O]gO_TO`POfTOnTOoTOpTO!eZO!hRO~OV!RO~O!q!SO!w!SO~O]!UOf!UO~OaXOw!VO~P(}Ozda!qda!wda!sdauda~P$iO]!XO!eZO~O!h!YO!j!YO!k!YO!l!YO!m!YO!n!YO~OkoO!h![O!jmO!knO~O]uOtsX~Ot!`O~O!s!aOP!fXQ!fXR!fXS!fXT!fXU!fXV!fXW!fXX!fXY!fXZ!fX[!fX~OT!cOU!cOV!bOW!bOX!bOY!bOZ!bO[!bO~OPsOQsORtOStO~P,tOPsOQsORtOStO!s!aO~Oz}O!s!aO~O]!dO`PO!eZO~Oz}O!qca!wca!scauca~Ot!hO~P,tOt!hO~O^`O|^O~P'}OPsOQsORiiSii!qii!wii!siiuii~O^`O|^O!q!kO~P'}O^`O|^O!q!pO~P'}Ou!qO~O^`O|^O!q!rOu!rP~P'}O!sqitqi~P,tOu!vO~O^`O|^O!q!rOu!rP!Q!rP!S!rP~P'}O!q!yO~O^`O|^O!q!rOu!rX!Q!rX!S!rX~P'}Ou!{O~Ou#QO!Q!|O!S#PO~Ou#VO!Q!|O!S#PO~Ot#XO~Ou#VO~Ot#YO~P,tOt#YO~Ou#ZO~O!q#[O~O!q#]O~Ofo~",
|
||||
goto: ",`!wPPPPPPPPPPPPPPPPPPP!x#X#gP$V#X$x%_P%x%xPPP%|&Y&sPP&vP&vPP&}P'Z'^'gP'kP&}'q'w'}(T(^(g(nPPPP(t(x)^PP)p*mP+[PPPPP+`+`P+sP+{,S,SdaOe!R!`!h!k!p!t#[#]R{Zi[OZe}!R!`!h!k!p!t#[#]fQOZe!R!`!h!k!p!t#[#]hgQS^ilst!b!c!d!e!|R!d}fSOZe!R!`!h!k!p!t#[#]hTQS^ilst!b!c!d!e!|Q!XmR!e}dWOe!R!`!h!k!p!t#[#]QzZQ!]sR!^t!PTOQSZ^eilst!R!`!b!c!d!e!h!k!p!t!|#[#]ToRqQ{ZQ!Q^Q!l!cR#T!|daOe!R!`!h!k!p!t#[#]YhQSl!d!eQ{ZR!ViRwXZjQSl!d!eeaOe!R!`!h!k!p!t#[#]R!o!hQ!x!pQ#^#[R#_#]T!}!x#OQ#R!xR#W#OQeOR!TeQqRR!ZqQvXR!_vW!t!k!p#[#]R!z!tWlQS!d!eR!WlS!O]|R!g!OQ#O!xR#U#OTdOeSbOeQ!i!RQ!j!`Q!n!hZ!s!k!p!t#[#]d]Oe!R!`!h!k!p!t#[#]Q|ZR!f}dVOe!R!`!h!k!p!t#[#]YhQSl!d!eQyZQ!P^Q!ViQ!]sQ!^tQ!l!bQ!m!cR#S!|dUOe!R!`!h!k!p!t#[#]hgQS^ilst!b!c!d!e!|RxZTpRqsYOQSZeil!R!`!d!e!h!k!p!t#[#]Q!u!kV!w!p#[#]ZkQSl!d!ee_Oe!R!`!h!k!p!t#[#]",
|
||||
nodeNames: "⚠ Star Slash Plus Minus And Or Eq Neq Lt Lte Gt Gte Identifier AssignableIdentifier Word IdentifierBeforeDot Do Program PipeExpr FunctionCall DotGet Number ParenExpr FunctionCallOrIdentifier BinOp String StringFragment Interpolation EscapeSeq Boolean Regex Null ConditionalOp FunctionDef Params colon keyword PositionalArg Underscore NamedArg NamedArgPrefix operator IfExpr keyword SingleLineThenBlock ThenBlock ElseIfExpr keyword ElseExpr keyword Assign",
|
||||
maxTerm: 85,
|
||||
states: "/xQYQbOOO!WOpO'#CqO#gQcO'#CtO$aOSO'#CvO%dQbO'#C|O&fQcO'#DuOOQa'#Du'#DuO'lQcO'#DtO(TQRO'#CuO(cQcO'#DpO(zQbO'#EQOOQ`'#DP'#DPO)SQbO'#CsOOQ`'#Dq'#DqO)wQbO'#DpO*VQbO'#EVOOQ`'#DY'#DYO*wQRO'#DbOOQ`'#Dp'#DpO*|QQO'#DoOOQ`'#Do'#DoOOQ`'#Dc'#DcQYQbOOO+UObO,59]OOQa'#Dt'#DtOOQ`'#DT'#DTO+^QbO'#DVOOQ`'#EU'#EUOOQ`'#Dh'#DhO+hQbO,59[O+{QbO'#CxO,TQWO'#CyOOOO'#Dw'#DwOOOO'#Dd'#DdO,iOSO,59bOOQa,59b,59bOOQ`'#De'#DeO,wQbO,59hOOQa,59h,59hO*VQbO,59aO*VQbO,59aOOQ`'#Df'#DfO-OQbO'#DQO-WQQO,5:lO-]QRO,59_O.rQRO'#CuO/SQRO,59_O/`QQO,59_O/eQQO,59_O/mQbO'#DiO/xQbO,59ZO0ZQRO,5:qO0bQQO,5:qO0gQbO,59|OOQ`,5:Z,5:ZOOQ`-E7a-E7aOOQa1G.w1G.wOOQ`,59q,59qOOQ`-E7f-E7fOOOO,59d,59dOOOO,59e,59eOOOO-E7b-E7bOOQa1G.|1G.|OOQ`-E7c-E7cOOQa1G/S1G/SOOQa1G.{1G.{O0qQcO1G.{OOQ`-E7d-E7dO1]QbO1G0WOOQa1G.y1G.yO*VQbO,59jO*VQbO,59jO!]QbO'#CtO%kQbO'#CpOOQ`,5:T,5:TOOQ`-E7g-E7gO1jQbO1G0]OOQ`1G/h1G/hO1wQbO7+%rO1|QbO7+%sOOQO1G/U1G/UO2^QRO1G/UOOQ`'#D['#D[O2hQbO7+%wO2mQbO7+%xOOQ`<<I^<<I^OOQ`'#Dg'#DgO3TQQO'#DgO3YQbO'#ESO3pQbO<<I_OOQ`<<Ic<<IcOOQ`'#D]'#D]O3uQbO<<IdOOQ`,5:R,5:ROOQ`-E7e-E7eOOQ`AN>yAN>yO*VQbO'#D^OOQ`'#Dj'#DjO4QQbOAN?OO4]QQO'#D`OOQ`AN?OAN?OO4bQbOAN?OO4gQRO,59xO4nQQO,59xOOQ`-E7h-E7hOOQ`G24jG24jO4sQbOG24jO4xQQO,59zO4}QQO1G/dOOQ`LD*ULD*UO1|QbO1G/fO2mQbO7+%OOOQ`7+%Q7+%QOOQ`<<Hj<<Hj",
|
||||
stateData: "5V~O!aOS!bOS~O]QO^aO_UO`POaYOfUOnUOoUOqUO}_O!g[O!jRO!qSO!rdO~O!fgO~O]hO_UO`POaYOfUOnUOoUOqUOxiOzjO!g[O!jRO!qSO{hX!rhX!{hX!whXvhX~OP!hXQ!hXR!hXS!hXT!hXU!hXV!hXW!hXX!hXY!hXZ!hX[!hX~P!]OkpO!jsO!lnO!moO~O]hO_UO`POfUOnUOoUOqUO!g[O!jRO!qSO!rtO~O!svO~P$oO]hO_UO`POaYOfUOnUOoUOqUOxiOzjO!g[O!jRO!qSO~OP!iXQ!iXR!iXS!iX!r!iX!{!iXT!iXU!iXV!iXW!iXX!iXY!iXZ!iX[!iX!w!iXv!iX~P%kOP!hXQ!hXR!hXS!hX!r!dX!{!dXv!dX~OPwOQwORxOSxO~OPwOQwORxOSxO!r!dX!{!dXv!dX~O]yOutP~O]QO_UO`POaYOfUOnUOoUOqUO!g[O!jRO!qSO~O{!RO!r!dX!{!dXv!dX~O]hO_UO`POfUOnUOoUOqUO!g[O!jRO!qSO~OV!VO~O!r!WO!{!WO~O]!YOf!YO~OaYOx!ZO~P*VO{da!rda!{da!wdavda~P%kO]!]O!g[O~O!j!^O!l!^O!m!^O!n!^O!o!^O!p!^O~OkpO!j!`O!lnO!moO~O!s!bO~P$oO]yOutX~Ou!fO~O!w!gOP!hXQ!hXR!hXS!hXT!hXU!hXV!hXW!hXX!hXY!hXZ!hX[!hX~OT!iOU!iOV!hOW!hOX!hOY!hOZ!hO[!hO~OPwOQwORxOSxO~P.WOPwOQwORxOSxO!w!gO~O{!RO!w!gO~O]!jO`PO!g[O~O{!RO!rca!{ca!wcavca~Ou!nO~P.WOu!nO~O^aO}_O~P)SOPwOQwORiiSii!rii!{ii!wiivii~O^aO}_O!r!qO~P)SO^aO}_O!r!vO~P)SOv!wO~O^aO}_O!r!xOv!vP~P)SO!wriuri~P.WOv!|O~O^aO}_O!r!xOv!vP!R!vP!T!vP~P)SO!r#PO~O^aO}_O!r!xOv!vX!R!vX!T!vX~P)SOv#RO~Ov#WO!R#SO!T#VO~Ov#]O!R#SO!T#VO~Ou#_O~Ov#]O~Ou#`O~P.WOu#`O~Ov#aO~O!r#bO~O!r#cO~Ofo~",
|
||||
goto: ",v!{PPPPPPPPPPPPPPPPPPP!|#]#kP$]#]%Q%gP&S&SPP%gP&W&d&}PP'QP'QPP'XP'e'h'qP'uP'X'{(R(X(_(e(n(w)OPPPP)U)Y)nPP*Q+RP+rPPPPPPPP+v+v,ZP,c,j,jdbOf!V!f!n!q!v!z#b#cR!P[i]O[f!R!V!f!n!q!v!z#b#cfQO[f!V!f!n!q!v!z#b#clhQST_jmuwx!h!i!j!k#SR!j!RfTO[f!V!f!n!q!v!z#b#clUQST_jmuwx!h!i!j!k#SQ!]nR!k!RdXOf!V!f!n!q!v!z#b#cQ!O[Q!cwR!dx!TUOQST[_fjmuwx!V!f!h!i!j!k!n!q!v!z#S#b#cTpRrQ!P[Q!U_Q!r!iR#Z#SdbOf!V!f!n!q!v!z#b#cYiQTm!j!kQ!P[R!ZjR{YZkQTm!j!kebOf!V!f!n!q!v!z#b#cR!u!nQ#O!vQ#d#bR#e#cT#T#O#UQ#X#OR#^#UQfOR!XfQrRR!_rQuSR!auQzYR!ezW!z!q!v#b#cR#Q!zWmQT!j!kR![mS!S^!QR!m!SQ#U#OR#[#UTeOfScOfQ!o!VQ!p!fQ!t!nZ!y!q!v!z#b#cd^Of!V!f!n!q!v!z#b#cQ!Q[R!l!RdWOf!V!f!n!q!v!z#b#cYiQTm!j!kStSuQ}[Q!T_Q!ZjQ!cwQ!dxQ!r!hQ!s!iR#Y#SdVOf!V!f!n!q!v!z#b#clhQST_jmuwx!h!i!j!k#SR|[TqRrsZOQT[fjm!V!f!j!k!n!q!v!z#b#cQ!{!qV!}!v#b#cZlQTm!j!ke`Of!V!f!n!q!v!z#b#c",
|
||||
nodeNames: "⚠ Star Slash Plus Minus And Or Eq Neq Lt Lte Gt Gte Identifier AssignableIdentifier Word IdentifierBeforeDot Do Program PipeExpr FunctionCall DotGet Number ParenExpr FunctionCallOrIdentifier BinOp String StringFragment Interpolation EscapeSeq Boolean Regex Array Null ConditionalOp FunctionDef Params colon keyword PositionalArg Underscore NamedArg NamedArgPrefix operator IfExpr keyword SingleLineThenBlock ThenBlock ElseIfExpr keyword ElseExpr keyword Assign",
|
||||
maxTerm: 89,
|
||||
context: trackScope,
|
||||
nodeProps: [
|
||||
["closedBy", 36,"end"]
|
||||
["closedBy", 37,"end"]
|
||||
],
|
||||
propSources: [highlighting],
|
||||
skippedNodes: [0],
|
||||
repeatNodeCount: 7,
|
||||
tokenData: ">i~RzOX#uXY$dYZ$}Zp#upq$dqs#ust%htu'Puw#uwx'Uxy'Zyz'tz{#u{|(_|}#u}!O(_!O!P#u!P!Q+R!Q![(|![!]3n!]!^$}!^#O#u#O#P4X#P#R#u#R#S4^#S#T#u#T#Y4w#Y#Z6V#Z#b4w#b#c:e#c#f4w#f#g;[#g#h4w#h#i<R#i#o4w#o#p#u#p#q=y#q;'S#u;'S;=`$^<%l~#u~O#u~~>dS#zUkSOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#uS$aP;=`<%l#u^$kUkS!_YOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#uU%UUkS!qQOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#u^%oZkS!`YOY%hYZ#uZt%htu&buw%hwx&bx#O%h#O#P&b#P;'S%h;'S;=`&y<%lO%hY&gS!`YOY&bZ;'S&b;'S;=`&s<%lO&bY&vP;=`<%l&b^&|P;=`<%l%h~'UO!j~~'ZO!h~U'bUkS!eQOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#uU'{UkS!sQOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#uU(dWkSOt#uuw#ux!Q#u!Q![(|![#O#u#P;'S#u;'S;=`$^<%lO#uU)TYkSfQOt#uuw#ux!O#u!O!P)s!P!Q#u!Q![(|![#O#u#P;'S#u;'S;=`$^<%lO#uU)xWkSOt#uuw#ux!Q#u!Q![*b![#O#u#P;'S#u;'S;=`$^<%lO#uU*iWkSfQOt#uuw#ux!Q#u!Q![*b![#O#u#P;'S#u;'S;=`$^<%lO#uU+WWkSOt#uuw#ux!P#u!P!Q+p!Q#O#u#P;'S#u;'S;=`$^<%lO#uU+u^kSOY,qYZ#uZt,qtu-tuw,qwx-tx!P,q!P!Q#u!Q!},q!}#O2g#O#P0S#P;'S,q;'S;=`3h<%lO,qU,x^kSoQOY,qYZ#uZt,qtu-tuw,qwx-tx!P,q!P!Q0i!Q!},q!}#O2g#O#P0S#P;'S,q;'S;=`3h<%lO,qQ-yXoQOY-tZ!P-t!P!Q.f!Q!}-t!}#O/T#O#P0S#P;'S-t;'S;=`0c<%lO-tQ.iP!P!Q.lQ.qUoQ#Z#[.l#]#^.l#a#b.l#g#h.l#i#j.l#m#n.lQ/WVOY/TZ#O/T#O#P/m#P#Q-t#Q;'S/T;'S;=`/|<%lO/TQ/pSOY/TZ;'S/T;'S;=`/|<%lO/TQ0PP;=`<%l/TQ0VSOY-tZ;'S-t;'S;=`0c<%lO-tQ0fP;=`<%l-tU0nWkSOt#uuw#ux!P#u!P!Q1W!Q#O#u#P;'S#u;'S;=`$^<%lO#uU1_bkSoQOt#uuw#ux#O#u#P#Z#u#Z#[1W#[#]#u#]#^1W#^#a#u#a#b1W#b#g#u#g#h1W#h#i#u#i#j1W#j#m#u#m#n1W#n;'S#u;'S;=`$^<%lO#uU2l[kSOY2gYZ#uZt2gtu/Tuw2gwx/Tx#O2g#O#P/m#P#Q,q#Q;'S2g;'S;=`3b<%lO2gU3eP;=`<%l2gU3kP;=`<%l,qU3uUkStQOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#u~4^O!k~U4eUkSwQOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#uU4|YkSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#o4w#o;'S#u;'S;=`$^<%lO#uU5sUyQkSOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#uU6[ZkSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#U6}#U#o4w#o;'S#u;'S;=`$^<%lO#uU7S[kSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#`4w#`#a7x#a#o4w#o;'S#u;'S;=`$^<%lO#uU7}[kSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#g4w#g#h8s#h#o4w#o;'S#u;'S;=`$^<%lO#uU8x[kSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#X4w#X#Y9n#Y#o4w#o;'S#u;'S;=`$^<%lO#uU9uYnQkSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#o4w#o;'S#u;'S;=`$^<%lO#u^:lY!lWkSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#o4w#o;'S#u;'S;=`$^<%lO#u^;cY!nWkSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#o4w#o;'S#u;'S;=`$^<%lO#u^<Y[!mWkSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#f4w#f#g=O#g#o4w#o;'S#u;'S;=`$^<%lO#uU=T[kSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#i4w#i#j8s#j#o4w#o;'S#u;'S;=`$^<%lO#uU>QUzQkSOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#u~>iO!w~",
|
||||
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO!d~~", 11)],
|
||||
repeatNodeCount: 8,
|
||||
tokenData: "?s~R|OX#{XY$jYZ%TZp#{pq$jqs#{st%ntu'Vuw#{wx'[xy'ayz'zz{#{{|(e|}#{}!O(e!O!P#{!P!Q+X!Q![)S![!]3t!]!^%T!^!}#{!}#O4_#O#P4x#P#Q4}#Q#R#{#R#S5h#S#T#{#T#Y6R#Y#Z7a#Z#b6R#b#c;o#c#f6R#f#g<f#g#h6R#h#i=]#i#o6R#o#p#{#p#q?T#q;'S#{;'S;=`$d<%l~#{~O#{~~?nS$QUkSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{S$gP;=`<%l#{^$qUkS!aYOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U%[UkS!rQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{^%uZkS!bYOY%nYZ#{Zt%ntu&huw%nwx&hx#O%n#O#P&h#P;'S%n;'S;=`'P<%lO%nY&mS!bYOY&hZ;'S&h;'S;=`&y<%lO&hY&|P;=`<%l&h^'SP;=`<%l%n~'[O!l~~'aO!j~U'hUkS!gQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(RUkS!wQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(jWkSOt#{uw#{x!Q#{!Q![)S![#O#{#P;'S#{;'S;=`$d<%lO#{U)ZYkSfQOt#{uw#{x!O#{!O!P)y!P!Q#{!Q![)S![#O#{#P;'S#{;'S;=`$d<%lO#{U*OWkSOt#{uw#{x!Q#{!Q![*h![#O#{#P;'S#{;'S;=`$d<%lO#{U*oWkSfQOt#{uw#{x!Q#{!Q![*h![#O#{#P;'S#{;'S;=`$d<%lO#{U+^WkSOt#{uw#{x!P#{!P!Q+v!Q#O#{#P;'S#{;'S;=`$d<%lO#{U+{^kSOY,wYZ#{Zt,wtu-zuw,wwx-zx!P,w!P!Q#{!Q!},w!}#O2m#O#P0Y#P;'S,w;'S;=`3n<%lO,wU-O^kSoQOY,wYZ#{Zt,wtu-zuw,wwx-zx!P,w!P!Q0o!Q!},w!}#O2m#O#P0Y#P;'S,w;'S;=`3n<%lO,wQ.PXoQOY-zZ!P-z!P!Q.l!Q!}-z!}#O/Z#O#P0Y#P;'S-z;'S;=`0i<%lO-zQ.oP!P!Q.rQ.wUoQ#Z#[.r#]#^.r#a#b.r#g#h.r#i#j.r#m#n.rQ/^VOY/ZZ#O/Z#O#P/s#P#Q-z#Q;'S/Z;'S;=`0S<%lO/ZQ/vSOY/ZZ;'S/Z;'S;=`0S<%lO/ZQ0VP;=`<%l/ZQ0]SOY-zZ;'S-z;'S;=`0i<%lO-zQ0lP;=`<%l-zU0tWkSOt#{uw#{x!P#{!P!Q1^!Q#O#{#P;'S#{;'S;=`$d<%lO#{U1ebkSoQOt#{uw#{x#O#{#P#Z#{#Z#[1^#[#]#{#]#^1^#^#a#{#a#b1^#b#g#{#g#h1^#h#i#{#i#j1^#j#m#{#m#n1^#n;'S#{;'S;=`$d<%lO#{U2r[kSOY2mYZ#{Zt2mtu/Zuw2mwx/Zx#O2m#O#P/s#P#Q,w#Q;'S2m;'S;=`3h<%lO2mU3kP;=`<%l2mU3qP;=`<%l,wU3{UkSuQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U4fU!qQkSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~4}O!m~U5UU!sQkSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U5oUkSxQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U6WYkSOt#{uw#{x!_#{!_!`6v!`#O#{#P#T#{#T#o6R#o;'S#{;'S;=`$d<%lO#{U6}UzQkSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U7fZkSOt#{uw#{x!_#{!_!`6v!`#O#{#P#T#{#T#U8X#U#o6R#o;'S#{;'S;=`$d<%lO#{U8^[kSOt#{uw#{x!_#{!_!`6v!`#O#{#P#T#{#T#`6R#`#a9S#a#o6R#o;'S#{;'S;=`$d<%lO#{U9X[kSOt#{uw#{x!_#{!_!`6v!`#O#{#P#T#{#T#g6R#g#h9}#h#o6R#o;'S#{;'S;=`$d<%lO#{U:S[kSOt#{uw#{x!_#{!_!`6v!`#O#{#P#T#{#T#X6R#X#Y:x#Y#o6R#o;'S#{;'S;=`$d<%lO#{U;PYnQkSOt#{uw#{x!_#{!_!`6v!`#O#{#P#T#{#T#o6R#o;'S#{;'S;=`$d<%lO#{^;vY!nWkSOt#{uw#{x!_#{!_!`6v!`#O#{#P#T#{#T#o6R#o;'S#{;'S;=`$d<%lO#{^<mY!pWkSOt#{uw#{x!_#{!_!`6v!`#O#{#P#T#{#T#o6R#o;'S#{;'S;=`$d<%lO#{^=d[!oWkSOt#{uw#{x!_#{!_!`6v!`#O#{#P#T#{#T#f6R#f#g>Y#g#o6R#o;'S#{;'S;=`$d<%lO#{U>_[kSOt#{uw#{x!_#{!_!`6v!`#O#{#P#T#{#T#i6R#i#j9}#j#o6R#o;'S#{;'S;=`$d<%lO#{U?[U{QkSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~?sO!{~",
|
||||
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO!f~~", 11)],
|
||||
topRules: {"Program":[0,18]},
|
||||
specialized: [{term: 13, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 13, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}],
|
||||
tokenPrec: 860
|
||||
tokenPrec: 924
|
||||
})
|
||||
|
|
|
|||
203
src/parser/tests/literals.test.ts
Normal file
203
src/parser/tests/literals.test.ts
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
import { expect, describe, test } from 'bun:test'
|
||||
|
||||
import '../shrimp.grammar' // Importing this so changes cause it to retest!
|
||||
|
||||
describe('array literals', () => {
|
||||
test('work with numbers', () => {
|
||||
expect('[1 2 3]').toMatchTree(`
|
||||
Array
|
||||
Number 1
|
||||
Number 2
|
||||
Number 3
|
||||
`)
|
||||
})
|
||||
|
||||
test('work with strings', () => {
|
||||
expect("['one' 'two' 'three']").toMatchTree(`
|
||||
Array
|
||||
String
|
||||
StringFragment one
|
||||
String
|
||||
StringFragment two
|
||||
String
|
||||
StringFragment three
|
||||
`)
|
||||
})
|
||||
|
||||
test('work with identifiers', () => {
|
||||
expect('[one two three]').toMatchTree(`
|
||||
Array
|
||||
Identifier one
|
||||
Identifier two
|
||||
Identifier three
|
||||
`)
|
||||
})
|
||||
|
||||
test('can be nested', () => {
|
||||
expect('[one [two [three]]]').toMatchTree(`
|
||||
Array
|
||||
Identifier one
|
||||
Array
|
||||
Identifier two
|
||||
Array
|
||||
Identifier three
|
||||
`)
|
||||
})
|
||||
|
||||
test('can span multiple lines', () => {
|
||||
expect(`[
|
||||
1
|
||||
2
|
||||
3
|
||||
]`).toMatchTree(`
|
||||
Array
|
||||
Number 1
|
||||
Number 2
|
||||
Number 3
|
||||
`)
|
||||
})
|
||||
|
||||
test('can span multiple w/o calling functions', () => {
|
||||
expect(`[
|
||||
one
|
||||
two
|
||||
three
|
||||
]`).toMatchTree(`
|
||||
Array
|
||||
Identifier one
|
||||
Identifier two
|
||||
Identifier three
|
||||
`)
|
||||
})
|
||||
|
||||
test('empty arrays', () => {
|
||||
expect('[]').toMatchTree(`
|
||||
Array []
|
||||
`)
|
||||
})
|
||||
|
||||
test('mixed types', () => {
|
||||
expect("[1 'two' three true null]").toMatchTree(`
|
||||
Array
|
||||
Number 1
|
||||
String
|
||||
StringFragment two
|
||||
Identifier three
|
||||
Boolean true
|
||||
Null null
|
||||
`)
|
||||
})
|
||||
|
||||
test('semicolons as separators', () => {
|
||||
expect('[1; 2; 3]').toMatchTree(`
|
||||
Array
|
||||
Number 1
|
||||
Number 2
|
||||
Number 3
|
||||
`)
|
||||
})
|
||||
|
||||
test('expressions in arrays', () => {
|
||||
expect('[(1 + 2) (3 * 4)]').toMatchTree(`
|
||||
Array
|
||||
ParenExpr
|
||||
BinOp
|
||||
Number 1
|
||||
Plus +
|
||||
Number 2
|
||||
ParenExpr
|
||||
BinOp
|
||||
Number 3
|
||||
Star *
|
||||
Number 4
|
||||
`)
|
||||
})
|
||||
|
||||
test('mixed separators - spaces and newlines', () => {
|
||||
expect(`[1 2
|
||||
3 4]`).toMatchTree(`
|
||||
Array
|
||||
Number 1
|
||||
Number 2
|
||||
Number 3
|
||||
Number 4
|
||||
`)
|
||||
})
|
||||
|
||||
test('mixed separators - spaces and semicolons', () => {
|
||||
expect('[1 2; 3 4]').toMatchTree(`
|
||||
Array
|
||||
Number 1
|
||||
Number 2
|
||||
Number 3
|
||||
Number 4
|
||||
`)
|
||||
})
|
||||
|
||||
test('empty lines within arrays', () => {
|
||||
expect(`[1
|
||||
|
||||
2]`).toMatchTree(`
|
||||
Array
|
||||
Number 1
|
||||
Number 2
|
||||
`)
|
||||
})
|
||||
|
||||
test('comments within arrays', () => {
|
||||
expect(`[ # something...
|
||||
1 # first
|
||||
2 # second
|
||||
]`).toMatchTree(`
|
||||
Array
|
||||
Number 1
|
||||
Number 2
|
||||
`)
|
||||
})
|
||||
|
||||
test('complex nested multiline', () => {
|
||||
expect(`[
|
||||
[1 2]
|
||||
[3 4]
|
||||
[5 6]
|
||||
]`).toMatchTree(`
|
||||
Array
|
||||
Array
|
||||
Number 1
|
||||
Number 2
|
||||
Array
|
||||
Number 3
|
||||
Number 4
|
||||
Array
|
||||
Number 5
|
||||
Number 6
|
||||
`)
|
||||
})
|
||||
|
||||
test('boolean and null literals', () => {
|
||||
expect('[true false null]').toMatchTree(`
|
||||
Array
|
||||
Boolean true
|
||||
Boolean false
|
||||
Null null
|
||||
`)
|
||||
})
|
||||
|
||||
test('regex literals', () => {
|
||||
expect('[//[0-9]+//]').toMatchTree(`
|
||||
Array
|
||||
Regex //[0-9]+//
|
||||
`)
|
||||
})
|
||||
|
||||
test('trailing newlines', () => {
|
||||
expect(`[
|
||||
1
|
||||
2
|
||||
]`).toMatchTree(`
|
||||
Array
|
||||
Number 1
|
||||
Number 2
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
|
@ -195,7 +195,13 @@ const isWhiteSpace = (ch: number): boolean => {
|
|||
}
|
||||
|
||||
const isWordChar = (ch: number): boolean => {
|
||||
return !isWhiteSpace(ch) && ch !== 10 /* \n */ && ch !== 41 /* ) */ && ch !== -1 /* EOF */
|
||||
return (
|
||||
!isWhiteSpace(ch) &&
|
||||
ch !== 10 /* \n */ &&
|
||||
ch !== 41 /* ) */ &&
|
||||
ch !== 93 /* ] */ &&
|
||||
ch !== -1 /* EOF */
|
||||
)
|
||||
}
|
||||
|
||||
const isLowercaseLetter = (ch: number): boolean => {
|
||||
|
|
|
|||
|
|
@ -93,11 +93,7 @@ expect.extend({
|
|||
}
|
||||
},
|
||||
|
||||
async toEvaluateTo(
|
||||
received: unknown,
|
||||
expected: unknown,
|
||||
globals: Record<string, any> = {}
|
||||
) {
|
||||
async toEvaluateTo(received: unknown, expected: unknown, globals: Record<string, any> = {}) {
|
||||
assert(typeof received === 'string', 'toEvaluateTo can only be used with string values')
|
||||
|
||||
try {
|
||||
|
|
@ -109,7 +105,7 @@ expect.extend({
|
|||
if (expected instanceof RegExp) expected = String(expected)
|
||||
if (value instanceof RegExp) value = String(value)
|
||||
|
||||
if (value === expected) {
|
||||
if (isEqual(value, expected)) {
|
||||
return { pass: true }
|
||||
} else {
|
||||
return {
|
||||
|
|
@ -165,3 +161,7 @@ const trimWhitespace = (str: string): string => {
|
|||
})
|
||||
.join('\n')
|
||||
}
|
||||
|
||||
function isEqual(a: any, b: any): boolean {
|
||||
return typeof a === 'object' ? JSON.stringify(a) === JSON.stringify(b) : a === b
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user