fix or/and chaining

This commit is contained in:
Chris Wanstrath 2025-10-29 11:13:30 -07:00
parent ced190488a
commit fd197a2dfc
3 changed files with 101 additions and 12 deletions

View File

@ -33,6 +33,9 @@
@precedence { @precedence {
pipe @left, pipe @left,
or @left,
and @left,
comparison @left,
multiplicative @left, multiplicative @left,
additive @left, additive @left,
call call
@ -129,14 +132,14 @@ SingleLineThenBlock {
} }
ConditionalOp { ConditionalOp {
expression Eq expression | expression !comparison Eq expression |
expression Neq expression | expression !comparison Neq expression |
expression Lt expression | expression !comparison Lt expression |
expression Lte expression | expression !comparison Lte expression |
expression Gt expression | expression !comparison Gt expression |
expression Gte expression | expression !comparison Gte expression |
expression And (expression | ConditionalOp) | (expression | ConditionalOp) !and And (expression | ConditionalOp) |
expression Or (expression | ConditionalOp) (expression | ConditionalOp) !or Or (expression | ConditionalOp)
} }
Params { Params {

View File

@ -7,9 +7,9 @@ import {highlighting} from "./highlight"
const spec_Identifier = {__proto__:null,end:78, null:84, if:94, elseif:102, else:106} const spec_Identifier = {__proto__:null,end:78, null:84, if:94, elseif:102, else:106}
export const parser = LRParser.deserialize({ export const parser = LRParser.deserialize({
version: 14, version: 14,
states: "2lQYQbOOO!ZOpO'#CrO#mQcO'#CuO$jOSO'#CwO$xQbO'#EUOOQ`'#DQ'#DQOOQa'#C}'#C}O%{QbO'#DVO'QQcO'#DyOOQa'#Dy'#DyO(lQcO'#DxO(yQRO'#CvO)[QcO'#DtO)sQbO'#CtOOQ`'#Du'#DuO*kQbO'#DtO*yQbO'#E[OOQ`'#D['#D[O+nQRO'#DdOOQ`'#Dt'#DtO+sQQO'#DsOOQ`'#Ds'#DsOOQ`'#De'#DeQYQbOOO+{ObO,59^O,TQbO'#DOOOQa'#Dx'#DxOOQ`'#DY'#DYOOQ`'#EZ'#EZOOQ`'#Dl'#DlO,_QbO,59]O,rQbO'#CyO,zQWO'#CzOOOO'#D{'#D{OOOO'#Df'#DfO-`OSO,59cOOQa,59c,59cOOQ`'#Dh'#DhO-nQbO'#DRO-vQQO,5:pOOQ`'#Dg'#DgO-{QbO,59qO.SQQO,59iOOQa,59q,59qO._QbO,59qO*yQbO,59bO*yQbO,59bO*yQbO,59bO.iQRO,59`O/YQRO'#CvO/vQRO,59`O0SQQO,59`O0XQQO,59`O0aQbO'#DmO0lQbO,59[O1iQRO,5:vO1pQQO,5:vO1uQbO,5:OOOQ`,5:_,5:_OOQ`-E7c-E7cOOQa1G.x1G.xOOQ`,59j,59jOOQ`-E7j-E7jOOOO,59e,59eOOOO,59f,59fOOOO-E7d-E7dOOQa1G.}1G.}OOQ`-E7f-E7fO2PQbO1G0[OOQ`-E7e-E7eO2^QQO1G/TOOQa1G/]1G/]O2iQbO1G/]OOQO'#Dj'#DjO2^QQO1G/TOOQa1G/T1G/TOOQ`'#Dk'#DkO2iQbO1G/]OOQa1G.|1G.|O3[QcO1G.|O3fQcO1G.|O3pQcO1G.|OOQa1G.z1G.zO*yQbO,59sO*yQbO,59sO!`QbO'#CuO&SQbO'#CqOOQ`,5:X,5:XOOQ`-E7k-E7kO4[QbO1G0bOOQ`1G/j1G/jO4iQbO7+%vO4nQbO7+%wO5OQQO7+$oOOQa7+$o7+$oO5ZQbO7+$wOOQa7+$w7+$wOOQO-E7h-E7hOOQ`-E7i-E7iOOQO1G/_1G/_O5eQRO1G/_OOQ`'#D^'#D^O5oQbO7+%|O5tQbO7+%}OOQ`<<Ib<<IbOOQ`'#Di'#DiO6[QQO'#DiO6aQbO'#EWO6wQbO<<IcOOQa<<HZ<<HZOOQa<<Hc<<HcOOQ`<<Ih<<IhOOQ`'#D_'#D_O6|QbO<<IiOOQ`,5:T,5:TOOQ`-E7g-E7gOOQ`AN>}AN>}O*yQbO'#D`OOQ`'#Dn'#DnO7XQbOAN?TO7dQQO'#DbOOQ`AN?TAN?TO7iQbOAN?TO7nQRO,59zO7uQQO,59zOOQ`-E7l-E7lOOQ`G24oG24oO7zQbOG24oO8PQQO,59|O8UQQO1G/fOOQ`LD*ZLD*ZO4nQbO1G/hO5tQbO7+%QOOQ`7+%S7+%SOOQ`<<Hl<<Hl", states: "3UQYQbOOO!ZOpO'#CrO#mQcO'#CuO$jOSO'#CwO$xQbO'#EUOOQ`'#DQ'#DQOOQa'#C}'#C}O%{QbO'#DVO'QQcO'#DyOOQa'#Dy'#DyO(lQcO'#DxO(yQRO'#CvO)[QcO'#DtO)sQbO'#CtOOQ`'#Du'#DuO*kQbO'#DtO*yQbO'#E[OOQ`'#D['#D[O+nQRO'#DdOOQ`'#Dt'#DtO+sQQO'#DsOOQ`'#Ds'#DsOOQ`'#De'#DeQYQbOOO+{ObO,59^O,TQbO'#DOOOQa'#Dx'#DxOOQ`'#DY'#DYOOQ`'#EZ'#EZOOQ`'#Dl'#DlO,_QbO,59]O,rQbO'#CyO,zQWO'#CzOOOO'#D{'#D{OOOO'#Df'#DfO-`OSO,59cOOQa,59c,59cOOQ`'#Dh'#DhO-nQbO'#DRO-vQQO,5:pOOQ`'#Dg'#DgO-{QbO,59qO.SQQO,59iOOQa,59q,59qO._QbO,59qO*yQbO,59bO*yQbO,59bO*yQbO,59bO.iQRO,59`O/YQRO'#CvO/vQRO,59`O0XQRO,59`O0SQQO,59`O0dQQO,59`O0lQbO'#DmO0wQbO,59[O1tQRO,5:vO1{QRO,5:vO2WQbO,5:OOOQ`,5:_,5:_OOQ`-E7c-E7cOOQa1G.x1G.xOOQ`,59j,59jOOQ`-E7j-E7jOOOO,59e,59eOOOO,59f,59fOOOO-E7d-E7dOOQa1G.}1G.}OOQ`-E7f-E7fO2bQbO1G0[OOQ`-E7e-E7eO2oQQO1G/TOOQa1G/]1G/]O2zQbO1G/]OOQO'#Dj'#DjO2oQQO1G/TOOQa1G/T1G/TOOQ`'#Dk'#DkO2zQbO1G/]OOQa1G.|1G.|O3mQcO1G.|O3wQcO1G.|O4RQcO1G.|OOQa1G.z1G.zO*yQbO,59sO*yQbO,59sO*yQbO,59sO!`QbO'#CuO&SQbO'#CqOOQ`,5:X,5:XOOQ`-E7k-E7kO4mQbO1G0bOOQ`1G/j1G/jO4zQbO7+%vO5PQbO7+%wO5aQQO7+$oOOQa7+$o7+$oO5lQbO7+$wOOQa7+$w7+$wOOQO-E7h-E7hOOQ`-E7i-E7iOOQP1G/_1G/_O6eQRO1G/_O6lQRO1G/_O6zQRO1G/_OOQ`'#D^'#D^O7RQbO7+%|O7WQbO7+%}OOQ`<<Ib<<IbOOQ`'#Di'#DiO7nQQO'#DiO7sQbO'#EWO8ZQbO<<IcOOQa<<HZ<<HZOOQa<<Hc<<HcOOQ`<<Ih<<IhOOQ`'#D_'#D_O8`QbO<<IiOOQ`,5:T,5:TOOQ`-E7g-E7gOOQ`AN>}AN>}O*yQbO'#D`OOQ`'#Dn'#DnO8kQbOAN?TO8vQQO'#DbOOQ`AN?TAN?TO8{QbOAN?TO9QQRO,59zO9XQRO,59zOOQ`-E7l-E7lOOQ`G24oG24oO9dQbOG24oO9iQQO,59|O9nQQO1G/fOOQ`LD*ZLD*ZO5PQbO1G/hO7WQbO7+%QOOQ`7+%S7+%SOOQ`<<Hl<<Hl",
stateData: "8^~O!eOS!fOS~O^QO_bO`XOaPObSOgXOoXOpXOzXO!P`O!k]O!nRO!uUO!vVO!weO~O!jhO~O^jO`XOaPObSOgXOoXOpXOsiOxkOzXO!k]O!nRO!uUO!vVO}iX!wiX#QiX!|iXwiX~OP!lXQ!lXR!lXS!lX]!lXT!lXU!lXV!lXW!lXX!lXY!lXZ!lX[!lX~P!`OlqO!ntO!poO!qpO~O^uOvuP~O^jO`XOaPOgXOoXOpXOsiOzXO!k]O!nRO!uUO!vVO!wxO~O!{{O~P%QO^jO`XOaPObSOgXOoXOpXOsiOxkOzXO!k]O!nRO!uUO!vVO~OP!mXQ!mXR!mXS!mX]!mX!w!mX#Q!mXT!mXU!mXV!mXW!mXX!mXY!mXZ!mX[!mX!|!mXw!mX~P&SOP!lXQ!lXR!lXS!lX]!lX~O!w!hX#Q!hXw!hX~P(ZOP!OOQ!OOR!POS!PO]}O~OP!OOQ!OOR!POS!PO!w!hX#Q!hXw!hX~O^QO`XOaPObSOgXOoXOpXOzXO!k]O!nRO!uUO!vVO~O}!VO!w!hX#Q!hXw!hX~O^jO`XOaPOgXOoXOpXOzXO!k]O!nRO!uUO!vVO~OV!ZO~O!w![O#Q![O~O^!^Og!^O~ObSOx!_O~P*yO}ea!wea#Qea!|eawea~P&SO^!aO!k]O~O!n!bO!p!bO!q!bO!r!bO!s!bO!t!bO~OlqO!n!dO!poO!qpO~O^uOvuX~Ov!fO~O!{!iO~P%QOsiO!w!kO!{!mO~O!w!nO!{!iO~P*yO!|!tOT!lXU!lXV!lXW!lXX!lXY!lXZ!lX[!lX~P(ZOT!vOU!vOV!uOW!uOX!uOY!uOZ!uO[!uO~P(yOP!OOQ!OOR!POS!PO!|!tO~O}!VO!|!tO~O^!wOaPO!k]O~O}!VO!wda#Qda!|dawda~OT!vOU!vOV!uOW!uOX!uOY!uOZ!uO[!uO~Ov!{O~P0}Ov!{O~O_bO!P`O~P)sO_bO!P`O!w#OO~P)sOsiO!w!kO!{#QO~O!w!nO!{#SO~P*yO]}ORjiSji!wji#Qji!|jiwji~OPjiQji~P2sOP!OOQ!OO~P2sOP!OOQ!OORjiSji!wji#Qji!|jiwji~O_bO!P`O!w#ZO~P)sOw#[O~O_bO!P`O!w#]Ow!zP~P)sOsiO!w!kO!{#aO~O!w!nO!{#bO~P*yO!|{iv{i~P0}Ow#cO~O_bO!P`O!w#]Ow!zP!T!zP!V!zP~P)sO!w#fO~O_bO!P`O!w#]Ow!zX!T!zX!V!zX~P)sOw#hO~Ow#mO!T#iO!V#lO~Ow#rO!T#iO!V#lO~Ov#tO~Ow#rO~Ov#uO~P0}Ov#uO~Ow#vO~O!w#wO~O!w#xO~Ogp~", stateData: "9v~O!eOS!fOS~O^QO_bO`XOaPObSOgXOoXOpXOzXO!P`O!k]O!nRO!uUO!vVO!weO~O!jhO~O^jO`XOaPObSOgXOoXOpXOsiOxkOzXO!k]O!nRO!uUO!vVO}iX!wiX#QiX!|iXwiX~OP!lXQ!lXR!lXS!lX]!lXT!lXU!lXV!lXW!lXX!lXY!lXZ!lX[!lX~P!`OlqO!ntO!poO!qpO~O^uOvuP~O^jO`XOaPOgXOoXOpXOsiOzXO!k]O!nRO!uUO!vVO!wxO~O!{{O~P%QO^jO`XOaPObSOgXOoXOpXOsiOxkOzXO!k]O!nRO!uUO!vVO~OP!mXQ!mXR!mXS!mX]!mX!w!mX#Q!mXT!mXU!mXV!mXW!mXX!mXY!mXZ!mX[!mX!|!mXw!mX~P&SOP!lXQ!lXR!lXS!lX]!lX~O!w!hX#Q!hXw!hX~P(ZOP!OOQ!OOR!POS!PO]}O~OP!OOQ!OOR!POS!PO!w!hX#Q!hXw!hX~O^QO`XOaPObSOgXOoXOpXOzXO!k]O!nRO!uUO!vVO~O}!WO!w!hX#Q!hXw!hX~O^jO`XOaPOgXOoXOpXOzXO!k]O!nRO!uUO!vVO~OV![O~O!w!]O#Q!]O~O^!_Og!_O~ObSOx!`O~P*yO}ea!wea#Qea!|eawea~P&SO^!bO!k]O~O!n!cO!p!cO!q!cO!r!cO!s!cO!t!cO~OlqO!n!eO!poO!qpO~O^uOvuX~Ov!gO~O!{!jO~P%QOsiO!w!lO!{!nO~O!w!oO!{!jO~P*yO!|!uOT!lXU!lXV!lXW!lXX!lXY!lXZ!lX[!lX~P(ZOT!wOU!xOV!vOW!vOX!vOY!vOZ!vO[!vO~P(yOP!OOQ!OOR!POS!PO!|!uO~OT!wOU!xO!|!uO~O}!WO!|!uO~O^!yOaPO!k]O~O}!WO!wda#Qda!|dawda~OT!wOU!xOV!vOW!vOX!vOY!vOZ!vO[!vO~Ov!}O~P1YOT!wOU!xOv!}O~O_bO!P`O~P)sO_bO!P`O!w#QO~P)sOsiO!w!lO!{#SO~O!w!oO!{#UO~P*yO]}ORjiSji!wji#Qji!|jiwji~OPjiQji~P3UOP!OOQ!OO~P3UOP!OOQ!OORjiSji!wji#Qji!|jiwji~O_bO!P`O!w#_O~P)sOw#`O~O_bO!P`O!w#aOw!zP~P)sOsiO!w!lO!{#eO~O!w!oO!{#fO~P*yOV!vOW!vOX!vOY!vOZ!vO[!vOT{i!|{iv{i~OU!xO~P5vOU!xOT{i!|{iv{i~OU{i~P5vOw#gO~O_bO!P`O!w#aOw!zP!T!zP!V!zP~P)sO!w#jO~O_bO!P`O!w#aOw!zX!T!zX!V!zX~P)sOw#lO~Ow#qO!T#mO!V#pO~Ow#vO!T#mO!V#pO~Ov#xO~Ow#vO~Ov#yO~P1YOT!wOU!xOv#yO~Ow#zO~O!w#{O~O!w#|O~Ogp~",
goto: ".e#QPPPPPPPPPPPPPPPPPPPP#R#b#pP$g#b%a%vP&h&hPP%v&lP'P'jPPP%vP'm'yP(QP(^(a(jP(nP(Q(t(z)Q)W)^)g)q){*U*]PPPP*c*g*{PP+_,kP-aPPPPPPPP-e-e-xPP.Q.X.XdcOg!Z!f!{#O#Z#_#w#xR!T]i^O]g!V!Z!f!{#O#Z#_#w#xfQO]g!Z!f!{#O#Z#_#w#xvjQVW`iny|}!O!P!j!o!u!v!w!x#R#iR!w!VfWO]g!Z!f!{#O#Z#_#w#xvXQVW`iny|}!O!P!j!o!u!v!w!x#R#iQ!aoR!x!Vd[Og!Z!f!{#O#Z#_#w#xQ!S]Q!p!OR!s!P!_XOQVW]`giny|}!O!P!Z!f!j!o!u!v!w!x!{#O#R#Z#_#i#w#xTqRsYlQWn!w!xQzVQ!hyX!kz!h!l#PdcOg!Z!f!{#O#Z#_#w#xYkQWn!w!xQ!T]R!_iRwSQ!T]Q!Y`Q#V!vR#p#iZlQWn!w!xecOg!Z!f!{#O#Z#_#w#xR#Y!{Q#e#ZQ#y#wR#z#xT#j#e#kQ#n#eR#s#kQgOR!]gQsRR!csQyVR!gyQvSR!evW#_#O#Z#w#xR#g#_Q!lzQ#P!hT#T!l#PQ!o|Q#R!jT#U!o#RWnQW!w!xR!`nS!W_!UR!z!WQ#k#eR#q#kTfOgSdOgQ!|!ZQ!}!fQ#X!{Z#^#O#Z#_#w#xd_Og!Z!f!{#O#Z#_#w#xQ!U]R!y!VdZOg!Z!f!{#O#Z#_#w#xYkQWn!w!xQ|VQ!R]Q!X`Q!_iQ!jyW!n|!j!o#RQ!p}Q!q!OQ!r!PQ#V!uQ#W!vR#o#idYOg!Z!f!{#O#Z#_#w#xvjQVW`iny|}!O!P!j!o!u!v!w!x#R#iR!Q]TrRssTOQW]gin!Z!f!w!x!{#O#Z#_#w#xQ#`#OV#d#Z#w#xZmQWn!w!xeaOg!Z!f!{#O#Z#_#w#x", goto: ".o#QPPPPPPPPPPPPPPPPPPPP#R#b#pP$h#b%c%xP&k&kPP%x&oP'S'mPPP%xP'p(PP(WP(d(g(pP(tP(W(z)Q)W)^)d)m)w*R*[*cPPPP*i*m+RPP+e,tP-kPPPPPPPP-o-o.SPP.[.c.cdcOg![!g!}#Q#_#c#{#|R!U]i^O]g!W![!g!}#Q#_#c#{#|fQO]g![!g!}#Q#_#c#{#|xjQVW`iny|}!O!P!k!p!v!w!x!y!z#T#mR!y!WfWO]g![!g!}#Q#_#c#{#|xXQVW`iny|}!O!P!k!p!v!w!x!y!z#T#mQ!boR!z!Wd[Og![!g!}#Q#_#c#{#|Q!S]Q!q!OR!t!P!aXOQVW]`giny|}!O!P![!g!k!p!v!w!x!y!z!}#Q#T#_#c#m#{#|TqRsYlQWn!y!zQzVQ!iyX!lz!i!m#RdcOg![!g!}#Q#_#c#{#|YkQWn!y!zQ!U]R!`iRwSQ!T]Q!Z`Q#X!xQ#Z!wR#t#mZlQWn!y!zecOg![!g!}#Q#_#c#{#|R#^!}Q#i#_Q#}#{R$O#|T#n#i#oQ#r#iR#w#oQgOR!^gQsRR!dsQyVR!hyQvSR!fvW#c#Q#_#{#|R#k#cQ!mzQ#R!iT#V!m#RQ!p|Q#T!kT#W!p#TWnQW!y!zR!anS!X_!VR!|!XQ#o#iR#u#oTfOgSdOgQ#O![Q#P!gQ#]!}Z#b#Q#_#c#{#|d_Og![!g!}#Q#_#c#{#|Q!V]R!{!WdZOg![!g!}#Q#_#c#{#|YkQWn!y!zQ|VQ!R]Q!Y`Q!`iQ!kyW!o|!k!p#TQ!q}Q!r!OQ!s!PQ#X!vQ#Y!wQ#[!xR#s#mdYOg![!g!}#Q#_#c#{#|xjQVW`iny|}!O!P!k!p!v!w!x!y!z#T#mR!Q]TrRssTOQW]gin![!g!y!z!}#Q#_#c#{#|Q#d#QV#h#_#{#|ZmQWn!y!zeaOg![!g!}#Q#_#c#{#|",
nodeNames: "⚠ Star Slash Plus Minus And Or Eq Neq Lt Lte Gt Gte Modulo Identifier AssignableIdentifier Word IdentifierBeforeDot Do Program PipeExpr FunctionCall DotGet Number ParenExpr FunctionCallOrIdentifier BinOp String StringFragment Interpolation EscapeSeq Boolean Regex Dict NamedArg NamedArgPrefix FunctionDef Params colon keyword Underscore Array Null ConditionalOp PositionalArg operator IfExpr keyword SingleLineThenBlock ThenBlock ElseIfExpr keyword ElseExpr keyword Assign", nodeNames: "⚠ Star Slash Plus Minus And Or Eq Neq Lt Lte Gt Gte Modulo Identifier AssignableIdentifier Word IdentifierBeforeDot Do Program PipeExpr FunctionCall DotGet Number ParenExpr FunctionCallOrIdentifier BinOp String StringFragment Interpolation EscapeSeq Boolean Regex Dict NamedArg NamedArgPrefix FunctionDef Params colon keyword Underscore Array Null ConditionalOp PositionalArg operator IfExpr keyword SingleLineThenBlock ThenBlock ElseIfExpr keyword ElseExpr keyword Assign",
maxTerm: 94, maxTerm: 94,
context: trackScope, context: trackScope,
@ -23,5 +23,5 @@ export const parser = LRParser.deserialize({
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO!j~~", 11)], tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO!j~~", 11)],
topRules: {"Program":[0,19]}, topRules: {"Program":[0,19]},
specialized: [{term: 14, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 14, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], specialized: [{term: 14, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 14, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}],
tokenPrec: 1069 tokenPrec: 1139
}) })

View File

@ -593,3 +593,89 @@ describe('Comments', () => {
Identifier prop`) Identifier prop`)
}) })
}) })
describe('Conditional ops', () => {
test('or can be chained', () => {
expect(`
is-positive = do x:
if x = 3 or x = 4 or x = 5:
true
end
end
`).toMatchTree(`
Assign
AssignableIdentifier is-positive
Eq =
FunctionDef
Do do
Params
Identifier x
colon :
IfExpr
keyword if
ConditionalOp
ConditionalOp
ConditionalOp
Identifier x
Eq =
Number 3
Or or
ConditionalOp
Identifier x
Eq =
Number 4
Or or
ConditionalOp
Identifier x
Eq =
Number 5
colon :
ThenBlock
Boolean true
keyword end
keyword end
`)
})
test('and can be chained', () => {
expect(`
is-positive = do x:
if x = 3 and x = 4 and x = 5:
true
end
end
`).toMatchTree(`
Assign
AssignableIdentifier is-positive
Eq =
FunctionDef
Do do
Params
Identifier x
colon :
IfExpr
keyword if
ConditionalOp
ConditionalOp
ConditionalOp
Identifier x
Eq =
Number 3
And and
ConditionalOp
Identifier x
Eq =
Number 4
And and
ConditionalOp
Identifier x
Eq =
Number 5
colon :
ThenBlock
Boolean true
keyword end
keyword end
`)
})
})