From 69bbe1799284674cbe5dac9541c2891d3ce2a93e Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Fri, 7 Nov 2025 22:03:00 -0800 Subject: [PATCH] "add double quoted strings" --- src/compiler/tests/literals.test.ts | 21 +++++++++++++ src/parser/shrimp.grammar | 3 +- src/parser/shrimp.terms.ts | 47 +++++++++++++++-------------- src/parser/shrimp.ts | 20 ++++++------ src/parser/tests/strings.test.ts | 18 +++++++++++ 5 files changed, 75 insertions(+), 34 deletions(-) diff --git a/src/compiler/tests/literals.test.ts b/src/compiler/tests/literals.test.ts index 3a2905f..96830cf 100644 --- a/src/compiler/tests/literals.test.ts +++ b/src/compiler/tests/literals.test.ts @@ -238,3 +238,24 @@ describe('curly strings', () => { expect(`a = 1;b = 2;c = 3;{$a$b$c}`).toEvaluateTo(`123`) }) }) + +describe('double quoted strings', () => { + test("work", () => { + expect(`"hello world"`).toEvaluateTo('hello world') + }) + + test("don't interpolate", () => { + expect(`"hello $world"`).toEvaluateTo('hello $world') + expect(`"hello $(1 + 2)"`).toEvaluateTo('hello $(1 + 2)') + }) + + test("equal regular strings", () => { + expect(`"hello world" == 'hello world'`).toEvaluateTo(true) + }) + + test("can contain newlines", () => { + expect(` + "hello + world"`).toEvaluateTo('hello\n world') + }) +}) \ No newline at end of file diff --git a/src/parser/shrimp.grammar b/src/parser/shrimp.grammar index 01d82ab..c01fd18 100644 --- a/src/parser/shrimp.grammar +++ b/src/parser/shrimp.grammar @@ -12,6 +12,7 @@ @precedence { Number Regex } StringFragment { !['\\$]+ } + DoubleQuote { '"' !["]* '"' } NamedArgPrefix { $[a-z] $[a-z0-9-]* "=" } Number { ("-" | "+")? "0x" $[0-9a-fA-F]+ | @@ -234,7 +235,7 @@ expression { } String { - "'" stringContent* "'" | CurlyString + "'" stringContent* "'" | CurlyString | DoubleQuote } } diff --git a/src/parser/shrimp.terms.ts b/src/parser/shrimp.terms.ts index fc03a61..0f49afe 100644 --- a/src/parser/shrimp.terms.ts +++ b/src/parser/shrimp.terms.ts @@ -37,7 +37,7 @@ export const Program = 35, PipeExpr = 36, WhileExpr = 38, - keyword = 80, + keyword = 81, ConditionalOp = 40, ParenExpr = 41, FunctionCallWithNewlines = 42, @@ -52,25 +52,26 @@ export const StringFragment = 51, Interpolation = 52, EscapeSeq = 53, - Boolean = 54, - Null = 55, - colon = 56, - CatchExpr = 57, - Block = 59, - FinallyExpr = 60, - Underscore = 63, - NamedArg = 64, - IfExpr = 65, - FunctionCall = 67, - ElseIfExpr = 68, - ElseExpr = 70, - FunctionCallOrIdentifier = 71, - BinOp = 72, - Regex = 73, - Dict = 74, - Array = 75, - FunctionCallWithBlock = 76, - TryExpr = 77, - Throw = 79, - CompoundAssign = 81, - Assign = 82 + DoubleQuote = 54, + Boolean = 55, + Null = 56, + colon = 57, + CatchExpr = 58, + Block = 60, + FinallyExpr = 61, + Underscore = 64, + NamedArg = 65, + IfExpr = 66, + FunctionCall = 68, + ElseIfExpr = 69, + ElseExpr = 71, + FunctionCallOrIdentifier = 72, + BinOp = 73, + Regex = 74, + Dict = 75, + Array = 76, + FunctionCallWithBlock = 77, + TryExpr = 78, + Throw = 80, + CompoundAssign = 82, + Assign = 83 diff --git a/src/parser/shrimp.ts b/src/parser/shrimp.ts index d7a0ea2..3f09fb1 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:78, null:110, catch:116, finally:122, end:124, if:132, else:138, try:156, throw:160} +const spec_Identifier = {__proto__:null,while:78, null:112, catch:118, finally:124, end:126, if:134, else:140, try:158, throw:162} export const parser = LRParser.deserialize({ version: 14, - states: "TQcO,5:iO?cQcO,5:iO@PQcO,5:iOOQa1G/_1G/_OOOO,59|,59|OOOO,59},59}OOOO-E8U-E8UOOQa1G/f1G/fOOQ`,5:Y,5:YOOQ`-E8X-E8XOOQa1G/|1G/|OA{QcO1G/|OBVQcO1G/|OCeQcO1G/|OCoQcO1G/|OC|QcO1G/|OOQa1G/[1G/[OE_QcO1G/[OEfQcO1G/[OEmQcO1G/[OFlQcO1G/[OEtQcO1G/[OOQ`-E8R-E8ROGSQRO1G/]OG^QQO1G/]OGcQQO1G/]OGkQQO1G/]OGvQRO1G/]OG}QRO1G/]OHUQbO,59rOH`QQO1G/]OOQa1G/]1G/]OHhQQO1G0OOOQa1G0P1G0POHsQbO1G0POOQO'#E]'#E]OHhQQO1G0OOOQa1G0O1G0OOOQ`'#E^'#E^OHsQbO1G0POH}QbO1G0WOIiQbO1G0VOJTQbO'#DiOJfQbO'#DiOJyQbO1G0QOOQ`-E8Q-E8QOOQ`,5:n,5:nOOQ`-E8S-E8SOKUQQO,59wOOQO,59x,59xOOQO-E8T-E8TOK^QbO1G/bO:SQbO1G/uO:SQbO1G/YOKeQbO1G0ROKpQQO7+$wOOQa7+$w7+$wOKxQQO1G/^OLQQQO7+%jOOQa7+%j7+%jOL]QbO7+%kOOQa7+%k7+%kOOQO-E8Z-E8ZOOQ`-E8[-E8[OOQ`'#EX'#EXOLgQQO'#EXOLoQbO'#EqOOQ`,5:T,5:TOMSQbO'#DgOMXQQO'#DjOOQ`7+%l7+%lOM^QbO7+%lOMcQbO7+%lOMkQbO7+$|OMyQbO7+$|ONZQbO7+%aONcQbO7+$tOOQ`7+%m7+%mONhQbO7+%mONmQbO7+%mOOQa<rAN>rOOQ`AN>SAN>SO!!wQbOAN>SO!!|QbOAN>SOOQ`-E8Y-E8YOOQ`AN>gAN>gO!#UQbOAN>gO2dQbO,5:^O:SQbO,5:`OOQ`AN>sAN>sPHUQbO'#ETOOQ`7+%X7+%XOOQ`G23nG23nO!#ZQbOG23nP!!ZQbO'#DrOOQ`G24RG24RO!#`QQO1G/xOOQ`1G/z1G/zOOQ`LD)YLD)YO:SQbO7+%dOOQ`<qOT!OOU!POj!QOu!qa#Z!qa#l!qa![!qa!_!qa!`!qa#h!qa!g!qa~O^yOR!jiS!jid!jie!jif!jig!jih!jii!jiu!ji#Z!ji#l!ji#h!ji![!ji!_!ji!`!ji!g!ji~OP!jiQ!ji~P@tOPzOQzO~P@tOPzOQzOd!jie!jif!jig!jih!jii!jiu!ji#Z!ji#l!ji#h!ji![!ji!_!ji!`!ji!g!ji~OR!jiS!ji~PBaOR{OS{O^yO~PBaOR{OS{O~PBaOW}OX}OY}OZ}O[}O]}OTxijxiuxi#Zxi#lxi#hxi!Yxi![xi!_xi!`xi!gxi~OU!PO~PDWOU!PO~PDjOUxi~PDWOT!OOU!POjxiuxi#Zxi#lxi#hxi!Yxi![xi!_xi!`xi!gxi~OW}OX}OY}OZ}O[}O]}O~PEtO#Z!RO#h$RO~P*[O#h$RO~O#h$ROu#VX~O!Y!dO#h$ROu#VX~O#h$RO~P.gO#h$RO~P7mOqgO!asO~P,wO#Z!RO#h$RO~O!RtO#Z#lO#k$UO~O#Z#oO#k$WO~P3[Ou!gO#Z!ti#l!ti![!ti!_!ti!`!ti#h!ti!g!ti~Ou!gO#Z!si#l!si![!si!_!si!`!si#h!si!g!si~Ou!gO![!]X!_!]X!`!]X!g!]X~O#Z$ZO![#eP!_#eP!`#eP!g#eP~P8xO![$_O!_$`O!`$aO~O!R!kO!Y!Pa~O#Z$eO~P8xO![$_O!_$`O!`$hO~O#Z!RO#h$kO~O#Z!RO#hzi~O!RtO#Z#lO#k$nO~O#Z#oO#k$oO~P3[Ou!gO#Z$pO~O#Z$ZO![#eX!_#eX!`#eX!g#eX~P8xOl$rO~O!Y$sO~O!`$tO~O!_$`O!`$tO~Ou!gO![$_O!_$`O!`$vO~O#Z$ZO![#eP!_#eP!`#eP~P8xO!`$}O!g$|O~O!`%PO~O!`%QO~O!_$`O!`%QO~OqgO!asO#hzq~P,wO#Z!RO#hzq~O!Y%VO~O!`%XO~O!`%YO~O!_$`O!`%YO~O![$_O!_$`O!`%YO~O!`%^O!g$|O~O!Y%aO!d%`O~O!`%^O~O!`%bO~OqgO!asO#hzy~P,wO!`%eO~O!_$`O!`%eO~O!`%hO~O!`%kO~O!Y%lO~O|!k~", - goto: "8g#hPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP#iP$SP$i%g&u&{P(V(c)])`P)fP*m*mPPP*qP*}+gPPP+}#iP,g-QP-U-[-qP.h/l$S$SP$SP$S$S0r0x1U1x2O2Y2`2g2m2w2}3XPPP3c3g4[6QPPP7[P7lPPPPP7p7v7|raOf!b!c!d!g!v#t#|#}$O$]$e$s%V%a%lQ!XXR#b!SwaOXf!S!b!c!d!g!v#t#|#}$O$]$e$s%V%a%lr_Of!b!c!d!g!v#t#|#}$O$]$e$s%V%a%lQ![XS!ph%`Q!uiQ!ykQ#X!PQ#Z!OQ#^!QR#e!SvTOfh!b!c!d!g!v#t#|#}$O$]$e$s%V%`%a%l!W[STZiktwyz{|}!O!P!Q!T!U!^!a!o#f#k#p$V$l%T%cS!UX!SQ!zlR!{mQ!WXR#a!SrSOf!b!c!d!g!v#t#|#}$O$]$e$s%V%a%l!WxSTZiktwyz{|}!O!P!Q!T!U!^!a!o#f#k#p$V$l%T%cS!TX!ST!oh%`euSTw!T!U!o#f$l%T%craOf!b!c!d!g!v#t#|#}$O$]$e$s%V%a%ldsSTw!T!U!o#f$l%T%cQ!XXQ#PtR#b!SR!ngX!lg!j!m#y#S[OSTXZfhiktwyz{|}!O!P!Q!S!T!U!^!a!b!c!d!g!o!v#f#k#p#t#|#}$O$V$]$e$l$s%T%V%`%a%c%lR#z!kToQqQ$c#uQ$j$PQ$x$dR%[$yQ#u!dQ$P!vQ$f#}Q$g$OQ%W$sQ%d%VQ%j%aR%m%lQ$b#uQ$i$PQ$u$cQ$w$dQ%R$jS%Z$x$yR%f%[duSTw!T!U!o#f$l%T%cQ!_ZQ#i!^X#l!_#i#m$TvUOXf!S!b!c!d!g!v#t#|#}$O$]$e$s%V%a%lT!rh%`T$z$f${Q%O$fR%_${wUOXf!S!b!c!d!g!v#t#|#}$O$]$e$s%V%a%lrWOf!b!c!d!g!v#t#|#}$O$]$e$s%V%a%lQ!VXQ!xkQ#RzQ#U{Q#W|R#`!S#T[OSTXZfhiktwyz{|}!O!P!Q!S!T!U!^!a!b!c!d!g!o!v#f#k#p#t#|#}$O$V$]$e$l$s%T%V%`%a%c%l![[STZhiktwyz{|}!O!P!Q!T!U!^!a!o#f#k#p$V$l%T%`%cw]OXf!S!b!c!d!g!v#t#|#}$O$]$e$s%V%a%lQfOR!hf^!ec!]#q#r#s$[$dR#v!eQ!SXQ!^Z`#_!S!^#f#g$Q$l%T%cS#f!T!US#g!V![S$Q#`#eQ$l$SR%T$mQ!jgR#x!jQ!mgQ#y!jT#{!m#yQqQR!}qS$]#t$eR$q$]Q$m$SR%U$mYwST!T!U!oR#QwQ${$fR%]${Q#m!_Q$T#iT$X#m$TQ#p!aQ$V#kT$Y#p$VTeOfScOfS!]X!SQ#q!bQ#r!c`#s!d!v#}$O$s%V%a%lQ#w!gU$[#t$]$eR$d#|vVOXf!S!b!c!d!g!v#t#|#}$O$]$e$s%V%a%ldsSTw!T!U!o#f$l%T%cQ!aZS!qh%`Q!tiQ!wkQ#PtQ#RyQ#SzQ#T{Q#V|Q#X}Q#Y!OQ#[!PQ#]!QQ#k!^X#o!a#k#p$Vr^Of!b!c!d!g!v#t#|#}$O$]$e$s%V%a%l![xSTZhiktwyz{|}!O!P!Q!T!U!^!a!o#f#k#p$V$l%T%`%cQ!ZXR#d!S[vSTw!T!U!oQ$S#fV%S$l%T%cTpQqQ$^#tR$y$eQ!shR%i%`rbOf!b!c!d!g!v#t#|#}$O$]$e$s%V%a%lQ!YXR#c!S", - nodeNames: "⚠ Star Slash Plus Minus And Or Eq EqEq Neq Lt Lte Gt Gte Modulo PlusEq MinusEq StarEq SlashEq ModuloEq Band Bor Bxor Shl Shr Ushr NullishCoalesce NullishEq Identifier AssignableIdentifier Word IdentifierBeforeDot CurlyString 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: 120, + states: "UQQO,5:[O>ZQRO,59nO>bQRO,59nO:lQbO,5:hO>pQcO,5:jO@OQcO,5:jO@lQcO,5:jOOQa1G/_1G/_OOOO,59|,59|OOOO,59},59}OOOO-E8V-E8VOOQa1G/f1G/fOOQ`,5:Z,5:ZOOQ`-E8Y-E8YOOQa1G/}1G/}OBhQcO1G/}OBrQcO1G/}ODQQcO1G/}OD[QcO1G/}ODiQcO1G/}OOQa1G/[1G/[OEzQcO1G/[OFRQcO1G/[OFYQcO1G/[OGXQcO1G/[OFaQcO1G/[OOQ`-E8S-E8SOGoQRO1G/]OGyQQO1G/]OHOQQO1G/]OHWQQO1G/]OHcQRO1G/]OHjQRO1G/]OHqQbO,59rOH{QQO1G/]OOQa1G/]1G/]OITQQO1G0POOQa1G0Q1G0QOI`QbO1G0QOOQO'#E^'#E^OITQQO1G0POOQa1G0P1G0POOQ`'#E_'#E_OI`QbO1G0QOIjQbO1G0XOJUQbO1G0WOJpQbO'#DjOKRQbO'#DjOKfQbO1G0ROOQ`-E8R-E8ROOQ`,5:o,5:oOOQ`-E8T-E8TOKqQQO,59wOOQO,59x,59xOOQO-E8U-E8UOKyQbO1G/bO:lQbO1G/vO:lQbO1G/YOLQQbO1G0SOL]QQO7+$wOOQa7+$w7+$wOLeQQO1G/^OLmQQO7+%kOOQa7+%k7+%kOLxQbO7+%lOOQa7+%l7+%lOOQO-E8[-E8[OOQ`-E8]-E8]OOQ`'#EY'#EYOMSQQO'#EYOM[QbO'#ErOOQ`,5:U,5:UOMoQbO'#DhOMtQQO'#DkOOQ`7+%m7+%mOMyQbO7+%mONOQbO7+%mONWQbO7+$|ONfQbO7+$|ONvQbO7+%bO! OQbO7+$tOOQ`7+%n7+%nO! TQbO7+%nO! YQbO7+%nOOQa<sAN>sOOQ`AN>SAN>SO!#dQbOAN>SO!#iQbOAN>SOOQ`-E8Z-E8ZOOQ`AN>hAN>hO!#qQbOAN>hO2sQbO,5:_O:lQbO,5:aOOQ`AN>tAN>tPHqQbO'#EUOOQ`7+%Y7+%YOOQ`G23nG23nO!#vQbOG23nP!!vQbO'#DsOOQ`G24SG24SO!#{QQO1G/yOOQ`1G/{1G/{OOQ`LD)YLD)YO:lQbO7+%eOOQ`<`#Z#be_!TSOt$Ouw$Ox}$O}!Od#S#T$R#T#Y>}#Y#Z@i#Z#b>}#b#cFV#c#f>}#f#gGY#g#h>}#h#iH]#i#o>}#o#p$R#p#qJm#q;'S$R;'S;=`$j<%l~$R~O$R~~KWS$WU!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RS$mP;=`<%l$R^$wU!TS#UYOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU%bU!TS#[QOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU%yZ!TSOr%trs&lst%ttu'Vuw%twx'Vx#O%t#O#P'V#P;'S%t;'S;=`'t<%lO%tU&sU!WQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RQ'YTOr'Vrs'is;'S'V;'S;=`'n<%lO'VQ'nO!WQQ'qP;=`<%l'VU'wP;=`<%l%t^(RZrY!TSOY'zYZ$RZt'ztu(tuw'zwx(tx#O'z#O#P(t#P;'S'z;'S;=`)]<%lO'zY(ySrYOY(tZ;'S(t;'S;=`)V<%lO(tY)YP;=`<%l(t^)`P;=`<%l'z~)hO#a~~)mO#_~U)tU!TS#ZQOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU*_U!TS#iQOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU*vX!TSOt$Ruw$Rx!Q$R!Q!R+c!R![.Q![#O$R#P;'S$R;'S;=`$j<%lO$RU+j`!TS|QOt$Ruw$Rx!O$R!O!P,l!P!Q$R!Q![.Q![#O$R#P#R$R#R#S.}#S#U$R#U#V/l#V#l$R#l#m1Q#m;'S$R;'S;=`$j<%lO$RU,qW!TSOt$Ruw$Rx!Q$R!Q![-Z![#O$R#P;'S$R;'S;=`$j<%lO$RU-bY!TS|QOt$Ruw$Rx!Q$R!Q![-Z![#O$R#P#R$R#R#S,l#S;'S$R;'S;=`$j<%lO$RU.X[!TS|QOt$Ruw$Rx!O$R!O!P,l!P!Q$R!Q![.Q![#O$R#P#R$R#R#S.}#S;'S$R;'S;=`$j<%lO$RU/SW!TSOt$Ruw$Rx!Q$R!Q![.Q![#O$R#P;'S$R;'S;=`$j<%lO$RU/qX!TSOt$Ruw$Rx!Q$R!Q!R0^!R!S0^!S#O$R#P;'S$R;'S;=`$j<%lO$RU0eX!TS|QOt$Ruw$Rx!Q$R!Q!R0^!R!S0^!S#O$R#P;'S$R;'S;=`$j<%lO$RU1V[!TSOt$Ruw$Rx!Q$R!Q![1{![!c$R!c!i1{!i#O$R#P#T$R#T#Z1{#Z;'S$R;'S;=`$j<%lO$RU2S[!TS|QOt$Ruw$Rx!Q$R!Q![1{![!c$R!c!i1{!i#O$R#P#T$R#T#Z1{#Z;'S$R;'S;=`$j<%lO$RU2}W!TSOt$Ruw$Rx!P$R!P!Q3g!Q#O$R#P;'S$R;'S;=`$j<%lO$RU3l^!TSOY4hYZ$RZt4htu5kuw4hwx5kx!P4h!P!Q$R!Q!}4h!}#O:^#O#P7y#P;'S4h;'S;=`;_<%lO4hU4o^!TS!lQOY4hYZ$RZt4htu5kuw4hwx5kx!P4h!P!Q8`!Q!}4h!}#O:^#O#P7y#P;'S4h;'S;=`;_<%lO4hQ5pX!lQOY5kZ!P5k!P!Q6]!Q!}5k!}#O6z#O#P7y#P;'S5k;'S;=`8Y<%lO5kQ6`P!P!Q6cQ6hU!lQ#Z#[6c#]#^6c#a#b6c#g#h6c#i#j6c#m#n6cQ6}VOY6zZ#O6z#O#P7d#P#Q5k#Q;'S6z;'S;=`7s<%lO6zQ7gSOY6zZ;'S6z;'S;=`7s<%lO6zQ7vP;=`<%l6zQ7|SOY5kZ;'S5k;'S;=`8Y<%lO5kQ8]P;=`<%l5kU8eW!TSOt$Ruw$Rx!P$R!P!Q8}!Q#O$R#P;'S$R;'S;=`$j<%lO$RU9Ub!TS!lQOt$Ruw$Rx#O$R#P#Z$R#Z#[8}#[#]$R#]#^8}#^#a$R#a#b8}#b#g$R#g#h8}#h#i$R#i#j8}#j#m$R#m#n8}#n;'S$R;'S;=`$j<%lO$RU:c[!TSOY:^YZ$RZt:^tu6zuw:^wx6zx#O:^#O#P7d#P#Q4h#Q;'S:^;'S;=`;X<%lO:^U;[P;=`<%l:^U;bP;=`<%l4hU;lU!TS!ZQOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RUQU#lQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU>kU!TS!bQOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU?S^!TSOt$Ruw$Rx}$R}!O>}!O!Q$R!Q![>}![!_$R!_!`@O!`#O$R#P#T$R#T#o>}#o;'S$R;'S;=`$j<%lO$RU@VU!RQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU@n_!TSOt$Ruw$Rx}$R}!O>}!O!Q$R!Q![>}![!_$R!_!`@O!`#O$R#P#T$R#T#UAm#U#o>}#o;'S$R;'S;=`$j<%lO$RUAr`!TSOt$Ruw$Rx}$R}!O>}!O!Q$R!Q![>}![!_$R!_!`@O!`#O$R#P#T$R#T#`>}#`#aBt#a#o>}#o;'S$R;'S;=`$j<%lO$RUBy`!TSOt$Ruw$Rx}$R}!O>}!O!Q$R!Q![>}![!_$R!_!`@O!`#O$R#P#T$R#T#g>}#g#hC{#h#o>}#o;'S$R;'S;=`$j<%lO$RUDQ`!TSOt$Ruw$Rx}$R}!O>}!O!Q$R!Q![>}![!_$R!_!`@O!`#O$R#P#T$R#T#X>}#X#YES#Y#o>}#o;'S$R;'S;=`$j<%lO$RUEZ^!XQ!TSOt$Ruw$Rx}$R}!O>}!O!Q$R!Q![>}![!_$R!_!`@O!`#O$R#P#T$R#T#o>}#o;'S$R;'S;=`$j<%lO$R^F^^#cW!TSOt$Ruw$Rx}$R}!O>}!O!Q$R!Q![>}![!_$R!_!`@O!`#O$R#P#T$R#T#o>}#o;'S$R;'S;=`$j<%lO$R^Ga^#eW!TSOt$Ruw$Rx}$R}!O>}!O!Q$R!Q![>}![!_$R!_!`@O!`#O$R#P#T$R#T#o>}#o;'S$R;'S;=`$j<%lO$R^Hd`#dW!TSOt$Ruw$Rx}$R}!O>}!O!Q$R!Q![>}![!_$R!_!`@O!`#O$R#P#T$R#T#f>}#f#gIf#g#o>}#o;'S$R;'S;=`$j<%lO$RUIk`!TSOt$Ruw$Rx}$R}!O>}!O!Q$R!Q![>}![!_$R!_!`@O!`#O$R#P#T$R#T#i>}#i#jC{#j#o>}#o;'S$R;'S;=`$j<%lO$RUJtUuQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$R~K]O#m~", + tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO#]~~", 11)], topRules: {"Program":[0,35]}, specialized: [{term: 28, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 28, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], - tokenPrec: 2229 + tokenPrec: 2256 }) diff --git a/src/parser/tests/strings.test.ts b/src/parser/tests/strings.test.ts index 22f780b..7b4a672 100644 --- a/src/parser/tests/strings.test.ts +++ b/src/parser/tests/strings.test.ts @@ -157,4 +157,22 @@ describe('curly strings', () => { two { three } }`) }) +}) + +describe('double quoted strings', () => { + test("work", () => { + expect(`"hello world"`).toMatchTree(` + String + DoubleQuote "hello world"`) + }) + + test("don't interpolate", () => { + expect(`"hello $world"`).toMatchTree(` + String + DoubleQuote "hello $world"`) + + expect(`"hello $(1 + 2)"`).toMatchTree(` + String + DoubleQuote "hello $(1 + 2)"`) + }) }) \ No newline at end of file