From 45f31d0678008ea7cb35dcd6becf4f945fad8d52 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sat, 8 Nov 2025 11:26:37 -0800 Subject: [PATCH] allow newlines in (some) parens expressions --- src/parser/shrimp.grammar | 21 ++++++++-- src/parser/shrimp.terms.ts | 69 +++++++++++++++---------------- src/parser/shrimp.ts | 22 +++++----- src/parser/tests/basics.test.ts | 72 +++++++++++++++++++++++++++++++++ 4 files changed, 136 insertions(+), 48 deletions(-) diff --git a/src/parser/shrimp.grammar b/src/parser/shrimp.grammar index a4f26e2..1fe1c9b 100644 --- a/src/parser/shrimp.grammar +++ b/src/parser/shrimp.grammar @@ -12,7 +12,7 @@ @precedence { Number Regex } StringFragment { !['\\$]+ } - NamedArgPrefix { $[a-z-]+ "=" } + NamedArgPrefix { $[a-z] $[a-z0-9-]* "=" } Number { ("-" | "+")? $[0-9]+ ('.' $[0-9]+)? } Boolean { "true" | "false" } newlineOrSemicolon { "\n" | ";" } @@ -47,7 +47,8 @@ null { @specialize[@name=Null] } comparison @left, multiplicative @left, additive @left, - call + call, + functionWithNewlines } item { @@ -188,7 +189,21 @@ BinOp { } ParenExpr { - leftParen (IfExpr | ambiguousFunctionCall | BinOp | expressionWithoutIdentifier | ConditionalOp | PipeExpr | FunctionDef) rightParen + leftParen newlineOrSemicolon* ( + FunctionCallWithNewlines | + IfExpr | + ambiguousFunctionCall | + BinOp newlineOrSemicolon* | + expressionWithoutIdentifier | + ConditionalOp newlineOrSemicolon* | + PipeExpr | + FunctionDef + ) + rightParen +} + +FunctionCallWithNewlines[@name=FunctionCall] { + (DotGet | Identifier | ParenExpr) newlineOrSemicolon+ arg !functionWithNewlines (newlineOrSemicolon+ arg)* newlineOrSemicolon* } expression { diff --git a/src/parser/shrimp.terms.ts b/src/parser/shrimp.terms.ts index 04cb710..82f4419 100644 --- a/src/parser/shrimp.terms.ts +++ b/src/parser/shrimp.terms.ts @@ -28,39 +28,40 @@ export const Program = 26, PipeExpr = 27, WhileExpr = 29, - keyword = 70, + keyword = 71, ConditionalOp = 31, ParenExpr = 32, - IfExpr = 33, - FunctionCall = 35, - DotGet = 36, - Number = 37, - PositionalArg = 38, - FunctionDef = 39, - Params = 40, - NamedParam = 41, - NamedArgPrefix = 42, - String = 43, - StringFragment = 44, - Interpolation = 45, - EscapeSeq = 46, - Boolean = 47, - Null = 48, - colon = 49, - CatchExpr = 50, - Block = 52, - FinallyExpr = 53, - Underscore = 56, - NamedArg = 57, - ElseIfExpr = 58, - ElseExpr = 60, - FunctionCallOrIdentifier = 61, - BinOp = 62, - Regex = 63, - Dict = 64, - Array = 65, - FunctionCallWithBlock = 66, - TryExpr = 67, - Throw = 69, - CompoundAssign = 71, - Assign = 72 + FunctionCallWithNewlines = 33, + DotGet = 34, + Number = 35, + PositionalArg = 36, + FunctionDef = 37, + Params = 38, + NamedParam = 39, + NamedArgPrefix = 40, + String = 41, + StringFragment = 42, + Interpolation = 43, + EscapeSeq = 44, + Boolean = 45, + Null = 46, + colon = 47, + CatchExpr = 48, + Block = 50, + FinallyExpr = 51, + Underscore = 54, + NamedArg = 55, + IfExpr = 56, + FunctionCall = 58, + ElseIfExpr = 59, + ElseExpr = 61, + FunctionCallOrIdentifier = 62, + BinOp = 63, + Regex = 64, + Dict = 65, + Array = 66, + FunctionCallWithBlock = 67, + TryExpr = 68, + Throw = 70, + CompoundAssign = 72, + Assign = 73 diff --git a/src/parser/shrimp.ts b/src/parser/shrimp.ts index 64892e8..2d53813 100644 --- a/src/parser/shrimp.ts +++ b/src/parser/shrimp.ts @@ -4,24 +4,24 @@ import {operatorTokenizer} from "./operatorTokenizer" import {tokenizer, specializeKeyword} from "./tokenizer" import {trackScope} from "./parserScopeContext" import {highlighting} from "./highlight" -const spec_Identifier = {__proto__:null,while:60, if:68, null:96, catch:102, finally:108, end:110, else:118, try:136, throw:140} +const spec_Identifier = {__proto__:null,while:60, null:92, catch:98, finally:104, end:106, if:114, else:120, try:138, throw:142} export const parser = LRParser.deserialize({ version: 14, - states: "9UQYQbOOO!dOpO'#DQO!iOSO'#DXO$_QcO'#DkO&rQcO'#EYOOQ`'#Eh'#EhO'uQRO'#DlO)[QcO'#EWO)lQbO'#C|OOQa'#Dn'#DnO+nQbO'#DoOOQa'#EY'#EYO+uQcO'#EYO+|QcO'#EXO,yQcO'#EWO-TQRO'#DuOOQ`'#EW'#EWO-iQbO'#EWO-pQQO'#EVOOQ`'#EV'#EVOOQ`'#Dw'#DwQYQbOOO-{QbO'#DTO.WQbO'#C}O.{QbO'#CyO/pQQO'#DqO.{QbO'#DsO/uObO,59lO0QQbO'#DZO0YQWO'#D[OOOO'#E`'#E`OOOO'#D|'#D|O0nOSO,59sOOQa,59s,59sOOQ`'#DS'#DSO0|QbO'#DgOOQ`'#E^'#E^OOQ`'#Dy'#DyO1WQbO,59kOOQa'#EX'#EXO.{QbO,5:WO.{QbO,5:WO.{QbO,5:WO.{QbO,59gO.{QbO,59gO.{QbO,59gO2QQRO,59hO2^QQO,59hO2fQQO,59hO2qQRO,59hO3[QRO,59hO3jQQO'#CwOOQ`'#EP'#EPO3oQbO,5:ZO3vQQO,5:YOOQa,5:Z,5:ZO4RQbO,5:ZO)lQbO,5:bO)lQbO,5:aO4]QbO,5:[O4dQbO,59cOOQ`,5:q,5:qO)lQbO'#DxOOQ`-E7u-E7uOOQ`'#Dz'#DzO5OQbO'#DUO5ZQbO'#DVOOQO'#D{'#D{O5RQQO'#DUO5iQQO,59oO5nQcO'#EXO5uQRO'#E[O6lQRO'#E[OOQO'#E['#E[O6sQQO,59iO6xQRO,59eO7PQRO,59eO4]QbO,5:]O7[QcO,5:_O8dQcO,5:_O8tQcO,5:_OOQa1G/W1G/WOOOO,59u,59uOOOO,59v,59vOOOO-E7z-E7zOOQa1G/_1G/_OOQ`,5:R,5:ROOQ`-E7w-E7wOOQa1G/r1G/rO9sQcO1G/rO9}QcO1G/rO:XQcO1G/rOOQa1G/R1G/ROVQbO'#DbO>hQbO'#DbO>{QbO1G/vOOQ`-E7v-E7vOOQ`,5:d,5:dOOQ`-E7x-E7xO?WQQO,59pOOQO,59q,59qOOQO-E7y-E7yO?`QbO1G/ZO4]QbO1G/TO4]QbO1G/PO?gQbO1G/wO?rQQO7+%`OOQa7+%`7+%`O?}QbO7+%aOOQa7+%a7+%aOOQO-E8O-E8OOOQ`-E8P-E8POOQ`'#D}'#D}O@XQQO'#D}O@aQbO'#EgOOQ`,59|,59|O@tQbO'#D`O@yQQO'#DcOOQ`7+%b7+%bOAOQbO7+%bOATQbO7+%bOA]QbO7+$uOAkQbO7+$uOA{QbO7+$oOBTQbO7+$kOOQ`7+%c7+%cOBYQbO7+%cOB_QbO7+%cOOQa<hAN>hOOQ`AN={AN={OCuQbOAN={OCzQbOAN={OOQ`-E7|-E7|OOQ`AN=uAN=uODSQbOAN=uO.WQbO,5:SO4]QbO,5:UOOQ`AN>iAN>iOOQ`7+%Q7+%QOOQ`G23gG23gODXQbOG23gPD^QbO'#DhOOQ`G23aG23aODcQQO1G/nOOQ`1G/p1G/pOOQ`LD)RLD)RO4]QbO7+%YOOQ`<iAN>iOOQ`AN=yAN=yOJWQbOAN=yOJ]QbOAN=yOOQ`-E8P-E8POOQ`AN>^AN>^OJeQbOAN>^O.mQbO,5:TO7[QbO,5:VOOQ`AN>jAN>jPAeQbO'#DzOOQ`7+%O7+%OOOQ`G23eG23eOJjQbOG23ePIjQbO'#DiOOQ`G23xG23xOJoQQO1G/oOOQ`1G/q1G/qOOQ`LD)PLD)PO7[QbO7+%ZOOQ`<qOU}O~P?TOUoi~P>qO#_#zO~P2uO#_#zO~O#_#zOl!|X~O!P!aO#_#zOl!|X~O#_#zO~P3zOT|OU}O#Q!OO#_#zOl!|X~OhfO!WrO~P*vO#Q!OO#_#zO~OxsO#Q#eO#b#}O~O#Q#hO#b$PO~P/bOl!dO#Q!ki#c!ki!R!ki!U!ki!V!ki#_!ki!^!ki~Ol!dO#Q!ji#c!ji!R!ji!U!ji!V!ji#_!ji!^!ji~Ol!dO!R!SX!U!SX!V!SX!^!SX~O#Q$SO!R#[P!U#[P!V#[P!^#[P~P6TO!R$WO!U$XO!V$YO~Ox!hO!Pva~O#Q$^O~P6TO!R$WO!U$XO!V$aO~O#Q!OO#_$dO~O#Q!OO#_qi~OxsO#Q#eO#b$gO~O#Q#hO#b$hO~P/bOl!dO#Q$iO~O#Q$SO!R#[X!U#[X!V#[X!^#[X~P6TOd$kO~O!P$lO~O!V$mO~O!U$XO!V$mO~Ol!dO!R$WO!U$XO!V$oO~O#Q$SO!R#[P!U#[P!V#[P~P6TO!V$vO!^$uO~O!V$xO~O!V$yO~O!U$XO!V$yO~OhfO!WrO#_qq~P*vO#Q!OO#_qq~O!P%OO~O!V%QO~O!V%RO~O!U$XO!V%RO~O!R$WO!U$XO!V%RO~O!V%VO!^$uO~O!P%YO!Z%XO~O!V%VO~O!V%ZO~OhfO!WrO#_qy~P*vO!V%^O~O!U$XO!V%^O~O!V%aO~O!V%dO~O!P%eO~Os!b~", + goto: "7t#_PPPPPPPPPPPPPPPPPPPPPPPPPPP#`P#yP$`%Z&g&mP'u(R({)OP)UP*Z*ZPPP*_P*k+TPPP+k#`P,T,nP,r,x-_P.R/T#y#yP#yP#y#y0X0_0k1_1e1o1u1|2S2^2d2nPPP2x2|3q5aPPP6iP6yPPPPP6}7T7Zr`Oe!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%eQ!UWR#Z!Pw`OWe!P!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%er^Oe!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%eQ!XWS!mg%XQ!rhQ!vjQ#S}Q#U|R#^!PvSOeg!_!`!a!d!s#m#u#v#w$U$^$l%O%X%Y%e!SZRSYhjsvxyz{|}!Q!R!Z!^!l#_#d#i$O$e$|%[S!RW!PQ!wkR!xlQ!TWR#Y!PrROe!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%e!SwRSYhjsvxyz{|}!Q!R!Z!^!l#_#d#i$O$e$|%[S!QW!PT!lg%XetRSv!Q!R!l#_$e$|%[r`Oe!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%edrRSv!Q!R!l#_$e$|%[Q!UWQ!|sR#Z!PR!kfX!if!g!j#r#OZORSWYeghjsvxyz{|}!P!Q!R!Z!^!_!`!a!d!l!s#_#d#i#m#u#v#w$O$U$^$e$l$|%O%X%Y%[%eR#s!hTnQpQ$[#nQ$c#xQ$q$]R%T$rQ#n!aQ#x!sQ$_#vQ$`#wQ%P$lQ%]%OQ%c%YR%f%eQ$Z#nQ$b#xQ$n$[Q$p$]Q$z$cS%S$q$rR%_%TdtRSv!Q!R!l#_$e$|%[Q![YQ#b!ZX#e![#b#f#|vTOWe!P!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%eT!og%XT$s$_$tQ$w$_R%W$twTOWe!P!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%erVOe!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%eQ!SWQ!ujQ#OyQ#RzR#X!P#PZORSWYeghjsvxyz{|}!P!Q!R!Z!^!_!`!a!d!l!s#_#d#i#m#u#v#w$O$U$^$e$l$|%O%X%Y%[%e!WZRSYghjsvxyz{|}!Q!R!Z!^!l#_#d#i$O$e$|%X%[w[OWe!P!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%eQeOR!ee^!bb!Y#j#k#l$T$]R#o!bQ!PWQ!ZY`#W!P!Z#_#`#y$e$|%[S#_!Q!RS#`!S!XS#y#X#^Q$e#{R$|$fQ!gfR#q!gQ!jfQ#r!gT#t!j#rQpQR!zpS$U#m$^R$j$UQ$f#{R$}$fYvRS!Q!R!lR!}vQ$t$_R%U$tQ#f![Q#|#bT$Q#f#|Q#i!^Q$O#dT$R#i$OTdOeSbOeS!YW!PQ#j!_Q#k!``#l!a!s#v#w$l%O%Y%eQ#p!dU$T#m$U$^R$]#uvUOWe!P!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%edrRSv!Q!R!l#_$e$|%[Q!^YS!ng%XQ!qhQ!tjQ!|sQ#OxQ#PyQ#QzQ#S{Q#T|Q#V}Q#d!ZX#h!^#d#i$Or]Oe!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%e!WwRSYghjsvxyz{|}!Q!R!Z!^!l#_#d#i$O$e$|%X%[Q!WWR#]!P[uRSv!Q!R!lQ#{#_V${$e$|%[ToQpQ$V#mR$r$^Q!pgR%b%XraOe!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%eQ!VWR#[!P", + nodeNames: "⚠ Star Slash Plus Minus And Or Eq EqEq Neq Lt Lte Gt Gte Modulo PlusEq MinusEq StarEq SlashEq ModuloEq Identifier AssignableIdentifier Word IdentifierBeforeDot Do Comment Program PipeExpr operator WhileExpr keyword ConditionalOp ParenExpr FunctionCall DotGet Number PositionalArg FunctionDef Params NamedParam NamedArgPrefix String StringFragment Interpolation EscapeSeq Boolean Null colon CatchExpr keyword Block FinallyExpr keyword keyword Underscore NamedArg IfExpr keyword FunctionCall ElseIfExpr keyword ElseExpr FunctionCallOrIdentifier BinOp Regex Dict Array FunctionCallWithBlock TryExpr keyword Throw keyword CompoundAssign Assign", + maxTerm: 111, context: trackScope, nodeProps: [ - ["closedBy", 49,"end"] + ["closedBy", 47,"end"] ], propSources: [highlighting], skippedNodes: [0,25], - repeatNodeCount: 11, - tokenData: "C_~R|OX#{XY$jYZ%TZp#{pq$jqs#{st%ntu'Vuw#{wx'[xy'ayz'zz{#{{|(e|}#{}!O+X!O!P#{!P!Q-n!Q![)S![!]6Z!]!^%T!^!}#{!}#O6t#O#P8j#P#Q8o#Q#R#{#R#S9Y#S#T#{#T#Y,Y#Y#Z9s#Z#b,Y#b#c>q#c#f,Y#f#g?n#g#h,Y#h#i@k#i#o,Y#o#p#{#p#qBo#q;'S#{;'S;=`$d<%l~#{~O#{~~CYS$QU|SOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{S$gP;=`<%l#{^$qU|S!xYOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U%[U|S#YQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{^%uZiY|SOY%nYZ#{Zt%ntu&huw%nwx&hx#O%n#O#P&h#P;'S%n;'S;=`'P<%lO%nY&mSiYOY&hZ;'S&h;'S;=`&y<%lO&hY&|P;=`<%l&h^'SP;=`<%l%n~'[O#T~~'aO#R~U'hU|S!}QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(RU|S#]QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(jW|SOt#{uw#{x!Q#{!Q![)S![#O#{#P;'S#{;'S;=`$d<%lO#{U)ZY|SuQOt#{uw#{x!O#{!O!P)y!P!Q#{!Q![)S![#O#{#P;'S#{;'S;=`$d<%lO#{U*OW|SOt#{uw#{x!Q#{!Q![*h![#O#{#P;'S#{;'S;=`$d<%lO#{U*oW|SuQOt#{uw#{x!Q#{!Q![*h![#O#{#P;'S#{;'S;=`$d<%lO#{U+^^|SOt#{uw#{x}#{}!O,Y!O!Q#{!Q![)S![!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{U,_[|SOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{U-[UzQ|SOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U-sW|SOt#{uw#{x!P#{!P!Q.]!Q#O#{#P;'S#{;'S;=`$d<%lO#{U.b^|SOY/^YZ#{Zt/^tu0auw/^wx0ax!P/^!P!Q#{!Q!}/^!}#O5S#O#P2o#P;'S/^;'S;=`6T<%lO/^U/e^|S!aQOY/^YZ#{Zt/^tu0auw/^wx0ax!P/^!P!Q3U!Q!}/^!}#O5S#O#P2o#P;'S/^;'S;=`6T<%lO/^Q0fX!aQOY0aZ!P0a!P!Q1R!Q!}0a!}#O1p#O#P2o#P;'S0a;'S;=`3O<%lO0aQ1UP!P!Q1XQ1^U!aQ#Z#[1X#]#^1X#a#b1X#g#h1X#i#j1X#m#n1XQ1sVOY1pZ#O1p#O#P2Y#P#Q0a#Q;'S1p;'S;=`2i<%lO1pQ2]SOY1pZ;'S1p;'S;=`2i<%lO1pQ2lP;=`<%l1pQ2rSOY0aZ;'S0a;'S;=`3O<%lO0aQ3RP;=`<%l0aU3ZW|SOt#{uw#{x!P#{!P!Q3s!Q#O#{#P;'S#{;'S;=`$d<%lO#{U3zb|S!aQOt#{uw#{x#O#{#P#Z#{#Z#[3s#[#]#{#]#^3s#^#a#{#a#b3s#b#g#{#g#h3s#h#i#{#i#j3s#j#m#{#m#n3s#n;'S#{;'S;=`$d<%lO#{U5X[|SOY5SYZ#{Zt5Stu1puw5Swx1px#O5S#O#P2Y#P#Q/^#Q;'S5S;'S;=`5}<%lO5SU6QP;=`<%l5SU6WP;=`<%l/^U6bU|S!RQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U6{W#_Q|SOt#{uw#{x!_#{!_!`7e!`#O#{#P;'S#{;'S;=`$d<%lO#{U7jV|SOt#{uw#{x#O#{#P#Q8P#Q;'S#{;'S;=`$d<%lO#{U8WU#^Q|SOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~8oO#U~U8vU#`Q|SOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U9aU|S!YQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U9x]|SOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#U:q#U#o,Y#o;'S#{;'S;=`$d<%lO#{U:v^|SOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#`,Y#`#a;r#a#o,Y#o;'S#{;'S;=`$d<%lO#{U;w^|SOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#g,Y#g#hx[#VW|SOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{^?u[#XW|SOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{^@r^#WW|SOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#f,Y#f#gAn#g#o,Y#o;'S#{;'S;=`$d<%lO#{UAs^|SOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#i,Y#i#jf#c#f7^#f#g?i#g#h7^#h#i@l#i#o7^#o#p#{#p#qB|#q;'S#{;'S;=`$d<%l~#{~O#{~~CgS$QUzSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{S$gP;=`<%l#{^$qUzS!zYOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U%[UzS#QQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{^%uZiYzSOY%nYZ#{Zt%ntu&huw%nwx&hx#O%n#O#P&h#P;'S%n;'S;=`'P<%lO%nY&mSiYOY&hZ;'S&h;'S;=`&y<%lO&hY&|P;=`<%l&h^'SP;=`<%l%n~'[O#V~~'aO#T~U'hUzS#PQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(RUzS#_QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(jWzSOt#{uw#{x!Q#{!Q![)S![#O#{#P;'S#{;'S;=`$d<%lO#{U)ZYzSsQOt#{uw#{x!O#{!O!P)y!P!Q#{!Q![)S![#O#{#P;'S#{;'S;=`$d<%lO#{U*OWzSOt#{uw#{x!Q#{!Q![*h![#O#{#P;'S#{;'S;=`$d<%lO#{U*oWzSsQOt#{uw#{x!Q#{!Q![*h![#O#{#P;'S#{;'S;=`$d<%lO#{U+^WzSOt#{uw#{x!P#{!P!Q+v!Q#O#{#P;'S#{;'S;=`$d<%lO#{U+{^zSOY,wYZ#{Zt,wtu-zuw,wwx-zx!P,w!P!Q#{!Q!},w!}#O2m#O#P0Y#P;'S,w;'S;=`3n<%lO,wU-O^zS!bQOY,wYZ#{Zt,wtu-zuw,wwx-zx!P,w!P!Q0o!Q!},w!}#O2m#O#P0Y#P;'S,w;'S;=`3n<%lO,wQ.PX!bQOY-zZ!P-z!P!Q.l!Q!}-z!}#O/Z#O#P0Y#P;'S-z;'S;=`0i<%lO-zQ.oP!P!Q.rQ.wU!bQ#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-zU0tWzSOt#{uw#{x!P#{!P!Q1^!Q#O#{#P;'S#{;'S;=`$d<%lO#{U1ebzS!bQOt#{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[zSOY2mYZ#{Zt2mtu/Zuw2mwx/Zx#O2m#O#P/s#P#Q,w#Q;'S2m;'S;=`3h<%lO2mU3kP;=`<%l2mU3qP;=`<%l,wU3{UzS!PQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U4fW#aQzSOt#{uw#{x!_#{!_!`5O!`#O#{#P;'S#{;'S;=`$d<%lO#{U5TVzSOt#{uw#{x#O#{#P#Q5j#Q;'S#{;'S;=`$d<%lO#{U5qU#`QzSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~6YO#W~U6aU#bQzSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U6zUzS!WQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U7c^zSOt#{uw#{x}#{}!O7^!O!Q#{!Q![7^![!_#{!_!`8_!`#O#{#P#T#{#T#o7^#o;'S#{;'S;=`$d<%lO#{U8fUxQzSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U8}_zSOt#{uw#{x}#{}!O7^!O!Q#{!Q![7^![!_#{!_!`8_!`#O#{#P#T#{#T#U9|#U#o7^#o;'S#{;'S;=`$d<%lO#{U:R`zSOt#{uw#{x}#{}!O7^!O!Q#{!Q![7^![!_#{!_!`8_!`#O#{#P#T#{#T#`7^#`#a;T#a#o7^#o;'S#{;'S;=`$d<%lO#{U;Y`zSOt#{uw#{x}#{}!O7^!O!Q#{!Q![7^![!_#{!_!`8_!`#O#{#P#T#{#T#g7^#g#h<[#h#o7^#o;'S#{;'S;=`$d<%lO#{Um^#XWzSOt#{uw#{x}#{}!O7^!O!Q#{!Q![7^![!_#{!_!`8_!`#O#{#P#T#{#T#o7^#o;'S#{;'S;=`$d<%lO#{^?p^#ZWzSOt#{uw#{x}#{}!O7^!O!Q#{!Q![7^![!_#{!_!`8_!`#O#{#P#T#{#T#o7^#o;'S#{;'S;=`$d<%lO#{^@s`#YWzSOt#{uw#{x}#{}!O7^!O!Q#{!Q![7^![!_#{!_!`8_!`#O#{#P#T#{#T#f7^#f#gAu#g#o7^#o;'S#{;'S;=`$d<%lO#{UAz`zSOt#{uw#{x}#{}!O7^!O!Q#{!Q![7^![!_#{!_!`8_!`#O#{#P#T#{#T#i7^#i#j<[#j#o7^#o;'S#{;'S;=`$d<%lO#{UCTUlQzSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~ClO#c~", + tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO#R~~", 11)], topRules: {"Program":[0,26]}, specialized: [{term: 20, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 20, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], - tokenPrec: 1634 + tokenPrec: 1922 }) diff --git a/src/parser/tests/basics.test.ts b/src/parser/tests/basics.test.ts index 3d0bb4d..c2a4f3d 100644 --- a/src/parser/tests/basics.test.ts +++ b/src/parser/tests/basics.test.ts @@ -368,6 +368,78 @@ describe('Parentheses', () => { PositionalArg Number 3`) }) + + test('function call with named args on multiple lines in parens', () => { + expect(`(tail + arg1=true + arg2=30 + )`).toMatchTree(` + ParenExpr + FunctionCall + Identifier tail + NamedArg + NamedArgPrefix arg1= + Boolean true + NamedArg + NamedArgPrefix arg2= + Number 30 + `) + + expect(`( + tail + arg1=true + arg2=30 + )`).toMatchTree(` + ParenExpr + FunctionCall + Identifier tail + NamedArg + NamedArgPrefix arg1= + Boolean true + NamedArg + NamedArgPrefix arg2= + Number 30 + `) + }) + + test('binop with newlines in parens', () => { + expect(`( + 1 + 2 +)`).toMatchTree(` + ParenExpr + BinOp + Number 1 + Plus + + Number 2`) + }) + + test('comparison with newlines in parens', () => { + expect(`( + 1 < 2 +)`).toMatchTree(` + ParenExpr + ConditionalOp + Number 1 + Lt < + Number 2`) + }) + + test('function call with multiple identifiers on separate lines in parens', () => { + expect(`(echo + arg1 + arg2 + arg3 +)`).toMatchTree(` + ParenExpr + FunctionCall + Identifier echo + PositionalArg + Identifier arg1 + PositionalArg + Identifier arg2 + PositionalArg + Identifier arg3`) + }) }) describe('BinOp', () => {