From fd3c5da59bc5ea3439fbd519e40523f29fbc0f87 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath <2+defunkt@users.noreply.github.com> Date: Fri, 7 Nov 2025 15:16:59 -0800 Subject: [PATCH 01/48] coerce values to string in str prelude functions --- src/prelude/str.ts | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/prelude/str.ts b/src/prelude/str.ts index 5aede56..ec688ae 100644 --- a/src/prelude/str.ts +++ b/src/prelude/str.ts @@ -1,37 +1,37 @@ // strings export const str = { join: (arr: string[], sep: string = ',') => arr.join(sep), - split: (str: string, sep: string = ',') => str.split(sep), - 'to-upper': (str: string) => str.toUpperCase(), - 'to-lower': (str: string) => str.toLowerCase(), - trim: (str: string) => str.trim(), + split: (str: string, sep: string = ',') => String(str ?? '').split(sep), + 'to-upper': (str: string) => String(str ?? '').toUpperCase(), + 'to-lower': (str: string) => String(str ?? '').toLowerCase(), + trim: (str: string) => String(str ?? '').trim(), // predicates - 'starts-with?': (str: string, prefix: string) => str.startsWith(prefix), - 'ends-with?': (str: string, suffix: string) => str.endsWith(suffix), - 'contains?': (str: string, substr: string) => str.includes(substr), - 'empty?': (str: string) => str.length === 0, + 'starts-with?': (str: string, prefix: string) => String(str ?? '').startsWith(prefix), + 'ends-with?': (str: string, suffix: string) => String(str ?? '').endsWith(suffix), + 'contains?': (str: string, substr: string) => String(str ?? '').includes(substr), + 'empty?': (str: string) => String(str ?? '').length === 0, // inspection - 'index-of': (str: string, search: string) => str.indexOf(search), - 'last-index-of': (str: string, search: string) => str.lastIndexOf(search), + 'index-of': (str: string, search: string) => String(str ?? '').indexOf(search), + 'last-index-of': (str: string, search: string) => String(str ?? '').lastIndexOf(search), // transformations - replace: (str: string, search: string, replacement: string) => str.replace(search, replacement), - 'replace-all': (str: string, search: string, replacement: string) => str.replaceAll(search, replacement), - slice: (str: string, start: number, end?: number | null) => str.slice(start, end ?? undefined), - substring: (str: string, start: number, end?: number | null) => str.substring(start, end ?? undefined), + replace: (str: string, search: string, replacement: string) => String(str ?? '').replace(search, replacement), + 'replace-all': (str: string, search: string, replacement: string) => String(str ?? '').replaceAll(search, replacement), + slice: (str: string, start: number, end?: number | null) => String(str ?? '').slice(start, end ?? undefined), + substring: (str: string, start: number, end?: number | null) => String(str ?? '').substring(start, end ?? undefined), repeat: (str: string, count: number) => { if (count < 0) throw new Error(`repeat: count must be non-negative, got ${count}`) if (!Number.isInteger(count)) throw new Error(`repeat: count must be an integer, got ${count}`) - return str.repeat(count) + return String(str ?? '').repeat(count) }, - 'pad-start': (str: string, length: number, pad: string = ' ') => str.padStart(length, pad), - 'pad-end': (str: string, length: number, pad: string = ' ') => str.padEnd(length, pad), - lines: (str: string) => str.split('\n'), - chars: (str: string) => str.split(''), + 'pad-start': (str: string, length: number, pad: string = ' ') => String(str ?? '').padStart(length, pad), + 'pad-end': (str: string, length: number, pad: string = ' ') => String(str ?? '').padEnd(length, pad), + lines: (str: string) => String(str ?? '').split('\n'), + chars: (str: string) => String(str ?? '').split(''), // regex - match: (str: string, regex: RegExp) => str.match(regex), - 'test?': (str: string, regex: RegExp) => regex.test(str), + match: (str: string, regex: RegExp) => String(str ?? '').match(regex), + 'test?': (str: string, regex: RegExp) => regex.test(String(str ?? '')), } \ No newline at end of file From 890eb811b96c181da591c4ed96fb13d11e986053 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath <2+defunkt@users.noreply.github.com> Date: Fri, 7 Nov 2025 15:17:23 -0800 Subject: [PATCH 02/48] pipe atoms/literals to functions --- src/compiler/tests/pipe.test.ts | 26 +++++++++++ src/parser/shrimp.grammar | 6 +-- src/parser/shrimp.terms.ts | 62 +++++++++++++------------- src/parser/shrimp.ts | 16 +++---- src/parser/tests/pipes.test.ts | 77 +++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 42 deletions(-) diff --git a/src/compiler/tests/pipe.test.ts b/src/compiler/tests/pipe.test.ts index 06d56c3..b8f92f1 100644 --- a/src/compiler/tests/pipe.test.ts +++ b/src/compiler/tests/pipe.test.ts @@ -92,4 +92,30 @@ describe('pipe expressions', () => { get-msg | length `).toEvaluateTo(5) }) + + + test('string literals can be piped', () => { + expect(`'hey there' | str.to-upper`).toEvaluateTo('HEY THERE') + }) + + test('number literals can be piped', () => { + expect(`42 | str.trim`).toEvaluateTo('42') + expect(`4.22 | str.trim`).toEvaluateTo('4.22') + }) + + test('null literals can be piped', () => { + expect(`null | type`).toEvaluateTo('null') + }) + + test('boolean literals can be piped', () => { + expect(`true | str.to-upper`).toEvaluateTo('TRUE') + }) + + test('array literals can be piped', () => { + expect(`[1 2 3] | str.join '-'`).toEvaluateTo('1-2-3') + }) + + test('dict literals can be piped', () => { + expect(`[a=1 b=2 c=3] | dict.values | list.sort | str.join '-'`).toEvaluateTo('1-2-3') + }) }) diff --git a/src/parser/shrimp.grammar b/src/parser/shrimp.grammar index ac38814..97908d9 100644 --- a/src/parser/shrimp.grammar +++ b/src/parser/shrimp.grammar @@ -50,7 +50,7 @@ null { @specialize[@name=Null] } call } -item { +item { consumeToTerminator newlineOrSemicolon | consumeToTerminator eof | newlineOrSemicolon // allow blank lines @@ -77,7 +77,7 @@ PipeExpr { } pipeOperand { - FunctionCall | FunctionCallOrIdentifier + consumeToTerminator } WhileExpr { @@ -235,7 +235,7 @@ Array { // 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 -// - expression → Identifier +// - expression → Identifier // Both want the same Identifier token! So we use expressionWithoutIdentifier // to remove Identifier from the second path, forcing standalone identifiers // to go through ambiguousFunctionCall (which is what we want semantically). diff --git a/src/parser/shrimp.terms.ts b/src/parser/shrimp.terms.ts index 716ef1c..04cb710 100644 --- a/src/parser/shrimp.terms.ts +++ b/src/parser/shrimp.terms.ts @@ -27,38 +27,38 @@ export const Comment = 25, Program = 26, PipeExpr = 27, - FunctionCall = 28, - DotGet = 29, - Number = 30, - ParenExpr = 31, - IfExpr = 32, + WhileExpr = 29, keyword = 70, - ConditionalOp = 34, - String = 35, - StringFragment = 36, - Interpolation = 37, - EscapeSeq = 38, - Boolean = 39, - Regex = 40, - Dict = 41, - NamedArg = 42, - NamedArgPrefix = 43, - FunctionDef = 44, - Params = 45, - NamedParam = 46, - Null = 47, - colon = 48, - CatchExpr = 49, - Block = 51, - FinallyExpr = 52, - Underscore = 55, - Array = 56, - ElseIfExpr = 57, - ElseExpr = 59, - FunctionCallOrIdentifier = 60, - BinOp = 61, - PositionalArg = 62, - WhileExpr = 64, + 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, diff --git a/src/parser/shrimp.ts b/src/parser/shrimp.ts index ffb958e..fa41719 100644 --- a/src/parser/shrimp.ts +++ b/src/parser/shrimp.ts @@ -4,23 +4,23 @@ import {operatorTokenizer} from "./operatorTokenizer" import {tokenizer, specializeKeyword} from "./tokenizer" import {trackScope} from "./parserScopeContext" import {highlighting} from "./highlight" -const spec_Identifier = {__proto__:null,if:66, null:94, catch:100, finally:106, end:108, else:116, while:130, try:136, throw:140} +const spec_Identifier = {__proto__:null,while:60, if:68, null:96, catch:102, finally:108, end:110, else:118, try:136, throw:140} export const parser = LRParser.deserialize({ version: 14, - states: "9[QYQbOOO!dOSO'#DPOOQa'#DV'#DVO#mQbO'#DfO%RQcO'#E^OOQa'#E^'#E^O&XQcO'#E^O'ZQcO'#E]O'qQcO'#E]O)^QRO'#DOO*mQcO'#EWO*wQcO'#EWO+XQbO'#C{O,SOpO'#CyOOQ`'#EX'#EXO,XQbO'#EWO,cQRO'#DuOOQ`'#EW'#EWO,wQQO'#EVOOQ`'#EV'#EVOOQ`'#Dw'#DwQYQbOOO-PQbO'#DYO-[QbO'#C|O.PQbO'#DnO.tQQO'#DqO.PQbO'#DsO.yQbO'#DRO/RQWO'#DSOOOO'#E`'#E`OOOO'#Dx'#DxO/gOSO,59kOOQa,59k,59kOOQ`'#Dy'#DyO/uQbO,5:QO/|QbO'#DWO0WQQO,59qOOQa,5:Q,5:QO0cQbO,5:QOOQa'#E]'#E]OOQ`'#Dl'#DlOOQ`'#El'#ElOOQ`'#EQ'#EQO0mQbO,59dO1gQbO,5:bO.PQbO,59jO.PQbO,59jO.PQbO,59jO.PQbO,5:VO.PQbO,5:VO.PQbO,5:VO1wQRO,59gO2OQRO,59gO2ZQRO,59gO2UQQO,59gO2lQQO,59gO2tObO,59eO3PQbO'#ERO3[QbO,59cO3vQbO,5:[O1gQbO,5:aOOQ`,5:q,5:qOOQ`-E7u-E7uOOQ`'#Dz'#DzO4ZQbO'#DZO4fQbO'#D[OOQO'#D{'#D{O4^QQO'#DZO4tQQO,59tO4yQcO'#E]O6_QRO'#E[O6fQRO'#E[OOQO'#E['#E[O6qQQO,59hO6vQRO,5:YO6}QRO,5:YO3vQbO,5:]O7YQcO,5:_O8UQcO,5:_O8`QcO,5:_OOOO,59m,59mOOOO,59n,59nOOOO-E7v-E7vOOQa1G/V1G/VOOQ`-E7w-E7wO8pQQO1G/]OOQa1G/l1G/lO8{QbO1G/lOOQ`,59r,59rOOQO'#D}'#D}O8pQQO1G/]OOQa1G/]1G/]OOQ`'#EO'#EOO8{QbO1G/lOOQ`-E8O-E8OOOQ`1G/|1G/|OOQa1G/U1G/UO:WQcO1G/UO:_QcO1G/UO:fQcO1G/UOOQa1G/q1G/qO;_QcO1G/qO;iQcO1G/qO;sQcO1G/qOOQa1G/R1G/ROOQa1G/P1G/PO]QbO1G/vOOQ`1G/{1G/{OOQ`-E7x-E7xO>hQQO,59uOOQO,59v,59vOOQO-E7y-E7yO>pQbO1G/`O3vQbO1G/SO3vQbO1G/tO?TQbO1G/wO?`QQO7+$wOOQa7+$w7+$wO?kQbO7+%WOOQa7+%W7+%WOOQO-E7{-E7{OOQ`-E7|-E7|OOQ`'#D|'#D|O?uQQO'#D|O?zQbO'#EiOOQ`,59{,59{O@kQbO'#D_O@pQQO'#DbOOQ`7+%b7+%bO@uQbO7+%bO@zQbO7+%bOASQbO7+$zOA_QbO7+$zOA{QbO7+$nOBTQbO7+%`OOQ`7+%c7+%cOBYQbO7+%cOB_QbO7+%cOOQa<hAN>hOOQ`AN>QAN>QOCuQbOAN>QOCzQbOAN>QOOQ`-E7}-E7}OOQ`AN=tAN=tODSQbOAN=tO-[QbO,5:RO3vQbO,5:TOOQ`AN>iAN>iOOQ`7+%P7+%POOQ`G23lG23lODXQbOG23lPD^QbO'#DgOOQ`G23`G23`ODcQQO1G/mOOQ`1G/o1G/oOOQ`LD)WLD)WO3vQbO7+%XOOQ`<VQbO'#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`<c#Y#o,w#o;'S#{;'S;=`$d<%lO#{U>j[wQtSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{^?g[#VWtSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{^@d[#XWtSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{^Aa^#WWtSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#f,w#f#gB]#g#o,w#o;'S#{;'S;=`$d<%lO#{UBb^tSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#i,w#i#j=b#j#o,w#o;'S#{;'S;=`$d<%lO#{UCeU!aQtSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~C|O#a~", - tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO!|~~", 11)], + tokenData: "C|~R|OX#{XY$jYZ%TZp#{pq$jqs#{st%ntu'tuw#{wx'yxy(Oyz(iz{#{{|)S|}#{}!O+v!O!P#{!P!Q.]!Q![)q![!]6x!]!^%T!^!}#{!}#O7c#O#P9X#P#Q9^#Q#R#{#R#S9w#S#T#{#T#Y,w#Y#Z:b#Z#b,w#b#c?`#c#f,w#f#g@]#g#h,w#h#iAY#i#o,w#o#p#{#p#qC^#q;'S#{;'S;=`$d<%l~#{~O#{~~CwS$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#{^%sW|SOp#{pq&]qt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{^&dZiY|SOY&]YZ#{Zt&]tu'Vuw&]wx'Vx#O&]#O#P'V#P;'S&];'S;=`'n<%lO&]Y'[SiYOY'VZ;'S'V;'S;=`'h<%lO'VY'kP;=`<%l'V^'qP;=`<%l&]~'yO#T~~(OO#R~U(VU|S!}QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(pU|S#]QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U)XW|SOt#{uw#{x!Q#{!Q![)q![#O#{#P;'S#{;'S;=`$d<%lO#{U)xY|SuQOt#{uw#{x!O#{!O!P*h!P!Q#{!Q![)q![#O#{#P;'S#{;'S;=`$d<%lO#{U*mW|SOt#{uw#{x!Q#{!Q![+V![#O#{#P;'S#{;'S;=`$d<%lO#{U+^W|SuQOt#{uw#{x!Q#{!Q![+V![#O#{#P;'S#{;'S;=`$d<%lO#{U+{^|SOt#{uw#{x}#{}!O,w!O!Q#{!Q![)q![!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{U,|[|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{U-yUzQ|SOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U.bW|SOt#{uw#{x!P#{!P!Q.z!Q#O#{#P;'S#{;'S;=`$d<%lO#{U/P^|SOY/{YZ#{Zt/{tu1Ouw/{wx1Ox!P/{!P!Q#{!Q!}/{!}#O5q#O#P3^#P;'S/{;'S;=`6r<%lO/{U0S^|S!aQOY/{YZ#{Zt/{tu1Ouw/{wx1Ox!P/{!P!Q3s!Q!}/{!}#O5q#O#P3^#P;'S/{;'S;=`6r<%lO/{Q1TX!aQOY1OZ!P1O!P!Q1p!Q!}1O!}#O2_#O#P3^#P;'S1O;'S;=`3m<%lO1OQ1sP!P!Q1vQ1{U!aQ#Z#[1v#]#^1v#a#b1v#g#h1v#i#j1v#m#n1vQ2bVOY2_Z#O2_#O#P2w#P#Q1O#Q;'S2_;'S;=`3W<%lO2_Q2zSOY2_Z;'S2_;'S;=`3W<%lO2_Q3ZP;=`<%l2_Q3aSOY1OZ;'S1O;'S;=`3m<%lO1OQ3pP;=`<%l1OU3xW|SOt#{uw#{x!P#{!P!Q4b!Q#O#{#P;'S#{;'S;=`$d<%lO#{U4ib|S!aQOt#{uw#{x#O#{#P#Z#{#Z#[4b#[#]#{#]#^4b#^#a#{#a#b4b#b#g#{#g#h4b#h#i#{#i#j4b#j#m#{#m#n4b#n;'S#{;'S;=`$d<%lO#{U5v[|SOY5qYZ#{Zt5qtu2_uw5qwx2_x#O5q#O#P2w#P#Q/{#Q;'S5q;'S;=`6l<%lO5qU6oP;=`<%l5qU6uP;=`<%l/{U7PU|S!RQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U7jW#_Q|SOt#{uw#{x!_#{!_!`8S!`#O#{#P;'S#{;'S;=`$d<%lO#{U8XV|SOt#{uw#{x#O#{#P#Q8n#Q;'S#{;'S;=`$d<%lO#{U8uU#^Q|SOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~9^O#U~U9eU#`Q|SOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U:OU|S!YQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U:g]|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#U;`#U#o,w#o;'S#{;'S;=`$d<%lO#{U;e^|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#`,w#`#ac#Y#o,w#o;'S#{;'S;=`$d<%lO#{U>j[!PQ|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{^?g[#VW|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{^@d[#XW|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{^Aa^#WW|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#f,w#f#gB]#g#o,w#o;'S#{;'S;=`$d<%lO#{UBb^|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#i,w#i#j=b#j#o,w#o;'S#{;'S;=`$d<%lO#{UCeUlQ|SOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~C|O#a~", + tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO#P~~", 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 diff --git a/src/parser/tests/pipes.test.ts b/src/parser/tests/pipes.test.ts index 9e87114..0d5da6a 100644 --- a/src/parser/tests/pipes.test.ts +++ b/src/parser/tests/pipes.test.ts @@ -98,4 +98,81 @@ describe('pipe expressions', () => { Identifier double `) }) + + test('string literals can be piped', () => { + expect(`'hey there' | echo`).toMatchTree(` + PipeExpr + String + StringFragment hey there + operator | + FunctionCallOrIdentifier + Identifier echo + `) + }) + + test('number literals can be piped', () => { + expect(`42 | echo`).toMatchTree(` + PipeExpr + Number 42 + operator | + FunctionCallOrIdentifier + Identifier echo`) + + expect(`4.22 | echo`).toMatchTree(` + PipeExpr + Number 4.22 + operator | + FunctionCallOrIdentifier + Identifier echo`) + }) + + test('null literals can be piped', () => { + expect(`null | echo`).toMatchTree(` + PipeExpr + Null null + operator | + FunctionCallOrIdentifier + Identifier echo`) + }) + + test('boolean literals can be piped', () => { + expect(`true | echo`).toMatchTree(` + PipeExpr + Boolean true + operator | + FunctionCallOrIdentifier + Identifier echo`) + }) + + test('array literals can be piped', () => { + expect(`[1 2 3] | echo`).toMatchTree(` + PipeExpr + Array + Number 1 + Number 2 + Number 3 + operator | + FunctionCallOrIdentifier + Identifier echo + `) + }) + + test('dict literals can be piped', () => { + expect(`[a=1 b=2 c=3] | echo`).toMatchTree(` + PipeExpr + Dict + NamedArg + NamedArgPrefix a= + Number 1 + NamedArg + NamedArgPrefix b= + Number 2 + NamedArg + NamedArgPrefix c= + Number 3 + operator | + FunctionCallOrIdentifier + Identifier echo + `) + }) }) From 59cf459d74f2f5e87caf8a6496987f8638bce548 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath <2+defunkt@users.noreply.github.com> Date: Fri, 7 Nov 2025 15:48:35 -0800 Subject: [PATCH 03/48] cli:install and cli:remove --- package.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f3fd090..472b4af 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,9 @@ "dev": "bun generate-parser && bun --hot src/server/server.tsx", "generate-parser": "lezer-generator src/parser/shrimp.grammar --typeScript -o src/parser/shrimp.ts", "repl": "bun generate-parser && bun bin/repl", - "update-reef": "rm -rf ~/.bun/install/cache/ && rm bun.lock && bun update reefvm" + "update-reef": "rm -rf ~/.bun/install/cache/ && rm bun.lock && bun update reefvm", + "cli:install": "ln -s \"$(pwd)/bin/shrimp\" ~/.bun/bin/shrimp", + "cli:remove": "rm ~/.bun/bin/shrimp", }, "dependencies": { "@codemirror/view": "^6.38.3", @@ -29,4 +31,4 @@ "singleQuote": true, "printWidth": 100 } -} \ No newline at end of file +} From d4a772e88b4d176042cec61b334f9a28accdc1dc Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Fri, 7 Nov 2025 19:36:22 -0800 Subject: [PATCH 04/48] json.encode & json.decode --- src/prelude/index.ts | 2 + src/prelude/json.ts | 7 +++ src/prelude/tests/json.test.ts | 84 ++++++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 src/prelude/json.ts create mode 100644 src/prelude/tests/json.test.ts diff --git a/src/prelude/index.ts b/src/prelude/index.ts index 488414b..a3966e1 100644 --- a/src/prelude/index.ts +++ b/src/prelude/index.ts @@ -6,6 +6,7 @@ import { } from 'reefvm' import { dict } from './dict' +import { json } from './json' import { load } from './load' import { list } from './list' import { math } from './math' @@ -13,6 +14,7 @@ import { str } from './str' export const globals = { dict, + json, load, list, math, diff --git a/src/prelude/json.ts b/src/prelude/json.ts new file mode 100644 index 0000000..c54a908 --- /dev/null +++ b/src/prelude/json.ts @@ -0,0 +1,7 @@ +export const json = { + encode: (s: any) => JSON.stringify(s), + decode: (s: string) => JSON.parse(s), +} + + ; (json as any).parse = json.decode + ; (json as any).stringify = json.encode \ No newline at end of file diff --git a/src/prelude/tests/json.test.ts b/src/prelude/tests/json.test.ts new file mode 100644 index 0000000..544722e --- /dev/null +++ b/src/prelude/tests/json.test.ts @@ -0,0 +1,84 @@ +import { expect, describe, test } from 'bun:test' + +describe('json', () => { + test('json.decode', () => { + expect(`json.decode '[1,2,3]'`).toEvaluateTo([1, 2, 3]) + expect(`json.decode '"heya"'`).toEvaluateTo('heya') + expect(`json.decode '[true, false, null]'`).toEvaluateTo([true, false, null]) + expect(`json.decode '{"a": true, "b": false, "c": "yeah"}'`).toEvaluateTo({ a: true, b: false, c: "yeah" }) + }) + + test('json.encode', () => { + expect(`json.encode [1 2 3]`).toEvaluateTo('[1,2,3]') + expect(`json.encode 'heya'`).toEvaluateTo('"heya"') + expect(`json.encode [true false null]`).toEvaluateTo('[true,false,null]') + expect(`json.encode [a=true b=false c='yeah'] | json.decode`).toEvaluateTo({ a: true, b: false, c: "yeah" }) + }) + + test('edge cases - empty structures', () => { + expect(`json.decode '[]'`).toEvaluateTo([]) + expect(`json.decode '{}'`).toEvaluateTo({}) + expect(`json.encode []`).toEvaluateTo('[]') + expect(`json.encode [=]`).toEvaluateTo('{}') + }) + + test('edge cases - special characters in strings', () => { + expect(`json.decode '"hello\\\\nworld"'`).toEvaluateTo('hello\nworld') + expect(`json.decode '"tab\\\\there"'`).toEvaluateTo('tab\there') + expect(`json.decode '"forward/slash"'`).toEvaluateTo('forward/slash') + expect(`json.decode '"with\\\\\\\\backslash"'`).toEvaluateTo('with\\backslash') + }) + + test('numbers - integers and floats', () => { + expect(`json.decode '42'`).toEvaluateTo(42) + expect(`json.decode '0'`).toEvaluateTo(0) + expect(`json.decode '-17'`).toEvaluateTo(-17) + expect(`json.decode '3.14159'`).toEvaluateTo(3.14159) + expect(`json.decode '-0.5'`).toEvaluateTo(-0.5) + }) + + test('numbers - scientific notation', () => { + expect(`json.decode '1e10'`).toEvaluateTo(1e10) + expect(`json.decode '2.5e-3'`).toEvaluateTo(2.5e-3) + expect(`json.decode '1.23E+5'`).toEvaluateTo(1.23e5) + }) + + test('unicode - emoji and special characters', () => { + expect(`json.decode '"hello 👋"'`).toEvaluateTo('hello 👋') + expect(`json.decode '"🎉🚀✨"'`).toEvaluateTo('🎉🚀✨') + expect(`json.encode '你好'`).toEvaluateTo('"你好"') + expect(`json.encode 'café'`).toEvaluateTo('"café"') + }) + + test('nested structures - arrays', () => { + expect(`json.decode '[[1,2],[3,4],[5,6]]'`).toEvaluateTo([[1, 2], [3, 4], [5, 6]]) + expect(`json.decode '[1,[2,[3,[4]]]]'`).toEvaluateTo([1, [2, [3, [4]]]]) + }) + + test('nested structures - objects', () => { + expect(`json.decode '{"user":{"name":"Alice","age":30}}'`).toEvaluateTo({ + user: { name: 'Alice', age: 30 } + }) + expect(`json.decode '{"a":{"b":{"c":"deep"}}}'`).toEvaluateTo({ + a: { b: { c: 'deep' } } + }) + }) + + test('nested structures - mixed arrays and objects', () => { + expect(`json.decode '[{"id":1,"tags":["a","b"]},{"id":2,"tags":["c"]}]'`).toEvaluateTo([ + { id: 1, tags: ['a', 'b'] }, + { id: 2, tags: ['c'] } + ]) + expect(`json.decode '{"items":[1,2,3],"meta":{"count":3}}'`).toEvaluateTo({ + items: [1, 2, 3], + meta: { count: 3 } + }) + }) + + test('error handling - invalid json', () => { + expect(`json.decode '{invalid}'`).toFailEvaluation() + expect(`json.decode '[1,2,3'`).toFailEvaluation() + expect(`json.decode 'undefined'`).toFailEvaluation() + expect(`json.decode ''`).toFailEvaluation() + }) +}) \ No newline at end of file From 4258503c0e5e473bbddcf119fbf4ffdfa56ef3a8 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Fri, 7 Nov 2025 20:50:48 -0800 Subject: [PATCH 05/48] shrimp cli wants the prelude too --- bin/shrimp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bin/shrimp b/bin/shrimp index 5706ab9..dd1bf9a 100755 --- a/bin/shrimp +++ b/bin/shrimp @@ -3,6 +3,7 @@ import { Compiler } from '../src/compiler/compiler' import { colors, globals } from '../src/prelude' import { parser } from '../src/parser/shrimp' +import { setGlobals } from '../src/parser/tokenizer' import { treeToString } from '../src/utils/tree' import { VM, fromValue, bytecodeToString } from 'reefvm' import { readFileSync, writeFileSync, mkdirSync } from 'fs' @@ -25,6 +26,7 @@ async function runFile(filePath: string) { async function compileFile(filePath: string) { try { + setGlobals(Object.keys(globals)) const code = readFileSync(filePath, 'utf-8') const compiler = new Compiler(code) return bytecodeToString(compiler.bytecode) @@ -36,6 +38,7 @@ async function compileFile(filePath: string) { async function parseFile(filePath: string) { try { + setGlobals(Object.keys(globals)) const code = readFileSync(filePath, 'utf-8') const tree = parser.parse(code) return treeToString(tree, code) From bae0da31c2a50ebe4c01a5812755c83ca691f333 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Fri, 7 Nov 2025 21:05:42 -0800 Subject: [PATCH 06/48] globals accepts {} too --- src/compiler/compiler.ts | 4 ++-- src/parser/tokenizer.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 429a94b..9ce7bce 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -58,9 +58,9 @@ export class Compiler { bytecode: Bytecode pipeCounter = 0 - constructor(public input: string, globals?: string[]) { + constructor(public input: string, globals?: string[] | Record) { try { - if (globals) setGlobals(globals) + if (globals) setGlobals(Array.isArray(globals) ? globals : Object.keys(globals)) const cst = parser.parse(input) const errors = checkTreeForErrors(cst) diff --git a/src/parser/tokenizer.ts b/src/parser/tokenizer.ts index ba8da48..3f5ca6c 100644 --- a/src/parser/tokenizer.ts +++ b/src/parser/tokenizer.ts @@ -8,9 +8,9 @@ export function specializeKeyword(ident: string) { // tell the dotGet searcher about builtin globals export const globals: string[] = [] -export const setGlobals = (newGlobals: string[]) => { +export const setGlobals = (newGlobals: string[] | Record) => { globals.length = 0 - globals.push(...newGlobals) + globals.push(...(Array.isArray(newGlobals) ? newGlobals : Object.keys(newGlobals))) } // The only chars that can't be words are whitespace, apostrophes, closing parens, and EOF. From 9f45252522e59d1afa6634fa4e9834372ece7133 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Fri, 7 Nov 2025 21:10:25 -0800 Subject: [PATCH 07/48] cli: don't re-implement stuff --- bin/shrimp | 50 ++++++-------------------------------------------- src/index.ts | 16 +++++++++++++++- 2 files changed, 21 insertions(+), 45 deletions(-) diff --git a/bin/shrimp b/bin/shrimp index dd1bf9a..d26ec68 100755 --- a/bin/shrimp +++ b/bin/shrimp @@ -1,53 +1,14 @@ #!/usr/bin/env bun -import { Compiler } from '../src/compiler/compiler' -import { colors, globals } from '../src/prelude' -import { parser } from '../src/parser/shrimp' -import { setGlobals } from '../src/parser/tokenizer' +import { colors } from '../src/prelude' import { treeToString } from '../src/utils/tree' -import { VM, fromValue, bytecodeToString } from 'reefvm' +import { runFile, compileFile, parseCode } from '../src' +import { bytecodeToString } from 'reefvm' import { readFileSync, writeFileSync, mkdirSync } from 'fs' import { randomUUID } from 'crypto' import { spawn } from 'child_process' import { join } from 'path' -async function runFile(filePath: string) { - try { - const code = readFileSync(filePath, 'utf-8') - const compiler = new Compiler(code, Object.keys(globals)) - const vm = new VM(compiler.bytecode, globals) - await vm.run() - return vm.stack.length ? fromValue(vm.stack[vm.stack.length - 1]) : null - } catch (error: any) { - console.error(`${colors.red}Error:${colors.reset} ${error.message}`) - process.exit(1) - } -} - -async function compileFile(filePath: string) { - try { - setGlobals(Object.keys(globals)) - const code = readFileSync(filePath, 'utf-8') - const compiler = new Compiler(code) - return bytecodeToString(compiler.bytecode) - } catch (error: any) { - console.error(`${colors.red}Error:${colors.reset} ${error.message}`) - process.exit(1) - } -} - -async function parseFile(filePath: string) { - try { - setGlobals(Object.keys(globals)) - const code = readFileSync(filePath, 'utf-8') - const tree = parser.parse(code) - return treeToString(tree, code) - } catch (error: any) { - console.error(`${colors.red}Error:${colors.reset} ${error.message}`) - process.exit(1) - } -} - function showHelp() { console.log(`${colors.bright}${colors.magenta}🦐 Shrimp${colors.reset} is a scripting language in a shell. @@ -115,7 +76,7 @@ async function main() { console.log(`${colors.bright}usage: shrimp bytecode ${colors.reset}`) process.exit(1) } - console.log(await compileFile(file)) + console.log(bytecodeToString(compileFile(file))) return } @@ -125,7 +86,8 @@ async function main() { console.log(`${colors.bright}usage: shrimp parse ${colors.reset}`) process.exit(1) } - console.log(await parseFile(file)) + const input = readFileSync(file, 'utf-8') + console.log(treeToString(parseCode(input), input)) return } diff --git a/src/index.ts b/src/index.ts index 3a91bed..ae6dd70 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,9 @@ -import { readFileSync } from 'fs' +import { glob, readFileSync } from 'fs' import { VM, fromValue, type Bytecode } from 'reefvm' +import { type Tree } from '@lezer/common' import { Compiler } from '#compiler/compiler' +import { parser } from '#parser/shrimp' +import { setGlobals } from '#parser/tokenizer' import { globals as shrimpGlobals, colors } from '#prelude' export { Compiler } from '#compiler/compiler' @@ -70,4 +73,15 @@ export function compileCode(code: string, globals?: Record): Byteco const globalNames = [...Object.keys(shrimpGlobals), ...(globals ? Object.keys(globals) : [])] const compiler = new Compiler(code, globalNames) return compiler.bytecode +} + +export function parseFile(path: string, globals?: Record): Tree { + const code = readFileSync(path, 'utf-8') + return parseCode(code, globals) +} + +export function parseCode(code: string, globals?: Record): Tree { + const globalNames = [...Object.keys(shrimpGlobals), ...(globals ? Object.keys(globals) : [])] + setGlobals(globalNames) + return parser.parse(code) } \ No newline at end of file From da0af799d839efb72ded2609cecae73697ad9b28 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Fri, 7 Nov 2025 21:43:55 -0800 Subject: [PATCH 08/48] add more Shrimp API, tests --- src/index.ts | 69 +++++-- src/tests/shrimp.test.ts | 401 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 450 insertions(+), 20 deletions(-) diff --git a/src/index.ts b/src/index.ts index ae6dd70..c1f4835 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,14 +1,17 @@ -import { glob, readFileSync } from 'fs' -import { VM, fromValue, type Bytecode } from 'reefvm' +import { readFileSync } from 'fs' +import { VM, fromValue, toValue, type Bytecode } from 'reefvm' import { type Tree } from '@lezer/common' import { Compiler } from '#compiler/compiler' import { parser } from '#parser/shrimp' -import { setGlobals } from '#parser/tokenizer' -import { globals as shrimpGlobals, colors } from '#prelude' +import { globals as parserGlobals, setGlobals as setParserGlobals } from '#parser/tokenizer' +import { globals as shrimpGlobals } from '#prelude' export { Compiler } from '#compiler/compiler' export { parser } from '#parser/shrimp' -export { globals } from '#prelude' +export { globals as prelude } from '#prelude' +export type { Tree } from '@lezer/common' +export { type Value, type Bytecode } from 'reefvm' +export { toValue, fromValue, Scope, VM, bytecodeToString } from 'reefvm' export class Shrimp { vm: VM @@ -20,6 +23,38 @@ export class Shrimp { this.globals = globals } + get(name: string): any { + const value = this.vm.scope.get(name) + return value ? fromValue(value) : null + } + + set(name: string, value: any) { + this.vm.scope.set(name, toValue(value, this.vm)) + } + + has(name: string): boolean { + return this.vm.scope.has(name) + } + + async call(name: string, ...args: any[]): Promise { + const result = await this.vm.call(name, ...args) + // vm.call() returns Value for native functions, JS values for Reef functions + // Check if it's a Value object and unwrap if needed + if (result && typeof result === 'object' && 'type' in result) { + return fromValue(result) + } + return result + } + + parse(code: string): Tree { + return parseCode(code, this.globals) + } + + compile(code: string): Bytecode { + return compileCode(code, this.globals) + } + + async run(code: string | Bytecode, locals?: Record): Promise { let bytecode @@ -38,10 +73,6 @@ export class Shrimp { return this.vm.stack.length ? fromValue(this.vm.stack.at(-1)!) : null } - get(name: string): any { - const value = this.vm.scope.get(name) - return value ? fromValue(value) : null - } } export async function runFile(path: string, globals?: Record): Promise { @@ -54,14 +85,9 @@ export async function runCode(code: string, globals?: Record): Prom } export async function runBytecode(bytecode: Bytecode, globals?: Record): Promise { - try { - const vm = new VM(bytecode, Object.assign({}, shrimpGlobals, globals)) - await vm.run() - return vm.stack.length ? fromValue(vm.stack[vm.stack.length - 1]!) : null - } catch (error: any) { - console.error(`${colors.red}Error:${colors.reset} ${error.message}`) - process.exit(1) - } + const vm = new VM(bytecode, Object.assign({}, shrimpGlobals, globals)) + await vm.run() + return vm.stack.length ? fromValue(vm.stack[vm.stack.length - 1]!) : null } export function compileFile(path: string, globals?: Record): Bytecode { @@ -81,7 +107,12 @@ export function parseFile(path: string, globals?: Record): Tree { } export function parseCode(code: string, globals?: Record): Tree { + const oldGlobals = [...parserGlobals] const globalNames = [...Object.keys(shrimpGlobals), ...(globals ? Object.keys(globals) : [])] - setGlobals(globalNames) - return parser.parse(code) + + setParserGlobals(globalNames) + const result = parser.parse(code) + setParserGlobals(oldGlobals) + + return result } \ No newline at end of file diff --git a/src/tests/shrimp.test.ts b/src/tests/shrimp.test.ts index 19a0553..e905901 100644 --- a/src/tests/shrimp.test.ts +++ b/src/tests/shrimp.test.ts @@ -1,6 +1,6 @@ import { describe } from 'bun:test' import { expect, test } from 'bun:test' -import { Shrimp } from '..' +import { Shrimp, runCode, compileCode, parseCode, bytecodeToString } from '..' describe('Shrimp', () => { test('allows running Shrimp code', async () => { @@ -50,4 +50,403 @@ describe('Shrimp', () => { await shrimp.run('abc = nothing') expect(shrimp.get('abc')).toEqual('nothing') }) + + describe('set()', () => { + test('allows setting variables', async () => { + const shrimp = new Shrimp() + + shrimp.set('foo', 42) + expect(shrimp.get('foo')).toEqual(42) + + shrimp.set('bar', 'hello') + expect(shrimp.get('bar')).toEqual('hello') + }) + + test('set variables are accessible in code', async () => { + const shrimp = new Shrimp() + + shrimp.set('x', 10) + shrimp.set('y', 20) + + const result = await shrimp.run('x + y') + expect(result).toEqual(30) + }) + + test('allows setting functions', async () => { + const shrimp = new Shrimp() + + shrimp.set('double', (n: number) => n * 2) + + const result = await shrimp.run('double 21') + expect(result).toEqual(42) + }) + + test('overwrites existing variables', async () => { + const shrimp = new Shrimp() + + await shrimp.run('x = 100') + expect(shrimp.get('x')).toEqual(100) + + shrimp.set('x', 200) + expect(shrimp.get('x')).toEqual(200) + }) + }) + + describe('has()', () => { + test('returns true for existing variables', async () => { + const shrimp = new Shrimp() + + await shrimp.run('x = 5') + expect(shrimp.has('x')).toEqual(true) + }) + + test('returns false for non-existing variables', () => { + const shrimp = new Shrimp() + + expect(shrimp.has('nonexistent')).toEqual(false) + }) + + test('returns true for globals', () => { + const shrimp = new Shrimp({ myGlobal: 42 }) + + expect(shrimp.has('myGlobal')).toEqual(true) + }) + + test('returns true for prelude functions', () => { + const shrimp = new Shrimp() + + expect(shrimp.has('echo')).toEqual(true) + expect(shrimp.has('type')).toEqual(true) + }) + }) + + describe('call()', () => { + test('calls Shrimp functions with positional args', async () => { + const shrimp = new Shrimp() + + await shrimp.run(`add = do x y: + x + y + end`) + + const result = await shrimp.call('add', 5, 10) + expect(result).toEqual(15) + }) + + test('calls Shrimp functions with named args', async () => { + const shrimp = new Shrimp() + + await shrimp.run(`greet = do name: + concat 'Hello ' name + end`) + + const result = await shrimp.call('greet', { name: 'World' }) + expect(result).toEqual('Hello World') + }) + + test('calls native functions', async () => { + const shrimp = new Shrimp() + + shrimp.set('multiply', (a: number, b: number) => a * b) + + const result = await shrimp.call('multiply', 6, 7) + expect(result).toEqual(42) + }) + + test('calls prelude functions', async () => { + const shrimp = new Shrimp() + + const result = await shrimp.call('type', 42) + expect(result).toEqual('number') + }) + + test('calls async functions', async () => { + const shrimp = new Shrimp() + + shrimp.set('fetchData', async () => { + return await Promise.resolve('async data') + }) + + const result = await shrimp.call('fetchData') + expect(result).toEqual('async data') + }) + }) + + describe('compile()', () => { + test('compiles code to bytecode', () => { + const shrimp = new Shrimp() + + const bytecode = shrimp.compile('x = 5') + + expect(bytecode).toHaveProperty('instructions') + expect(bytecode).toHaveProperty('constants') + expect(bytecode).toHaveProperty('labels') + expect(bytecode.instructions.length).toBeGreaterThan(0) + }) + + test('respects globals when compiling', () => { + const shrimp = new Shrimp({ customGlobal: 42 }) + + const bytecode = shrimp.compile('x = customGlobal') + expect(bytecode.instructions.length).toBeGreaterThan(0) + }) + + test('compiled bytecode can be run', async () => { + const shrimp = new Shrimp() + + const bytecode = shrimp.compile('2 * 21') + const result = await shrimp.run(bytecode) + + expect(result).toEqual(42) + }) + }) + + describe('parse()', () => { + test('parses code to syntax tree', () => { + const shrimp = new Shrimp() + + const tree = shrimp.parse('x = 5') + + expect(tree).toHaveProperty('length') + expect(tree).toHaveProperty('cursor') + expect(tree.length).toBeGreaterThan(0) + }) + + test('respects globals when parsing', () => { + const shrimp = new Shrimp({ myVar: 42 }) + + const tree = shrimp.parse('x = myVar + 10') + + // Should parse without errors + expect(tree).toHaveProperty('length') + expect(tree.length).toBeGreaterThan(0) + }) + + test('parses function definitions', () => { + const shrimp = new Shrimp() + + const tree = shrimp.parse(`add = do x y: + x + y + end`) + + expect(tree.length).toBeGreaterThan(0) + }) + }) + + describe('get()', () => { + test('returns null for undefined variables', () => { + const shrimp = new Shrimp() + + expect(shrimp.get('undefined')).toEqual(null) + }) + + test('returns values from code execution', async () => { + const shrimp = new Shrimp() + + await shrimp.run('x = 42') + expect(shrimp.get('x')).toEqual(42) + }) + + test('returns arrays', async () => { + const shrimp = new Shrimp() + + await shrimp.run('arr = [1 2 3]') + expect(shrimp.get('arr')).toEqual([1, 2, 3]) + }) + + test('returns dicts', async () => { + const shrimp = new Shrimp() + + await shrimp.run('dict = [a=1 b=2]') + expect(shrimp.get('dict')).toEqual({ a: 1, b: 2 }) + }) + }) + + describe('running bytecode directly', () => { + test('can run pre-compiled bytecode', async () => { + const shrimp = new Shrimp() + + const bytecode = shrimp.compile('x = 100') + const result = await shrimp.run(bytecode) + + expect(result).toEqual(100) + expect(shrimp.get('x')).toEqual(100) + }) + + test('maintains state across bytecode runs', async () => { + const shrimp = new Shrimp() + + const bytecode1 = shrimp.compile('x = 10') + const bytecode2 = shrimp.compile('x + 5') + + await shrimp.run(bytecode1) + const result = await shrimp.run(bytecode2) + + expect(result).toEqual(15) + }) + }) +}) + +describe('Functional API', () => { + describe('runCode()', () => { + test('runs code and returns result', async () => { + const result = await runCode('1 + 1') + expect(result).toEqual(2) + }) + + test('works with globals', async () => { + const result = await runCode('greet', { greet: () => 'hello' }) + expect(result).toEqual('hello') + }) + + test('has access to prelude', async () => { + const result = await runCode('type 42') + expect(result).toEqual('number') + }) + + test('returns null for empty code', async () => { + const result = await runCode('') + expect(result).toEqual(null) + }) + }) + + describe('compileCode()', () => { + test('compiles code to bytecode', () => { + const bytecode = compileCode('x = 5') + + expect(bytecode).toHaveProperty('instructions') + expect(bytecode).toHaveProperty('constants') + expect(bytecode.instructions.length).toBeGreaterThan(0) + }) + + test('respects globals', () => { + const bytecode = compileCode('x = myGlobal', { myGlobal: 42 }) + + expect(bytecode.instructions.length).toBeGreaterThan(0) + }) + + test('compiled bytecode is usable', async () => { + const bytecode = compileCode('21 * 2') + const result = await runCode('21 * 2') + + expect(result).toEqual(42) + }) + }) + + describe('parseCode()', () => { + test('parses code to syntax tree', () => { + const tree = parseCode('x = 5') + + expect(tree).toHaveProperty('length') + expect(tree.length).toBeGreaterThan(0) + }) + + test('respects globals', () => { + const tree = parseCode('x = myGlobal', { myGlobal: 42 }) + + expect(tree.length).toBeGreaterThan(0) + }) + + test('handles complex expressions', () => { + const tree = parseCode(`add = do x y: + x + y + end + result = add 5 10`) + + expect(tree.length).toBeGreaterThan(0) + }) + }) + + describe('bytecodeToString()', () => { + test('converts bytecode to human-readable format', () => { + const bytecode = compileCode('x = 42') + const str = bytecodeToString(bytecode) + + expect(typeof str).toEqual('string') + expect(str.length).toBeGreaterThan(0) + }) + + test('shows instructions', () => { + const bytecode = compileCode('1 + 1') + const str = bytecodeToString(bytecode) + + // Should contain some opcodes + expect(str).toContain('PUSH') + }) + }) +}) + +describe('Integration tests', () => { + test('complex REPL-like workflow', async () => { + const shrimp = new Shrimp() + + // Define a function + await shrimp.run(`double = do x: + x * 2 + end`) + expect(shrimp.has('double')).toEqual(true) + + // Use the function + const result1 = await shrimp.run('double 21') + expect(result1).toEqual(42) + + // Call it from TypeScript + const result2 = await shrimp.call('double', 50) + expect(result2).toEqual(100) + + // Define another function using the first + await shrimp.run(`quadruple = do x: + double (double x) + end`) + + const result3 = await shrimp.run('quadruple 5') + expect(result3).toEqual(20) + }) + + test('mixing native and Shrimp functions', async () => { + const shrimp = new Shrimp({ + log: (msg: string) => `Logged: ${msg}`, + multiply: (a: number, b: number) => a * b, + }) + + await shrimp.run(`greet = do name: + log name + end`) + + const result1 = await shrimp.run('greet Alice') + expect(result1).toEqual('Logged: Alice') + + await shrimp.run(`calc = do x: + multiply x 3 + end`) + + const result2 = await shrimp.run('calc 7') + expect(result2).toEqual(21) + }) + + test('working with arrays and dicts', async () => { + const shrimp = new Shrimp() + + await shrimp.run('nums = [1 2 3 4 5]') + expect(shrimp.get('nums')).toEqual([1, 2, 3, 4, 5]) + + await shrimp.run("config = [host='localhost' port=3000]") + expect(shrimp.get('config')).toEqual({ host: 'localhost', port: 3000 }) + + const result = await shrimp.run('length nums') + expect(result).toEqual(5) + }) + + test('compile once, run multiple times', async () => { + const bytecode = compileCode('x * 2') + + const shrimp1 = new Shrimp() + shrimp1.set('x', 10) + const result1 = await shrimp1.run(bytecode) + expect(result1).toEqual(20) + + const shrimp2 = new Shrimp() + shrimp2.set('x', 100) + const result2 = await shrimp2.run(bytecode) + expect(result2).toEqual(200) + }) }) From 3aa40ae2c2413a60118e552b12069e694ec6a1be Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Fri, 7 Nov 2025 21:50:01 -0800 Subject: [PATCH 09/48] fix fromValue --- src/index.ts | 18 ++++++------------ src/tests/shrimp.test.ts | 2 +- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/index.ts b/src/index.ts index c1f4835..687e513 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ import { readFileSync } from 'fs' -import { VM, fromValue, toValue, type Bytecode } from 'reefvm' +import { VM, fromValue, toValue, isValue, type Bytecode } from 'reefvm' import { type Tree } from '@lezer/common' import { Compiler } from '#compiler/compiler' import { parser } from '#parser/shrimp' @@ -11,7 +11,7 @@ export { parser } from '#parser/shrimp' export { globals as prelude } from '#prelude' export type { Tree } from '@lezer/common' export { type Value, type Bytecode } from 'reefvm' -export { toValue, fromValue, Scope, VM, bytecodeToString } from 'reefvm' +export { toValue, fromValue, isValue, Scope, VM, bytecodeToString } from 'reefvm' export class Shrimp { vm: VM @@ -25,7 +25,7 @@ export class Shrimp { get(name: string): any { const value = this.vm.scope.get(name) - return value ? fromValue(value) : null + return value ? fromValue(value, this.vm) : null } set(name: string, value: any) { @@ -38,12 +38,7 @@ export class Shrimp { async call(name: string, ...args: any[]): Promise { const result = await this.vm.call(name, ...args) - // vm.call() returns Value for native functions, JS values for Reef functions - // Check if it's a Value object and unwrap if needed - if (result && typeof result === 'object' && 'type' in result) { - return fromValue(result) - } - return result + return isValue(result) ? fromValue(result, this.vm) : result } parse(code: string): Tree { @@ -54,7 +49,6 @@ export class Shrimp { return compileCode(code, this.globals) } - async run(code: string | Bytecode, locals?: Record): Promise { let bytecode @@ -70,7 +64,7 @@ export class Shrimp { await this.vm.continue() if (locals) this.vm.popScope() - return this.vm.stack.length ? fromValue(this.vm.stack.at(-1)!) : null + return this.vm.stack.length ? fromValue(this.vm.stack.at(-1)!, this.vm) : null } } @@ -87,7 +81,7 @@ export async function runCode(code: string, globals?: Record): Prom export async function runBytecode(bytecode: Bytecode, globals?: Record): Promise { const vm = new VM(bytecode, Object.assign({}, shrimpGlobals, globals)) await vm.run() - return vm.stack.length ? fromValue(vm.stack[vm.stack.length - 1]!) : null + return vm.stack.length ? fromValue(vm.stack[vm.stack.length - 1]!, vm) : null } export function compileFile(path: string, globals?: Record): Bytecode { diff --git a/src/tests/shrimp.test.ts b/src/tests/shrimp.test.ts index e905901..b838cf5 100644 --- a/src/tests/shrimp.test.ts +++ b/src/tests/shrimp.test.ts @@ -136,7 +136,7 @@ describe('Shrimp', () => { const shrimp = new Shrimp() await shrimp.run(`greet = do name: - concat 'Hello ' name + str.join [ 'Hello ' name ] '' end`) const result = await shrimp.call('greet', { name: 'World' }) From 4590d661059c1803cac3fcfee0e38cbe1568f797 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sat, 8 Nov 2025 08:09:01 -0800 Subject: [PATCH 10/48] '#comments are comments too, but not hash#in#words' --- src/parser/shrimp.grammar | 2 +- src/parser/shrimp.ts | 2 +- src/parser/tests/basics.test.ts | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/parser/shrimp.grammar b/src/parser/shrimp.grammar index 97908d9..a4f26e2 100644 --- a/src/parser/shrimp.grammar +++ b/src/parser/shrimp.grammar @@ -18,7 +18,7 @@ newlineOrSemicolon { "\n" | ";" } eof { @eof } space { " " | "\t" } - Comment { "#" " " ![\n]* } + Comment { "#" ![\n]* } leftParen { "(" } rightParen { ")" } colon[closedBy="end", @name="colon"] { ":" } diff --git a/src/parser/shrimp.ts b/src/parser/shrimp.ts index fa41719..64892e8 100644 --- a/src/parser/shrimp.ts +++ b/src/parser/shrimp.ts @@ -19,7 +19,7 @@ export const parser = LRParser.deserialize({ propSources: [highlighting], skippedNodes: [0,25], repeatNodeCount: 11, - tokenData: "C|~R|OX#{XY$jYZ%TZp#{pq$jqs#{st%ntu'tuw#{wx'yxy(Oyz(iz{#{{|)S|}#{}!O+v!O!P#{!P!Q.]!Q![)q![!]6x!]!^%T!^!}#{!}#O7c#O#P9X#P#Q9^#Q#R#{#R#S9w#S#T#{#T#Y,w#Y#Z:b#Z#b,w#b#c?`#c#f,w#f#g@]#g#h,w#h#iAY#i#o,w#o#p#{#p#qC^#q;'S#{;'S;=`$d<%l~#{~O#{~~CwS$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#{^%sW|SOp#{pq&]qt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{^&dZiY|SOY&]YZ#{Zt&]tu'Vuw&]wx'Vx#O&]#O#P'V#P;'S&];'S;=`'n<%lO&]Y'[SiYOY'VZ;'S'V;'S;=`'h<%lO'VY'kP;=`<%l'V^'qP;=`<%l&]~'yO#T~~(OO#R~U(VU|S!}QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(pU|S#]QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U)XW|SOt#{uw#{x!Q#{!Q![)q![#O#{#P;'S#{;'S;=`$d<%lO#{U)xY|SuQOt#{uw#{x!O#{!O!P*h!P!Q#{!Q![)q![#O#{#P;'S#{;'S;=`$d<%lO#{U*mW|SOt#{uw#{x!Q#{!Q![+V![#O#{#P;'S#{;'S;=`$d<%lO#{U+^W|SuQOt#{uw#{x!Q#{!Q![+V![#O#{#P;'S#{;'S;=`$d<%lO#{U+{^|SOt#{uw#{x}#{}!O,w!O!Q#{!Q![)q![!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{U,|[|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{U-yUzQ|SOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U.bW|SOt#{uw#{x!P#{!P!Q.z!Q#O#{#P;'S#{;'S;=`$d<%lO#{U/P^|SOY/{YZ#{Zt/{tu1Ouw/{wx1Ox!P/{!P!Q#{!Q!}/{!}#O5q#O#P3^#P;'S/{;'S;=`6r<%lO/{U0S^|S!aQOY/{YZ#{Zt/{tu1Ouw/{wx1Ox!P/{!P!Q3s!Q!}/{!}#O5q#O#P3^#P;'S/{;'S;=`6r<%lO/{Q1TX!aQOY1OZ!P1O!P!Q1p!Q!}1O!}#O2_#O#P3^#P;'S1O;'S;=`3m<%lO1OQ1sP!P!Q1vQ1{U!aQ#Z#[1v#]#^1v#a#b1v#g#h1v#i#j1v#m#n1vQ2bVOY2_Z#O2_#O#P2w#P#Q1O#Q;'S2_;'S;=`3W<%lO2_Q2zSOY2_Z;'S2_;'S;=`3W<%lO2_Q3ZP;=`<%l2_Q3aSOY1OZ;'S1O;'S;=`3m<%lO1OQ3pP;=`<%l1OU3xW|SOt#{uw#{x!P#{!P!Q4b!Q#O#{#P;'S#{;'S;=`$d<%lO#{U4ib|S!aQOt#{uw#{x#O#{#P#Z#{#Z#[4b#[#]#{#]#^4b#^#a#{#a#b4b#b#g#{#g#h4b#h#i#{#i#j4b#j#m#{#m#n4b#n;'S#{;'S;=`$d<%lO#{U5v[|SOY5qYZ#{Zt5qtu2_uw5qwx2_x#O5q#O#P2w#P#Q/{#Q;'S5q;'S;=`6l<%lO5qU6oP;=`<%l5qU6uP;=`<%l/{U7PU|S!RQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U7jW#_Q|SOt#{uw#{x!_#{!_!`8S!`#O#{#P;'S#{;'S;=`$d<%lO#{U8XV|SOt#{uw#{x#O#{#P#Q8n#Q;'S#{;'S;=`$d<%lO#{U8uU#^Q|SOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~9^O#U~U9eU#`Q|SOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U:OU|S!YQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U:g]|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#U;`#U#o,w#o;'S#{;'S;=`$d<%lO#{U;e^|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#`,w#`#ac#Y#o,w#o;'S#{;'S;=`$d<%lO#{U>j[!PQ|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{^?g[#VW|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{^@d[#XW|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{^Aa^#WW|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#f,w#f#gB]#g#o,w#o;'S#{;'S;=`$d<%lO#{UBb^|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#i,w#i#j=b#j#o,w#o;'S#{;'S;=`$d<%lO#{UCeUlQ|SOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~C|O#a~", + 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#j (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 20, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], diff --git a/src/parser/tests/basics.test.ts b/src/parser/tests/basics.test.ts index dea9d7b..3d0bb4d 100644 --- a/src/parser/tests/basics.test.ts +++ b/src/parser/tests/basics.test.ts @@ -639,7 +639,7 @@ describe('Comments', () => { test('are greedy', () => { expect(` x = 5 # one banana -y = 2 # two bananas`).toMatchTree(` +y = 2 #two bananas`).toMatchTree(` Assign AssignableIdentifier x Eq = @@ -649,7 +649,7 @@ y = 2 # two bananas`).toMatchTree(` AssignableIdentifier y Eq = Number 2 - Comment # two bananas`) + Comment #two bananas`) expect(` # some comment @@ -670,11 +670,11 @@ basename = 5 # very astute }) test('words with # are not considered comments', () => { - expect('find #hashtag-file.txt').toMatchTree(` + expect('find my#hashtag-file.txt').toMatchTree(` FunctionCall Identifier find PositionalArg - Word #hashtag-file.txt`) + Word my#hashtag-file.txt`) }) test('hastags in strings are not comments', () => { From f57452ece2db1521bc473cc212fce0661c428b7d Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sat, 8 Nov 2025 08:14:46 -0800 Subject: [PATCH 11/48] list.reject --- src/prelude/list.ts | 7 +++++++ src/prelude/tests/prelude.test.ts | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/src/prelude/list.ts b/src/prelude/list.ts index 1f0ec76..8e861b9 100644 --- a/src/prelude/list.ts +++ b/src/prelude/list.ts @@ -14,6 +14,13 @@ export const list = { } return acc }, + reject: async (list: any[], cb: Function) => { + let acc: any[] = [] + for (const value of list) { + if (!(await cb(value))) acc.push(value) + } + return acc + }, reduce: async (list: any[], cb: Function, initial: any) => { let acc = initial for (const value of list) acc = await cb(acc, value) diff --git a/src/prelude/tests/prelude.test.ts b/src/prelude/tests/prelude.test.ts index 04ceb8b..069b174 100644 --- a/src/prelude/tests/prelude.test.ts +++ b/src/prelude/tests/prelude.test.ts @@ -193,6 +193,15 @@ describe('collections', () => { `).toEvaluateTo([3, 4, 5]) }) + test('list.reject doesnt keep matching elements', async () => { + await expect(` + is-even = do x: + (x % 2) == 0 + end + list.reject [1 2 3 4 5] is-even + `).toEvaluateTo([1, 3, 5]) + }) + test('list.reduce accumulates values', async () => { await expect(` add = do acc x: From 19c4fb5033b09697a3150b18955253b0ed1cb6b1 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sat, 8 Nov 2025 08:22:41 -0800 Subject: [PATCH 12/48] args and exit --- src/prelude/index.ts | 4 ++++ src/prelude/tests/info.test.ts | 14 ++++++++++++++ src/prelude/tests/prelude.test.ts | 1 + 3 files changed, 19 insertions(+) diff --git a/src/prelude/index.ts b/src/prelude/index.ts index 488414b..6ec11d9 100644 --- a/src/prelude/index.ts +++ b/src/prelude/index.ts @@ -41,6 +41,10 @@ export const globals = { return typeof v !== 'string' || this.scope.has(v) }, + // env + args: Bun.argv.slice(1), + exit: (num: number) => process.exit(num ?? 0), + // type predicates 'string?': (v: any) => toValue(v).type === 'string', 'number?': (v: any) => toValue(v).type === 'number', diff --git a/src/prelude/tests/info.test.ts b/src/prelude/tests/info.test.ts index 9c24a8a..1fa5e3b 100644 --- a/src/prelude/tests/info.test.ts +++ b/src/prelude/tests/info.test.ts @@ -77,3 +77,17 @@ describe('introspection', () => { await expect(`describe 'hello'`).toEvaluateTo("#", globals) }) }) + +describe('environment', () => { + test('args is an array', async () => { + await expect(`array? args`).toEvaluateTo(true, globals) + }) + + test('args can be accessed', async () => { + await expect(`type args`).toEvaluateTo('array', globals) + }) + + test('', async () => { + await expect(`list.first args | str.ends-with? 'shrimp.test.ts'`).toEvaluateTo(true) + }) +}) diff --git a/src/prelude/tests/prelude.test.ts b/src/prelude/tests/prelude.test.ts index 069b174..86341d3 100644 --- a/src/prelude/tests/prelude.test.ts +++ b/src/prelude/tests/prelude.test.ts @@ -66,6 +66,7 @@ describe('string operations', () => { test('slice extracts substring', async () => { await expect(`str.slice 'hello' 1 3`).toEvaluateTo('el') await expect(`str.slice 'hello' 2 null`).toEvaluateTo('llo') + await expect(`str.slice 'hello' 2`).toEvaluateTo('llo') }) test('repeat repeats string', async () => { From 740379d7b223863af7752ec5a28f07fd4469eaeb Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sat, 8 Nov 2025 08:42:43 -0800 Subject: [PATCH 13/48] fix dotgets in repl --- bin/repl | 4 ++-- bun.lock | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bin/repl b/bin/repl index c6ff98c..4cc5fd6 100755 --- a/bin/repl +++ b/bin/repl @@ -145,7 +145,7 @@ async function repl() { } try { - const compiler = new Compiler(trimmed, Object.keys(globals)) + const compiler = new Compiler(trimmed, [...Object.keys(globals), ...vm.vars()]) // Save VM state before appending bytecode, in case execution fails const savedInstructions = [...vm.instructions] @@ -235,7 +235,7 @@ async function loadFile(filePath: string): Promise<{ vm: VM; codeHistory: string if (!trimmed) continue try { - const compiler = new Compiler(trimmed) + const compiler = new Compiler(trimmed, [...Object.keys(globals), ...vm.vars()]) vm.appendBytecode(compiler.bytecode) await vm.continue() codeHistory.push(trimmed) diff --git a/bun.lock b/bun.lock index a89800c..9d07e0c 100644 --- a/bun.lock +++ b/bun.lock @@ -44,7 +44,7 @@ "@marijn/find-cluster-break": ["@marijn/find-cluster-break@1.0.2", "", {}, "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g=="], - "@types/bun": ["@types/bun@1.3.1", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="], + "@types/bun": ["@types/bun@1.3.2", "", { "dependencies": { "bun-types": "1.3.2" } }, "sha512-t15P7k5UIgHKkxwnMNkJbWlh/617rkDGEdSsDbu+qNHTaz9SKf7aC8fiIlUdD5RPpH6GEkP0cK7WlvmrEBRtWg=="], "@types/node": ["@types/node@24.10.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A=="], @@ -52,7 +52,7 @@ "bun-plugin-tailwind": ["bun-plugin-tailwind@0.0.15", "", { "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-qtAXMNGG4R0UGGI8zWrqm2B7BdXqx48vunJXBPzfDOHPA5WkRUZdTSbE7TFwO4jLhYqSE23YMWsM9NhE6ovobw=="], - "bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="], + "bun-types": ["bun-types@1.3.2", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-i/Gln4tbzKNuxP70OWhJRZz1MRfvqExowP7U6JKoI8cntFrtxg7RJK3jvz7wQW54UuvNC8tbKHHri5fy74FVqg=="], "codemirror": ["codemirror@6.0.2", "", { "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/commands": "^6.0.0", "@codemirror/language": "^6.0.0", "@codemirror/lint": "^6.0.0", "@codemirror/search": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0" } }, "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw=="], @@ -62,11 +62,11 @@ "hono": ["hono@4.10.4", "", {}, "sha512-YG/fo7zlU3KwrBL5vDpWKisLYiM+nVstBQqfr7gCPbSYURnNEP9BDxEMz8KfsDR9JX0lJWDRNc6nXX31v7ZEyg=="], - "reefvm": ["reefvm@git+https://git.nose.space/defunkt/reefvm#bffb83a5280a4d74e424c4e0f4fbd46f790227a3", { "peerDependencies": { "typescript": "^5" } }, "bffb83a5280a4d74e424c4e0f4fbd46f790227a3"], + "reefvm": ["reefvm@git+https://git.nose.space/defunkt/reefvm#d7a971db24aea5ddcaae2c18ce9f10dab793db19", { "peerDependencies": { "typescript": "^5" } }, "d7a971db24aea5ddcaae2c18ce9f10dab793db19"], "style-mod": ["style-mod@4.1.3", "", {}, "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ=="], - "tailwindcss": ["tailwindcss@4.1.16", "", {}, "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA=="], + "tailwindcss": ["tailwindcss@4.1.17", "", {}, "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q=="], "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], From 51f67ac9081907ae22672a5ce04cdc0dd4adb466 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sat, 8 Nov 2025 10:47:51 -0800 Subject: [PATCH 14/48] Passing null to a function triggers default value --- bun.lock | 2 +- package.json | 2 +- src/compiler/tests/compiler.test.ts | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/bun.lock b/bun.lock index 9d07e0c..1767760 100644 --- a/bun.lock +++ b/bun.lock @@ -62,7 +62,7 @@ "hono": ["hono@4.10.4", "", {}, "sha512-YG/fo7zlU3KwrBL5vDpWKisLYiM+nVstBQqfr7gCPbSYURnNEP9BDxEMz8KfsDR9JX0lJWDRNc6nXX31v7ZEyg=="], - "reefvm": ["reefvm@git+https://git.nose.space/defunkt/reefvm#d7a971db24aea5ddcaae2c18ce9f10dab793db19", { "peerDependencies": { "typescript": "^5" } }, "d7a971db24aea5ddcaae2c18ce9f10dab793db19"], + "reefvm": ["reefvm@git+https://git.nose.space/defunkt/reefvm#3e2e68b31f504347225a4d705c7568a0957d629e", { "peerDependencies": { "typescript": "^5" } }, "3e2e68b31f504347225a4d705c7568a0957d629e"], "style-mod": ["style-mod@4.1.3", "", {}, "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ=="], diff --git a/package.json b/package.json index 472b4af..2000575 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "repl": "bun generate-parser && bun bin/repl", "update-reef": "rm -rf ~/.bun/install/cache/ && rm bun.lock && bun update reefvm", "cli:install": "ln -s \"$(pwd)/bin/shrimp\" ~/.bun/bin/shrimp", - "cli:remove": "rm ~/.bun/bin/shrimp", + "cli:remove": "rm ~/.bun/bin/shrimp" }, "dependencies": { "@codemirror/view": "^6.38.3", diff --git a/src/compiler/tests/compiler.test.ts b/src/compiler/tests/compiler.test.ts index ad62392..2931675 100644 --- a/src/compiler/tests/compiler.test.ts +++ b/src/compiler/tests/compiler.test.ts @@ -298,6 +298,23 @@ describe('default params', () => { expect('multiply = do x y=5: x * y end; multiply 5 2').toEvaluateTo(10) }) + test('null triggers default value', () => { + expect('test = do n=true: n end; test').toEvaluateTo(true) + expect('test = do n=true: n end; test false').toEvaluateTo(false) + expect('test = do n=true: n end; test null').toEvaluateTo(true) + }) + + test('null triggers default for named parameters', () => { + expect("greet = do name='World': name end; greet name=null").toEvaluateTo('World') + expect("greet = do name='World': name end; greet name='Bob'").toEvaluateTo('Bob') + }) + + test('null triggers default with multiple parameters', () => { + expect('calc = do x=10 y=20: x + y end; calc null 5').toEvaluateTo(15) + expect('calc = do x=10 y=20: x + y end; calc 3 null').toEvaluateTo(23) + expect('calc = do x=10 y=20: x + y end; calc null null').toEvaluateTo(30) + }) + test.skip('array default', () => { expect('abc = do alpha=[a b c]: alpha end; abc').toEvaluateTo(['a', 'b', 'c']) expect('abc = do alpha=[a b c]: alpha end; abc [x y z]').toEvaluateTo(['x', 'y', 'z']) From 49a6320fef5c4a8695cf6e70725c7f4c8139bacf Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sat, 8 Nov 2025 11:17:45 -0800 Subject: [PATCH 15/48] add list.insert --- src/prelude/list.ts | 10 +++++++++- src/prelude/tests/prelude.test.ts | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/prelude/list.ts b/src/prelude/list.ts index 8e861b9..0c7d7be 100644 --- a/src/prelude/list.ts +++ b/src/prelude/list.ts @@ -73,6 +73,13 @@ export const list = { const realItems = items.map(item => item.value) return toValue(realList.splice(realStart, realDeleteCount, ...realItems)) }, + insert: (list: Value, index: Value, item: Value) => { + if (list.type !== 'array') return toNull() + const realList = list.value as any[] + const realIndex = index.value as number + realList.splice(realIndex, 0, item) + return toValue(realList.length) + }, // sequence operations reverse: (list: any[]) => list.slice().reverse(), @@ -142,4 +149,5 @@ export const list = { ; (list.push as any).raw = true ; (list.pop as any).raw = true ; (list.shift as any).raw = true - ; (list.unshift as any).raw = true \ No newline at end of file + ; (list.unshift as any).raw = true + ; (list.insert as any).raw = true \ No newline at end of file diff --git a/src/prelude/tests/prelude.test.ts b/src/prelude/tests/prelude.test.ts index 86341d3..27bb919 100644 --- a/src/prelude/tests/prelude.test.ts +++ b/src/prelude/tests/prelude.test.ts @@ -349,6 +349,22 @@ describe('collections', () => { await expect(`arr = [1 2 3 4 5]; list.splice arr 3 2; arr`).toEvaluateTo([1, 2, 3]) }) + test('list.insert adds element at index and mutates array', async () => { + await expect(`arr = [1 2 4 5]; list.insert arr 2 3; arr`).toEvaluateTo([1, 2, 3, 4, 5]) + }) + + test('list.insert returns array length', async () => { + await expect(`list.insert [1 2 4] 2 3`).toEvaluateTo(4) + }) + + test('list.insert at start', async () => { + await expect(`arr = [2 3]; list.insert arr 0 1; arr`).toEvaluateTo([1, 2, 3]) + }) + + test('list.insert at end', async () => { + await expect(`arr = [1 2]; list.insert arr 2 99; arr`).toEvaluateTo([1, 2, 99]) + }) + test('list.sort with no callback sorts ascending', async () => { await expect(`list.sort [3 1 4 1 5] null`).toEvaluateTo([1, 1, 3, 4, 5]) }) From 45f31d0678008ea7cb35dcd6becf4f945fad8d52 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sat, 8 Nov 2025 11:26:37 -0800 Subject: [PATCH 16/48] 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', () => { From 7e69356f79db95ab1e443a634c55a39d75c79e27 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Fri, 7 Nov 2025 19:48:33 -0800 Subject: [PATCH 17/48] allow _ in numbers (10_000_000) --- src/parser/shrimp.grammar | 2 +- src/parser/shrimp.ts | 2 +- src/parser/tests/basics.test.ts | 40 +++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/parser/shrimp.grammar b/src/parser/shrimp.grammar index 1fe1c9b..cbc14b5 100644 --- a/src/parser/shrimp.grammar +++ b/src/parser/shrimp.grammar @@ -13,7 +13,7 @@ StringFragment { !['\\$]+ } NamedArgPrefix { $[a-z] $[a-z0-9-]* "=" } - Number { ("-" | "+")? $[0-9]+ ('.' $[0-9]+)? } + Number { ("-" | "+")? $[0-9]+ ("_"? $[0-9]+)* ('.' $[0-9]+ ("_"? $[0-9]+)*)? } Boolean { "true" | "false" } newlineOrSemicolon { "\n" | ";" } eof { @eof } diff --git a/src/parser/shrimp.ts b/src/parser/shrimp.ts index 2d53813..9920125 100644 --- a/src/parser/shrimp.ts +++ b/src/parser/shrimp.ts @@ -19,7 +19,7 @@ export const parser = LRParser.deserialize({ propSources: [highlighting], skippedNodes: [0,25], repeatNodeCount: 12, - tokenData: "Cl~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#P6T#P#Q6Y#Q#R#{#R#S6s#S#T#{#T#Y7^#Y#Z8x#Z#b7^#b#c>f#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~", + tokenData: "Cx~R|OX#{XY$jYZ%TZp#{pq$jqs#{st%ntu'Vuw#{wx'[xy'ayz'zz{#{{|(e|}#{}!O(e!O!P#{!P!Q+e!Q![)S![!]4Q!]!^%T!^!}#{!}#O4k#O#P6a#P#Q6f#Q#R#{#R#S7P#S#T#{#T#Y7j#Y#Z9U#Z#b7j#b#c>r#c#f7j#f#g?u#g#h7j#h#i@x#i#o7j#o#p#{#p#qCY#q;'S#{;'S;=`$d<%l~#{~O#{~~CsS$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)Z[zSsQOt#{uw#{x!O#{!O!P*P!P!Q#{!Q![)S![#O#{#P#R#{#R#S(e#S;'S#{;'S;=`$d<%lO#{U*UWzSOt#{uw#{x!Q#{!Q![*n![#O#{#P;'S#{;'S;=`$d<%lO#{U*uYzSsQOt#{uw#{x!Q#{!Q![*n![#O#{#P#R#{#R#S*P#S;'S#{;'S;=`$d<%lO#{U+jWzSOt#{uw#{x!P#{!P!Q,S!Q#O#{#P;'S#{;'S;=`$d<%lO#{U,X^zSOY-TYZ#{Zt-Ttu.Wuw-Twx.Wx!P-T!P!Q#{!Q!}-T!}#O2y#O#P0f#P;'S-T;'S;=`3z<%lO-TU-[^zS!bQOY-TYZ#{Zt-Ttu.Wuw-Twx.Wx!P-T!P!Q0{!Q!}-T!}#O2y#O#P0f#P;'S-T;'S;=`3z<%lO-TQ.]X!bQOY.WZ!P.W!P!Q.x!Q!}.W!}#O/g#O#P0f#P;'S.W;'S;=`0u<%lO.WQ.{P!P!Q/OQ/TU!bQ#Z#[/O#]#^/O#a#b/O#g#h/O#i#j/O#m#n/OQ/jVOY/gZ#O/g#O#P0P#P#Q.W#Q;'S/g;'S;=`0`<%lO/gQ0SSOY/gZ;'S/g;'S;=`0`<%lO/gQ0cP;=`<%l/gQ0iSOY.WZ;'S.W;'S;=`0u<%lO.WQ0xP;=`<%l.WU1QWzSOt#{uw#{x!P#{!P!Q1j!Q#O#{#P;'S#{;'S;=`$d<%lO#{U1qbzS!bQOt#{uw#{x#O#{#P#Z#{#Z#[1j#[#]#{#]#^1j#^#a#{#a#b1j#b#g#{#g#h1j#h#i#{#i#j1j#j#m#{#m#n1j#n;'S#{;'S;=`$d<%lO#{U3O[zSOY2yYZ#{Zt2ytu/guw2ywx/gx#O2y#O#P0P#P#Q-T#Q;'S2y;'S;=`3t<%lO2yU3wP;=`<%l2yU3}P;=`<%l-TU4XUzS!PQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U4rW#aQzSOt#{uw#{x!_#{!_!`5[!`#O#{#P;'S#{;'S;=`$d<%lO#{U5aVzSOt#{uw#{x#O#{#P#Q5v#Q;'S#{;'S;=`$d<%lO#{U5}U#`QzSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~6fO#W~U6mU#bQzSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U7WUzS!WQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U7o^zSOt#{uw#{x}#{}!O7j!O!Q#{!Q![7j![!_#{!_!`8k!`#O#{#P#T#{#T#o7j#o;'S#{;'S;=`$d<%lO#{U8rUxQzSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U9Z_zSOt#{uw#{x}#{}!O7j!O!Q#{!Q![7j![!_#{!_!`8k!`#O#{#P#T#{#T#U:Y#U#o7j#o;'S#{;'S;=`$d<%lO#{U:_`zSOt#{uw#{x}#{}!O7j!O!Q#{!Q![7j![!_#{!_!`8k!`#O#{#P#T#{#T#`7j#`#a;a#a#o7j#o;'S#{;'S;=`$d<%lO#{U;f`zSOt#{uw#{x}#{}!O7j!O!Q#{!Q![7j![!_#{!_!`8k!`#O#{#P#T#{#T#g7j#g#hy^#XWzSOt#{uw#{x}#{}!O7j!O!Q#{!Q![7j![!_#{!_!`8k!`#O#{#P#T#{#T#o7j#o;'S#{;'S;=`$d<%lO#{^?|^#ZWzSOt#{uw#{x}#{}!O7j!O!Q#{!Q![7j![!_#{!_!`8k!`#O#{#P#T#{#T#o7j#o;'S#{;'S;=`$d<%lO#{^AP`#YWzSOt#{uw#{x}#{}!O7j!O!Q#{!Q![7j![!_#{!_!`8k!`#O#{#P#T#{#T#f7j#f#gBR#g#o7j#o;'S#{;'S;=`$d<%lO#{UBW`zSOt#{uw#{x}#{}!O7j!O!Q#{!Q![7j![!_#{!_!`8k!`#O#{#P#T#{#T#i7j#i#j (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 20, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], diff --git a/src/parser/tests/basics.test.ts b/src/parser/tests/basics.test.ts index c2a4f3d..c941140 100644 --- a/src/parser/tests/basics.test.ts +++ b/src/parser/tests/basics.test.ts @@ -442,6 +442,46 @@ describe('Parentheses', () => { }) }) +describe('Number literals', () => { + test('allows underscores in integer literals', () => { + expect('10_000').toMatchTree(`Number 10_000`) + expect('1_000_000').toMatchTree(`Number 1_000_000`) + expect('100_000').toMatchTree(`Number 100_000`) + }) + + test('allows underscores in decimal literals', () => { + expect('3.14_159').toMatchTree(`Number 3.14_159`) + expect('1_000.50').toMatchTree(`Number 1_000.50`) + expect('0.000_001').toMatchTree(`Number 0.000_001`) + }) + + test('allows underscores in negative numbers', () => { + expect('-10_000').toMatchTree(`Number -10_000`) + expect('-3.14_159').toMatchTree(`Number -3.14_159`) + }) + + test('allows underscores in positive numbers with explicit sign', () => { + expect('+10_000').toMatchTree(`Number +10_000`) + expect('+3.14_159').toMatchTree(`Number +3.14_159`) + }) + + test('works in expressions', () => { + expect('1_000 + 2_000').toMatchTree(` + BinOp + Number 1_000 + Plus + + Number 2_000`) + }) + + test('works in function calls', () => { + expect('echo 10_000').toMatchTree(` + FunctionCall + Identifier echo + PositionalArg + Number 10_000`) + }) +}) + describe('BinOp', () => { test('addition tests', () => { expect('2 + 3').toMatchTree(` From 0d1dce486858e7c2602ed09acf046c4cdfb092c9 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sat, 8 Nov 2025 00:17:08 -0800 Subject: [PATCH 18/48] fix calling no-arg dotget functions --- bun.lock | 2 +- src/compiler/compiler.ts | 25 ++++++++++++++++++++++++- src/compiler/tests/compiler.test.ts | 5 ++++- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/bun.lock b/bun.lock index 9d07e0c..1767760 100644 --- a/bun.lock +++ b/bun.lock @@ -62,7 +62,7 @@ "hono": ["hono@4.10.4", "", {}, "sha512-YG/fo7zlU3KwrBL5vDpWKisLYiM+nVstBQqfr7gCPbSYURnNEP9BDxEMz8KfsDR9JX0lJWDRNc6nXX31v7ZEyg=="], - "reefvm": ["reefvm@git+https://git.nose.space/defunkt/reefvm#d7a971db24aea5ddcaae2c18ce9f10dab793db19", { "peerDependencies": { "typescript": "^5" } }, "d7a971db24aea5ddcaae2c18ce9f10dab793db19"], + "reefvm": ["reefvm@git+https://git.nose.space/defunkt/reefvm#3e2e68b31f504347225a4d705c7568a0957d629e", { "peerDependencies": { "typescript": "^5" } }, "3e2e68b31f504347225a4d705c7568a0957d629e"], "style-mod": ["style-mod@4.1.3", "", {}, "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ=="], diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 9ce7bce..408bdc2 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -51,6 +51,7 @@ function processEscapeSeq(escapeSeq: string): string { export class Compiler { instructions: ProgramItem[] = [] + labelCount = 0 fnLabelCount = 0 ifLabelCount = 0 tryLabelCount = 0 @@ -367,7 +368,29 @@ export class Compiler { case terms.FunctionCallOrIdentifier: { if (node.firstChild?.type.id === terms.DotGet) { - return this.#compileNode(node.firstChild, input) + const instructions: ProgramItem[] = [] + const callLabel = `.call_dotget_${++this.labelCount}` + const afterLabel = `.after_dotget_${++this.labelCount}` + + instructions.push(...this.#compileNode(node.firstChild, input)) + instructions.push(['DUP']) + instructions.push(['TYPE']) + instructions.push(['PUSH', 'function']) + instructions.push(['EQ']) + instructions.push(['JUMP_IF_TRUE', callLabel]) + instructions.push(['DUP']) + instructions.push(['TYPE']) + instructions.push(['PUSH', 'native']) + instructions.push(['EQ']) + instructions.push(['JUMP_IF_TRUE', callLabel]) + instructions.push(['JUMP', afterLabel]) + instructions.push([`${callLabel}:`]) + instructions.push(['PUSH', 0]) + instructions.push(['PUSH', 0]) + instructions.push(['CALL']) + instructions.push([`${afterLabel}:`]) + + return instructions } return [['TRY_CALL', value]] diff --git a/src/compiler/tests/compiler.test.ts b/src/compiler/tests/compiler.test.ts index ad62392..263eeb7 100644 --- a/src/compiler/tests/compiler.test.ts +++ b/src/compiler/tests/compiler.test.ts @@ -110,7 +110,10 @@ describe('compiler', () => { }) test('function call with no args', () => { - expect(`bloop = do: 'bloop' end; bloop`).toEvaluateTo('bloop') + expect(`bloop = do: 'bleep' end; bloop`).toEvaluateTo('bleep') + expect(`bloop = [ go=do: 'bleep' end ]; bloop.go`).toEvaluateTo('bleep') + expect(`bloop = [ go=do: 'bleep' end ]; abc = do x: x end; abc (bloop.go)`).toEvaluateTo('bleep') + expect(`num = ((math.random) * 10 + 1) | math.floor; num >= 1 and num <= 10 `).toEvaluateTo(true) }) test('function call with if statement and multiple expressions', () => { From 4c3f7a8bfc816849254f7c37958d4123b293bf54 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sat, 8 Nov 2025 00:58:17 -0800 Subject: [PATCH 19/48] add `ref` for grabbing a function --- src/prelude/index.ts | 1 + src/prelude/tests/info.test.ts | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/prelude/index.ts b/src/prelude/index.ts index c67de8a..c2c77ec 100644 --- a/src/prelude/index.ts +++ b/src/prelude/index.ts @@ -42,6 +42,7 @@ export const globals = { 'var?': function (this: VM, v: string) { return typeof v !== 'string' || this.scope.has(v) }, + ref: (fn: Function) => fn, // env args: Bun.argv.slice(1), diff --git a/src/prelude/tests/info.test.ts b/src/prelude/tests/info.test.ts index 1fa5e3b..39e2545 100644 --- a/src/prelude/tests/info.test.ts +++ b/src/prelude/tests/info.test.ts @@ -91,3 +91,15 @@ describe('environment', () => { await expect(`list.first args | str.ends-with? 'shrimp.test.ts'`).toEvaluateTo(true) }) }) + +describe('ref', () => { + expect(`rnd = do x: true end; rnd | type`).toEvaluateTo('boolean') + expect(`rnd = do x: true end; ref rnd | type`).toEvaluateTo('function') + + expect(`math.random | type`).toEvaluateTo('number') + expect(`ref math.random | type`).toEvaluateTo('native') + + expect(`rnd = math.random; rnd | type`).toEvaluateTo('number') + expect(`rnd = ref math.random; rnd | type`).toEvaluateTo('number') + expect(`rnd = ref math.random; ref rnd | type`).toEvaluateTo('native') +}) From c741cfee5122f00c0787553ebb5ffb353f6e7316 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sat, 8 Nov 2025 10:25:29 -0800 Subject: [PATCH 20/48] hex and binary numbers --- src/compiler/compiler.ts | 16 ++++++-- src/compiler/tests/literals.test.ts | 38 +++++++++++++++++++ src/parser/shrimp.grammar | 6 ++- src/parser/shrimp.ts | 2 +- src/parser/tests/literals.test.ts | 59 +++++++++++++++++++++++++++++ 5 files changed, 116 insertions(+), 5 deletions(-) diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 408bdc2..a5327ab 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -106,11 +106,21 @@ export class Compiler { switch (node.type.id) { case terms.Number: - const number = Number(value) - if (Number.isNaN(number)) + // Handle sign prefix for hex and binary literals + // Number() doesn't parse '-0xFF' or '+0xFF' correctly + let numberValue: number + if (value.startsWith('-') && (value.includes('0x') || value.includes('0b'))) { + numberValue = -Number(value.slice(1)) + } else if (value.startsWith('+') && (value.includes('0x') || value.includes('0b'))) { + numberValue = Number(value.slice(1)) + } else { + numberValue = Number(value) + } + + if (Number.isNaN(numberValue)) throw new CompilerError(`Invalid number literal: ${value}`, node.from, node.to) - return [[`PUSH`, number]] + return [[`PUSH`, numberValue]] case terms.String: { const { parts, hasInterpolation } = getStringParts(node, input) diff --git a/src/compiler/tests/literals.test.ts b/src/compiler/tests/literals.test.ts index c1dc14b..45f9fba 100644 --- a/src/compiler/tests/literals.test.ts +++ b/src/compiler/tests/literals.test.ts @@ -1,6 +1,44 @@ import { describe } from 'bun:test' import { expect, test } from 'bun:test' +describe('number literals', () => { + test('binary literals', () => { + expect('0b110').toEvaluateTo(6) + expect('0b1010').toEvaluateTo(10) + expect('0b11111111').toEvaluateTo(255) + expect('0b0').toEvaluateTo(0) + expect('0b1').toEvaluateTo(1) + }) + + test('hex literals', () => { + expect('0xdeadbeef').toEvaluateTo(0xdeadbeef) + expect('0xdeadbeef').toEvaluateTo(3735928559) + expect('0xFF').toEvaluateTo(255) + expect('0xff').toEvaluateTo(255) + expect('0x10').toEvaluateTo(16) + expect('0x0').toEvaluateTo(0) + expect('0xABCDEF').toEvaluateTo(0xabcdef) + }) + + test('decimal literals still work', () => { + expect('42').toEvaluateTo(42) + expect('3.14').toEvaluateTo(3.14) + expect('0').toEvaluateTo(0) + expect('999999').toEvaluateTo(999999) + }) + + test('negative hex and binary', () => { + expect('-0xFF').toEvaluateTo(-255) + expect('-0b1010').toEvaluateTo(-10) + }) + + test('positive prefix', () => { + expect('+0xFF').toEvaluateTo(255) + expect('+0b110').toEvaluateTo(6) + expect('+42').toEvaluateTo(42) + }) +}) + describe('array literals', () => { test('work with numbers', () => { expect('[1 2 3]').toEvaluateTo([1, 2, 3]) diff --git a/src/parser/shrimp.grammar b/src/parser/shrimp.grammar index cbc14b5..c021018 100644 --- a/src/parser/shrimp.grammar +++ b/src/parser/shrimp.grammar @@ -13,7 +13,11 @@ StringFragment { !['\\$]+ } NamedArgPrefix { $[a-z] $[a-z0-9-]* "=" } - Number { ("-" | "+")? $[0-9]+ ("_"? $[0-9]+)* ('.' $[0-9]+ ("_"? $[0-9]+)*)? } + Number { + ("-" | "+")? "0x" $[0-9a-fA-F]+ | + ("-" | "+")? "0b" $[01]+ | + ("-" | "+")? $[0-9]+ ("_"? $[0-9]+)* ('.' $[0-9]+ ("_"? $[0-9]+)*)? + } Boolean { "true" | "false" } newlineOrSemicolon { "\n" | ";" } eof { @eof } diff --git a/src/parser/shrimp.ts b/src/parser/shrimp.ts index 9920125..fdab5b4 100644 --- a/src/parser/shrimp.ts +++ b/src/parser/shrimp.ts @@ -19,7 +19,7 @@ export const parser = LRParser.deserialize({ propSources: [highlighting], skippedNodes: [0,25], repeatNodeCount: 12, - tokenData: "Cx~R|OX#{XY$jYZ%TZp#{pq$jqs#{st%ntu'Vuw#{wx'[xy'ayz'zz{#{{|(e|}#{}!O(e!O!P#{!P!Q+e!Q![)S![!]4Q!]!^%T!^!}#{!}#O4k#O#P6a#P#Q6f#Q#R#{#R#S7P#S#T#{#T#Y7j#Y#Z9U#Z#b7j#b#c>r#c#f7j#f#g?u#g#h7j#h#i@x#i#o7j#o#p#{#p#qCY#q;'S#{;'S;=`$d<%l~#{~O#{~~CsS$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)Z[zSsQOt#{uw#{x!O#{!O!P*P!P!Q#{!Q![)S![#O#{#P#R#{#R#S(e#S;'S#{;'S;=`$d<%lO#{U*UWzSOt#{uw#{x!Q#{!Q![*n![#O#{#P;'S#{;'S;=`$d<%lO#{U*uYzSsQOt#{uw#{x!Q#{!Q![*n![#O#{#P#R#{#R#S*P#S;'S#{;'S;=`$d<%lO#{U+jWzSOt#{uw#{x!P#{!P!Q,S!Q#O#{#P;'S#{;'S;=`$d<%lO#{U,X^zSOY-TYZ#{Zt-Ttu.Wuw-Twx.Wx!P-T!P!Q#{!Q!}-T!}#O2y#O#P0f#P;'S-T;'S;=`3z<%lO-TU-[^zS!bQOY-TYZ#{Zt-Ttu.Wuw-Twx.Wx!P-T!P!Q0{!Q!}-T!}#O2y#O#P0f#P;'S-T;'S;=`3z<%lO-TQ.]X!bQOY.WZ!P.W!P!Q.x!Q!}.W!}#O/g#O#P0f#P;'S.W;'S;=`0u<%lO.WQ.{P!P!Q/OQ/TU!bQ#Z#[/O#]#^/O#a#b/O#g#h/O#i#j/O#m#n/OQ/jVOY/gZ#O/g#O#P0P#P#Q.W#Q;'S/g;'S;=`0`<%lO/gQ0SSOY/gZ;'S/g;'S;=`0`<%lO/gQ0cP;=`<%l/gQ0iSOY.WZ;'S.W;'S;=`0u<%lO.WQ0xP;=`<%l.WU1QWzSOt#{uw#{x!P#{!P!Q1j!Q#O#{#P;'S#{;'S;=`$d<%lO#{U1qbzS!bQOt#{uw#{x#O#{#P#Z#{#Z#[1j#[#]#{#]#^1j#^#a#{#a#b1j#b#g#{#g#h1j#h#i#{#i#j1j#j#m#{#m#n1j#n;'S#{;'S;=`$d<%lO#{U3O[zSOY2yYZ#{Zt2ytu/guw2ywx/gx#O2y#O#P0P#P#Q-T#Q;'S2y;'S;=`3t<%lO2yU3wP;=`<%l2yU3}P;=`<%l-TU4XUzS!PQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U4rW#aQzSOt#{uw#{x!_#{!_!`5[!`#O#{#P;'S#{;'S;=`$d<%lO#{U5aVzSOt#{uw#{x#O#{#P#Q5v#Q;'S#{;'S;=`$d<%lO#{U5}U#`QzSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~6fO#W~U6mU#bQzSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U7WUzS!WQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U7o^zSOt#{uw#{x}#{}!O7j!O!Q#{!Q![7j![!_#{!_!`8k!`#O#{#P#T#{#T#o7j#o;'S#{;'S;=`$d<%lO#{U8rUxQzSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U9Z_zSOt#{uw#{x}#{}!O7j!O!Q#{!Q![7j![!_#{!_!`8k!`#O#{#P#T#{#T#U:Y#U#o7j#o;'S#{;'S;=`$d<%lO#{U:_`zSOt#{uw#{x}#{}!O7j!O!Q#{!Q![7j![!_#{!_!`8k!`#O#{#P#T#{#T#`7j#`#a;a#a#o7j#o;'S#{;'S;=`$d<%lO#{U;f`zSOt#{uw#{x}#{}!O7j!O!Q#{!Q![7j![!_#{!_!`8k!`#O#{#P#T#{#T#g7j#g#hy^#XWzSOt#{uw#{x}#{}!O7j!O!Q#{!Q![7j![!_#{!_!`8k!`#O#{#P#T#{#T#o7j#o;'S#{;'S;=`$d<%lO#{^?|^#ZWzSOt#{uw#{x}#{}!O7j!O!Q#{!Q![7j![!_#{!_!`8k!`#O#{#P#T#{#T#o7j#o;'S#{;'S;=`$d<%lO#{^AP`#YWzSOt#{uw#{x}#{}!O7j!O!Q#{!Q![7j![!_#{!_!`8k!`#O#{#P#T#{#T#f7j#f#gBR#g#o7j#o;'S#{;'S;=`$d<%lO#{UBW`zSOt#{uw#{x}#{}!O7j!O!Q#{!Q![7j![!_#{!_!`8k!`#O#{#P#T#{#T#i7j#i#j`#Z#be_zSOt$Ouw$Ox}$O}!O (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 20, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], diff --git a/src/parser/tests/literals.test.ts b/src/parser/tests/literals.test.ts index 3a63381..fe1b4ca 100644 --- a/src/parser/tests/literals.test.ts +++ b/src/parser/tests/literals.test.ts @@ -2,6 +2,65 @@ import { expect, describe, test } from 'bun:test' import '../shrimp.grammar' // Importing this so changes cause it to retest! +describe('number literals', () => { + test('binary numbers', () => { + expect('0b110').toMatchTree(` + Number 0b110 + `) + }) + + test('hex numbers', () => { + expect('0xdeadbeef').toMatchTree(` + Number 0xdeadbeef + `) + }) + + test('hex numbers uppercase', () => { + expect('0xFF').toMatchTree(` + Number 0xFF + `) + }) + + test('decimal numbers still work', () => { + expect('42').toMatchTree(` + Number 42 + `) + }) + + test('negative binary', () => { + expect('-0b110').toMatchTree(` + Number -0b110 + `) + }) + + test('negative hex', () => { + expect('-0xFF').toMatchTree(` + Number -0xFF + `) + }) + + test('positive prefix binary', () => { + expect('+0b110').toMatchTree(` + Number +0b110 + `) + }) + + test('positive prefix hex', () => { + expect('+0xFF').toMatchTree(` + Number +0xFF + `) + }) + + test('hex and binary in arrays', () => { + expect('[0xFF 0b110 42]').toMatchTree(` + Array + Number 0xFF + Number 0b110 + Number 42 + `) + }) +}) + describe('array literals', () => { test('work with numbers', () => { expect('[1 2 3]').toMatchTree(` From 7bbf43a7258f199c3db0679bdf18133da805f34c Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Fri, 7 Nov 2025 22:42:26 -0800 Subject: [PATCH 21/48] bitwise operators --- src/compiler/compiler.ts | 18 +++ src/compiler/tests/bitwise.test.ts | 178 +++++++++++++++++++++++++++++ src/parser/operatorTokenizer.ts | 6 + src/parser/shrimp.grammar | 11 +- src/parser/shrimp.terms.ts | 98 ++++++++-------- src/parser/shrimp.ts | 26 ++--- src/parser/tests/bitwise.test.ts | 72 ++++++++++++ src/prelude/index.ts | 1 + 8 files changed, 349 insertions(+), 61 deletions(-) create mode 100644 src/compiler/tests/bitwise.test.ts create mode 100644 src/parser/tests/bitwise.test.ts diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index a5327ab..74ed78d 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -243,6 +243,24 @@ export class Compiler { case '%': instructions.push(['MOD']) break + case 'band': + instructions.push(['BIT_AND']) + break + case 'bor': + instructions.push(['BIT_OR']) + break + case 'bxor': + instructions.push(['BIT_XOR']) + break + case '<<': + instructions.push(['BIT_SHL']) + break + case '>>': + instructions.push(['BIT_SHR']) + break + case '>>>': + instructions.push(['BIT_USHR']) + break default: throw new CompilerError(`Unsupported binary operator: ${opValue}`, op.from, op.to) } diff --git a/src/compiler/tests/bitwise.test.ts b/src/compiler/tests/bitwise.test.ts new file mode 100644 index 0000000..0d39a52 --- /dev/null +++ b/src/compiler/tests/bitwise.test.ts @@ -0,0 +1,178 @@ +import { expect, describe, test } from 'bun:test' + +describe('bitwise operators', () => { + describe('band (bitwise AND)', () => { + test('basic AND operation', () => { + expect('5 band 3').toEvaluateTo(1) + // 5 = 0101, 3 = 0011, result = 0001 = 1 + }) + + test('AND with zero', () => { + expect('5 band 0').toEvaluateTo(0) + }) + + test('AND with all bits set', () => { + expect('15 band 7').toEvaluateTo(7) + // 15 = 1111, 7 = 0111, result = 0111 = 7 + }) + + test('AND in assignment', () => { + expect('x = 12 band 10').toEvaluateTo(8) + // 12 = 1100, 10 = 1010, result = 1000 = 8 + }) + }) + + describe('bor (bitwise OR)', () => { + test('basic OR operation', () => { + expect('5 bor 3').toEvaluateTo(7) + // 5 = 0101, 3 = 0011, result = 0111 = 7 + }) + + test('OR with zero', () => { + expect('5 bor 0').toEvaluateTo(5) + }) + + test('OR with all bits set', () => { + expect('8 bor 4').toEvaluateTo(12) + // 8 = 1000, 4 = 0100, result = 1100 = 12 + }) + }) + + describe('bxor (bitwise XOR)', () => { + test('basic XOR operation', () => { + expect('5 bxor 3').toEvaluateTo(6) + // 5 = 0101, 3 = 0011, result = 0110 = 6 + }) + + test('XOR with itself returns zero', () => { + expect('5 bxor 5').toEvaluateTo(0) + }) + + test('XOR with zero returns same value', () => { + expect('7 bxor 0').toEvaluateTo(7) + }) + + test('XOR in assignment', () => { + expect('result = 8 bxor 12').toEvaluateTo(4) + // 8 = 1000, 12 = 1100, result = 0100 = 4 + }) + }) + + describe('bnot (bitwise NOT)', () => { + test('NOT of positive number', () => { + expect('bnot 5').toEvaluateTo(-6) + // ~5 = -6 (two\'s complement) + }) + + test('NOT of zero', () => { + expect('bnot 0').toEvaluateTo(-1) + }) + + test('NOT of negative number', () => { + expect('bnot -1').toEvaluateTo(0) + }) + + test('double NOT returns original', () => { + expect('bnot (bnot 5)').toEvaluateTo(5) + }) + }) + + describe('<< (left shift)', () => { + test('basic left shift', () => { + expect('5 << 2').toEvaluateTo(20) + // 5 << 2 = 20 + }) + + test('shift by zero', () => { + expect('5 << 0').toEvaluateTo(5) + }) + + test('shift by one', () => { + expect('3 << 1').toEvaluateTo(6) + }) + + test('large shift', () => { + expect('1 << 10').toEvaluateTo(1024) + }) + }) + + describe('>> (signed right shift)', () => { + test('basic right shift', () => { + expect('20 >> 2').toEvaluateTo(5) + // 20 >> 2 = 5 + }) + + test('shift by zero', () => { + expect('20 >> 0').toEvaluateTo(20) + }) + + test('preserves sign for negative numbers', () => { + expect('-20 >> 2').toEvaluateTo(-5) + // Sign is preserved + }) + + test('negative number right shift', () => { + expect('-8 >> 1').toEvaluateTo(-4) + }) + }) + + describe('>>> (unsigned right shift)', () => { + test('basic unsigned right shift', () => { + expect('20 >>> 2').toEvaluateTo(5) + }) + + test('unsigned shift of -1', () => { + expect('-1 >>> 1').toEvaluateTo(2147483647) + // -1 >>> 1 = 2147483647 (unsigned, no sign extension) + }) + + test('unsigned shift of negative number', () => { + expect('-8 >>> 1').toEvaluateTo(2147483644) + }) + }) + + describe('compound expressions', () => { + test('multiple bitwise operations', () => { + expect('(5 band 3) bor (8 bxor 12)').toEvaluateTo(5) + // (5 & 3) | (8 ^ 12) = 1 | 4 = 5 + }) + + test('bitwise with variables', () => { + expect(` + a = 5 + b = 3 + a bor b + `).toEvaluateTo(7) + }) + + test('shift operations with variables', () => { + expect(` + x = 16 + y = 2 + x >> y + `).toEvaluateTo(4) + }) + + test('mixing shifts and bitwise', () => { + expect('(8 << 1) band 15').toEvaluateTo(0) + // (8 << 1) & 15 = 16 & 15 = 0 + }) + + test('mixing shifts and bitwise 2', () => { + expect('(7 << 1) band 15').toEvaluateTo(14) + // (7 << 1) & 15 = 14 & 15 = 14 + }) + }) + + describe('precedence', () => { + test('bitwise has correct precedence with arithmetic', () => { + expect('1 + 2 band 3').toEvaluateTo(3) + // (1 + 2) & 3 = 3 & 3 = 3 + }) + + test('shift has correct precedence', () => { + expect('4 + 8 << 1').toEvaluateTo(24) + // (4 + 8) << 1 = 12 << 1 = 24 + }) + }) +}) diff --git a/src/parser/operatorTokenizer.ts b/src/parser/operatorTokenizer.ts index 3c85400..c909543 100644 --- a/src/parser/operatorTokenizer.ts +++ b/src/parser/operatorTokenizer.ts @@ -5,6 +5,12 @@ type Operator = { str: string; tokenName: keyof typeof terms } const operators: Array = [ { str: 'and', tokenName: 'And' }, { str: 'or', tokenName: 'Or' }, + { str: 'band', tokenName: 'Band' }, + { str: 'bor', tokenName: 'Bor' }, + { str: 'bxor', tokenName: 'Bxor' }, + { str: '>>>', tokenName: 'Ushr' }, // Must come before >> + { str: '>>', tokenName: 'Shr' }, + { str: '<<', tokenName: 'Shl' }, { str: '>=', tokenName: 'Gte' }, { str: '<=', tokenName: 'Lte' }, { str: '!=', tokenName: 'Neq' }, diff --git a/src/parser/shrimp.grammar b/src/parser/shrimp.grammar index c021018..965c206 100644 --- a/src/parser/shrimp.grammar +++ b/src/parser/shrimp.grammar @@ -6,7 +6,7 @@ @top Program { item* } -@external tokens operatorTokenizer from "./operatorTokenizer" { Star, Slash, Plus, Minus, And, Or, Eq, EqEq, Neq, Lt, Lte, Gt, Gte, Modulo, PlusEq, MinusEq, StarEq, SlashEq, ModuloEq } +@external tokens operatorTokenizer from "./operatorTokenizer" { 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 } @tokens { @precedence { Number Regex } @@ -51,6 +51,7 @@ null { @specialize[@name=Null] } comparison @left, multiplicative @left, additive @left, + bitwise @left, call, functionWithNewlines } @@ -189,7 +190,13 @@ BinOp { (expression | BinOp) !multiplicative Star (expression | BinOp) | (expression | BinOp) !multiplicative Slash (expression | BinOp) | (expression | BinOp) !additive Plus (expression | BinOp) | - (expression | BinOp) !additive Minus (expression | BinOp) + (expression | BinOp) !additive Minus (expression | BinOp) | + (expression | BinOp) !bitwise Band (expression | BinOp) | + (expression | BinOp) !bitwise Bor (expression | BinOp) | + (expression | BinOp) !bitwise Bxor (expression | BinOp) | + (expression | BinOp) !bitwise Shl (expression | BinOp) | + (expression | BinOp) !bitwise Shr (expression | BinOp) | + (expression | BinOp) !bitwise Ushr (expression | BinOp) } ParenExpr { diff --git a/src/parser/shrimp.terms.ts b/src/parser/shrimp.terms.ts index 82f4419..da329d7 100644 --- a/src/parser/shrimp.terms.ts +++ b/src/parser/shrimp.terms.ts @@ -19,49 +19,55 @@ export const StarEq = 17, SlashEq = 18, ModuloEq = 19, - Identifier = 20, - AssignableIdentifier = 21, - Word = 22, - IdentifierBeforeDot = 23, - Do = 24, - Comment = 25, - Program = 26, - PipeExpr = 27, - WhileExpr = 29, - keyword = 71, - ConditionalOp = 31, - ParenExpr = 32, - 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 + Band = 20, + Bor = 21, + Bxor = 22, + Shl = 23, + Shr = 24, + Ushr = 25, + Identifier = 26, + AssignableIdentifier = 27, + Word = 28, + IdentifierBeforeDot = 29, + Do = 30, + Comment = 31, + Program = 32, + PipeExpr = 33, + WhileExpr = 35, + keyword = 77, + ConditionalOp = 37, + ParenExpr = 38, + FunctionCallWithNewlines = 39, + DotGet = 40, + Number = 41, + PositionalArg = 42, + FunctionDef = 43, + Params = 44, + NamedParam = 45, + NamedArgPrefix = 46, + String = 47, + StringFragment = 48, + Interpolation = 49, + EscapeSeq = 50, + Boolean = 51, + Null = 52, + colon = 53, + CatchExpr = 54, + Block = 56, + FinallyExpr = 57, + Underscore = 60, + NamedArg = 61, + IfExpr = 62, + FunctionCall = 64, + ElseIfExpr = 65, + ElseExpr = 67, + FunctionCallOrIdentifier = 68, + BinOp = 69, + Regex = 70, + Dict = 71, + Array = 72, + FunctionCallWithBlock = 73, + TryExpr = 74, + Throw = 76, + CompoundAssign = 78, + Assign = 79 diff --git a/src/parser/shrimp.ts b/src/parser/shrimp.ts index fdab5b4..419523f 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, null:92, catch:98, finally:104, end:106, if:114, else:120, try:138, throw:142} +const spec_Identifier = {__proto__:null,while:72, null:104, catch:110, finally:116, end:118, if:126, else:132, try:150, throw:154} export const parser = LRParser.deserialize({ version: 14, - states: "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, + states: "PQcO,5:fO>mQcO,5:fOOQa1G/[1G/[OOOO,59y,59yOOOO,59z,59zOOOO-E8R-E8ROOQa1G/c1G/cOOQ`,5:V,5:VOOQ`-E8U-E8UOOQa1G/y1G/yO@fQcO1G/yO@pQcO1G/yOBOQcO1G/yOBYQcO1G/yOBgQcO1G/yOOQa1G/X1G/XOCuQcO1G/XOC|QcO1G/XODTQcO1G/XOOQ`-E8O-E8OOD[QRO1G/YODfQQO1G/YODkQQO1G/YODsQQO1G/YOEOQRO1G/YOEVQRO1G/YOEhQbO,59oOErQQO1G/YOOQa1G/Y1G/YOEzQQO1G/{OOQa1G/|1G/|OFVQbO1G/|OOQO'#EY'#EYOEzQQO1G/{OOQa1G/{1G/{OOQ`'#EZ'#EZOFVQbO1G/|OFaQbO1G0TOF{QbO1G0SOGgQbO'#DfOGxQbO'#DfOH]QbO1G/}OOQ`-E7}-E7}OOQ`,5:k,5:kOOQ`-E8P-E8POHhQQO,59tOOQO,59u,59uOOQO-E8Q-E8QOHpQbO1G/_O9PQbO1G/rO9PQbO1G/VOHwQbO1G0OOISQQO7+$tOOQa7+$t7+$tOI[QQO1G/ZOIdQQO7+%gOOQa7+%g7+%gOIoQbO7+%hOOQa7+%h7+%hOOQO-E8W-E8WOOQ`-E8X-E8XOOQ`'#EU'#EUOIyQQO'#EUOJRQbO'#EnOOQ`,5:Q,5:QOJfQbO'#DdOJkQQO'#DgOOQ`7+%i7+%iOJpQbO7+%iOJuQbO7+%iOJ}QbO7+$yOK]QbO7+$yOKmQbO7+%^OKuQbO7+$qOOQ`7+%j7+%jOKzQbO7+%jOLPQbO7+%jOOQa<oAN>oOOQ`AN>PAN>PONZQbOAN>PON`QbOAN>POOQ`-E8V-E8VOOQ`AN>dAN>dONhQbOAN>dO1qQbO,5:ZO9PQbO,5:]OOQ`AN>pAN>pPEhQbO'#EQOOQ`7+%U7+%UOOQ`G23kG23kONmQbOG23kPMmQbO'#DoOOQ`G24OG24OONrQQO1G/uOOQ`1G/w1G/wOOQ`LD)VLD)VO9PQbO7+%aOOQ`<`#Z#be_zSOt$Ouw$Ox}$O}!O (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 20, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], - tokenPrec: 1922 + tokenData: "IS~R}OX$OXY$mYZ%WZp$Opq$mqs$Ost%qtu'Yuw$Owx'_xy'dyz'}z{$O{|(h|}$O}!O(h!O!P$O!P!Q0o!Q!R)Y!R![+w![!]9[!]!^%W!^!}$O!}#O9u#O#P;k#P#Q;p#Q#R$O#R#S`#Z#be_!QSOt$Ouw$Ox}$O}!O (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 26, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], + tokenPrec: 2109 }) diff --git a/src/parser/tests/bitwise.test.ts b/src/parser/tests/bitwise.test.ts new file mode 100644 index 0000000..5ccc8ed --- /dev/null +++ b/src/parser/tests/bitwise.test.ts @@ -0,0 +1,72 @@ +import { expect, describe, test } from 'bun:test' + +import '../shrimp.grammar' // Importing this so changes cause it to retest! + +describe('bitwise operators - grammar', () => { + test('parses band (bitwise AND)', () => { + expect('5 band 3').toMatchTree(` + BinOp + Number 5 + Band band + Number 3`) + }) + + test('parses bor (bitwise OR)', () => { + expect('5 bor 3').toMatchTree(` + BinOp + Number 5 + Bor bor + Number 3`) + }) + + test('parses bxor (bitwise XOR)', () => { + expect('5 bxor 3').toMatchTree(` + BinOp + Number 5 + Bxor bxor + Number 3`) + }) + + test('parses << (left shift)', () => { + expect('5 << 2').toMatchTree(` + BinOp + Number 5 + Shl << + Number 2`) + }) + + test('parses >> (signed right shift)', () => { + expect('20 >> 2').toMatchTree(` + BinOp + Number 20 + Shr >> + Number 2`) + }) + + test('parses >>> (unsigned right shift)', () => { + expect('-1 >>> 1').toMatchTree(` + BinOp + Number -1 + Ushr >>> + Number 1`) + }) + + test('parses bnot (bitwise NOT) as function call', () => { + expect('bnot 5').toMatchTree(` + FunctionCall + Identifier bnot + PositionalArg + Number 5`) + }) + + test('bitwise operators work in expressions', () => { + expect('x = 5 band 3').toMatchTree(` + Assign + AssignableIdentifier x + Eq = + BinOp + Number 5 + Band band + Number 3`) + }) +}) diff --git a/src/prelude/index.ts b/src/prelude/index.ts index c2c77ec..c153870 100644 --- a/src/prelude/index.ts +++ b/src/prelude/index.ts @@ -63,6 +63,7 @@ export const globals = { // boolean/logic not: (v: any) => !v, + bnot: (n: number) => ~(n | 0), // utilities inc: (n: number) => n + 1, From 99a5aa5312b24f9c91f0b42c1912451caa58ec8c Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Fri, 7 Nov 2025 22:55:34 -0800 Subject: [PATCH 22/48] update reef, fix precedence test --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2000575..f167e90 100644 --- a/package.json +++ b/package.json @@ -31,4 +31,4 @@ "singleQuote": true, "printWidth": 100 } -} +} \ No newline at end of file From 019f7d84b1f61de48de2b6acad204abd125eddc5 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sat, 8 Nov 2025 11:43:22 -0800 Subject: [PATCH 23/48] Add ?? and ??= operators --- src/compiler/compiler.ts | 36 ++++++++- src/compiler/tests/compiler.test.ts | 115 ++++++++++++++++++++++++++++ src/parser/operatorTokenizer.ts | 4 + src/parser/shrimp.grammar | 8 +- src/parser/shrimp.terms.ts | 94 ++++++++++++----------- src/parser/shrimp.ts | 26 +++---- src/parser/tests/basics.test.ts | 81 ++++++++++++++++++++ src/parser/tokenizer.ts | 9 +++ 8 files changed, 308 insertions(+), 65 deletions(-) diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 74ed78d..2cb076b 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -303,13 +303,31 @@ export class Compiler { const { identifier, operator, right } = getCompoundAssignmentParts(node) const identifierName = input.slice(identifier.from, identifier.to) const instructions: ProgramItem[] = [] + const opValue = input.slice(operator.from, operator.to) - // will throw if undefined - instructions.push(['LOAD', identifierName]) + // Special handling for ??= since it needs conditional evaluation + if (opValue === '??=') { + instructions.push(['LOAD', identifierName]) + const rightInstructions = this.#compileNode(right, input) + + instructions.push(['DUP']) + instructions.push(['PUSH', null]) + instructions.push(['NEQ']) + instructions.push(['JUMP_IF_TRUE', rightInstructions.length + 1]) + instructions.push(['POP']) + instructions.push(...rightInstructions) + + instructions.push(['DUP']) + instructions.push(['STORE', identifierName]) + + return instructions + } + + // Standard compound assignments: evaluate both sides, then operate + instructions.push(['LOAD', identifierName]) // will throw if undefined instructions.push(...this.#compileNode(right, input)) - const opValue = input.slice(operator.from, operator.to) switch (opValue) { case '+=': instructions.push(['ADD']) @@ -638,6 +656,18 @@ export class Compiler { break + case '??': + // Nullish coalescing: return left if not null, else right + instructions.push(...leftInstructions) + instructions.push(['DUP']) + instructions.push(['PUSH', null]) + instructions.push(['NEQ']) + instructions.push(['JUMP_IF_TRUE', rightInstructions.length + 1]) + instructions.push(['POP']) + instructions.push(...rightInstructions) + + break + default: throw new CompilerError(`Unsupported conditional operator: ${opValue}`, op.from, op.to) } diff --git a/src/compiler/tests/compiler.test.ts b/src/compiler/tests/compiler.test.ts index 184475f..1965e89 100644 --- a/src/compiler/tests/compiler.test.ts +++ b/src/compiler/tests/compiler.test.ts @@ -333,3 +333,118 @@ describe('default params', () => { ).toEvaluateTo({ name: 'Jon', age: 21 }) }) }) + +describe('Nullish coalescing operator (??)', () => { + test('returns left side when not null', () => { + expect('5 ?? 10').toEvaluateTo(5) + }) + + test('returns right side when left is null', () => { + expect('null ?? 10').toEvaluateTo(10) + }) + + test('returns left side when left is false', () => { + expect('false ?? 10').toEvaluateTo(false) + }) + + test('returns left side when left is 0', () => { + expect('0 ?? 10').toEvaluateTo(0) + }) + + test('returns left side when left is empty string', () => { + expect(`'' ?? 'default'`).toEvaluateTo('') + }) + + test('chains left to right', () => { + expect('null ?? null ?? 42').toEvaluateTo(42) + expect('null ?? 10 ?? 20').toEvaluateTo(10) + }) + + test('short-circuits evaluation', () => { + const throwError = () => { throw new Error('Should not evaluate') } + expect('5 ?? throw-error').toEvaluateTo(5, { 'throw-error': throwError }) + }) + + test('works with variables', () => { + expect('x = null; x ?? 5').toEvaluateTo(5) + expect('y = 3; y ?? 5').toEvaluateTo(3) + }) + + test('works with function calls', () => { + const getValue = () => null + const getDefault = () => 42 + // Note: identifiers without parentheses refer to the function, not call it + // Use explicit call syntax to invoke the function + expect('(get-value) ?? (get-default)').toEvaluateTo(42, { + 'get-value': getValue, + 'get-default': getDefault + }) + }) +}) + +describe('Nullish coalescing assignment (??=)', () => { + test('assigns when variable is null', () => { + expect('x = null; x ??= 5; x').toEvaluateTo(5) + }) + + test('does not assign when variable is not null', () => { + expect('x = 3; x ??= 10; x').toEvaluateTo(3) + }) + + test('does not assign when variable is false', () => { + expect('x = false; x ??= true; x').toEvaluateTo(false) + }) + + test('does not assign when variable is 0', () => { + expect('x = 0; x ??= 100; x').toEvaluateTo(0) + }) + + test('does not assign when variable is empty string', () => { + expect(`x = ''; x ??= 'default'; x`).toEvaluateTo('') + }) + + test('returns the final value', () => { + expect('x = null; x ??= 5').toEvaluateTo(5) + expect('y = 3; y ??= 10').toEvaluateTo(3) + }) + + test('short-circuits evaluation when not null', () => { + const throwError = () => { throw new Error('Should not evaluate') } + expect('x = 5; x ??= throw-error; x').toEvaluateTo(5, { 'throw-error': throwError }) + }) + + test('works with expressions', () => { + expect('x = null; x ??= 2 + 3; x').toEvaluateTo(5) + }) + + test('works with function calls', () => { + const getDefault = () => 42 + expect('x = null; x ??= (get-default); x').toEvaluateTo(42, { 'get-default': getDefault }) + }) + + test('throws when variable is undefined', () => { + expect(() => expect('undefined-var ??= 5').toEvaluateTo(null)).toThrow() + }) +}) + +describe('Compound assignment operators', () => { + test('+=', () => { + expect('x = 5; x += 3; x').toEvaluateTo(8) + }) + + test('-=', () => { + expect('x = 10; x -= 4; x').toEvaluateTo(6) + }) + + test('*=', () => { + expect('x = 3; x *= 4; x').toEvaluateTo(12) + }) + + test('/=', () => { + expect('x = 20; x /= 5; x').toEvaluateTo(4) + }) + + test('%=', () => { + expect('x = 10; x %= 3; x').toEvaluateTo(1) + }) +}) diff --git a/src/parser/operatorTokenizer.ts b/src/parser/operatorTokenizer.ts index c909543..1ef7a94 100644 --- a/src/parser/operatorTokenizer.ts +++ b/src/parser/operatorTokenizer.ts @@ -17,12 +17,16 @@ const operators: Array = [ { str: '==', tokenName: 'EqEq' }, // Compound assignment operators (must come before single-char operators) + { str: '??=', tokenName: 'NullishEq' }, { str: '+=', tokenName: 'PlusEq' }, { str: '-=', tokenName: 'MinusEq' }, { str: '*=', tokenName: 'StarEq' }, { str: '/=', tokenName: 'SlashEq' }, { str: '%=', tokenName: 'ModuloEq' }, + // Nullish coalescing (must come before it could be mistaken for other tokens) + { str: '??', tokenName: 'NullishCoalesce' }, + // Single-char operators { str: '*', tokenName: 'Star' }, { str: '=', tokenName: 'Eq' }, diff --git a/src/parser/shrimp.grammar b/src/parser/shrimp.grammar index 965c206..f4ac3e7 100644 --- a/src/parser/shrimp.grammar +++ b/src/parser/shrimp.grammar @@ -6,7 +6,7 @@ @top Program { item* } -@external tokens operatorTokenizer from "./operatorTokenizer" { 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 } +@external tokens operatorTokenizer from "./operatorTokenizer" { 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 } @tokens { @precedence { Number Regex } @@ -48,6 +48,7 @@ null { @specialize[@name=Null] } pipe @left, or @left, and @left, + nullish @left, comparison @left, multiplicative @left, additive @left, @@ -166,7 +167,8 @@ ConditionalOp { expression !comparison Gt expression | expression !comparison Gte expression | (expression | ConditionalOp) !and And (expression | ConditionalOp) | - (expression | ConditionalOp) !or Or (expression | ConditionalOp) + (expression | ConditionalOp) !or Or (expression | ConditionalOp) | + (expression | ConditionalOp) !nullish NullishCoalesce (expression | ConditionalOp) } Params { @@ -182,7 +184,7 @@ Assign { } CompoundAssign { - AssignableIdentifier (PlusEq | MinusEq | StarEq | SlashEq | ModuloEq) consumeToTerminator + AssignableIdentifier (PlusEq | MinusEq | StarEq | SlashEq | ModuloEq | NullishEq) consumeToTerminator } BinOp { diff --git a/src/parser/shrimp.terms.ts b/src/parser/shrimp.terms.ts index da329d7..3da47bb 100644 --- a/src/parser/shrimp.terms.ts +++ b/src/parser/shrimp.terms.ts @@ -25,49 +25,51 @@ export const Shl = 23, Shr = 24, Ushr = 25, - Identifier = 26, - AssignableIdentifier = 27, - Word = 28, - IdentifierBeforeDot = 29, - Do = 30, - Comment = 31, - Program = 32, - PipeExpr = 33, - WhileExpr = 35, - keyword = 77, - ConditionalOp = 37, - ParenExpr = 38, - FunctionCallWithNewlines = 39, - DotGet = 40, - Number = 41, - PositionalArg = 42, - FunctionDef = 43, - Params = 44, - NamedParam = 45, - NamedArgPrefix = 46, - String = 47, - StringFragment = 48, - Interpolation = 49, - EscapeSeq = 50, - Boolean = 51, - Null = 52, - colon = 53, - CatchExpr = 54, - Block = 56, - FinallyExpr = 57, - Underscore = 60, - NamedArg = 61, - IfExpr = 62, - FunctionCall = 64, - ElseIfExpr = 65, - ElseExpr = 67, - FunctionCallOrIdentifier = 68, - BinOp = 69, - Regex = 70, - Dict = 71, - Array = 72, - FunctionCallWithBlock = 73, - TryExpr = 74, - Throw = 76, - CompoundAssign = 78, - Assign = 79 + NullishCoalesce = 26, + NullishEq = 27, + Identifier = 28, + AssignableIdentifier = 29, + Word = 30, + IdentifierBeforeDot = 31, + Do = 32, + Comment = 33, + Program = 34, + PipeExpr = 35, + WhileExpr = 37, + keyword = 79, + ConditionalOp = 39, + ParenExpr = 40, + FunctionCallWithNewlines = 41, + DotGet = 42, + Number = 43, + PositionalArg = 44, + FunctionDef = 45, + Params = 46, + NamedParam = 47, + NamedArgPrefix = 48, + String = 49, + StringFragment = 50, + Interpolation = 51, + EscapeSeq = 52, + Boolean = 53, + Null = 54, + colon = 55, + CatchExpr = 56, + Block = 58, + FinallyExpr = 59, + Underscore = 62, + NamedArg = 63, + IfExpr = 64, + FunctionCall = 66, + ElseIfExpr = 67, + ElseExpr = 69, + FunctionCallOrIdentifier = 70, + BinOp = 71, + Regex = 72, + Dict = 73, + Array = 74, + FunctionCallWithBlock = 75, + TryExpr = 76, + Throw = 78, + CompoundAssign = 80, + Assign = 81 diff --git a/src/parser/shrimp.ts b/src/parser/shrimp.ts index 419523f..901d667 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:72, null:104, catch:110, finally:116, end:118, if:126, else:132, try:150, throw:154} +const spec_Identifier = {__proto__:null,while:76, null:108, catch:114, finally:120, end:122, if:130, else:136, try:154, throw:158} export const parser = LRParser.deserialize({ version: 14, - states: "PQcO,5:fO>mQcO,5:fOOQa1G/[1G/[OOOO,59y,59yOOOO,59z,59zOOOO-E8R-E8ROOQa1G/c1G/cOOQ`,5:V,5:VOOQ`-E8U-E8UOOQa1G/y1G/yO@fQcO1G/yO@pQcO1G/yOBOQcO1G/yOBYQcO1G/yOBgQcO1G/yOOQa1G/X1G/XOCuQcO1G/XOC|QcO1G/XODTQcO1G/XOOQ`-E8O-E8OOD[QRO1G/YODfQQO1G/YODkQQO1G/YODsQQO1G/YOEOQRO1G/YOEVQRO1G/YOEhQbO,59oOErQQO1G/YOOQa1G/Y1G/YOEzQQO1G/{OOQa1G/|1G/|OFVQbO1G/|OOQO'#EY'#EYOEzQQO1G/{OOQa1G/{1G/{OOQ`'#EZ'#EZOFVQbO1G/|OFaQbO1G0TOF{QbO1G0SOGgQbO'#DfOGxQbO'#DfOH]QbO1G/}OOQ`-E7}-E7}OOQ`,5:k,5:kOOQ`-E8P-E8POHhQQO,59tOOQO,59u,59uOOQO-E8Q-E8QOHpQbO1G/_O9PQbO1G/rO9PQbO1G/VOHwQbO1G0OOISQQO7+$tOOQa7+$t7+$tOI[QQO1G/ZOIdQQO7+%gOOQa7+%g7+%gOIoQbO7+%hOOQa7+%h7+%hOOQO-E8W-E8WOOQ`-E8X-E8XOOQ`'#EU'#EUOIyQQO'#EUOJRQbO'#EnOOQ`,5:Q,5:QOJfQbO'#DdOJkQQO'#DgOOQ`7+%i7+%iOJpQbO7+%iOJuQbO7+%iOJ}QbO7+$yOK]QbO7+$yOKmQbO7+%^OKuQbO7+$qOOQ`7+%j7+%jOKzQbO7+%jOLPQbO7+%jOOQa<oAN>oOOQ`AN>PAN>PONZQbOAN>PON`QbOAN>POOQ`-E8V-E8VOOQ`AN>dAN>dONhQbOAN>dO1qQbO,5:ZO9PQbO,5:]OOQ`AN>pAN>pPEhQbO'#EQOOQ`7+%U7+%UOOQ`G23kG23kONmQbOG23kPMmQbO'#DoOOQ`G24OG24OONrQQO1G/uOOQ`1G/w1G/wOOQ`LD)VLD)VO9PQbO7+%aOOQ`<vQcO,5:hO?dQcO,5:hOOQa1G/^1G/^OOOO,59{,59{OOOO,59|,59|OOOO-E8T-E8TOOQa1G/e1G/eOOQ`,5:X,5:XOOQ`-E8W-E8WOOQa1G/{1G/{OA`QcO1G/{OAjQcO1G/{OBxQcO1G/{OCSQcO1G/{OCaQcO1G/{OOQa1G/Z1G/ZODrQcO1G/ZODyQcO1G/ZOEQQcO1G/ZOFPQcO1G/ZOEXQcO1G/ZOOQ`-E8Q-E8QOFgQRO1G/[OFqQQO1G/[OFvQQO1G/[OGOQQO1G/[OGZQRO1G/[OGbQRO1G/[OGiQbO,59qOGsQQO1G/[OOQa1G/[1G/[OG{QQO1G/}OOQa1G0O1G0OOHWQbO1G0OOOQO'#E['#E[OG{QQO1G/}OOQa1G/}1G/}OOQ`'#E]'#E]OHWQbO1G0OOHbQbO1G0VOH|QbO1G0UOIhQbO'#DhOIyQbO'#DhOJ^QbO1G0POOQ`-E8P-E8POOQ`,5:m,5:mOOQ`-E8R-E8ROJiQQO,59vOOQO,59w,59wOOQO-E8S-E8SOJqQbO1G/aO9jQbO1G/tO9jQbO1G/XOJxQbO1G0QOKTQQO7+$vOOQa7+$v7+$vOK]QQO1G/]OKeQQO7+%iOOQa7+%i7+%iOKpQbO7+%jOOQa7+%j7+%jOOQO-E8Y-E8YOOQ`-E8Z-E8ZOOQ`'#EW'#EWOKzQQO'#EWOLSQbO'#EpOOQ`,5:S,5:SOLgQbO'#DfOLlQQO'#DiOOQ`7+%k7+%kOLqQbO7+%kOLvQbO7+%kOMOQbO7+${OM^QbO7+${OMnQbO7+%`OMvQbO7+$sOOQ`7+%l7+%lOM{QbO7+%lONQQbO7+%lOOQa<qAN>qOOQ`AN>RAN>RO!![QbOAN>RO!!aQbOAN>ROOQ`-E8X-E8XOOQ`AN>fAN>fO!!iQbOAN>fO2TQbO,5:]O9jQbO,5:_OOQ`AN>rAN>rPGiQbO'#ESOOQ`7+%W7+%WOOQ`G23mG23mO!!nQbOG23mP! nQbO'#DqOOQ`G24QG24QO!!sQQO1G/wOOQ`1G/y1G/yOOQ`LD)XLD)XO9jQbO7+%cOOQ`<UOT}OU!OOj!POt!pa#Y!pa#k!pa!Z!pa!^!pa!_!pa#g!pa!f!pa~O^xOR!iiS!iid!iie!iif!iig!iih!iii!iit!ii#Y!ii#k!ii#g!ii!Z!ii!^!ii!_!ii!f!ii~OP!iiQ!ii~P@XOPyOQyO~P@XOPyOQyOd!iie!iif!iig!iih!iii!iit!ii#Y!ii#k!ii#g!ii!Z!ii!^!ii!_!ii!f!ii~OR!iiS!ii~PAtORzOSzO^xO~PAtORzOSzO~PAtOW|OX|OY|OZ|O[|O]|OTwijwitwi#Ywi#kwi#gwi!Xwi!Zwi!^wi!_wi!fwi~OU!OO~PCkOU!OO~PC}OUwi~PCkOT}OU!OOjwitwi#Ywi#kwi#gwi!Xwi!Zwi!^wi!_wi!fwi~OW|OX|OY|OZ|O[|O]|O~PEXO#Y!QO#g$QO~P*RO#g$QO~O#g$QOt#UX~O!X!cO#g$QOt#UX~O#g$QO~P.WO#g$QO~P7WOpfO!`rO~P,kO#Y!QO#g$QO~O!QsO#Y#kO#j$TO~O#Y#nO#j$VO~P2xOt!fO#Y!si#k!si!Z!si!^!si!_!si#g!si!f!si~Ot!fO#Y!ri#k!ri!Z!ri!^!ri!_!ri#g!ri!f!ri~Ot!fO!Z![X!^![X!_![X!f![X~O#Y$YO!Z#dP!^#dP!_#dP!f#dP~P8cO!Z$^O!^$_O!_$`O~O!Q!jO!X!Oa~O#Y$dO~P8cO!Z$^O!^$_O!_$gO~O#Y!QO#g$jO~O#Y!QO#gyi~O!QsO#Y#kO#j$mO~O#Y#nO#j$nO~P2xOt!fO#Y$oO~O#Y$YO!Z#dX!^#dX!_#dX!f#dX~P8cOl$qO~O!X$rO~O!_$sO~O!^$_O!_$sO~Ot!fO!Z$^O!^$_O!_$uO~O#Y$YO!Z#dP!^#dP!_#dP~P8cO!_$|O!f${O~O!_%OO~O!_%PO~O!^$_O!_%PO~OpfO!`rO#gyq~P,kO#Y!QO#gyq~O!X%UO~O!_%WO~O!_%XO~O!^$_O!_%XO~O!Z$^O!^$_O!_%XO~O!_%]O!f${O~O!X%`O!c%_O~O!_%]O~O!_%aO~OpfO!`rO#gyy~P,kO!_%dO~O!^$_O!_%dO~O!_%gO~O!_%jO~O!X%kO~O{!j~", + goto: "8f#gPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP#hP$RP$h%f&t&zP(U(b)[)_P)eP*l*lPPP*pP*|+fPPP+|#hP,f-PP-T-Z-pP.g/k$R$RP$RP$R$R0q0w1T1w1}2X2_2f2l2v2|3WPPP3b3f4Z6PPPP7ZP7kPPPPP7o7u7{r`Oe!a!b!c!f!u#s#{#|#}$[$d$r%U%`%kQ!WWR#a!Rw`OWe!R!a!b!c!f!u#s#{#|#}$[$d$r%U%`%kr^Oe!a!b!c!f!u#s#{#|#}$[$d$r%U%`%kQ!ZWS!og%_Q!thQ!xjQ#W!OQ#Y}Q#]!PR#d!RvSOeg!a!b!c!f!u#s#{#|#}$[$d$r%U%_%`%k!WZRSYhjsvxyz{|}!O!P!S!T!]!`!n#e#j#o$U$k%S%bS!TW!RQ!ykR!zlQ!VWR#`!RrROe!a!b!c!f!u#s#{#|#}$[$d$r%U%`%k!WwRSYhjsvxyz{|}!O!P!S!T!]!`!n#e#j#o$U$k%S%bS!SW!RT!ng%_etRSv!S!T!n#e$k%S%br`Oe!a!b!c!f!u#s#{#|#}$[$d$r%U%`%kdrRSv!S!T!n#e$k%S%bQ!WWQ#OsR#a!RR!mfX!kf!i!l#x#SZORSWYeghjsvxyz{|}!O!P!R!S!T!]!`!a!b!c!f!n!u#e#j#o#s#{#|#}$U$[$d$k$r%S%U%_%`%b%kR#y!jTnQpQ$b#tQ$i$OQ$w$cR%Z$xQ#t!cQ$O!uQ$e#|Q$f#}Q%V$rQ%c%UQ%i%`R%l%kQ$a#tQ$h$OQ$t$bQ$v$cQ%Q$iS%Y$w$xR%e%ZdtRSv!S!T!n#e$k%S%bQ!^YQ#h!]X#k!^#h#l$SvTOWe!R!a!b!c!f!u#s#{#|#}$[$d$r%U%`%kT!qg%_T$y$e$zQ$}$eR%^$zwTOWe!R!a!b!c!f!u#s#{#|#}$[$d$r%U%`%krVOe!a!b!c!f!u#s#{#|#}$[$d$r%U%`%kQ!UWQ!wjQ#QyQ#TzQ#V{R#_!R#TZORSWYeghjsvxyz{|}!O!P!R!S!T!]!`!a!b!c!f!n!u#e#j#o#s#{#|#}$U$[$d$k$r%S%U%_%`%b%k![ZRSYghjsvxyz{|}!O!P!S!T!]!`!n#e#j#o$U$k%S%_%bw[OWe!R!a!b!c!f!u#s#{#|#}$[$d$r%U%`%kQeOR!ge^!db![#p#q#r$Z$cR#u!dQ!RWQ!]Y`#^!R!]#e#f$P$k%S%bS#e!S!TS#f!U!ZS$P#_#dQ$k$RR%S$lQ!ifR#w!iQ!lfQ#x!iT#z!l#xQpQR!|pS$[#s$dR$p$[Q$l$RR%T$lYvRS!S!T!nR#PvQ$z$eR%[$zQ#l!^Q$S#hT$W#l$SQ#o!`Q$U#jT$X#o$UTdOeSbOeS![W!RQ#p!aQ#q!b`#r!c!u#|#}$r%U%`%kQ#v!fU$Z#s$[$dR$c#{vUOWe!R!a!b!c!f!u#s#{#|#}$[$d$r%U%`%kdrRSv!S!T!n#e$k%S%bQ!`YS!pg%_Q!shQ!vjQ#OsQ#QxQ#RyQ#SzQ#U{Q#W|Q#X}Q#Z!OQ#[!PQ#j!]X#n!`#j#o$Ur]Oe!a!b!c!f!u#s#{#|#}$[$d$r%U%`%k![wRSYghjsvxyz{|}!O!P!S!T!]!`!n#e#j#o$U$k%S%_%bQ!YWR#c!R[uRSv!S!T!nQ$R#eV%R$k%S%bToQpQ$]#sR$x$dQ!rgR%h%_raOe!a!b!c!f!u#s#{#|#}$[$d$r%U%`%kQ!XWR#b!R", + 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 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: 119, context: trackScope, nodeProps: [ - ["closedBy", 53,"end"] + ["closedBy", 55,"end"] ], propSources: [highlighting], - skippedNodes: [0,31], + skippedNodes: [0,33], repeatNodeCount: 12, - tokenData: "IS~R}OX$OXY$mYZ%WZp$Opq$mqs$Ost%qtu'Yuw$Owx'_xy'dyz'}z{$O{|(h|}$O}!O(h!O!P$O!P!Q0o!Q!R)Y!R![+w![!]9[!]!^%W!^!}$O!}#O9u#O#P;k#P#Q;p#Q#R$O#R#S`#Z#be_!QSOt$Ouw$Ox}$O}!O (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 26, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], - tokenPrec: 2109 + tokenData: "IS~R}OX$OXY$mYZ%WZp$Opq$mqs$Ost%qtu'Yuw$Owx'_xy'dyz'}z{$O{|(h|}$O}!O(h!O!P$O!P!Q0o!Q!R)Y!R![+w![!]9[!]!^%W!^!}$O!}#O9u#O#P;k#P#Q;p#Q#R$O#R#S`#Z#be_!SSOt$Ouw$Ox}$O}!O (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 28, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], + tokenPrec: 2202 }) diff --git a/src/parser/tests/basics.test.ts b/src/parser/tests/basics.test.ts index c941140..28731f9 100644 --- a/src/parser/tests/basics.test.ts +++ b/src/parser/tests/basics.test.ts @@ -707,6 +707,87 @@ describe('CompoundAssign', () => { PositionalArg Number 3`) }) + + test('parses ??= operator', () => { + expect('x ??= 5').toMatchTree(` + CompoundAssign + AssignableIdentifier x + NullishEq ??= + Number 5`) + }) + + test('parses ??= with expression', () => { + expect('config ??= get-default').toMatchTree(` + CompoundAssign + AssignableIdentifier config + NullishEq ??= + FunctionCallOrIdentifier + Identifier get-default`) + }) +}) + +describe('Nullish coalescing operator', () => { + test('? can still end an identifier', () => { + expect('what?').toMatchTree(` + FunctionCallOrIdentifier + Identifier what?`) + }) + + test('?? can still end an identifier', () => { + expect('what??').toMatchTree(` + FunctionCallOrIdentifier + Identifier what??`) + }) + + test('?? can still be in a word', () => { + expect('what??the').toMatchTree(` + FunctionCallOrIdentifier + Identifier what??the`) + }) + + test('?? can still start a word', () => { + expect('??what??the').toMatchTree(` + Word ??what??the`) + }) + + test('parses ?? operator', () => { + expect('x ?? 5').toMatchTree(` + ConditionalOp + Identifier x + NullishCoalesce ?? + Number 5`) + }) + + test('parses chained ?? operators', () => { + expect('a ?? b ?? c').toMatchTree(` + ConditionalOp + ConditionalOp + Identifier a + NullishCoalesce ?? + Identifier b + NullishCoalesce ?? + Identifier c`) + }) + + test('parses ?? with expressions', () => { + expect('get-value ?? default-value').toMatchTree(` + ConditionalOp + Identifier get-value + NullishCoalesce ?? + Identifier default-value`) + }) + + test('parses ?? with parenthesized function call', () => { + expect('get-value ?? (default 10)').toMatchTree(` + ConditionalOp + Identifier get-value + NullishCoalesce ?? + ParenExpr + FunctionCall + Identifier default + PositionalArg + Number 10`) + }) }) describe('DotGet whitespace sensitivity', () => { diff --git a/src/parser/tokenizer.ts b/src/parser/tokenizer.ts index 3f5ca6c..d18a515 100644 --- a/src/parser/tokenizer.ts +++ b/src/parser/tokenizer.ts @@ -193,6 +193,15 @@ const chooseIdentifierToken = (input: InputStream, stack: Stack): number => { const nextCh = getFullCodePoint(input, peekPos) const nextCh2 = getFullCodePoint(input, peekPos + 1) + const nextCh3 = getFullCodePoint(input, peekPos + 2) + + // Check for ??= (three-character compound operator) + if (nextCh === 63 /* ? */ && nextCh2 === 63 /* ? */ && nextCh3 === 61 /* = */) { + const charAfterOp = getFullCodePoint(input, peekPos + 3) + if (isWhiteSpace(charAfterOp) || charAfterOp === -1 /* EOF */) { + return AssignableIdentifier + } + } // Check for compound assignment operators: +=, -=, *=, /=, %= if ( From 503ca411551ab9fc941b70a4fadd56d2ddab885d Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Thu, 6 Nov 2025 10:40:09 -0800 Subject: [PATCH 24/48] { curly strings } --- src/compiler/tests/literals.test.ts | 26 +++++++++++++++++++++++ src/parser/shrimp.grammar | 6 ++++-- src/parser/shrimp.terms.ts | 1 + src/parser/shrimp.ts | 14 ++++++------ src/parser/tests/strings.test.ts | 28 ++++++++++++++++++++++++ src/parser/tokenizer.ts | 33 ++++++++++++++++++++++++++++- 6 files changed, 98 insertions(+), 10 deletions(-) diff --git a/src/compiler/tests/literals.test.ts b/src/compiler/tests/literals.test.ts index 45f9fba..705b825 100644 --- a/src/compiler/tests/literals.test.ts +++ b/src/compiler/tests/literals.test.ts @@ -193,3 +193,29 @@ describe('dict literals', () => { c=3]`).toEvaluateTo({ a: 1, b: 2, c: 3 }) }) }) + +describe('curly strings', () => { + test('work on one line', () => { + expect('{ one two three }').toEvaluateTo(" one two three ") + }) + + test('work on multiple lines', () => { + expect(`{ + one + two + three + }`).toEvaluateTo("\n one\n two\n three\n ") + }) + + test('can contain other curlies', () => { + expect(`{ + { one } + two + { three } + }`).toEvaluateTo("\n { one }\n two\n { three }\n ") + }) + + test("don't interpolate", () => { + expect(`{ sum is $(a + b)! }`).toEvaluateTo(` sum is $(a + b)! `) + }) +}) \ No newline at end of file diff --git a/src/parser/shrimp.grammar b/src/parser/shrimp.grammar index f4ac3e7..e658096 100644 --- a/src/parser/shrimp.grammar +++ b/src/parser/shrimp.grammar @@ -41,7 +41,7 @@ finally { @specialize[@name=keyword] } throw { @specialize[@name=keyword] } null { @specialize[@name=Null] } -@external tokens tokenizer from "./tokenizer" { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot } +@external tokens tokenizer from "./tokenizer" { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot, curlyString } @external specialize {Identifier} specializeKeyword from "./tokenizer" { Do } @precedence { @@ -233,7 +233,9 @@ expression { IdentifierBeforeDot dot (Number | Identifier | ParenExpr) } - String { "'" stringContent* "'" } + String { + "'" stringContent* "'" | curlyString + } } stringContent { diff --git a/src/parser/shrimp.terms.ts b/src/parser/shrimp.terms.ts index 3da47bb..beab2bb 100644 --- a/src/parser/shrimp.terms.ts +++ b/src/parser/shrimp.terms.ts @@ -31,6 +31,7 @@ export const AssignableIdentifier = 29, Word = 30, IdentifierBeforeDot = 31, + curlyString = 95, Do = 32, Comment = 33, Program = 34, diff --git a/src/parser/shrimp.ts b/src/parser/shrimp.ts index 901d667..47b13e6 100644 --- a/src/parser/shrimp.ts +++ b/src/parser/shrimp.ts @@ -7,11 +7,11 @@ import {highlighting} from "./highlight" const spec_Identifier = {__proto__:null,while:76, null:108, catch:114, finally:120, end:122, if:130, else:136, try:154, throw:158} export const parser = LRParser.deserialize({ version: 14, - states: "vQcO,5:hO?dQcO,5:hOOQa1G/^1G/^OOOO,59{,59{OOOO,59|,59|OOOO-E8T-E8TOOQa1G/e1G/eOOQ`,5:X,5:XOOQ`-E8W-E8WOOQa1G/{1G/{OA`QcO1G/{OAjQcO1G/{OBxQcO1G/{OCSQcO1G/{OCaQcO1G/{OOQa1G/Z1G/ZODrQcO1G/ZODyQcO1G/ZOEQQcO1G/ZOFPQcO1G/ZOEXQcO1G/ZOOQ`-E8Q-E8QOFgQRO1G/[OFqQQO1G/[OFvQQO1G/[OGOQQO1G/[OGZQRO1G/[OGbQRO1G/[OGiQbO,59qOGsQQO1G/[OOQa1G/[1G/[OG{QQO1G/}OOQa1G0O1G0OOHWQbO1G0OOOQO'#E['#E[OG{QQO1G/}OOQa1G/}1G/}OOQ`'#E]'#E]OHWQbO1G0OOHbQbO1G0VOH|QbO1G0UOIhQbO'#DhOIyQbO'#DhOJ^QbO1G0POOQ`-E8P-E8POOQ`,5:m,5:mOOQ`-E8R-E8ROJiQQO,59vOOQO,59w,59wOOQO-E8S-E8SOJqQbO1G/aO9jQbO1G/tO9jQbO1G/XOJxQbO1G0QOKTQQO7+$vOOQa7+$v7+$vOK]QQO1G/]OKeQQO7+%iOOQa7+%i7+%iOKpQbO7+%jOOQa7+%j7+%jOOQO-E8Y-E8YOOQ`-E8Z-E8ZOOQ`'#EW'#EWOKzQQO'#EWOLSQbO'#EpOOQ`,5:S,5:SOLgQbO'#DfOLlQQO'#DiOOQ`7+%k7+%kOLqQbO7+%kOLvQbO7+%kOMOQbO7+${OM^QbO7+${OMnQbO7+%`OMvQbO7+$sOOQ`7+%l7+%lOM{QbO7+%lONQQbO7+%lOOQa<qAN>qOOQ`AN>RAN>RO!![QbOAN>RO!!aQbOAN>ROOQ`-E8X-E8XOOQ`AN>fAN>fO!!iQbOAN>fO2TQbO,5:]O9jQbO,5:_OOQ`AN>rAN>rPGiQbO'#ESOOQ`7+%W7+%WOOQ`G23mG23mO!!nQbOG23mP! nQbO'#DqOOQ`G24QG24QO!!sQQO1G/wOOQ`1G/y1G/yOOQ`LD)XLD)XO9jQbO7+%cOOQ`<UOT}OU!OOj!POt!pa#Y!pa#k!pa!Z!pa!^!pa!_!pa#g!pa!f!pa~O^xOR!iiS!iid!iie!iif!iig!iih!iii!iit!ii#Y!ii#k!ii#g!ii!Z!ii!^!ii!_!ii!f!ii~OP!iiQ!ii~P@XOPyOQyO~P@XOPyOQyOd!iie!iif!iig!iih!iii!iit!ii#Y!ii#k!ii#g!ii!Z!ii!^!ii!_!ii!f!ii~OR!iiS!ii~PAtORzOSzO^xO~PAtORzOSzO~PAtOW|OX|OY|OZ|O[|O]|OTwijwitwi#Ywi#kwi#gwi!Xwi!Zwi!^wi!_wi!fwi~OU!OO~PCkOU!OO~PC}OUwi~PCkOT}OU!OOjwitwi#Ywi#kwi#gwi!Xwi!Zwi!^wi!_wi!fwi~OW|OX|OY|OZ|O[|O]|O~PEXO#Y!QO#g$QO~P*RO#g$QO~O#g$QOt#UX~O!X!cO#g$QOt#UX~O#g$QO~P.WO#g$QO~P7WOpfO!`rO~P,kO#Y!QO#g$QO~O!QsO#Y#kO#j$TO~O#Y#nO#j$VO~P2xOt!fO#Y!si#k!si!Z!si!^!si!_!si#g!si!f!si~Ot!fO#Y!ri#k!ri!Z!ri!^!ri!_!ri#g!ri!f!ri~Ot!fO!Z![X!^![X!_![X!f![X~O#Y$YO!Z#dP!^#dP!_#dP!f#dP~P8cO!Z$^O!^$_O!_$`O~O!Q!jO!X!Oa~O#Y$dO~P8cO!Z$^O!^$_O!_$gO~O#Y!QO#g$jO~O#Y!QO#gyi~O!QsO#Y#kO#j$mO~O#Y#nO#j$nO~P2xOt!fO#Y$oO~O#Y$YO!Z#dX!^#dX!_#dX!f#dX~P8cOl$qO~O!X$rO~O!_$sO~O!^$_O!_$sO~Ot!fO!Z$^O!^$_O!_$uO~O#Y$YO!Z#dP!^#dP!_#dP~P8cO!_$|O!f${O~O!_%OO~O!_%PO~O!^$_O!_%PO~OpfO!`rO#gyq~P,kO#Y!QO#gyq~O!X%UO~O!_%WO~O!_%XO~O!^$_O!_%XO~O!Z$^O!^$_O!_%XO~O!_%]O!f${O~O!X%`O!c%_O~O!_%]O~O!_%aO~OpfO!`rO#gyy~P,kO!_%dO~O!^$_O!_%dO~O!_%gO~O!_%jO~O!X%kO~O{!j~", - goto: "8f#gPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP#hP$RP$h%f&t&zP(U(b)[)_P)eP*l*lPPP*pP*|+fPPP+|#hP,f-PP-T-Z-pP.g/k$R$RP$RP$R$R0q0w1T1w1}2X2_2f2l2v2|3WPPP3b3f4Z6PPPP7ZP7kPPPPP7o7u7{r`Oe!a!b!c!f!u#s#{#|#}$[$d$r%U%`%kQ!WWR#a!Rw`OWe!R!a!b!c!f!u#s#{#|#}$[$d$r%U%`%kr^Oe!a!b!c!f!u#s#{#|#}$[$d$r%U%`%kQ!ZWS!og%_Q!thQ!xjQ#W!OQ#Y}Q#]!PR#d!RvSOeg!a!b!c!f!u#s#{#|#}$[$d$r%U%_%`%k!WZRSYhjsvxyz{|}!O!P!S!T!]!`!n#e#j#o$U$k%S%bS!TW!RQ!ykR!zlQ!VWR#`!RrROe!a!b!c!f!u#s#{#|#}$[$d$r%U%`%k!WwRSYhjsvxyz{|}!O!P!S!T!]!`!n#e#j#o$U$k%S%bS!SW!RT!ng%_etRSv!S!T!n#e$k%S%br`Oe!a!b!c!f!u#s#{#|#}$[$d$r%U%`%kdrRSv!S!T!n#e$k%S%bQ!WWQ#OsR#a!RR!mfX!kf!i!l#x#SZORSWYeghjsvxyz{|}!O!P!R!S!T!]!`!a!b!c!f!n!u#e#j#o#s#{#|#}$U$[$d$k$r%S%U%_%`%b%kR#y!jTnQpQ$b#tQ$i$OQ$w$cR%Z$xQ#t!cQ$O!uQ$e#|Q$f#}Q%V$rQ%c%UQ%i%`R%l%kQ$a#tQ$h$OQ$t$bQ$v$cQ%Q$iS%Y$w$xR%e%ZdtRSv!S!T!n#e$k%S%bQ!^YQ#h!]X#k!^#h#l$SvTOWe!R!a!b!c!f!u#s#{#|#}$[$d$r%U%`%kT!qg%_T$y$e$zQ$}$eR%^$zwTOWe!R!a!b!c!f!u#s#{#|#}$[$d$r%U%`%krVOe!a!b!c!f!u#s#{#|#}$[$d$r%U%`%kQ!UWQ!wjQ#QyQ#TzQ#V{R#_!R#TZORSWYeghjsvxyz{|}!O!P!R!S!T!]!`!a!b!c!f!n!u#e#j#o#s#{#|#}$U$[$d$k$r%S%U%_%`%b%k![ZRSYghjsvxyz{|}!O!P!S!T!]!`!n#e#j#o$U$k%S%_%bw[OWe!R!a!b!c!f!u#s#{#|#}$[$d$r%U%`%kQeOR!ge^!db![#p#q#r$Z$cR#u!dQ!RWQ!]Y`#^!R!]#e#f$P$k%S%bS#e!S!TS#f!U!ZS$P#_#dQ$k$RR%S$lQ!ifR#w!iQ!lfQ#x!iT#z!l#xQpQR!|pS$[#s$dR$p$[Q$l$RR%T$lYvRS!S!T!nR#PvQ$z$eR%[$zQ#l!^Q$S#hT$W#l$SQ#o!`Q$U#jT$X#o$UTdOeSbOeS![W!RQ#p!aQ#q!b`#r!c!u#|#}$r%U%`%kQ#v!fU$Z#s$[$dR$c#{vUOWe!R!a!b!c!f!u#s#{#|#}$[$d$r%U%`%kdrRSv!S!T!n#e$k%S%bQ!`YS!pg%_Q!shQ!vjQ#OsQ#QxQ#RyQ#SzQ#U{Q#W|Q#X}Q#Z!OQ#[!PQ#j!]X#n!`#j#o$Ur]Oe!a!b!c!f!u#s#{#|#}$[$d$r%U%`%k![wRSYghjsvxyz{|}!O!P!S!T!]!`!n#e#j#o$U$k%S%_%bQ!YWR#c!R[uRSv!S!T!nQ$R#eV%R$k%S%bToQpQ$]#sR$x$dQ!rgR%h%_raOe!a!b!c!f!u#s#{#|#}$[$d$r%U%`%kQ!XWR#b!R", + states: "TQcO,5:hO?cQcO,5:hO@PQcO,5:hOOQa1G/^1G/^OOOO,59{,59{OOOO,59|,59|OOOO-E8T-E8TOOQa1G/e1G/eOOQ`,5:X,5:XOOQ`-E8W-E8WOOQa1G/{1G/{OA{QcO1G/{OBVQcO1G/{OCeQcO1G/{OCoQcO1G/{OC|QcO1G/{OOQa1G/Z1G/ZOE_QcO1G/ZOEfQcO1G/ZOEmQcO1G/ZOFlQcO1G/ZOEtQcO1G/ZOOQ`-E8Q-E8QOGSQRO1G/[OG^QQO1G/[OGcQQO1G/[OGkQQO1G/[OGvQRO1G/[OG}QRO1G/[OHUQbO,59qOH`QQO1G/[OOQa1G/[1G/[OHhQQO1G/}OOQa1G0O1G0OOHsQbO1G0OOOQO'#E['#E[OHhQQO1G/}OOQa1G/}1G/}OOQ`'#E]'#E]OHsQbO1G0OOH}QbO1G0VOIiQbO1G0UOJTQbO'#DhOJfQbO'#DhOJyQbO1G0POOQ`-E8P-E8POOQ`,5:m,5:mOOQ`-E8R-E8ROKUQQO,59vOOQO,59w,59wOOQO-E8S-E8SOK^QbO1G/aO:SQbO1G/tO:SQbO1G/XOKeQbO1G0QOKpQQO7+$vOOQa7+$v7+$vOKxQQO1G/]OLQQQO7+%iOOQa7+%i7+%iOL]QbO7+%jOOQa7+%j7+%jOOQO-E8Y-E8YOOQ`-E8Z-E8ZOOQ`'#EW'#EWOLgQQO'#EWOLoQbO'#EqOOQ`,5:S,5:SOMSQbO'#DfOMXQQO'#DiOOQ`7+%k7+%kOM^QbO7+%kOMcQbO7+%kOMkQbO7+${OMyQbO7+${ONZQbO7+%`ONcQbO7+$sOOQ`7+%l7+%lONhQbO7+%lONmQbO7+%lOOQa<qAN>qOOQ`AN>RAN>RO!!wQbOAN>RO!!|QbOAN>ROOQ`-E8X-E8XOOQ`AN>fAN>fO!#UQbOAN>fO2dQbO,5:]O:SQbO,5:_OOQ`AN>rAN>rPHUQbO'#ESOOQ`7+%W7+%WOOQ`G23mG23mO!#ZQbOG23mP!!ZQbO'#DqOOQ`G24QG24QO!#`QQO1G/wOOQ`1G/y1G/yOOQ`LD)XLD)XO:SQbO7+%cOOQ`<qOT!OOU!POj!QOt!pa#Z!pa#l!pa!Z!pa!^!pa!_!pa#h!pa!f!pa~O^yOR!iiS!iid!iie!iif!iig!iih!iii!iit!ii#Z!ii#l!ii#h!ii!Z!ii!^!ii!_!ii!f!ii~OP!iiQ!ii~P@tOPzOQzO~P@tOPzOQzOd!iie!iif!iig!iih!iii!iit!ii#Z!ii#l!ii#h!ii!Z!ii!^!ii!_!ii!f!ii~OR!iiS!ii~PBaOR{OS{O^yO~PBaOR{OS{O~PBaOW}OX}OY}OZ}O[}O]}OTwijwitwi#Zwi#lwi#hwi!Xwi!Zwi!^wi!_wi!fwi~OU!PO~PDWOU!PO~PDjOUwi~PDWOT!OOU!POjwitwi#Zwi#lwi#hwi!Xwi!Zwi!^wi!_wi!fwi~OW}OX}OY}OZ}O[}O]}O~PEtO#Z!RO#h$RO~P*[O#h$RO~O#h$ROt#VX~O!X!dO#h$ROt#VX~O#h$RO~P.gO#h$RO~P7mOpgO!`sO~P,wO#Z!RO#h$RO~O!QtO#Z#lO#k$UO~O#Z#oO#k$WO~P3[Ot!gO#Z!si#l!si!Z!si!^!si!_!si#h!si!f!si~Ot!gO#Z!ri#l!ri!Z!ri!^!ri!_!ri#h!ri!f!ri~Ot!gO!Z![X!^![X!_![X!f![X~O#Z$ZO!Z#eP!^#eP!_#eP!f#eP~P8xO!Z$_O!^$`O!_$aO~O!Q!kO!X!Oa~O#Z$eO~P8xO!Z$_O!^$`O!_$hO~O#Z!RO#h$kO~O#Z!RO#hyi~O!QtO#Z#lO#k$nO~O#Z#oO#k$oO~P3[Ot!gO#Z$pO~O#Z$ZO!Z#eX!^#eX!_#eX!f#eX~P8xOl$rO~O!X$sO~O!_$tO~O!^$`O!_$tO~Ot!gO!Z$_O!^$`O!_$vO~O#Z$ZO!Z#eP!^#eP!_#eP~P8xO!_$}O!f$|O~O!_%PO~O!_%QO~O!^$`O!_%QO~OpgO!`sO#hyq~P,wO#Z!RO#hyq~O!X%VO~O!_%XO~O!_%YO~O!^$`O!_%YO~O!Z$_O!^$`O!_%YO~O!_%^O!f$|O~O!X%aO!c%`O~O!_%^O~O!_%bO~OpgO!`sO#hyy~P,wO!_%eO~O!^$`O!_%eO~O!_%hO~O!_%kO~O!X%lO~O{!j~", + goto: "8g#hPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP#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}3XPPPP3c3g4[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 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: 119, + maxTerm: 120, context: trackScope, nodeProps: [ ["closedBy", 55,"end"] @@ -19,9 +19,9 @@ export const parser = LRParser.deserialize({ propSources: [highlighting], skippedNodes: [0,33], repeatNodeCount: 12, - tokenData: "IS~R}OX$OXY$mYZ%WZp$Opq$mqs$Ost%qtu'Yuw$Owx'_xy'dyz'}z{$O{|(h|}$O}!O(h!O!P$O!P!Q0o!Q!R)Y!R![+w![!]9[!]!^%W!^!}$O!}#O9u#O#P;k#P#Q;p#Q#R$O#R#S`#Z#be_!SSOt$Ouw$Ox}$O}!O`#Z#be_!SSOt$Ouw$Ox}$O}!O (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 28, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], - tokenPrec: 2202 + tokenPrec: 2229 }) diff --git a/src/parser/tests/strings.test.ts b/src/parser/tests/strings.test.ts index 3f78f56..288315b 100644 --- a/src/parser/tests/strings.test.ts +++ b/src/parser/tests/strings.test.ts @@ -127,3 +127,31 @@ describe('string escape sequences', () => { `) }) }) + +describe('curly strings', () => { + test('work on one line', () => { + expect('{ one two three }').toMatchTree(` + String one two three + `) + }) + + test('work on multiple lines', () => { + expect(`{ + one + two + three }`).toMatchTree(` + String + one + two + three `) + }) + + test('can contain other curlies', () => { + expect(`{ { one } + two + { three } }`).toMatchTree(` + String { one } + two + { three } `) + }) +}) \ No newline at end of file diff --git a/src/parser/tokenizer.ts b/src/parser/tokenizer.ts index d18a515..0189033 100644 --- a/src/parser/tokenizer.ts +++ b/src/parser/tokenizer.ts @@ -1,5 +1,5 @@ import { ExternalTokenizer, InputStream, Stack } from '@lezer/lr' -import { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot, Do } from './shrimp.terms' +import { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot, Do, curlyString } from './shrimp.terms' // doobie doobie do (we need the `do` keyword to know when we're defining params) export function specializeKeyword(ident: string) { @@ -18,6 +18,12 @@ export const setGlobals = (newGlobals: string[] | Record) => { export const tokenizer = new ExternalTokenizer( (input: InputStream, stack: Stack) => { const ch = getFullCodePoint(input, 0) + + // Handle curly strings + if (ch === 123) { // { + return consumeCurlyString(input, stack) + } + if (!isWordChar(ch)) return // Don't consume things that start with digits - let Number token handle it @@ -157,6 +163,31 @@ const consumeRestOfWord = (input: InputStream, startPos: number, canBeWord: bool return pos } +const consumeCurlyString = (input: InputStream, stack: Stack) => { + if (!stack.canShift(curlyString)) return + + let depth = 0 + let pos = 0 + + while (true) { + const ch = input.peek(pos) + if (ch < 0) return // EOF - invalid + + if (ch === 123) depth++ // { + else if (ch === 125) { // } + depth-- + if (depth === 0) { + pos++ // consume final } + break + } + } + + pos++ + } + + input.acceptToken(curlyString, pos) +} + // Check if this identifier is in scope (for property access detection) // Returns IdentifierBeforeDot token if in scope, null otherwise const checkForDotGet = (input: InputStream, stack: Stack, pos: number): number | null => { From 63ee57e7f0169bcb844b1df6e59b70a390dface4 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Thu, 6 Nov 2025 13:32:31 -0800 Subject: [PATCH 25/48] curly -> Curly --- src/parser/shrimp.grammar | 4 +- src/parser/shrimp.terms.ts | 86 ++++++++++++++++---------------- src/parser/shrimp.ts | 18 +++---- src/parser/tests/strings.test.ts | 15 +++--- src/parser/tokenizer.ts | 6 +-- 5 files changed, 66 insertions(+), 63 deletions(-) diff --git a/src/parser/shrimp.grammar b/src/parser/shrimp.grammar index e658096..01d82ab 100644 --- a/src/parser/shrimp.grammar +++ b/src/parser/shrimp.grammar @@ -41,7 +41,7 @@ finally { @specialize[@name=keyword] } throw { @specialize[@name=keyword] } null { @specialize[@name=Null] } -@external tokens tokenizer from "./tokenizer" { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot, curlyString } +@external tokens tokenizer from "./tokenizer" { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot, CurlyString } @external specialize {Identifier} specializeKeyword from "./tokenizer" { Do } @precedence { @@ -234,7 +234,7 @@ expression { } String { - "'" stringContent* "'" | curlyString + "'" stringContent* "'" | CurlyString } } diff --git a/src/parser/shrimp.terms.ts b/src/parser/shrimp.terms.ts index beab2bb..fc03a61 100644 --- a/src/parser/shrimp.terms.ts +++ b/src/parser/shrimp.terms.ts @@ -31,46 +31,46 @@ export const AssignableIdentifier = 29, Word = 30, IdentifierBeforeDot = 31, - curlyString = 95, - Do = 32, - Comment = 33, - Program = 34, - PipeExpr = 35, - WhileExpr = 37, - keyword = 79, - ConditionalOp = 39, - ParenExpr = 40, - FunctionCallWithNewlines = 41, - DotGet = 42, - Number = 43, - PositionalArg = 44, - FunctionDef = 45, - Params = 46, - NamedParam = 47, - NamedArgPrefix = 48, - String = 49, - StringFragment = 50, - Interpolation = 51, - EscapeSeq = 52, - Boolean = 53, - Null = 54, - colon = 55, - CatchExpr = 56, - Block = 58, - FinallyExpr = 59, - Underscore = 62, - NamedArg = 63, - IfExpr = 64, - FunctionCall = 66, - ElseIfExpr = 67, - ElseExpr = 69, - FunctionCallOrIdentifier = 70, - BinOp = 71, - Regex = 72, - Dict = 73, - Array = 74, - FunctionCallWithBlock = 75, - TryExpr = 76, - Throw = 78, - CompoundAssign = 80, - Assign = 81 + CurlyString = 32, + Do = 33, + Comment = 34, + Program = 35, + PipeExpr = 36, + WhileExpr = 38, + keyword = 80, + ConditionalOp = 40, + ParenExpr = 41, + FunctionCallWithNewlines = 42, + DotGet = 43, + Number = 44, + PositionalArg = 45, + FunctionDef = 46, + Params = 47, + NamedParam = 48, + NamedArgPrefix = 49, + String = 50, + 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 diff --git a/src/parser/shrimp.ts b/src/parser/shrimp.ts index 47b13e6..d7a0ea2 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:76, null:108, catch:114, finally:120, end:122, if:130, else:136, try:154, throw:158} +const spec_Identifier = {__proto__:null,while:78, null:110, catch:116, finally:122, end:124, if:132, else:138, try:156, throw:160} export const parser = LRParser.deserialize({ version: 14, - states: "TQcO,5:hO?cQcO,5:hO@PQcO,5:hOOQa1G/^1G/^OOOO,59{,59{OOOO,59|,59|OOOO-E8T-E8TOOQa1G/e1G/eOOQ`,5:X,5:XOOQ`-E8W-E8WOOQa1G/{1G/{OA{QcO1G/{OBVQcO1G/{OCeQcO1G/{OCoQcO1G/{OC|QcO1G/{OOQa1G/Z1G/ZOE_QcO1G/ZOEfQcO1G/ZOEmQcO1G/ZOFlQcO1G/ZOEtQcO1G/ZOOQ`-E8Q-E8QOGSQRO1G/[OG^QQO1G/[OGcQQO1G/[OGkQQO1G/[OGvQRO1G/[OG}QRO1G/[OHUQbO,59qOH`QQO1G/[OOQa1G/[1G/[OHhQQO1G/}OOQa1G0O1G0OOHsQbO1G0OOOQO'#E['#E[OHhQQO1G/}OOQa1G/}1G/}OOQ`'#E]'#E]OHsQbO1G0OOH}QbO1G0VOIiQbO1G0UOJTQbO'#DhOJfQbO'#DhOJyQbO1G0POOQ`-E8P-E8POOQ`,5:m,5:mOOQ`-E8R-E8ROKUQQO,59vOOQO,59w,59wOOQO-E8S-E8SOK^QbO1G/aO:SQbO1G/tO:SQbO1G/XOKeQbO1G0QOKpQQO7+$vOOQa7+$v7+$vOKxQQO1G/]OLQQQO7+%iOOQa7+%i7+%iOL]QbO7+%jOOQa7+%j7+%jOOQO-E8Y-E8YOOQ`-E8Z-E8ZOOQ`'#EW'#EWOLgQQO'#EWOLoQbO'#EqOOQ`,5:S,5:SOMSQbO'#DfOMXQQO'#DiOOQ`7+%k7+%kOM^QbO7+%kOMcQbO7+%kOMkQbO7+${OMyQbO7+${ONZQbO7+%`ONcQbO7+$sOOQ`7+%l7+%lONhQbO7+%lONmQbO7+%lOOQa<qAN>qOOQ`AN>RAN>RO!!wQbOAN>RO!!|QbOAN>ROOQ`-E8X-E8XOOQ`AN>fAN>fO!#UQbOAN>fO2dQbO,5:]O:SQbO,5:_OOQ`AN>rAN>rPHUQbO'#ESOOQ`7+%W7+%WOOQ`G23mG23mO!#ZQbOG23mP!!ZQbO'#DqOOQ`G24QG24QO!#`QQO1G/wOOQ`1G/y1G/yOOQ`LD)XLD)XO:SQbO7+%cOOQ`<qOT!OOU!POj!QOt!pa#Z!pa#l!pa!Z!pa!^!pa!_!pa#h!pa!f!pa~O^yOR!iiS!iid!iie!iif!iig!iih!iii!iit!ii#Z!ii#l!ii#h!ii!Z!ii!^!ii!_!ii!f!ii~OP!iiQ!ii~P@tOPzOQzO~P@tOPzOQzOd!iie!iif!iig!iih!iii!iit!ii#Z!ii#l!ii#h!ii!Z!ii!^!ii!_!ii!f!ii~OR!iiS!ii~PBaOR{OS{O^yO~PBaOR{OS{O~PBaOW}OX}OY}OZ}O[}O]}OTwijwitwi#Zwi#lwi#hwi!Xwi!Zwi!^wi!_wi!fwi~OU!PO~PDWOU!PO~PDjOUwi~PDWOT!OOU!POjwitwi#Zwi#lwi#hwi!Xwi!Zwi!^wi!_wi!fwi~OW}OX}OY}OZ}O[}O]}O~PEtO#Z!RO#h$RO~P*[O#h$RO~O#h$ROt#VX~O!X!dO#h$ROt#VX~O#h$RO~P.gO#h$RO~P7mOpgO!`sO~P,wO#Z!RO#h$RO~O!QtO#Z#lO#k$UO~O#Z#oO#k$WO~P3[Ot!gO#Z!si#l!si!Z!si!^!si!_!si#h!si!f!si~Ot!gO#Z!ri#l!ri!Z!ri!^!ri!_!ri#h!ri!f!ri~Ot!gO!Z![X!^![X!_![X!f![X~O#Z$ZO!Z#eP!^#eP!_#eP!f#eP~P8xO!Z$_O!^$`O!_$aO~O!Q!kO!X!Oa~O#Z$eO~P8xO!Z$_O!^$`O!_$hO~O#Z!RO#h$kO~O#Z!RO#hyi~O!QtO#Z#lO#k$nO~O#Z#oO#k$oO~P3[Ot!gO#Z$pO~O#Z$ZO!Z#eX!^#eX!_#eX!f#eX~P8xOl$rO~O!X$sO~O!_$tO~O!^$`O!_$tO~Ot!gO!Z$_O!^$`O!_$vO~O#Z$ZO!Z#eP!^#eP!_#eP~P8xO!_$}O!f$|O~O!_%PO~O!_%QO~O!^$`O!_%QO~OpgO!`sO#hyq~P,wO#Z!RO#hyq~O!X%VO~O!_%XO~O!_%YO~O!^$`O!_%YO~O!Z$_O!^$`O!_%YO~O!_%^O!f$|O~O!X%aO!c%`O~O!_%^O~O!_%bO~OpgO!`sO#hyy~P,wO!_%eO~O!^$`O!_%eO~O!_%hO~O!_%kO~O!X%lO~O{!j~", - goto: "8g#hPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP#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}3XPPPP3c3g4[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 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", + 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, context: trackScope, nodeProps: [ - ["closedBy", 55,"end"] + ["closedBy", 56,"end"] ], propSources: [highlighting], - skippedNodes: [0,33], + skippedNodes: [0,34], repeatNodeCount: 12, - tokenData: "IS~R}OX$OXY$mYZ%WZp$Opq$mqs$Ost%qtu'Yuw$Owx'_xy'dyz'}z{$O{|(h|}$O}!O(h!O!P$O!P!Q0o!Q!R)Y!R![+w![!]9[!]!^%W!^!}$O!}#O9u#O#P;k#P#Q;p#Q#R$O#R#S`#Z#be_!SSOt$Ouw$Ox}$O}!O`#Z#be_!TSOt$Ouw$Ox}$O}!O (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 28, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], tokenPrec: 2229 }) diff --git a/src/parser/tests/strings.test.ts b/src/parser/tests/strings.test.ts index 288315b..22f780b 100644 --- a/src/parser/tests/strings.test.ts +++ b/src/parser/tests/strings.test.ts @@ -131,27 +131,30 @@ describe('string escape sequences', () => { describe('curly strings', () => { test('work on one line', () => { expect('{ one two three }').toMatchTree(` - String one two three + String + CurlyString { one two three } `) }) test('work on multiple lines', () => { - expect(`{ + expect(`{ one two three }`).toMatchTree(` - String + String + CurlyString { one two - three `) + three }`) }) test('can contain other curlies', () => { expect(`{ { one } two { three } }`).toMatchTree(` - String { one } + String + CurlyString { { one } two - { three } `) + { three } }`) }) }) \ No newline at end of file diff --git a/src/parser/tokenizer.ts b/src/parser/tokenizer.ts index 0189033..cb2d210 100644 --- a/src/parser/tokenizer.ts +++ b/src/parser/tokenizer.ts @@ -1,5 +1,5 @@ import { ExternalTokenizer, InputStream, Stack } from '@lezer/lr' -import { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot, Do, curlyString } from './shrimp.terms' +import { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot, Do, CurlyString } from './shrimp.terms' // doobie doobie do (we need the `do` keyword to know when we're defining params) export function specializeKeyword(ident: string) { @@ -164,7 +164,7 @@ const consumeRestOfWord = (input: InputStream, startPos: number, canBeWord: bool } const consumeCurlyString = (input: InputStream, stack: Stack) => { - if (!stack.canShift(curlyString)) return + if (!stack.canShift(CurlyString)) return let depth = 0 let pos = 0 @@ -185,7 +185,7 @@ const consumeCurlyString = (input: InputStream, stack: Stack) => { pos++ } - input.acceptToken(curlyString, pos) + input.acceptToken(CurlyString, pos) } // Check if this identifier is in scope (for property access detection) From a6c283759db896075488fec67c3a8dc85df4e6ef Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Thu, 6 Nov 2025 21:04:23 -0800 Subject: [PATCH 26/48] interpolation in { curly strings } --- src/compiler/compiler.ts | 26 ++++++++++++ src/compiler/tests/literals.test.ts | 17 +++++++- src/compiler/utils.ts | 7 +++- src/parser/curlyTokenizer.ts | 62 +++++++++++++++++++++++++++++ src/parser/tokenizer.ts | 23 ++++++----- 5 files changed, 120 insertions(+), 15 deletions(-) create mode 100644 src/parser/curlyTokenizer.ts diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 2cb076b..eec198c 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -2,6 +2,7 @@ import { CompilerError } from '#compiler/compilerError.ts' import { parser } from '#parser/shrimp.ts' import * as terms from '#parser/shrimp.terms' import { setGlobals } from '#parser/tokenizer' +import { tokenizeCurlyString } from '#parser/curlyTokenizer' import type { SyntaxNode, Tree } from '@lezer/common' import { assert, errorMessage } from '#utils/utils' import { toBytecode, type Bytecode, type ProgramItem, bytecodeToString } from 'reefvm' @@ -123,6 +124,9 @@ export class Compiler { return [[`PUSH`, numberValue]] case terms.String: { + if (node.firstChild?.type.id === terms.CurlyString) + return this.#compileCurlyString(value, input) + const { parts, hasInterpolation } = getStringParts(node, input) // Simple string without interpolation or escapes - extract text directly @@ -853,4 +857,26 @@ export class Compiler { return instructions } + + #compileCurlyString(value: string, input: string): ProgramItem[] { + const instructions: ProgramItem[] = [] + const nodes = tokenizeCurlyString(value) + + nodes.forEach((node) => { + if (typeof node === 'string') { + instructions.push(['PUSH', node]) + } else { + const [input, topNode] = node + let child = topNode.topNode.firstChild + while (child) { + instructions.push(...this.#compileNode(child, input)) + child = child.nextSibling + } + } + }) + + instructions.push(['STR_CONCAT', nodes.length]) + + return instructions + } } diff --git a/src/compiler/tests/literals.test.ts b/src/compiler/tests/literals.test.ts index 705b825..e2321f7 100644 --- a/src/compiler/tests/literals.test.ts +++ b/src/compiler/tests/literals.test.ts @@ -215,7 +215,20 @@ describe('curly strings', () => { }`).toEvaluateTo("\n { one }\n two\n { three }\n ") }) - test("don't interpolate", () => { - expect(`{ sum is $(a + b)! }`).toEvaluateTo(` sum is $(a + b)! `) + test('interpolates variables', () => { + expect(`name = Bob; { Hello $name! }`).toEvaluateTo(` Hello Bob! `) + }) + + test("doesn't interpolate escaped variables ", () => { + expect(`name = Bob; { Hello \\$name }`).toEvaluateTo(` Hello $name `) + expect(`a = 1; b = 2; { sum is \\$(a + b)! }`).toEvaluateTo(` sum is $(a + b)! `) + }) + + test('interpolates expressions', () => { + expect(`a = 1; b = 2; { sum is $(a + b)! }`).toEvaluateTo(` sum is 3! `) + expect(`a = 1; b = 2; { sum is { $(a + b) }! }`).toEvaluateTo(` sum is { 3 }! `) + expect(`a = 1; b = 2; { sum is $(a + (b * b))! }`).toEvaluateTo(` sum is 5! `) + expect(`{ This is $({twisted}). }`).toEvaluateTo(` This is twisted. `) + expect(`{ This is $({{twisted}}). }`).toEvaluateTo(` This is {twisted}. `) }) }) \ No newline at end of file diff --git a/src/compiler/utils.ts b/src/compiler/utils.ts index 20afa96..c424be2 100644 --- a/src/compiler/utils.ts +++ b/src/compiler/utils.ts @@ -251,7 +251,9 @@ export const getStringParts = (node: SyntaxNode, input: string) => { return ( child.type.id === terms.StringFragment || child.type.id === terms.Interpolation || - child.type.id === terms.EscapeSeq + child.type.id === terms.EscapeSeq || + child.type.id === terms.CurlyString + ) }) @@ -260,7 +262,8 @@ export const getStringParts = (node: SyntaxNode, input: string) => { if ( part.type.id !== terms.StringFragment && part.type.id !== terms.Interpolation && - part.type.id !== terms.EscapeSeq + part.type.id !== terms.EscapeSeq && + part.type.id !== terms.CurlyString ) { throw new CompilerError( `String child must be StringFragment, Interpolation, or EscapeSeq, got ${part.type.name}`, diff --git a/src/parser/curlyTokenizer.ts b/src/parser/curlyTokenizer.ts new file mode 100644 index 0000000..6a6de66 --- /dev/null +++ b/src/parser/curlyTokenizer.ts @@ -0,0 +1,62 @@ +import { parser } from '#parser/shrimp.ts' +import type { Tree } from '@lezer/common' +import { isIdentStart, isIdentChar } from './tokenizer' + +// Turns a { curly string } into separate tokens for interpolation +export const tokenizeCurlyString = (value: string): (string | [string, Tree])[] => { + let pos = 1 + let start = 1 + let char = value[pos] + const tokens: (string | [string, Tree])[] = [] + + while (pos < value.length) { + if (char === '$') { + // escaped \$ + if (value[pos - 1] === '\\' && value[pos - 2] !== '\\') { + tokens.push(value.slice(start, pos - 1)) + start = pos + char = value[++pos] + continue + } + + tokens.push(value.slice(start, pos)) + start = pos + + if (value[pos + 1] === '(') { + pos++ // slip opening '(' + + char = value[++pos] + if (!char) break + + let depth = 0 + while (char) { + if (char === '(') depth++ + if (char === ')') depth-- + if (depth < 0) break + char = value[++pos] + } + + const input = value.slice(start + 2, pos) // skip '$(' + tokens.push([input, parser.parse(input)]) + start = ++pos // skip ')' + } else { + char = value[++pos] + if (!char) break + if (!isIdentStart(char.charCodeAt(0))) break + + while (char && isIdentChar(char.charCodeAt(0))) + char = value[++pos] + + const input = value.slice(start + 1, pos) // skip '$' + tokens.push([input, parser.parse(input)]) + start = pos + } + } + + char = value[++pos] + } + + tokens.push(value.slice(start, pos - 1)) + + return tokens +} \ No newline at end of file diff --git a/src/parser/tokenizer.ts b/src/parser/tokenizer.ts index cb2d210..8ad55c2 100644 --- a/src/parser/tokenizer.ts +++ b/src/parser/tokenizer.ts @@ -20,9 +20,7 @@ export const tokenizer = new ExternalTokenizer( const ch = getFullCodePoint(input, 0) // Handle curly strings - if (ch === 123) { // { - return consumeCurlyString(input, stack) - } + if (ch === 123 /* { */) return consumeCurlyString(input, stack) if (!isWordChar(ch)) return @@ -32,7 +30,7 @@ export const tokenizer = new ExternalTokenizer( // Don't consume things that start with - or + followed by a digit (negative/positive numbers) if ((ch === 45 /* - */ || ch === 43) /* + */ && isDigit(input.peek(1))) return - const isValidStart = isLowercaseLetter(ch) || isEmojiOrUnicode(ch) + const isValidStart = isIdentStart(ch) const canBeWord = stack.canShift(Word) // Consume all word characters, tracking if it remains a valid identifier @@ -125,13 +123,7 @@ const consumeWordToken = ( } // Track identifier validity: must be lowercase, digit, dash, or emoji/unicode - if ( - !isLowercaseLetter(ch) && - !isDigit(ch) && - ch !== 45 /* - */ && - ch !== 63 /* ? */ && - !isEmojiOrUnicode(ch) - ) { + if (!isIdentChar(ch)) { if (!canBeWord) break isValidIdentifier = false } @@ -163,6 +155,7 @@ const consumeRestOfWord = (input: InputStream, startPos: number, canBeWord: bool return pos } +// Consumes { curly strings } and tracks braces so you can { have { braces { inside { braces } } } const consumeCurlyString = (input: InputStream, stack: Stack) => { if (!stack.canShift(CurlyString)) return @@ -259,6 +252,14 @@ const chooseIdentifierToken = (input: InputStream, stack: Stack): number => { } // Character classification helpers +export const isIdentStart = (ch: number): boolean => { + return isLowercaseLetter(ch) || isEmojiOrUnicode(ch) +} + +export const isIdentChar = (ch: number): boolean => { + return isLowercaseLetter(ch) || isDigit(ch) || ch === 45 /* - */ || ch === 63 /* ? */ || isEmojiOrUnicode(ch) +} + const isWhiteSpace = (ch: number): boolean => { return ch === 32 /* space */ || ch === 9 /* tab */ || ch === 13 /* \r */ } From 238af9affc4a3a69a0a60ed7376c1f086fbb0d15 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Thu, 6 Nov 2025 21:23:20 -0800 Subject: [PATCH 27/48] fix edge case --- src/compiler/tests/literals.test.ts | 8 +++++++- src/parser/curlyTokenizer.ts | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/compiler/tests/literals.test.ts b/src/compiler/tests/literals.test.ts index e2321f7..3a2905f 100644 --- a/src/compiler/tests/literals.test.ts +++ b/src/compiler/tests/literals.test.ts @@ -231,4 +231,10 @@ describe('curly strings', () => { expect(`{ This is $({twisted}). }`).toEvaluateTo(` This is twisted. `) expect(`{ This is $({{twisted}}). }`).toEvaluateTo(` This is {twisted}. `) }) -}) \ No newline at end of file + + test('interpolation edge cases', () => { + expect(`{[a=1 b=2 c={wild}]}`).toEvaluateTo(`[a=1 b=2 c={wild}]`) + expect(`a = 1;b = 2;c = 3;{$a $b $c}`).toEvaluateTo(`1 2 3`) + expect(`a = 1;b = 2;c = 3;{$a$b$c}`).toEvaluateTo(`123`) + }) +}) diff --git a/src/parser/curlyTokenizer.ts b/src/parser/curlyTokenizer.ts index 6a6de66..bc8acf0 100644 --- a/src/parser/curlyTokenizer.ts +++ b/src/parser/curlyTokenizer.ts @@ -49,7 +49,7 @@ export const tokenizeCurlyString = (value: string): (string | [string, Tree])[] const input = value.slice(start + 1, pos) // skip '$' tokens.push([input, parser.parse(input)]) - start = pos + start = pos-- // backtrack and start over } } From 2d4c79b30fe05e0c02293ee44d37cf81165619f9 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Thu, 6 Nov 2025 21:30:50 -0800 Subject: [PATCH 28/48] topNode.topNode --- src/compiler/compiler.ts | 2 +- src/parser/curlyTokenizer.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index eec198c..7cf9255 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -867,7 +867,7 @@ export class Compiler { instructions.push(['PUSH', node]) } else { const [input, topNode] = node - let child = topNode.topNode.firstChild + let child = topNode.firstChild while (child) { instructions.push(...this.#compileNode(child, input)) child = child.nextSibling diff --git a/src/parser/curlyTokenizer.ts b/src/parser/curlyTokenizer.ts index bc8acf0..00e3ce1 100644 --- a/src/parser/curlyTokenizer.ts +++ b/src/parser/curlyTokenizer.ts @@ -1,13 +1,13 @@ import { parser } from '#parser/shrimp.ts' -import type { Tree } from '@lezer/common' +import type { SyntaxNode } from '@lezer/common' import { isIdentStart, isIdentChar } from './tokenizer' -// Turns a { curly string } into separate tokens for interpolation -export const tokenizeCurlyString = (value: string): (string | [string, Tree])[] => { +// Turns a { curly string } into strings and nodes for interpolation +export const tokenizeCurlyString = (value: string): (string | [string, SyntaxNode])[] => { let pos = 1 let start = 1 let char = value[pos] - const tokens: (string | [string, Tree])[] = [] + const tokens: (string | [string, SyntaxNode])[] = [] while (pos < value.length) { if (char === '$') { @@ -37,7 +37,7 @@ export const tokenizeCurlyString = (value: string): (string | [string, Tree])[] } const input = value.slice(start + 2, pos) // skip '$(' - tokens.push([input, parser.parse(input)]) + tokens.push([input, parser.parse(input).topNode]) start = ++pos // skip ')' } else { char = value[++pos] @@ -48,7 +48,7 @@ export const tokenizeCurlyString = (value: string): (string | [string, Tree])[] char = value[++pos] const input = value.slice(start + 1, pos) // skip '$' - tokens.push([input, parser.parse(input)]) + tokens.push([input, parser.parse(input).topNode]) start = pos-- // backtrack and start over } } From 69bbe1799284674cbe5dac9541c2891d3ce2a93e Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Fri, 7 Nov 2025 22:03:00 -0800 Subject: [PATCH 29/48] "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 From f8718ac05b78f4156ba7729d9bb8aedb22e94a11 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sun, 9 Nov 2025 14:36:52 -0800 Subject: [PATCH 30/48] minor bugfix (x2) --- src/prelude/list.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/prelude/list.ts b/src/prelude/list.ts index 0c7d7be..86320e0 100644 --- a/src/prelude/list.ts +++ b/src/prelude/list.ts @@ -1,7 +1,7 @@ import { type Value, toValue, toNull } from 'reefvm' export const list = { - slice: (list: any[], start: number, end?: number) => list.slice(start, end), + slice: (list: any[], start: number, end?: number) => list.slice(start, end ? end : undefined), map: async (list: any[], cb: Function) => { let acc: any[] = [] for (const value of list) acc.push(await cb(value)) @@ -70,8 +70,7 @@ export const list = { const realList = list.value as any[] const realStart = start.value as number const realDeleteCount = deleteCount.value as number - const realItems = items.map(item => item.value) - return toValue(realList.splice(realStart, realDeleteCount, ...realItems)) + return toValue(realList.splice(realStart, realDeleteCount, ...items)) }, insert: (list: Value, index: Value, item: Value) => { if (list.type !== 'array') return toNull() From 398cd57b1dcc5c8370b41750b7682869df566014 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sun, 9 Nov 2025 17:39:42 -0800 Subject: [PATCH 31/48] fs functions --- src/prelude/dict.ts | 2 +- src/prelude/fs.ts | 128 ++++++++ src/prelude/index.ts | 2 + src/prelude/load.ts | 4 +- src/prelude/tests/fs.test.ts | 329 ++++++++++++++++++++ src/prelude/tests/{load.ts => load.test.ts} | 18 +- 6 files changed, 472 insertions(+), 11 deletions(-) create mode 100644 src/prelude/fs.ts create mode 100644 src/prelude/tests/fs.test.ts rename src/prelude/tests/{load.ts => load.test.ts} (62%) diff --git a/src/prelude/dict.ts b/src/prelude/dict.ts index bf406e1..15b71f8 100644 --- a/src/prelude/dict.ts +++ b/src/prelude/dict.ts @@ -1,4 +1,4 @@ -import { type Value, toString, toValue } from 'reefvm' +import { type Value, toString } from 'reefvm' export const dict = { keys: (dict: Record) => Object.keys(dict), diff --git a/src/prelude/fs.ts b/src/prelude/fs.ts new file mode 100644 index 0000000..bb97a60 --- /dev/null +++ b/src/prelude/fs.ts @@ -0,0 +1,128 @@ +import { join, resolve, basename, dirname, extname } from 'path' +import { + readdirSync, mkdirSync, rmdirSync, + readFileSync, writeFileSync, appendFileSync, + rmSync, copyFileSync, + statSync, lstatSync, chmodSync, symlinkSync, readlinkSync, + watch +} from "fs" + +export const fs = { + // Directory operations + ls: (path: string) => readdirSync(path), + mkdir: (path: string) => mkdirSync(path, { recursive: true }), + rmdir: (path: string) => rmdirSync(path === '/' || path === '' ? '/tmp/*' : path, { recursive: true }), + pwd: () => process.cwd(), + cd: (path: string) => process.chdir(path), + + // Reading + read: (path: string) => readFileSync(path, 'utf-8'), + cat: (path: string) => { }, // added below + 'read-bytes': (path: string) => [...readFileSync(path)], + + // Writing + write: (path: string, content: string) => writeFileSync(path, content), + append: (path: string, content: string) => appendFileSync(path, content), + + // File operations + delete: (path: string) => rmSync(path), + rm: (path: string) => { }, // added below + copy: (from: string, to: string) => copyFileSync(from, to), + move: (from: string, to: string) => { + fs.copy(from, to) + fs.rm(from) + }, + mv: (from: string, to: string) => { }, // added below + + // Path operations + basename: (path: string) => basename(path), + dirname: (path: string) => dirname(path), + extname: (path: string) => extname(path), + join: (...paths: string[]) => join(...paths), + resolve: (...paths: string[]) => resolve(...paths), + + // File info + stat: (path: string) => { + try { + const stats = statSync(path) + const record = Object.fromEntries(Object.entries(stats)) + record['atime'] = record['atimeMs'] + record['ctime'] = record['ctimeMs'] + record['mtime'] = record['mtimeMs'] + + delete record['atimeMs'] + delete record['ctimeMs'] + delete record['mtimeMs'] + + return record + } catch { + return {} + } + + }, + 'exists?': (path: string) => { + try { + statSync(path) + return true + } + catch { + return false + } + }, + 'file?': (path: string) => { + try { return statSync(path).isFile() } + catch { return false } + }, + 'dir?': (path: string) => { + try { return statSync(path).isDirectory() } + catch { return false } + }, + 'symlink?': (path: string) => { + try { return lstatSync(path).isSymbolicLink() } + catch { return false } + }, + 'exec?': (path: string) => { + try { + const stats = statSync(path) + return !!(stats.mode & 0o111) + } + catch { return false } + }, + size: (path: string) => { + try { return statSync(path).size } + catch { return 0 } + }, + + // Permissions + chmod: (path: string, mode: number | string) => { + const numMode = typeof mode === 'string' ? parseInt(mode, 8) : mode + chmodSync(path, numMode) + }, + + // Symlinks + symlink: (target: string, path: string) => symlinkSync(target, path), + readlink: (path: string) => readlinkSync(path, 'utf-8'), + + // Other + glob: (pattern: string) => { + const dir = pattern.substring(0, pattern.lastIndexOf('/')) + const match = pattern.substring(pattern.lastIndexOf('/') + 1) + + if (!match.includes('*')) throw new Error('only * patterns supported') + + const ext = match.split('*').pop()! + return readdirSync(dir) + .filter((f) => f.endsWith(ext)) + .map((f) => join(dir, f)) + + }, + + watch: (path: string, callback: Function) => + watch(path, (event, filename) => callback(event, filename)), +} + + + ; (fs as any).cat = fs.read + ; (fs as any).mv = fs.move + ; (fs as any).cp = fs.copy + ; (fs as any).rm = fs.delete \ No newline at end of file diff --git a/src/prelude/index.ts b/src/prelude/index.ts index c153870..f92a2f5 100644 --- a/src/prelude/index.ts +++ b/src/prelude/index.ts @@ -6,6 +6,7 @@ import { } from 'reefvm' import { dict } from './dict' +import { fs } from './fs' import { json } from './json' import { load } from './load' import { list } from './list' @@ -14,6 +15,7 @@ import { str } from './str' export const globals = { dict, + fs, json, load, list, diff --git a/src/prelude/load.ts b/src/prelude/load.ts index 3f317c1..cd188a0 100644 --- a/src/prelude/load.ts +++ b/src/prelude/load.ts @@ -7,7 +7,9 @@ export const load = async function (this: VM, path: string): Promise { + if (existsSync(TEST_DIR)) { + rmSync(TEST_DIR, { recursive: true }) + } + mkdirSync(TEST_DIR, { recursive: true }) +}) + +afterEach(() => { + process.chdir(CWD) + if (existsSync(TEST_DIR)) { + rmSync(TEST_DIR, { recursive: true }) + } +}) + +describe('fs - directory operations', () => { + test('fs.ls lists directory contents', () => { + writeFileSync(join(TEST_DIR, 'file1.txt'), 'content1') + writeFileSync(join(TEST_DIR, 'file2.txt'), 'content2') + + const result = fs.ls(TEST_DIR) + expect(result).toContain('file1.txt') + expect(result).toContain('file2.txt') + }) + + test('fs.mkdir creates directory', () => { + const newDir = join(TEST_DIR, 'newdir') + fs.mkdir(newDir) + expect(existsSync(newDir)).toBe(true) + }) + + test('fs.rmdir removes empty directory', () => { + const dir = join(TEST_DIR, 'toremove') + mkdirSync(dir) + fs.rmdir(dir) + expect(existsSync(dir)).toBe(false) + }) + + test('fs.pwd returns current working directory', () => { + const result = fs.pwd() + expect(typeof result).toBe('string') + expect(result.length).toBeGreaterThan(0) + }) + + test('fs.cd changes current working directory', () => { + const originalCwd = process.cwd() + fs.cd(TEST_DIR) + expect(process.cwd()).toBe(TEST_DIR) + process.chdir(originalCwd) // restore + }) +}) + +describe('fs - reading', () => { + test('fs.read reads file contents as string', () => { + const file = join(TEST_DIR, 'test.txt') + writeFileSync(file, 'hello world') + + const result = fs.read(file) + expect(result).toBe('hello world') + }) + + test('fs.cat is alias for fs.read', () => { + const file = join(TEST_DIR, 'test.txt') + writeFileSync(file, 'hello world') + + const result = fs.cat(file) + expect(result).toBe('hello world') + }) + + test('fs.read-bytes reads file as buffer', () => { + const file = join(TEST_DIR, 'test.bin') + writeFileSync(file, Buffer.from([1, 2, 3, 4])) + + const result = fs['read-bytes'](file) + expect(result).toBeInstanceOf(Array) + expect(result).toEqual([1, 2, 3, 4]) + }) +}) + +describe('fs - writing', () => { + test('fs.write writes string to file', async () => { + const file = join(TEST_DIR, 'output.txt') + fs.write(file, 'test content') + + const content = Bun.file(file).text() + expect(await content).toBe('test content') + }) + + test('fs.append appends to existing file', async () => { + const file = join(TEST_DIR, 'append.txt') + writeFileSync(file, 'first') + fs.append(file, ' second') + + const content = await Bun.file(file).text() + expect(content).toBe('first second') + }) +}) + +describe('fs - file operations', () => { + test('fs.rm removes file', () => { + const file = join(TEST_DIR, 'remove.txt') + writeFileSync(file, 'content') + + fs.rm(file) + expect(existsSync(file)).toBe(false) + }) + + test('fs.delete is alias for fs.rm', () => { + const file = join(TEST_DIR, 'delete.txt') + writeFileSync(file, 'content') + + fs.delete(file) + expect(existsSync(file)).toBe(false) + }) + + test('fs.copy copies file', async () => { + const src = join(TEST_DIR, 'source.txt') + const dest = join(TEST_DIR, 'dest.txt') + writeFileSync(src, 'content') + + fs.copy(src, dest) + expect(await Bun.file(dest).text()).toBe('content') + }) + + test('fs.cp is alias for fs.copy', async () => { + const src = join(TEST_DIR, 'source2.txt') + const dest = join(TEST_DIR, 'dest2.txt') + writeFileSync(src, 'content') + + fs.cp(src, dest) + expect(await Bun.file(dest).text()).toBe('content') + }) + + test('fs.move moves file', async () => { + const src = join(TEST_DIR, 'source.txt') + const dest = join(TEST_DIR, 'moved.txt') + writeFileSync(src, 'content') + + fs.move(src, dest) + expect(existsSync(src)).toBe(false) + expect(await Bun.file(dest).text()).toBe('content') + }) + + test('fs.mv is alias for fs.move', async () => { + const src = join(TEST_DIR, 'source2.txt') + const dest = join(TEST_DIR, 'moved2.txt') + writeFileSync(src, 'content') + + fs.mv(src, dest) + expect(existsSync(src)).toBe(false) + expect(await Bun.file(dest).text()).toBe('content') + }) +}) + +describe('fs - path operations', () => { + test('fs.basename extracts filename from path', () => { + expect(fs.basename('/path/to/file.txt')).toBe('file.txt') + expect(fs.basename('/path/to/dir/')).toBe('dir') + }) + + test('fs.dirname extracts directory from path', () => { + expect(fs.dirname('/path/to/file.txt')).toBe('/path/to') + expect(fs.dirname('/path/to/dir/')).toBe('/path/to') + }) + + test('fs.extname extracts file extension', () => { + expect(fs.extname('file.txt')).toBe('.txt') + expect(fs.extname('file.tar.gz')).toBe('.gz') + expect(fs.extname('noext')).toBe('') + }) + + test('fs.join joins path segments', () => { + expect(fs.join('path', 'to', 'file.txt')).toBe('path/to/file.txt') + expect(fs.join('/absolute', 'path')).toBe('/absolute/path') + }) + + test('fs.resolve resolves to absolute path', () => { + const result = fs.resolve('relative', 'path') + expect(result.startsWith('/')).toBe(true) + expect(result).toContain('relative') + }) +}) + +describe('fs - file info', () => { + test('fs.stat returns file stats', () => { + const file = join(TEST_DIR, 'stat.txt') + writeFileSync(file, 'content') + + const stats = fs.stat(file) + expect(stats).toHaveProperty('size') + expect(stats).toHaveProperty('mtime') + expect(stats.size).toBe(7) // 'content' is 7 bytes + }) + + test('fs.exists? checks if path exists', () => { + const file = join(TEST_DIR, 'exists.txt') + expect(fs['exists?'](file)).toBe(false) + + writeFileSync(file, 'content') + expect(fs['exists?'](file)).toBe(true) + }) + + test('fs.file? checks if path is a file', () => { + const file = join(TEST_DIR, 'isfile.txt') + writeFileSync(file, 'content') + + expect(fs['file?'](file)).toBe(true) + expect(fs['file?'](TEST_DIR)).toBe(false) + }) + + test('fs.dir? checks if path is a directory', () => { + const dir = join(TEST_DIR, 'isdir') + mkdirSync(dir) + + expect(fs['dir?'](dir)).toBe(true) + expect(fs['dir?'](join(TEST_DIR, 'isfile.txt'))).toBe(false) + }) + + test('fs.symlink? checks if path is a symbolic link', () => { + const file = join(TEST_DIR, 'target.txt') + const link = join(TEST_DIR, 'link.txt') + writeFileSync(file, 'content') + + fs.symlink(file, link) + expect(fs['symlink?'](link)).toBe(true) + expect(fs['symlink?'](file)).toBe(false) + }) + + test('fs.exec? checks if file is executable', () => { + const file = join(TEST_DIR, 'script.sh') + writeFileSync(file, '#!/bin/bash\necho hello') + + fs.chmod(file, 0o755) + expect(fs['exec?'](file)).toBe(true) + + fs.chmod(file, 0o644) + expect(fs['exec?'](file)).toBe(false) + }) + + test('fs.size returns file size in bytes', () => { + const file = join(TEST_DIR, 'sizeme.txt') + writeFileSync(file, 'content') + + expect(fs.size(file)).toBe(7) // 'content' is 7 bytes + }) +}) + +describe('fs - permissions', () => { + test('fs.chmod changes file permissions with octal number', () => { + const file = join(TEST_DIR, 'perms.txt') + writeFileSync(file, 'content') + + fs.chmod(file, 0o755) + expect(fs['exec?'](file)).toBe(true) + + fs.chmod(file, 0o644) + expect(fs['exec?'](file)).toBe(false) + }) + + test('fs.chmod changes file permissions with string', () => { + const file = join(TEST_DIR, 'perms2.txt') + writeFileSync(file, 'content') + + fs.chmod(file, '755') + expect(fs['exec?'](file)).toBe(true) + + fs.chmod(file, '644') + expect(fs['exec?'](file)).toBe(false) + }) +}) + +describe('fs - symlinks', () => { + test('fs.symlink creates symbolic link', () => { + const target = join(TEST_DIR, 'target.txt') + const link = join(TEST_DIR, 'link.txt') + writeFileSync(target, 'content') + + fs.symlink(target, link) + expect(fs['symlink?'](link)).toBe(true) + expect(fs.read(link)).toBe('content') + }) + + test('fs.readlink reads symbolic link target', () => { + const target = join(TEST_DIR, 'target.txt') + const link = join(TEST_DIR, 'link.txt') + writeFileSync(target, 'content') + + fs.symlink(target, link) + expect(fs.readlink(link)).toBe(target) + }) +}) + +describe('fs - other', () => { + test('fs.glob matches file patterns', () => { + writeFileSync(join(TEST_DIR, 'file1.txt'), '') + writeFileSync(join(TEST_DIR, 'file2.txt'), '') + writeFileSync(join(TEST_DIR, 'file3.md'), '') + + const result = fs.glob(join(TEST_DIR, '*.txt')) + expect(result).toHaveLength(2) + expect(result).toContain(join(TEST_DIR, 'file1.txt')) + expect(result).toContain(join(TEST_DIR, 'file2.txt')) + }) + + test('fs.watch calls callback on file change', async () => { + const file = join(TEST_DIR, 'watch.txt') + writeFileSync(file, 'initial') + + let called = false + const watcher = fs.watch(file, () => { called = true }) + + // Trigger change + await new Promise(resolve => setTimeout(resolve, 100)) + writeFileSync(file, 'updated') + + // Wait for watcher + await new Promise(resolve => setTimeout(resolve, 500)) + + expect(called).toBe(true) + watcher.close?.() + }) +}) \ No newline at end of file diff --git a/src/prelude/tests/load.ts b/src/prelude/tests/load.test.ts similarity index 62% rename from src/prelude/tests/load.ts rename to src/prelude/tests/load.test.ts index 7ce8172..f79326c 100644 --- a/src/prelude/tests/load.ts +++ b/src/prelude/tests/load.test.ts @@ -1,41 +1,41 @@ import { expect, describe, test } from 'bun:test' import { globals } from '#prelude' -describe('use', () => { +describe('loading a file', () => { test(`imports all a file's functions`, async () => { expect(` - math = load ./src/prelude/tests/math + math = load ./src/prelude/tests/math.sh math.double 4 `).toEvaluateTo(8, globals) expect(` - math = load ./src/prelude/tests/math + math = load ./src/prelude/tests/math.sh math.double (math.double 4) `).toEvaluateTo(16, globals) expect(` - math = load ./src/prelude/tests/math - dbl = math.double + math = load ./src/prelude/tests/math.sh + dbl = ref math.double dbl (dbl 2) `).toEvaluateTo(8, globals) expect(` - math = load ./src/prelude/tests/math + math = load ./src/prelude/tests/math.sh math.pi `).toEvaluateTo(3.14, globals) expect(` - math = load ./src/prelude/tests/math + math = load ./src/prelude/tests/math.sh math | at 🥧 `).toEvaluateTo(3.14159265359, globals) expect(` - math = load ./src/prelude/tests/math + math = load ./src/prelude/tests/math.sh math.🥧 `).toEvaluateTo(3.14159265359, globals) expect(` - math = load ./src/prelude/tests/math + math = load ./src/prelude/tests/math.sh math.add1 5 `).toEvaluateTo(6, globals) }) From ba5ce0a88c8d26509d23b7416535013f4b4a9a83 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sun, 9 Nov 2025 17:43:32 -0800 Subject: [PATCH 32/48] octal numbers 0o644 --- src/compiler/compiler.ts | 8 ++++---- src/compiler/tests/literals.test.ts | 13 ++++++++++++- src/parser/shrimp.grammar | 1 + src/parser/shrimp.ts | 2 +- src/parser/tests/literals.test.ts | 27 +++++++++++++++++++++++++-- 5 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 7cf9255..80ce825 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -107,12 +107,12 @@ export class Compiler { switch (node.type.id) { case terms.Number: - // Handle sign prefix for hex and binary literals - // Number() doesn't parse '-0xFF' or '+0xFF' correctly + // Handle sign prefix for hex, binary, and octal literals + // Number() doesn't parse '-0xFF', '+0xFF', '-0o77', etc. correctly let numberValue: number - if (value.startsWith('-') && (value.includes('0x') || value.includes('0b'))) { + if (value.startsWith('-') && (value.includes('0x') || value.includes('0b') || value.includes('0o'))) { numberValue = -Number(value.slice(1)) - } else if (value.startsWith('+') && (value.includes('0x') || value.includes('0b'))) { + } else if (value.startsWith('+') && (value.includes('0x') || value.includes('0b') || value.includes('0o'))) { numberValue = Number(value.slice(1)) } else { numberValue = Number(value) diff --git a/src/compiler/tests/literals.test.ts b/src/compiler/tests/literals.test.ts index 96830cf..1783830 100644 --- a/src/compiler/tests/literals.test.ts +++ b/src/compiler/tests/literals.test.ts @@ -20,6 +20,15 @@ describe('number literals', () => { expect('0xABCDEF').toEvaluateTo(0xabcdef) }) + test('octal literals', () => { + expect('0o644').toEvaluateTo(420) + expect('0o755').toEvaluateTo(493) + expect('0o777').toEvaluateTo(511) + expect('0o10').toEvaluateTo(8) + expect('0o0').toEvaluateTo(0) + expect('0o123').toEvaluateTo(83) + }) + test('decimal literals still work', () => { expect('42').toEvaluateTo(42) expect('3.14').toEvaluateTo(3.14) @@ -27,14 +36,16 @@ describe('number literals', () => { expect('999999').toEvaluateTo(999999) }) - test('negative hex and binary', () => { + test('negative hex, binary, and octal', () => { expect('-0xFF').toEvaluateTo(-255) expect('-0b1010').toEvaluateTo(-10) + expect('-0o755').toEvaluateTo(-493) }) test('positive prefix', () => { expect('+0xFF').toEvaluateTo(255) expect('+0b110').toEvaluateTo(6) + expect('+0o644').toEvaluateTo(420) expect('+42').toEvaluateTo(42) }) }) diff --git a/src/parser/shrimp.grammar b/src/parser/shrimp.grammar index c01fd18..506f036 100644 --- a/src/parser/shrimp.grammar +++ b/src/parser/shrimp.grammar @@ -17,6 +17,7 @@ Number { ("-" | "+")? "0x" $[0-9a-fA-F]+ | ("-" | "+")? "0b" $[01]+ | + ("-" | "+")? "0o" $[0-7]+ | ("-" | "+")? $[0-9]+ ("_"? $[0-9]+)* ('.' $[0-9]+ ("_"? $[0-9]+)*)? } Boolean { "true" | "false" } diff --git a/src/parser/shrimp.ts b/src/parser/shrimp.ts index 3f09fb1..6435fd6 100644 --- a/src/parser/shrimp.ts +++ b/src/parser/shrimp.ts @@ -19,7 +19,7 @@ export const parser = LRParser.deserialize({ propSources: [highlighting], skippedNodes: [0,34], repeatNodeCount: 12, - tokenData: "K]~R!OOX$RXY$pYZ%ZZp$Rpq$pqr$Rrs%tst'ztu)cuw$Rwx)hxy)myz*Wz{$R{|*q|}$R}!O*q!O!P$R!P!Q2x!Q!R+c!R![.Q![!];e!]!^%Z!^!}$R!}#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~", + tokenData: "Lq~R!OOX$RXY$pYZ%ZZp$Rpq$pqr$Rrs%tst'ztu)cuw$Rwx)hxy)myz*Wz{$R{|*q|}$R}!O*q!O!P$R!P!Q4^!Q!R+c!R![.W![!]T!`#O$R#P;'S$R;'S;=`$j<%lO$RU>YV!TSOt$Ruw$Rx#O$R#P#Q>o#Q;'S$R;'S;=`$j<%lO$RU>vU#jQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$R~?_O#b~U?fU#lQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU@PU!TS!bQOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU@h^!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#o@c#o;'S$R;'S;=`$j<%lO$RUAkU!RQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RUBS_!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#UCR#U#o@c#o;'S$R;'S;=`$j<%lO$RUCW`!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#`@c#`#aDY#a#o@c#o;'S$R;'S;=`$j<%lO$RUD_`!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#g@c#g#hEa#h#o@c#o;'S$R;'S;=`$j<%lO$RUEf`!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#X@c#X#YFh#Y#o@c#o;'S$R;'S;=`$j<%lO$RUFo^!XQ!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#o@c#o;'S$R;'S;=`$j<%lO$R^Gr^#cW!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#o@c#o;'S$R;'S;=`$j<%lO$R^Hu^#eW!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#o@c#o;'S$R;'S;=`$j<%lO$R^Ix`#dW!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#f@c#f#gJz#g#o@c#o;'S$R;'S;=`$j<%lO$RUKP`!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#i@c#i#jEa#j#o@c#o;'S$R;'S;=`$j<%lO$RULYUuQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$R~LqO#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}], diff --git a/src/parser/tests/literals.test.ts b/src/parser/tests/literals.test.ts index fe1b4ca..e20368b 100644 --- a/src/parser/tests/literals.test.ts +++ b/src/parser/tests/literals.test.ts @@ -21,6 +21,16 @@ describe('number literals', () => { `) }) + test('octal numbers', () => { + expect('0o644').toMatchTree(` + Number 0o644 + `) + + expect('0o055').toMatchTree(` + Number 0o055 + `) + }) + test('decimal numbers still work', () => { expect('42').toMatchTree(` Number 42 @@ -39,6 +49,12 @@ describe('number literals', () => { `) }) + test('negative octal', () => { + expect('-0o755').toMatchTree(` + Number -0o755 + `) + }) + test('positive prefix binary', () => { expect('+0b110').toMatchTree(` Number +0b110 @@ -51,11 +67,18 @@ describe('number literals', () => { `) }) - test('hex and binary in arrays', () => { - expect('[0xFF 0b110 42]').toMatchTree(` + test('positive prefix octal', () => { + expect('+0o644').toMatchTree(` + Number +0o644 + `) + }) + + test('hex, binary, and octal in arrays', () => { + expect('[0xFF 0b110 0o644 42]').toMatchTree(` Array Number 0xFF Number 0b110 + Number 0o644 Number 42 `) }) From e2f5024a4c94e1cd61f09b65e8a7170415bbf748 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sun, 9 Nov 2025 18:55:31 -0800 Subject: [PATCH 33/48] add `import` keyword for importing keys of dicts into local scope --- src/compiler/compiler.ts | 17 +++++++++++++++++ src/compiler/tests/compiler.test.ts | 18 ++++++++++++++++++ src/parser/shrimp.grammar | 7 +++++++ src/parser/shrimp.terms.ts | 7 ++++--- src/parser/shrimp.ts | 20 ++++++++++---------- src/parser/tests/basics.test.ts | 20 ++++++++++++++++++++ src/prelude/index.ts | 13 +++++++++++++ 7 files changed, 89 insertions(+), 13 deletions(-) diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 80ce825..a96155d 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -797,6 +797,23 @@ export class Compiler { return instructions } + case terms.Import: { + const instructions: ProgramItem[] = [] + const [_import, ...dicts] = getAllChildren(node) + + instructions.push(['LOAD', 'import']) + + dicts.forEach((dict) => + instructions.push(['PUSH', input.slice(dict.from, dict.to)]) + ) + + instructions.push(['PUSH', dicts.length]) + instructions.push(['PUSH', 0]) + instructions.push(['CALL']) + + return instructions + } + case terms.Comment: { return [] // ignore comments } diff --git a/src/compiler/tests/compiler.test.ts b/src/compiler/tests/compiler.test.ts index 1965e89..5194300 100644 --- a/src/compiler/tests/compiler.test.ts +++ b/src/compiler/tests/compiler.test.ts @@ -448,3 +448,21 @@ describe('Compound assignment operators', () => { expect('x = 10; x %= 3; x').toEvaluateTo(1) }) }) + +describe('import', () => { + test('imports single dict', () => { + expect(`import str; starts-with? abc a`).toEvaluateTo(true) + }) + + test('imports multiple dicts', () => { + expect(`import str math list; map [1 2 3] do x: x * 2 end`).toEvaluateTo([2, 4, 6]) + }) + + test('imports non-prelude dicts', () => { + expect(` + abc = [a=true b=yes c=si] + import abc + abc.b + `).toEvaluateTo('yes') + }) +}) \ No newline at end of file diff --git a/src/parser/shrimp.grammar b/src/parser/shrimp.grammar index 506f036..8857fce 100644 --- a/src/parser/shrimp.grammar +++ b/src/parser/shrimp.grammar @@ -41,6 +41,7 @@ try { @specialize[@name=keyword] } catch { @specialize[@name=keyword] } finally { @specialize[@name=keyword] } throw { @specialize[@name=keyword] } +import { @specialize[@name=keyword] } null { @specialize[@name=Null] } @external tokens tokenizer from "./tokenizer" { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot, CurlyString } @@ -72,6 +73,7 @@ consumeToTerminator { ambiguousFunctionCall | TryExpr | Throw | + Import | IfExpr | FunctionDef | CompoundAssign | @@ -161,6 +163,11 @@ Throw { throw (BinOp | ConditionalOp | expression) } +// this has to be in the parse tree so the scope tracker can use it +Import { + import AssignableIdentifier+ +} + ConditionalOp { expression !comparison EqEq expression | expression !comparison Neq expression | diff --git a/src/parser/shrimp.terms.ts b/src/parser/shrimp.terms.ts index 0f49afe..0f3ba5b 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 = 81, + keyword = 83, ConditionalOp = 40, ParenExpr = 41, FunctionCallWithNewlines = 42, @@ -73,5 +73,6 @@ export const FunctionCallWithBlock = 77, TryExpr = 78, Throw = 80, - CompoundAssign = 82, - Assign = 83 + Import = 82, + CompoundAssign = 84, + Assign = 85 diff --git a/src/parser/shrimp.ts b/src/parser/shrimp.ts index 6435fd6..41e9d73 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:112, catch:118, finally:124, end:126, if:134, else:140, try:158, throw:162} +const spec_Identifier = {__proto__:null,while:78, null:112, catch:118, finally:124, end:126, if:134, else:140, try:158, throw:162, import:166} export const parser = LRParser.deserialize({ version: 14, - 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`<]QRO'#EvOOQO'#Ev'#EvO>dQQO,5:[O>iQRO,59nO>pQRO,59nO:zQbO,5:hO?OQcO,5:jO@^QcO,5:jO@zQcO,5:jOOQ`'#Eb'#EbOAoQbO,5:lOOQa1G/_1G/_OOOO,59|,59|OOOO,59},59}OOOO-E8X-E8XOOQa1G/f1G/fOOQ`,5:Z,5:ZOOQ`-E8[-E8[OOQa1G/}1G/}OCeQcO1G/}OCoQcO1G/}OD}QcO1G/}OEXQcO1G/}OEfQcO1G/}OOQa1G/[1G/[OFwQcO1G/[OGOQcO1G/[OGVQcO1G/[OHUQcO1G/[OG^QcO1G/[OOQ`-E8U-E8UOHlQRO1G/]OHvQQO1G/]OH{QQO1G/]OITQQO1G/]OI`QRO1G/]OIgQRO1G/]OInQbO,59rOIxQQO1G/]OOQa1G/]1G/]OJQQQO1G0POOQa1G0Q1G0QOJ]QbO1G0QOOQO'#E`'#E`OJQQQO1G0POOQa1G0P1G0POOQ`'#Ea'#EaOJ]QbO1G0QOJgQbO1G0ZOKRQbO1G0YOKmQbO'#DjOLOQbO'#DjOLcQbO1G0ROOQ`-E8T-E8TOOQ`,5:q,5:qOOQ`-E8V-E8VOLnQQO,59wOOQO,59x,59xOOQO-E8W-E8WOLvQbO1G/bO:zQbO1G/vO:zQbO1G/YOL}QbO1G0SOOQ`-E8`-E8`OMYQQO7+$wOOQa7+$w7+$wOMbQQO1G/^OMjQQO7+%kOOQa7+%k7+%kOMuQbO7+%lOOQa7+%l7+%lOOQO-E8^-E8^OOQ`-E8_-E8_OOQ`'#E['#E[ONPQQO'#E[ONXQbO'#EuOOQ`,5:U,5:UONlQbO'#DhONqQQO'#DkOOQ`7+%m7+%mONvQbO7+%mON{QbO7+%mO! TQbO7+$|O! cQbO7+$|O! sQbO7+%bO! {QbO7+$tOOQ`7+%n7+%nO!!QQbO7+%nO!!VQbO7+%nOOQa<sAN>sOOQ`AN>SAN>SO!$aQbOAN>SO!$fQbOAN>SOOQ`-E8]-E8]OOQ`AN>hAN>hO!$nQbOAN>hO2yQbO,5:_O:zQbO,5:aOOQ`AN>tAN>tPInQbO'#EWOOQ`7+%Y7+%YOOQ`G23nG23nO!$sQbOG23nP!#sQbO'#DsOOQ`G24SG24SO!$xQQO1G/yOOQ`1G/{1G/{OOQ`LD)YLD)YO:zQbO7+%eOOQ`<T!`#O$R#P;'S$R;'S;=`$j<%lO$RU>YV!TSOt$Ruw$Rx#O$R#P#Q>o#Q;'S$R;'S;=`$j<%lO$RU>vU#jQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$R~?_O#b~U?fU#lQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU@PU!TS!bQOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU@h^!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#o@c#o;'S$R;'S;=`$j<%lO$RUAkU!RQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RUBS_!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#UCR#U#o@c#o;'S$R;'S;=`$j<%lO$RUCW`!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#`@c#`#aDY#a#o@c#o;'S$R;'S;=`$j<%lO$RUD_`!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#g@c#g#hEa#h#o@c#o;'S$R;'S;=`$j<%lO$RUEf`!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#X@c#X#YFh#Y#o@c#o;'S$R;'S;=`$j<%lO$RUFo^!XQ!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#o@c#o;'S$R;'S;=`$j<%lO$R^Gr^#cW!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#o@c#o;'S$R;'S;=`$j<%lO$R^Hu^#eW!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#o@c#o;'S$R;'S;=`$j<%lO$R^Ix`#dW!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#f@c#f#gJz#g#o@c#o;'S$R;'S;=`$j<%lO$RUKP`!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#i@c#i#jEa#j#o@c#o;'S$R;'S;=`$j<%lO$RULYUuQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$R~LqO#m~", - tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO#]~~", 11)], + repeatNodeCount: 13, + tokenData: "Lq~R!OOX$RXY$pYZ%ZZp$Rpq$pqr$Rrs%tst'ztu)cuw$Rwx)hxy)myz*Wz{$R{|*q|}$R}!O*q!O!P$R!P!Q4^!Q!R+c!R![.W![!]T!`#O$R#P;'S$R;'S;=`$j<%lO$RU>YV!TSOt$Ruw$Rx#O$R#P#Q>o#Q;'S$R;'S;=`$j<%lO$RU>vU#mQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$R~?_O#e~U?fU#oQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU@PU!TS!bQOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU@h^!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#o@c#o;'S$R;'S;=`$j<%lO$RUAkU!RQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RUBS_!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#UCR#U#o@c#o;'S$R;'S;=`$j<%lO$RUCW`!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#`@c#`#aDY#a#o@c#o;'S$R;'S;=`$j<%lO$RUD_`!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#g@c#g#hEa#h#o@c#o;'S$R;'S;=`$j<%lO$RUEf`!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#X@c#X#YFh#Y#o@c#o;'S$R;'S;=`$j<%lO$RUFo^!XQ!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#o@c#o;'S$R;'S;=`$j<%lO$R^Gr^#fW!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#o@c#o;'S$R;'S;=`$j<%lO$R^Hu^#hW!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#o@c#o;'S$R;'S;=`$j<%lO$R^Ix`#gW!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#f@c#f#gJz#g#o@c#o;'S$R;'S;=`$j<%lO$RUKP`!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#i@c#i#jEa#j#o@c#o;'S$R;'S;=`$j<%lO$RULYUuQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$R~LqO#p~", + 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: 2256 + tokenPrec: 2299 }) diff --git a/src/parser/tests/basics.test.ts b/src/parser/tests/basics.test.ts index 28731f9..b9e7c76 100644 --- a/src/parser/tests/basics.test.ts +++ b/src/parser/tests/basics.test.ts @@ -1017,3 +1017,23 @@ Assign `) }) }) + +describe('import', () => { + test('parses single import', () => { + expect(`import str`).toMatchTree(` + Import + keyword import + AssignableIdentifier str + `) + }) + + test('parses multiple imports', () => { + expect(`import str math list`).toMatchTree(` + Import + keyword import + AssignableIdentifier str + AssignableIdentifier math + AssignableIdentifier list + `) + }) +}) \ No newline at end of file diff --git a/src/prelude/index.ts b/src/prelude/index.ts index f92a2f5..4097565 100644 --- a/src/prelude/index.ts +++ b/src/prelude/index.ts @@ -45,6 +45,19 @@ export const globals = { return typeof v !== 'string' || this.scope.has(v) }, ref: (fn: Function) => fn, + import: function (this: VM, ...idents: string[]) { + for (const ident of idents) { + const module = this.get(ident) + + if (!module) throw new Error(`import: can't find ${ident}`) + if (module.type !== 'dict') throw new Error(`import: can't import ${module.type}`) + + for (const [name, value] of module.value.entries()) { + if (value.type === 'dict') throw new Error(`import: can't import dicts in dicts`) + this.set(name, value) + } + } + }, // env args: Bun.argv.slice(1), From 970ceeb8b0b3a17465d1bf269ffb4f00b3dbc6d5 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sun, 9 Nov 2025 19:20:48 -0800 Subject: [PATCH 34/48] import dict only=something --- src/compiler/compiler.ts | 16 ++++++++++++---- src/compiler/tests/compiler.test.ts | 10 ++++++++++ src/parser/shrimp.grammar | 2 +- src/parser/shrimp.ts | 8 ++++---- src/parser/tests/basics.test.ts | 19 +++++++++++++++---- src/prelude/index.ts | 8 +++++++- 6 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index a96155d..d52576e 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -799,16 +799,24 @@ export class Compiler { case terms.Import: { const instructions: ProgramItem[] = [] - const [_import, ...dicts] = getAllChildren(node) + const [_import, ...nodes] = getAllChildren(node) + const args = nodes.filter(node => node.type.id === terms.Identifier) + const namedArgs = nodes.filter(node => node.type.id === terms.NamedArg) instructions.push(['LOAD', 'import']) - dicts.forEach((dict) => + args.forEach((dict) => instructions.push(['PUSH', input.slice(dict.from, dict.to)]) ) - instructions.push(['PUSH', dicts.length]) - instructions.push(['PUSH', 0]) + namedArgs.forEach((arg) => { + const { name, valueNode } = getNamedArgParts(arg, input) + instructions.push(['PUSH', name]) + instructions.push(...this.#compileNode(valueNode, input)) + }) + + instructions.push(['PUSH', args.length]) + instructions.push(['PUSH', namedArgs.length]) instructions.push(['CALL']) return instructions diff --git a/src/compiler/tests/compiler.test.ts b/src/compiler/tests/compiler.test.ts index 5194300..45fdb39 100644 --- a/src/compiler/tests/compiler.test.ts +++ b/src/compiler/tests/compiler.test.ts @@ -465,4 +465,14 @@ describe('import', () => { abc.b `).toEvaluateTo('yes') }) + + test('can specify imports', () => { + expect(`import str only=ends-with?; ref ends-with? | function?`).toEvaluateTo(true) + expect(`import str only=ends-with?; ref starts-with? | function?`).toEvaluateTo(false) + expect(` + abc = [a=true b=yes c=si] + import abc only=[a c] + [a c] + `).toEvaluateTo([true, 'si']) + }) }) \ No newline at end of file diff --git a/src/parser/shrimp.grammar b/src/parser/shrimp.grammar index 8857fce..80adcdb 100644 --- a/src/parser/shrimp.grammar +++ b/src/parser/shrimp.grammar @@ -165,7 +165,7 @@ Throw { // this has to be in the parse tree so the scope tracker can use it Import { - import AssignableIdentifier+ + import NamedArg* Identifier+ NamedArg* } ConditionalOp { diff --git a/src/parser/shrimp.ts b/src/parser/shrimp.ts index 41e9d73..051f00e 100644 --- a/src/parser/shrimp.ts +++ b/src/parser/shrimp.ts @@ -7,9 +7,9 @@ import {highlighting} from "./highlight" const spec_Identifier = {__proto__:null,while:78, null:112, catch:118, finally:124, end:126, if:134, else:140, try:158, throw:162, import:166} export const parser = LRParser.deserialize({ version: 14, - states: "=dQYQbOOO!mOpO'#DXO!rOSO'#D`OOQa'#D`'#D`O%mQcO'#DvO(mQcO'#EiOOQ`'#Ew'#EwO)WQRO'#DwO+]QcO'#EgO+vQbO'#DVOOQa'#Dy'#DyO.[QbO'#DzOOQa'#Ei'#EiO.cQcO'#EiO0aQcO'#EhO1fQcO'#EgO1sQRO'#ESOOQ`'#Eg'#EgO2[QbO'#EgO2cQQO'#EfOOQ`'#Ef'#EfOOQ`'#EU'#EUQYQbOOO2nQbO'#D[O2yQbO'#DpO3tQbO'#DSO4oQQO'#D|O3tQbO'#EOO4tQbO'#EQO4yObO,59sO5UQbO'#DbO5^QWO'#DcOOOO'#Eo'#EoOOOO'#EZ'#EZO5rOSO,59zOOQa,59z,59zOOQ`'#DZ'#DZO6QQbO'#DoOOQ`'#Em'#EmOOQ`'#E^'#E^O6[QbO,5:^OOQa'#Eh'#EhO3tQbO,5:cO3tQbO,5:cO3tQbO,5:cO3tQbO,5:cO3tQbO,59pO3tQbO,59pO3tQbO,59pO3tQbO,59pOOQ`'#EW'#EWO+vQbO,59qO7UQcO'#DvO7]QcO'#EiO7dQRO,59qO7nQQO,59qO7sQQO,59qO7{QQO,59qO8WQRO,59qO8pQRO,59qO8wQQO'#DQO8|QbO,5:fO9TQQO,5:eOOQa,5:f,5:fO9`QbO,5:fO9jQbO,5:oO9jQbO,5:nO:zQbO,5:gO;RQbO,59lOOQ`,5;Q,5;QO9jQbO'#EVOOQ`-E8S-E8SOOQ`'#EX'#EXO;mQbO'#D]O;xQbO'#D^OOQO'#EY'#EYO;pQQO'#D]O<^QQO,59vO]QRO'#EvOOQO'#Ev'#EvO>dQQO,5:[O>iQRO,59nO>pQRO,59nO:zQbO,5:hO?OQcO,5:jO@^QcO,5:jO@zQcO,5:jOOQ`'#Eb'#EbOAoQbO,5:lOOQa1G/_1G/_OOOO,59|,59|OOOO,59},59}OOOO-E8X-E8XOOQa1G/f1G/fOOQ`,5:Z,5:ZOOQ`-E8[-E8[OOQa1G/}1G/}OCeQcO1G/}OCoQcO1G/}OD}QcO1G/}OEXQcO1G/}OEfQcO1G/}OOQa1G/[1G/[OFwQcO1G/[OGOQcO1G/[OGVQcO1G/[OHUQcO1G/[OG^QcO1G/[OOQ`-E8U-E8UOHlQRO1G/]OHvQQO1G/]OH{QQO1G/]OITQQO1G/]OI`QRO1G/]OIgQRO1G/]OInQbO,59rOIxQQO1G/]OOQa1G/]1G/]OJQQQO1G0POOQa1G0Q1G0QOJ]QbO1G0QOOQO'#E`'#E`OJQQQO1G0POOQa1G0P1G0POOQ`'#Ea'#EaOJ]QbO1G0QOJgQbO1G0ZOKRQbO1G0YOKmQbO'#DjOLOQbO'#DjOLcQbO1G0ROOQ`-E8T-E8TOOQ`,5:q,5:qOOQ`-E8V-E8VOLnQQO,59wOOQO,59x,59xOOQO-E8W-E8WOLvQbO1G/bO:zQbO1G/vO:zQbO1G/YOL}QbO1G0SOOQ`-E8`-E8`OMYQQO7+$wOOQa7+$w7+$wOMbQQO1G/^OMjQQO7+%kOOQa7+%k7+%kOMuQbO7+%lOOQa7+%l7+%lOOQO-E8^-E8^OOQ`-E8_-E8_OOQ`'#E['#E[ONPQQO'#E[ONXQbO'#EuOOQ`,5:U,5:UONlQbO'#DhONqQQO'#DkOOQ`7+%m7+%mONvQbO7+%mON{QbO7+%mO! TQbO7+$|O! cQbO7+$|O! sQbO7+%bO! {QbO7+$tOOQ`7+%n7+%nO!!QQbO7+%nO!!VQbO7+%nOOQa<sAN>sOOQ`AN>SAN>SO!$aQbOAN>SO!$fQbOAN>SOOQ`-E8]-E8]OOQ`AN>hAN>hO!$nQbOAN>hO2yQbO,5:_O:zQbO,5:aOOQ`AN>tAN>tPInQbO'#EWOOQ`7+%Y7+%YOOQ`G23nG23nO!$sQbOG23nP!#sQbO'#DsOOQ`G24SG24SO!$xQQO1G/yOOQ`1G/{1G/{OOQ`LD)YLD)YO:zQbO7+%eOOQ`<`QRO'#EvOOQO'#Ev'#EvO>gQQO,5:[O>lQRO,59nO>sQRO,59nO:}QbO,5:hO?RQcO,5:jO@aQcO,5:jO@}QcO,5:jOArQbO,5:lOOQ`'#Eb'#EbO4tQbO,5:lOOQa1G/_1G/_OOOO,59|,59|OOOO,59},59}OOOO-E8X-E8XOOQa1G/f1G/fOOQ`,5:Z,5:ZOOQ`-E8[-E8[OOQa1G/}1G/}OCkQcO1G/}OCuQcO1G/}OETQcO1G/}OE_QcO1G/}OElQcO1G/}OOQa1G/[1G/[OF}QcO1G/[OGUQcO1G/[OG]QcO1G/[OH[QcO1G/[OGdQcO1G/[OOQ`-E8U-E8UOHrQRO1G/]OH|QQO1G/]OIRQQO1G/]OIZQQO1G/]OIfQRO1G/]OImQRO1G/]OItQbO,59rOJOQQO1G/]OOQa1G/]1G/]OJWQQO1G0POOQa1G0Q1G0QOJcQbO1G0QOOQO'#E`'#E`OJWQQO1G0POOQa1G0P1G0POOQ`'#Ea'#EaOJcQbO1G0QOJmQbO1G0ZOKXQbO1G0YOKsQbO'#DjOLUQbO'#DjOLiQbO1G0ROOQ`-E8T-E8TOOQ`,5:q,5:qOOQ`-E8V-E8VOLtQQO,59wOOQO,59x,59xOOQO-E8W-E8WOL|QbO1G/bO:}QbO1G/vO:}QbO1G/YOMTQbO1G0SOM`QbO1G0WOM}QbO1G0WOOQ`-E8`-E8`ONUQQO7+$wOOQa7+$w7+$wON^QQO1G/^ONfQQO7+%kOOQa7+%k7+%kONqQbO7+%lOOQa7+%l7+%lOOQO-E8^-E8^OOQ`-E8_-E8_OOQ`'#E['#E[ON{QQO'#E[O! TQbO'#EuOOQ`,5:U,5:UO! hQbO'#DhO! mQQO'#DkOOQ`7+%m7+%mO! rQbO7+%mO! wQbO7+%mO!!PQbO7+$|O!!_QbO7+$|O!!oQbO7+%bO!!wQbO7+$tOOQ`7+%n7+%nO!!|QbO7+%nO!#RQbO7+%nO!#ZQbO7+%rOOQa<sAN>sOOQ`AN>SAN>SO!%zQbOAN>SO!&PQbOAN>SOOQ`-E8]-E8]OOQ`AN>hAN>hO!&XQbOAN>hO2yQbO,5:_O:}QbO,5:aOOQ`AN>tAN>tPItQbO'#EWOOQ`7+%Y7+%YOOQ`G23nG23nO!&^QbOG23nP!%^QbO'#DsOOQ`G24SG24SO!&cQQO1G/yOOQ`1G/{1G/{OOQ`LD)YLD)YO:}QbO7+%eOOQ`< (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 28, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], - tokenPrec: 2299 + tokenPrec: 2370 }) diff --git a/src/parser/tests/basics.test.ts b/src/parser/tests/basics.test.ts index b9e7c76..3558b5d 100644 --- a/src/parser/tests/basics.test.ts +++ b/src/parser/tests/basics.test.ts @@ -1023,7 +1023,7 @@ describe('import', () => { expect(`import str`).toMatchTree(` Import keyword import - AssignableIdentifier str + Identifier str `) }) @@ -1031,9 +1031,20 @@ describe('import', () => { expect(`import str math list`).toMatchTree(` Import keyword import - AssignableIdentifier str - AssignableIdentifier math - AssignableIdentifier list + Identifier str + Identifier math + Identifier list + `) + }) + + test('parses named args', () => { + expect(`import str only=ends-with?`).toMatchTree(` + Import + keyword import + Identifier str + NamedArg + NamedArgPrefix only= + Identifier ends-with? `) }) }) \ No newline at end of file diff --git a/src/prelude/index.ts b/src/prelude/index.ts index 4097565..8233834 100644 --- a/src/prelude/index.ts +++ b/src/prelude/index.ts @@ -45,7 +45,12 @@ export const globals = { return typeof v !== 'string' || this.scope.has(v) }, ref: (fn: Function) => fn, - import: function (this: VM, ...idents: string[]) { + import: function (this: VM, atNamed: Record = {}, ...idents: string[]) { + const onlyArray = Array.isArray(atNamed.only) ? atNamed.only : [atNamed.only].filter(a => a) + const only = new Set(onlyArray) + const wantsOnly = only.size > 0 + + for (const ident of idents) { const module = this.get(ident) @@ -54,6 +59,7 @@ export const globals = { for (const [name, value] of module.value.entries()) { if (value.type === 'dict') throw new Error(`import: can't import dicts in dicts`) + if (wantsOnly && !only.has(name)) continue this.set(name, value) } } From f58ff1785ae52a3c71c2635a8802b6de655e48d8 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sun, 9 Nov 2025 19:45:02 -0800 Subject: [PATCH 35/48] dont print eval result --- bin/shrimp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/shrimp b/bin/shrimp index d26ec68..f75e6b2 100755 --- a/bin/shrimp +++ b/bin/shrimp @@ -65,8 +65,8 @@ async function main() { try { mkdirSync('/tmp/shrimp') } catch { } const path = `/tmp/shrimp/${randomUUID()}.sh` - writeFileSync(path, code) - console.log(await runFile(path)) + writeFileSync(path, fullCode) + await runFile(path) return } From 9eaa71fe2df243f932ef26bbf71f2f8814afc631 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sun, 9 Nov 2025 19:46:54 -0800 Subject: [PATCH 36/48] cli: Add -I (import) --- bin/shrimp | 50 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/bin/shrimp b/bin/shrimp index f75e6b2..49d1d78 100755 --- a/bin/shrimp +++ b/bin/shrimp @@ -12,16 +12,21 @@ import { join } from 'path' function showHelp() { console.log(`${colors.bright}${colors.magenta}🦐 Shrimp${colors.reset} is a scripting language in a shell. -${colors.bright}Usage:${colors.reset} shrimp [...args] +${colors.bright}Usage:${colors.reset} shrimp [options] [...args] ${colors.bright}Commands:${colors.reset} ${colors.cyan}run ${colors.yellow}./my-file.sh${colors.reset} Execute a file with Shrimp ${colors.cyan}parse ${colors.yellow}./my-file.sh${colors.reset} Print parse tree for Shrimp file ${colors.cyan}bytecode ${colors.yellow}./my-file.sh${colors.reset} Print bytecode for Shrimp file - ${colors.cyan}eval ${colors.yellow}'some code'${colors.reset} Evaluate a line of Shrimp code + ${colors.cyan}eval ${colors.yellow}'some code'${colors.reset} Evaluate a line of Shrimp code ${colors.cyan}repl${colors.reset} Start REPL ${colors.cyan}help${colors.reset} Print this help message - ${colors.cyan}version${colors.reset} Print version`) + ${colors.cyan}version${colors.reset} Print version + +${colors.bright}Options:${colors.reset} + ${colors.cyan}eval -I${colors.reset} ${colors.yellow}${colors.reset} Import module (can be repeated) + Example: shrimp -I math -e 'random | echo' + Example: shrimp -Imath -Istr -e 'random | echo'`) } function showVersion() { @@ -29,7 +34,40 @@ function showVersion() { } async function main() { - const args = process.argv.slice(2) + let args = process.argv.slice(2) + + if (args.length === 0) { + showHelp() + return + } + + // Parse -I flags for imports (supports both "-I math" and "-Imath") + const imports: string[] = [] + + while (args.length > 0) { + const arg = args[0] + + if (arg === '-I') { + // "-I math" format + if (args.length < 2) { + console.log(`${colors.bright}error: -I requires a module name${colors.reset}`) + process.exit(1) + } + imports.push(args[1]) + args = args.slice(2) + } else if (arg.startsWith('-I')) { + // "-Imath" format + const moduleName = arg.slice(2) + if (!moduleName) { + console.log(`${colors.bright}error: -I requires a module name${colors.reset}`) + process.exit(1) + } + imports.push(moduleName) + args = args.slice(1) + } else { + break + } + } if (args.length === 0) { showHelp() @@ -63,6 +101,10 @@ async function main() { process.exit(1) } + // Prepend import statement if -I flags were provided + const importStatement = imports.length > 0 ? `import ${imports.join(' ')}` : '' + const fullCode = importStatement ? `${importStatement}; ${code}` : code + try { mkdirSync('/tmp/shrimp') } catch { } const path = `/tmp/shrimp/${randomUUID()}.sh` writeFileSync(path, fullCode) From 10e1986fe22344134846e6d170bfdb6ae9e6a7b6 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sun, 9 Nov 2025 19:52:59 -0800 Subject: [PATCH 37/48] cli: add print/-E --- bin/shrimp | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/bin/shrimp b/bin/shrimp index 49d1d78..fe856b3 100755 --- a/bin/shrimp +++ b/bin/shrimp @@ -2,10 +2,9 @@ import { colors } from '../src/prelude' import { treeToString } from '../src/utils/tree' -import { runFile, compileFile, parseCode } from '../src' +import { runCode, runFile, compileFile, parseCode } from '../src' import { bytecodeToString } from 'reefvm' -import { readFileSync, writeFileSync, mkdirSync } from 'fs' -import { randomUUID } from 'crypto' +import { readFileSync } from 'fs' import { spawn } from 'child_process' import { join } from 'path' @@ -19,6 +18,7 @@ ${colors.bright}Commands:${colors.reset} ${colors.cyan}parse ${colors.yellow}./my-file.sh${colors.reset} Print parse tree for Shrimp file ${colors.cyan}bytecode ${colors.yellow}./my-file.sh${colors.reset} Print bytecode for Shrimp file ${colors.cyan}eval ${colors.yellow}'some code'${colors.reset} Evaluate a line of Shrimp code + ${colors.cyan}print ${colors.yellow}'some code'${colors.reset} Evaluate a line of Shrimp code and print the result ${colors.cyan}repl${colors.reset} Start REPL ${colors.cyan}help${colors.reset} Print this help message ${colors.cyan}version${colors.reset} Print version @@ -33,6 +33,12 @@ function showVersion() { console.log('🦐 v0.0.1') } +async function evalCode(code: string, imports: string[]) { + const importStatement = imports.length > 0 ? `import ${imports.join(' ')}` : '' + if (importStatement) code = `${importStatement}; ${code}` + return await runCode(code) +} + async function main() { let args = process.argv.slice(2) @@ -101,14 +107,18 @@ async function main() { process.exit(1) } - // Prepend import statement if -I flags were provided - const importStatement = imports.length > 0 ? `import ${imports.join(' ')}` : '' - const fullCode = importStatement ? `${importStatement}; ${code}` : code + await evalCode(code, imports) + return + } - try { mkdirSync('/tmp/shrimp') } catch { } - const path = `/tmp/shrimp/${randomUUID()}.sh` - writeFileSync(path, fullCode) - await runFile(path) + if (['print', '-print', '--print', '-E'].includes(command)) { + const code = args[1] + if (!code) { + console.log(`${colors.bright}usage: shrimp print ${colors.reset}`) + process.exit(1) + } + + console.log(await evalCode(code, imports)) return } From 793565cafad2aea5a909b67aff259a91d2ce3d4f Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sun, 9 Nov 2025 21:50:58 -0800 Subject: [PATCH 38/48] JUMP relative #s are broken in array, use labels --- src/compiler/compiler.ts | 42 +++++++++++++++++++---------- src/compiler/tests/compiler.test.ts | 10 +++++++ 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index d52576e..3515ae2 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -313,15 +313,17 @@ export class Compiler { if (opValue === '??=') { instructions.push(['LOAD', identifierName]) + const skipLabel: Label = `.skip_${this.labelCount++}` const rightInstructions = this.#compileNode(right, input) instructions.push(['DUP']) instructions.push(['PUSH', null]) instructions.push(['NEQ']) - instructions.push(['JUMP_IF_TRUE', rightInstructions.length + 1]) + instructions.push(['JUMP_IF_TRUE', skipLabel]) instructions.push(['POP']) instructions.push(...rightInstructions) + instructions.push([`${skipLabel}:`]) instructions.push(['DUP']) instructions.push(['STORE', identifierName]) @@ -419,8 +421,8 @@ export class Compiler { case terms.FunctionCallOrIdentifier: { if (node.firstChild?.type.id === terms.DotGet) { const instructions: ProgramItem[] = [] - const callLabel = `.call_dotget_${++this.labelCount}` - const afterLabel = `.after_dotget_${++this.labelCount}` + const callLabel: Label = `.call_dotget_${++this.labelCount}` + const afterLabel: Label = `.after_dotget_${++this.labelCount}` instructions.push(...this.#compileNode(node.firstChild, input)) instructions.push(['DUP']) @@ -582,19 +584,24 @@ export class Compiler { instructions.push(...this.#compileNode(conditionNode, input)) this.ifLabelCount++ const endLabel: Label = `.end_${this.ifLabelCount}` + const elseLabel: Label = `.else_${this.ifLabelCount}` const thenBlockInstructions = this.#compileNode(thenBlock, input) - instructions.push(['JUMP_IF_FALSE', thenBlockInstructions.length + 1]) + instructions.push(['JUMP_IF_FALSE', elseLabel]) instructions.push(...thenBlockInstructions) instructions.push(['JUMP', endLabel]) + instructions.push([`${elseLabel}:`]) + // Else if - elseIfBlocks.forEach(({ conditional, thenBlock }) => { + elseIfBlocks.forEach(({ conditional, thenBlock }, index) => { instructions.push(...this.#compileNode(conditional, input)) + const nextLabel: Label = `.elsif_${this.ifLabelCount}_${index}` const elseIfInstructions = this.#compileNode(thenBlock, input) - instructions.push(['JUMP_IF_FALSE', elseIfInstructions.length + 1]) + instructions.push(['JUMP_IF_FALSE', nextLabel]) instructions.push(...elseIfInstructions) instructions.push(['JUMP', endLabel]) + instructions.push([`${nextLabel}:`]) }) // Else @@ -643,34 +650,41 @@ export class Compiler { instructions.push(...leftInstructions, ...rightInstructions, ['GTE']) break - case 'and': + case 'and': { + const skipLabel: Label = `.skip_${this.labelCount++}` instructions.push(...leftInstructions) instructions.push(['DUP']) - instructions.push(['JUMP_IF_FALSE', rightInstructions.length + 1]) + instructions.push(['JUMP_IF_FALSE', skipLabel]) instructions.push(['POP']) instructions.push(...rightInstructions) + instructions.push([`${skipLabel}:`]) break + } - case 'or': + case 'or': { + const skipLabel: Label = `.skip_${this.labelCount++}` instructions.push(...leftInstructions) instructions.push(['DUP']) - instructions.push(['JUMP_IF_TRUE', rightInstructions.length + 1]) + instructions.push(['JUMP_IF_TRUE', skipLabel]) instructions.push(['POP']) instructions.push(...rightInstructions) - + instructions.push([`${skipLabel}:`]) break + } - case '??': + case '??': { // Nullish coalescing: return left if not null, else right + const skipLabel: Label = `.skip_${this.labelCount++}` instructions.push(...leftInstructions) instructions.push(['DUP']) instructions.push(['PUSH', null]) instructions.push(['NEQ']) - instructions.push(['JUMP_IF_TRUE', rightInstructions.length + 1]) + instructions.push(['JUMP_IF_TRUE', skipLabel]) instructions.push(['POP']) instructions.push(...rightInstructions) - + instructions.push([`${skipLabel}:`]) break + } default: throw new CompilerError(`Unsupported conditional operator: ${opValue}`, op.from, op.to) diff --git a/src/compiler/tests/compiler.test.ts b/src/compiler/tests/compiler.test.ts index 45fdb39..daecec8 100644 --- a/src/compiler/tests/compiler.test.ts +++ b/src/compiler/tests/compiler.test.ts @@ -188,6 +188,16 @@ describe('compiler', () => { test('single line if', () => { expect(`if 3 < 9: shire end`).toEvaluateTo('shire') }) + + test('if statement with function definition (bytecode labels)', () => { + expect(` + if false: + abc = do x: x end + else: + nope + end + `).toEvaluateTo('nope') + }) }) describe('errors', () => { From b400f48676bf18e961b00501fbfe7a4a93c4d570 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sun, 9 Nov 2025 23:49:48 -0800 Subject: [PATCH 39/48] Chained DotGet, meta.script.name. Doesn't work for numbers (row.2.1) yet --- src/compiler/compiler.ts | 33 ++++-- src/compiler/tests/compiler.test.ts | 37 +++++++ src/compiler/utils.ts | 6 +- src/parser/shrimp.grammar | 2 +- src/parser/shrimp.ts | 8 +- src/parser/tests/dot-get.test.ts | 150 ++++++++++++++++++++++++++++ src/parser/tokenizer.ts | 22 ++-- 7 files changed, 238 insertions(+), 20 deletions(-) diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 3515ae2..6b98c1b 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -211,16 +211,37 @@ export class Compiler { } case terms.DotGet: { + // DotGet is parsed into a nested tree because it's hard to parse it into a flat one. + // However, we want a flat tree - so we're going to pretend like we are getting one from the parser. + // + // This: DotGet(config, DotGet(script, name)) + // Becomes: DotGet(config, script, name) const { objectName, property } = getDotGetParts(node, input) const instructions: ProgramItem[] = [] + instructions.push(['TRY_LOAD', objectName]) - if (property.type.id === terms.ParenExpr) { - instructions.push(...this.#compileNode(property, input)) - } else { - const propertyValue = input.slice(property.from, property.to) - instructions.push(['PUSH', propertyValue]) + + const flattenProperty = (prop: SyntaxNode): void => { + if (prop.type.id === terms.DotGet) { + const nestedParts = getDotGetParts(prop, input) + + const nestedObjectValue = input.slice(nestedParts.object.from, nestedParts.object.to) + instructions.push(['PUSH', nestedObjectValue]) + instructions.push(['DOT_GET']) + + flattenProperty(nestedParts.property) + } else { + if (prop.type.id === terms.ParenExpr) { + instructions.push(...this.#compileNode(prop, input)) + } else { + const propertyValue = input.slice(prop.from, prop.to) + instructions.push(['PUSH', propertyValue]) + } + instructions.push(['DOT_GET']) + } } - instructions.push(['DOT_GET']) + + flattenProperty(property) return instructions } diff --git a/src/compiler/tests/compiler.test.ts b/src/compiler/tests/compiler.test.ts index daecec8..bcddafb 100644 --- a/src/compiler/tests/compiler.test.ts +++ b/src/compiler/tests/compiler.test.ts @@ -294,6 +294,43 @@ describe('dot get', () => { test('use parens expr with dot-get', () => { expect(`a = 1; arr = array 'a' 'b' 'c'; arr.(1 + a)`).toEvaluateTo('c', { array }) }) + + test('chained dot get: two levels', () => { + expect(`obj = [inner=[value=42]]; obj.inner.value`).toEvaluateTo(42) + }) + + test('chained dot get: three levels', () => { + expect(`obj = [a=[b=[c=123]]]; obj.a.b.c`).toEvaluateTo(123) + }) + + test('chained dot get: four levels', () => { + expect(`obj = [w=[x=[y=[z='deep']]]]; obj.w.x.y.z`).toEvaluateTo('deep') + }) + + test('chained dot get with numeric index', () => { + expect(`obj = [items=[1 2 3]]; obj.items.0`).toEvaluateTo(1) + }) + + test('chained dot get in expression', () => { + expect(`config = [server=[port=3000]]; config.server.port + 1`).toEvaluateTo(3001) + }) + + test('chained dot get as function argument', () => { + const double = (x: number) => x * 2 + expect(`obj = [val=[num=21]]; double obj.val.num`).toEvaluateTo(42, { double }) + }) + + test('chained dot get in binary operation', () => { + expect(`a = [x=[y=10]]; b = [x=[y=20]]; a.x.y + b.x.y`).toEvaluateTo(30) + }) + + test('chained dot get with parens at end', () => { + expect(`idx = 1; obj = [items=[10 20 30]]; obj.items.(idx)`).toEvaluateTo(20) + }) + + test('mixed chained and simple dot get', () => { + expect(`obj = [a=1 b=[c=2]]; obj.a + obj.b.c`).toEvaluateTo(3) + }) }) describe('default params', () => { diff --git a/src/compiler/utils.ts b/src/compiler/utils.ts index c424be2..2aae236 100644 --- a/src/compiler/utils.ts +++ b/src/compiler/utils.ts @@ -301,9 +301,9 @@ export const getDotGetParts = (node: SyntaxNode, input: string) => { ) } - if (![terms.Identifier, terms.Number, terms.ParenExpr].includes(property.type.id)) { + if (![terms.Identifier, terms.Number, terms.ParenExpr, terms.DotGet].includes(property.type.id)) { throw new CompilerError( - `DotGet property must be an Identifier or Number, got ${property.type.name}`, + `DotGet property must be an Identifier, Number, ParenExpr, or DotGet, got ${property.type.name}`, property.from, property.to ) @@ -311,7 +311,7 @@ export const getDotGetParts = (node: SyntaxNode, input: string) => { const objectName = input.slice(object.from, object.to) - return { objectName, property } + return { object, objectName, property } } export const getTryExprParts = (node: SyntaxNode, input: string) => { diff --git a/src/parser/shrimp.grammar b/src/parser/shrimp.grammar index 80adcdb..b8aa46a 100644 --- a/src/parser/shrimp.grammar +++ b/src/parser/shrimp.grammar @@ -239,7 +239,7 @@ expression { @skip {} { DotGet { - IdentifierBeforeDot dot (Number | Identifier | ParenExpr) + IdentifierBeforeDot dot (DotGet | Number | Identifier | ParenExpr) } String { diff --git a/src/parser/shrimp.ts b/src/parser/shrimp.ts index 051f00e..b941d88 100644 --- a/src/parser/shrimp.ts +++ b/src/parser/shrimp.ts @@ -7,9 +7,9 @@ import {highlighting} from "./highlight" const spec_Identifier = {__proto__:null,while:78, null:112, catch:118, finally:124, end:126, if:134, else:140, try:158, throw:162, import:166} export const parser = LRParser.deserialize({ version: 14, - states: "=|QYQbOOO!mOpO'#DXO!rOSO'#D`OOQa'#D`'#D`O%mQcO'#DvO(mQcO'#EiOOQ`'#Ew'#EwO)WQRO'#DwO+]QcO'#EgO+vQbO'#DVOOQa'#Dy'#DyO.[QbO'#DzOOQa'#Ei'#EiO.cQcO'#EiO0aQcO'#EhO1fQcO'#EgO1sQRO'#ESOOQ`'#Eg'#EgO2[QbO'#EgO2cQQO'#EfOOQ`'#Ef'#EfOOQ`'#EU'#EUQYQbOOO2nQbO'#D[O2yQbO'#DpO3tQbO'#DSO4oQQO'#D|O3tQbO'#EOO4tQbO'#EQO4|ObO,59sO5XQbO'#DbO5aQWO'#DcOOOO'#Eo'#EoOOOO'#EZ'#EZO5uOSO,59zOOQa,59z,59zOOQ`'#DZ'#DZO6TQbO'#DoOOQ`'#Em'#EmOOQ`'#E^'#E^O6_QbO,5:^OOQa'#Eh'#EhO3tQbO,5:cO3tQbO,5:cO3tQbO,5:cO3tQbO,5:cO3tQbO,59pO3tQbO,59pO3tQbO,59pO3tQbO,59pOOQ`'#EW'#EWO+vQbO,59qO7XQcO'#DvO7`QcO'#EiO7gQRO,59qO7qQQO,59qO7vQQO,59qO8OQQO,59qO8ZQRO,59qO8sQRO,59qO8zQQO'#DQO9PQbO,5:fO9WQQO,5:eOOQa,5:f,5:fO9cQbO,5:fO9mQbO,5:oO9mQbO,5:nO:}QbO,5:gO;UQbO,59lOOQ`,5;Q,5;QO9mQbO'#EVOOQ`-E8S-E8SOOQ`'#EX'#EXO;pQbO'#D]O;{QbO'#D^OOQO'#EY'#EYO;sQQO'#D]O`QRO'#EvOOQO'#Ev'#EvO>gQQO,5:[O>lQRO,59nO>sQRO,59nO:}QbO,5:hO?RQcO,5:jO@aQcO,5:jO@}QcO,5:jOArQbO,5:lOOQ`'#Eb'#EbO4tQbO,5:lOOQa1G/_1G/_OOOO,59|,59|OOOO,59},59}OOOO-E8X-E8XOOQa1G/f1G/fOOQ`,5:Z,5:ZOOQ`-E8[-E8[OOQa1G/}1G/}OCkQcO1G/}OCuQcO1G/}OETQcO1G/}OE_QcO1G/}OElQcO1G/}OOQa1G/[1G/[OF}QcO1G/[OGUQcO1G/[OG]QcO1G/[OH[QcO1G/[OGdQcO1G/[OOQ`-E8U-E8UOHrQRO1G/]OH|QQO1G/]OIRQQO1G/]OIZQQO1G/]OIfQRO1G/]OImQRO1G/]OItQbO,59rOJOQQO1G/]OOQa1G/]1G/]OJWQQO1G0POOQa1G0Q1G0QOJcQbO1G0QOOQO'#E`'#E`OJWQQO1G0POOQa1G0P1G0POOQ`'#Ea'#EaOJcQbO1G0QOJmQbO1G0ZOKXQbO1G0YOKsQbO'#DjOLUQbO'#DjOLiQbO1G0ROOQ`-E8T-E8TOOQ`,5:q,5:qOOQ`-E8V-E8VOLtQQO,59wOOQO,59x,59xOOQO-E8W-E8WOL|QbO1G/bO:}QbO1G/vO:}QbO1G/YOMTQbO1G0SOM`QbO1G0WOM}QbO1G0WOOQ`-E8`-E8`ONUQQO7+$wOOQa7+$w7+$wON^QQO1G/^ONfQQO7+%kOOQa7+%k7+%kONqQbO7+%lOOQa7+%l7+%lOOQO-E8^-E8^OOQ`-E8_-E8_OOQ`'#E['#E[ON{QQO'#E[O! TQbO'#EuOOQ`,5:U,5:UO! hQbO'#DhO! mQQO'#DkOOQ`7+%m7+%mO! rQbO7+%mO! wQbO7+%mO!!PQbO7+$|O!!_QbO7+$|O!!oQbO7+%bO!!wQbO7+$tOOQ`7+%n7+%nO!!|QbO7+%nO!#RQbO7+%nO!#ZQbO7+%rOOQa<sAN>sOOQ`AN>SAN>SO!%zQbOAN>SO!&PQbOAN>SOOQ`-E8]-E8]OOQ`AN>hAN>hO!&XQbOAN>hO2yQbO,5:_O:}QbO,5:aOOQ`AN>tAN>tPItQbO'#EWOOQ`7+%Y7+%YOOQ`G23nG23nO!&^QbOG23nP!%^QbO'#DsOOQ`G24SG24SO!&cQQO1G/yOOQ`1G/{1G/{OOQ`LD)YLD)YO:}QbO7+%eOOQ`<cQRO'#EvOOQO'#Ev'#EvO>jQQO,5:[O>oQRO,59nO>vQRO,59nO;QQbO,5:hO?UQcO,5:jO@dQcO,5:jOAQQcO,5:jOAuQbO,5:lOOQ`'#Eb'#EbO4tQbO,5:lOOQa1G/_1G/_OOOO,59|,59|OOOO,59},59}OOOO-E8X-E8XOOQa1G/f1G/fOOQ`,5:Z,5:ZOOQ`-E8[-E8[OOQa1G/}1G/}OCnQcO1G/}OCxQcO1G/}OEWQcO1G/}OEbQcO1G/}OEoQcO1G/}OOQa1G/[1G/[OGQQcO1G/[OGXQcO1G/[OG`QcO1G/[OH_QcO1G/[OGgQcO1G/[OOQ`-E8U-E8UOHuQRO1G/]OIPQQO1G/]OIUQQO1G/]OI^QQO1G/]OIiQRO1G/]OIpQRO1G/]OIwQbO,59rOJRQQO1G/]OOQa1G/]1G/]OJZQQO1G0POOQa1G0Q1G0QOJfQbO1G0QOOQO'#E`'#E`OJZQQO1G0POOQa1G0P1G0POOQ`'#Ea'#EaOJfQbO1G0QOJpQbO1G0ZOK[QbO1G0YOKvQbO'#DjOLXQbO'#DjOLlQbO1G0ROOQ`-E8T-E8TOOQ`,5:q,5:qOOQ`-E8V-E8VOLwQQO,59wOOQO,59x,59xOOQO-E8W-E8WOMPQbO1G/bO;QQbO1G/vO;QQbO1G/YOMWQbO1G0SOMcQbO1G0WONQQbO1G0WOOQ`-E8`-E8`ONXQQO7+$wOOQa7+$w7+$wONaQQO1G/^ONiQQO7+%kOOQa7+%k7+%kONtQbO7+%lOOQa7+%l7+%lOOQO-E8^-E8^OOQ`-E8_-E8_OOQ`'#E['#E[O! OQQO'#E[O! WQbO'#EuOOQ`,5:U,5:UO! kQbO'#DhO! pQQO'#DkOOQ`7+%m7+%mO! uQbO7+%mO! zQbO7+%mO!!SQbO7+$|O!!bQbO7+$|O!!rQbO7+%bO!!zQbO7+$tOOQ`7+%n7+%nO!#PQbO7+%nO!#UQbO7+%nO!#^QbO7+%rOOQa<sAN>sOOQ`AN>SAN>SO!%}QbOAN>SO!&SQbOAN>SOOQ`-E8]-E8]OOQ`AN>hAN>hO!&[QbOAN>hO2yQbO,5:_O;QQbO,5:aOOQ`AN>tAN>tPIwQbO'#EWOOQ`7+%Y7+%YOOQ`G23nG23nO!&aQbOG23nP!%aQbO'#DsOOQ`G24SG24SO!&fQQO1G/yOOQ`1G/{1G/{OOQ`LD)YLD)YO;QQbO7+%eOOQ`< (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 28, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], - tokenPrec: 2370 + tokenPrec: 2373 }) diff --git a/src/parser/tests/dot-get.test.ts b/src/parser/tests/dot-get.test.ts index f781937..4181ce6 100644 --- a/src/parser/tests/dot-get.test.ts +++ b/src/parser/tests/dot-get.test.ts @@ -298,4 +298,154 @@ end`).toMatchTree(` Number 2 `) }) + + // NOTE: these are parsed as DotGet(meta, DotGet(script, name)) because that's easiest, + // but the compiler flattens them + test('chained dot get: meta.script.name', () => { + expect('meta = 42; meta.script.name').toMatchTree(` + Assign + AssignableIdentifier meta + Eq = + Number 42 + FunctionCallOrIdentifier + DotGet + IdentifierBeforeDot meta + DotGet + IdentifierBeforeDot script + Identifier name + `) + }) + + test('chained dot get: a.b.c.d', () => { + expect('a = 1; a.b.c.d').toMatchTree(` + Assign + AssignableIdentifier a + Eq = + Number 1 + FunctionCallOrIdentifier + DotGet + IdentifierBeforeDot a + DotGet + IdentifierBeforeDot b + DotGet + IdentifierBeforeDot c + Identifier d + `) + }) + + test('chained dot get in function call', () => { + expect('config = 1; echo config.db.host').toMatchTree(` + Assign + AssignableIdentifier config + Eq = + Number 1 + FunctionCall + Identifier echo + PositionalArg + DotGet + IdentifierBeforeDot config + DotGet + IdentifierBeforeDot db + Identifier host + `) + }) + + test('chained dot get with numeric index at end', () => { + expect('obj = 1; obj.items.0').toMatchTree(` + Assign + AssignableIdentifier obj + Eq = + Number 1 + FunctionCallOrIdentifier + DotGet + IdentifierBeforeDot obj + DotGet + IdentifierBeforeDot items + Number 0 + `) + }) + + test('chained dot get with ParenExpr at end', () => { + expect('obj = 1; obj.items.(i)').toMatchTree(` + Assign + AssignableIdentifier obj + Eq = + Number 1 + FunctionCallOrIdentifier + DotGet + IdentifierBeforeDot obj + DotGet + IdentifierBeforeDot items + ParenExpr + FunctionCallOrIdentifier + Identifier i + `) + }) + + test('not in scope remains Word with chained dots', () => { + expect('readme.md.bak').toMatchTree(`Word readme.md.bak`) + }) + + test('chained dot get in nested functions', () => { + expect(`do cfg: + do inner: + cfg.db.host + end +end`).toMatchTree(` + FunctionDef + Do do + Params + Identifier cfg + colon : + FunctionDef + Do do + Params + Identifier inner + colon : + FunctionCallOrIdentifier + DotGet + IdentifierBeforeDot cfg + DotGet + IdentifierBeforeDot db + Identifier host + keyword end + keyword end + `) + }) + + test('mixed simple and chained dot get', () => { + expect('obj = 1; obj.a; obj.b.c').toMatchTree(` + Assign + AssignableIdentifier obj + Eq = + Number 1 + FunctionCallOrIdentifier + DotGet + IdentifierBeforeDot obj + Identifier a + FunctionCallOrIdentifier + DotGet + IdentifierBeforeDot obj + DotGet + IdentifierBeforeDot b + Identifier c + `) + }) + + test.skip('chained numeric dot get: row.2.1.b', () => { + expect('row = []; row.2.1').toMatchTree(` + Assign + AssignableIdentifier row + Eq = + Array [] + FunctionCallOrIdentifier + DotGet + IdentifierBeforeDot row + DotGet + Number 2 + DotGet + Number 1 + Identifier b + `) + }) }) diff --git a/src/parser/tokenizer.ts b/src/parser/tokenizer.ts index 8ad55c2..75c027a 100644 --- a/src/parser/tokenizer.ts +++ b/src/parser/tokenizer.ts @@ -1,5 +1,5 @@ import { ExternalTokenizer, InputStream, Stack } from '@lezer/lr' -import { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot, Do, CurlyString } from './shrimp.terms' +import { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot, Do, CurlyString, DotGet } from './shrimp.terms' // doobie doobie do (we need the `do` keyword to know when we're defining params) export function specializeKeyword(ident: string) { @@ -187,11 +187,21 @@ const checkForDotGet = (input: InputStream, stack: Stack, pos: number): number | const identifierText = buildIdentifierText(input, pos) const context = stack.context as { scope: { has(name: string): boolean } } | undefined - // If identifier is in scope, this is property access (e.g., obj.prop) - // If not in scope, it should be consumed as a Word (e.g., file.txt) - return context?.scope.has(identifierText) || globals.includes(identifierText) - ? IdentifierBeforeDot - : null + // Check if identifier is in scope (lexical scope or globals) + const inScope = context?.scope.has(identifierText) || globals.includes(identifierText) + + // property access + if (inScope) return IdentifierBeforeDot + + // Not in scope - check if we're inside a DotGet chain + // Inside the @skip {} block where DotGet is defined, Word cannot be shifted + // but Identifier can be. This tells us we're at the RHS of a DotGet. + const canShiftIdentifier = stack.canShift(Identifier) + const canShiftWord = stack.canShift(Word) + const inDotGetChain = canShiftIdentifier && !canShiftWord + + // continue if we're inside a DotGet + return inDotGetChain ? IdentifierBeforeDot : null } // Decide between AssignableIdentifier and Identifier using grammar state + peek-ahead From c244435ae25776f7420d289ff49e4d309e119e8c Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Mon, 10 Nov 2025 00:53:16 -0800 Subject: [PATCH 40/48] add $. w/ shrimp runtime info --- bin/repl | 3 ++ bin/shrimp | 8 +++- src/compiler/utils.ts | 2 +- src/index.ts | 12 +++--- src/parser/shrimp.grammar | 6 ++- src/parser/shrimp.terms.ts | 69 ++++++++++++++++---------------- src/parser/shrimp.ts | 20 ++++----- src/parser/tests/dot-get.test.ts | 9 +++++ src/parser/tests/strings.test.ts | 9 +++-- src/prelude/index.ts | 16 +++++++- src/prelude/tests/info.test.ts | 43 ++++++++++++++++++-- 11 files changed, 135 insertions(+), 62 deletions(-) diff --git a/bin/repl b/bin/repl index 4cc5fd6..326d71a 100755 --- a/bin/repl +++ b/bin/repl @@ -7,6 +7,9 @@ import * as readline from 'readline' import { readFileSync, writeFileSync } from 'fs' import { basename } from 'path' +globals.$.script.name = '(repl)' +globals.$.script.path = '(repl)' + async function repl() { const commands = ['/clear', '/reset', '/vars', '/funcs', '/history', '/bytecode', '/exit', '/save', '/quit'] diff --git a/bin/shrimp b/bin/shrimp index fe856b3..04169bf 100755 --- a/bin/shrimp +++ b/bin/shrimp @@ -1,13 +1,17 @@ #!/usr/bin/env bun -import { colors } from '../src/prelude' +import { colors, globals as prelude } from '../src/prelude' import { treeToString } from '../src/utils/tree' import { runCode, runFile, compileFile, parseCode } from '../src' +import { resolve } from 'path' import { bytecodeToString } from 'reefvm' import { readFileSync } from 'fs' import { spawn } from 'child_process' import { join } from 'path' +const idx = Bun.argv.indexOf('--') +prelude.$.args = idx >= 0 ? Bun.argv.slice(idx + 1) : [] + function showHelp() { console.log(`${colors.bright}${colors.magenta}🦐 Shrimp${colors.reset} is a scripting language in a shell. @@ -149,10 +153,12 @@ async function main() { console.log(`${colors.bright}usage: shrimp run ${colors.reset}`) process.exit(1) } + prelude.$.script.path = resolve(file) await runFile(file) return } + prelude.$.script.path = resolve(command) await runFile(command) } diff --git a/src/compiler/utils.ts b/src/compiler/utils.ts index 2aae236..446aab3 100644 --- a/src/compiler/utils.ts +++ b/src/compiler/utils.ts @@ -293,7 +293,7 @@ export const getDotGetParts = (node: SyntaxNode, input: string) => { ) } - if (object.type.id !== terms.IdentifierBeforeDot) { + if (object.type.id !== terms.IdentifierBeforeDot && object.type.id !== terms.Dollar) { throw new CompilerError( `DotGet object must be an IdentifierBeforeDot, got ${object.type.name}`, object.from, diff --git a/src/index.ts b/src/index.ts index 687e513..47f5444 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,7 +4,7 @@ import { type Tree } from '@lezer/common' import { Compiler } from '#compiler/compiler' import { parser } from '#parser/shrimp' import { globals as parserGlobals, setGlobals as setParserGlobals } from '#parser/tokenizer' -import { globals as shrimpGlobals } from '#prelude' +import { globals as prelude } from '#prelude' export { Compiler } from '#compiler/compiler' export { parser } from '#parser/shrimp' @@ -19,7 +19,7 @@ export class Shrimp { constructor(globals?: Record) { const emptyBytecode = { instructions: [], constants: [], labels: new Map() } - this.vm = new VM(emptyBytecode, Object.assign({}, shrimpGlobals, globals ?? {})) + this.vm = new VM(emptyBytecode, Object.assign({}, prelude, globals ?? {})) this.globals = globals } @@ -53,7 +53,7 @@ export class Shrimp { let bytecode if (typeof code === 'string') { - const compiler = new Compiler(code, Object.keys(Object.assign({}, shrimpGlobals, this.globals ?? {}, locals ?? {}))) + const compiler = new Compiler(code, Object.keys(Object.assign({}, prelude, this.globals ?? {}, locals ?? {}))) bytecode = compiler.bytecode } else { bytecode = code @@ -79,7 +79,7 @@ export async function runCode(code: string, globals?: Record): Prom } export async function runBytecode(bytecode: Bytecode, globals?: Record): Promise { - const vm = new VM(bytecode, Object.assign({}, shrimpGlobals, globals)) + const vm = new VM(bytecode, Object.assign({}, prelude, globals)) await vm.run() return vm.stack.length ? fromValue(vm.stack[vm.stack.length - 1]!, vm) : null } @@ -90,7 +90,7 @@ export function compileFile(path: string, globals?: Record): Byteco } export function compileCode(code: string, globals?: Record): Bytecode { - const globalNames = [...Object.keys(shrimpGlobals), ...(globals ? Object.keys(globals) : [])] + const globalNames = [...Object.keys(prelude), ...(globals ? Object.keys(globals) : [])] const compiler = new Compiler(code, globalNames) return compiler.bytecode } @@ -102,7 +102,7 @@ export function parseFile(path: string, globals?: Record): Tree { export function parseCode(code: string, globals?: Record): Tree { const oldGlobals = [...parserGlobals] - const globalNames = [...Object.keys(shrimpGlobals), ...(globals ? Object.keys(globals) : [])] + const globalNames = [...Object.keys(prelude), ...(globals ? Object.keys(globals) : [])] setParserGlobals(globalNames) const result = parser.parse(code) diff --git a/src/parser/shrimp.grammar b/src/parser/shrimp.grammar index b8aa46a..e8f2fa4 100644 --- a/src/parser/shrimp.grammar +++ b/src/parser/shrimp.grammar @@ -29,6 +29,7 @@ rightParen { ")" } colon[closedBy="end", @name="colon"] { ":" } Underscore { "_" } + Dollar { "$" } Regex { "//" (![/\\\n[] | "\\" ![\n] | "[" (![\n\\\]] | "\\" ![\n])* "]")+ ("//" $[gimsuy]*)? } // Stolen from the lezer JavaScript grammar "|"[@name=operator] } @@ -239,7 +240,8 @@ expression { @skip {} { DotGet { - IdentifierBeforeDot dot (DotGet | Number | Identifier | ParenExpr) + IdentifierBeforeDot dot (DotGet | Number | Identifier | ParenExpr) | + Dollar dot (DotGet | Number | Identifier | ParenExpr) } String { @@ -254,7 +256,7 @@ stringContent { } Interpolation { - "$" Identifier | + "$" FunctionCallOrIdentifier | "$" ParenExpr } diff --git a/src/parser/shrimp.terms.ts b/src/parser/shrimp.terms.ts index 0f3ba5b..2085974 100644 --- a/src/parser/shrimp.terms.ts +++ b/src/parser/shrimp.terms.ts @@ -37,42 +37,43 @@ export const Program = 35, PipeExpr = 36, WhileExpr = 38, - keyword = 83, + keyword = 84, ConditionalOp = 40, ParenExpr = 41, FunctionCallWithNewlines = 42, DotGet = 43, Number = 44, - PositionalArg = 45, - FunctionDef = 46, - Params = 47, - NamedParam = 48, - NamedArgPrefix = 49, - String = 50, - StringFragment = 51, - Interpolation = 52, - EscapeSeq = 53, - 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, - Import = 82, - CompoundAssign = 84, - Assign = 85 + Dollar = 45, + PositionalArg = 46, + FunctionDef = 47, + Params = 48, + NamedParam = 49, + NamedArgPrefix = 50, + String = 51, + StringFragment = 52, + Interpolation = 53, + FunctionCallOrIdentifier = 54, + EscapeSeq = 55, + DoubleQuote = 56, + Boolean = 57, + Null = 58, + colon = 59, + CatchExpr = 60, + Block = 62, + FinallyExpr = 63, + Underscore = 66, + NamedArg = 67, + IfExpr = 68, + FunctionCall = 70, + ElseIfExpr = 71, + ElseExpr = 73, + BinOp = 74, + Regex = 75, + Dict = 76, + Array = 77, + FunctionCallWithBlock = 78, + TryExpr = 79, + Throw = 81, + Import = 83, + CompoundAssign = 85, + Assign = 86 diff --git a/src/parser/shrimp.ts b/src/parser/shrimp.ts index b941d88..8b7359e 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:112, catch:118, finally:124, end:126, if:134, else:140, try:158, throw:162, import:166} +const spec_Identifier = {__proto__:null,while:78, null:116, catch:122, finally:128, end:130, if:138, else:144, try:160, throw:164, import:168} export const parser = LRParser.deserialize({ version: 14, - states: "=|QYQbOOO!mOpO'#DXO!rOSO'#D`OOQa'#D`'#D`O%mQcO'#DvO(mQcO'#EiOOQ`'#Ew'#EwO)WQRO'#DwO+]QcO'#EgO+vQbO'#DVOOQa'#Dy'#DyO.[QbO'#DzOOQa'#Ei'#EiO.cQcO'#EiO0aQcO'#EhO1fQcO'#EgO1sQRO'#ESOOQ`'#Eg'#EgO2[QbO'#EgO2cQQO'#EfOOQ`'#Ef'#EfOOQ`'#EU'#EUQYQbOOO2nQbO'#D[O2yQbO'#DpO3tQbO'#DSO4oQQO'#D|O3tQbO'#EOO4tQbO'#EQO4|ObO,59sO5[QbO'#DbO5dQWO'#DcOOOO'#Eo'#EoOOOO'#EZ'#EZO5xOSO,59zOOQa,59z,59zOOQ`'#DZ'#DZO6WQbO'#DoOOQ`'#Em'#EmOOQ`'#E^'#E^O6bQbO,5:^OOQa'#Eh'#EhO3tQbO,5:cO3tQbO,5:cO3tQbO,5:cO3tQbO,5:cO3tQbO,59pO3tQbO,59pO3tQbO,59pO3tQbO,59pOOQ`'#EW'#EWO+vQbO,59qO7[QcO'#DvO7cQcO'#EiO7jQRO,59qO7tQQO,59qO7yQQO,59qO8RQQO,59qO8^QRO,59qO8vQRO,59qO8}QQO'#DQO9SQbO,5:fO9ZQQO,5:eOOQa,5:f,5:fO9fQbO,5:fO9pQbO,5:oO9pQbO,5:nO;QQbO,5:gO;XQbO,59lOOQ`,5;Q,5;QO9pQbO'#EVOOQ`-E8S-E8SOOQ`'#EX'#EXO;sQbO'#D]OcQRO'#EvOOQO'#Ev'#EvO>jQQO,5:[O>oQRO,59nO>vQRO,59nO;QQbO,5:hO?UQcO,5:jO@dQcO,5:jOAQQcO,5:jOAuQbO,5:lOOQ`'#Eb'#EbO4tQbO,5:lOOQa1G/_1G/_OOOO,59|,59|OOOO,59},59}OOOO-E8X-E8XOOQa1G/f1G/fOOQ`,5:Z,5:ZOOQ`-E8[-E8[OOQa1G/}1G/}OCnQcO1G/}OCxQcO1G/}OEWQcO1G/}OEbQcO1G/}OEoQcO1G/}OOQa1G/[1G/[OGQQcO1G/[OGXQcO1G/[OG`QcO1G/[OH_QcO1G/[OGgQcO1G/[OOQ`-E8U-E8UOHuQRO1G/]OIPQQO1G/]OIUQQO1G/]OI^QQO1G/]OIiQRO1G/]OIpQRO1G/]OIwQbO,59rOJRQQO1G/]OOQa1G/]1G/]OJZQQO1G0POOQa1G0Q1G0QOJfQbO1G0QOOQO'#E`'#E`OJZQQO1G0POOQa1G0P1G0POOQ`'#Ea'#EaOJfQbO1G0QOJpQbO1G0ZOK[QbO1G0YOKvQbO'#DjOLXQbO'#DjOLlQbO1G0ROOQ`-E8T-E8TOOQ`,5:q,5:qOOQ`-E8V-E8VOLwQQO,59wOOQO,59x,59xOOQO-E8W-E8WOMPQbO1G/bO;QQbO1G/vO;QQbO1G/YOMWQbO1G0SOMcQbO1G0WONQQbO1G0WOOQ`-E8`-E8`ONXQQO7+$wOOQa7+$w7+$wONaQQO1G/^ONiQQO7+%kOOQa7+%k7+%kONtQbO7+%lOOQa7+%l7+%lOOQO-E8^-E8^OOQ`-E8_-E8_OOQ`'#E['#E[O! OQQO'#E[O! WQbO'#EuOOQ`,5:U,5:UO! kQbO'#DhO! pQQO'#DkOOQ`7+%m7+%mO! uQbO7+%mO! zQbO7+%mO!!SQbO7+$|O!!bQbO7+$|O!!rQbO7+%bO!!zQbO7+$tOOQ`7+%n7+%nO!#PQbO7+%nO!#UQbO7+%nO!#^QbO7+%rOOQa<sAN>sOOQ`AN>SAN>SO!%}QbOAN>SO!&SQbOAN>SOOQ`-E8]-E8]OOQ`AN>hAN>hO!&[QbOAN>hO2yQbO,5:_O;QQbO,5:aOOQ`AN>tAN>tPIwQbO'#EWOOQ`7+%Y7+%YOOQ`G23nG23nO!&aQbOG23nP!%aQbO'#DsOOQ`G24SG24SO!&fQQO1G/yOOQ`1G/{1G/{OOQ`LD)YLD)YO;QQbO7+%eOOQ`<`QYQbOOO!pOpO'#DXO%eQcO'#DdO%{OSO'#DaOOQa'#Da'#DaO(vQcO'#EjOOQ`'#Ex'#ExO)aQRO'#DxO+fQcO'#EhO,PQbO'#DVOOQa'#Dz'#DzO.kQbO'#D{OOQa'#Ej'#EjO.rQcO'#EjO0pQcO'#EiO1uQcO'#EhO2SQRO'#ETOOQ`'#Eh'#EhO2kQbO'#EhO2rQQO'#EgOOQ`'#Eg'#EgOOQ`'#EV'#EVQYQbOOO2}QbO'#D]O3YQbO'#DrO4WQbO'#DSO5UQQO'#D}O4WQbO'#EPO5ZQbO'#ERO5cObO,59sOOQ`'#D['#D[O5tQbO'#DqOOQ`'#En'#EnOOQ`'#E_'#E_O6OQbO,5:`OOQa'#Ei'#EiO6xQbO'#DcO7WQWO'#DeOOOO'#Ep'#EpOOOO'#E['#E[O7lOSO,59{OOQa,59{,59{O4WQbO,5:dO4WQbO,5:dO4WQbO,5:dO4WQbO,5:dO4WQbO,59pO4WQbO,59pO4WQbO,59pO4WQbO,59pOOQ`'#EX'#EXO,PQbO,59qO7zQcO'#DdO8RQcO'#EjO8YQRO,59qO8dQQO,59qO8iQQO,59qO8qQQO,59qO8|QRO,59qO9fQRO,59qO9mQQO'#DQO9rQbO,5:gO9yQQO,5:fOOQa,5:g,5:gO:UQbO,5:gO:`QbO,5:pO:`QbO,5:oO;sQbO,5:hO;zQbO,59lOOQ`,5;R,5;RO:`QbO'#EWOOQ`-E8T-E8TOOQ`'#EY'#EYOXQRO'#EwO?UQRO'#EwOOQO'#Ew'#EwO?]QQO,5:^O?bQRO,59nO?iQRO,59nO;sQbO,5:iO?wQcO,5:kOAVQcO,5:kOAsQcO,5:kOBhQbO,5:mOOQ`'#Ec'#EcO5ZQbO,5:mOOQa1G/_1G/_OOQ`,5:],5:]OOQ`-E8]-E8]OOOO'#Dd'#DdOOOO,59},59}OOOO,5:P,5:POOOO-E8Y-E8YOOQa1G/g1G/gOOQa1G0O1G0OODaQcO1G0OODkQcO1G0OOEyQcO1G0OOFTQcO1G0OOFbQcO1G0OOOQa1G/[1G/[OGsQcO1G/[OGzQcO1G/[OHRQcO1G/[OIQQcO1G/[OHYQcO1G/[OOQ`-E8V-E8VOIhQRO1G/]OIrQQO1G/]OIwQQO1G/]OJPQQO1G/]OJ[QRO1G/]OJcQRO1G/]OJjQbO,59rOJtQQO1G/]OOQa1G/]1G/]OJ|QQO1G0QOOQa1G0R1G0ROKXQbO1G0ROOQO'#Ea'#EaOJ|QQO1G0QOOQa1G0Q1G0QOOQ`'#Eb'#EbOKXQbO1G0ROKcQbO1G0[OK}QbO1G0ZOLiQbO'#DlOLzQbO'#DlOM_QbO1G0SOOQ`-E8U-E8UOOQ`,5:r,5:rOOQ`-E8W-E8WOMjQQO,59xOOQO,59y,59yOOQO-E8X-E8XOMrQbO1G/cO;sQbO1G/xO;sQbO1G/YOMyQbO1G0TONUQbO1G0XONsQbO1G0XOOQ`-E8a-E8aONzQQO7+$wOOQa7+$w7+$wO! SQQO1G/^O! [QQO7+%lOOQa7+%l7+%lO! gQbO7+%mOOQa7+%m7+%mOOQO-E8_-E8_OOQ`-E8`-E8`OOQ`'#E]'#E]O! qQQO'#E]O! yQbO'#EvOOQ`,5:W,5:WO!!^QbO'#DjO!!cQQO'#DmOOQ`7+%n7+%nO!!hQbO7+%nO!!mQbO7+%nO!!uQbO7+$}O!#TQbO7+$}O!#eQbO7+%dO!#mQbO7+$tOOQ`7+%o7+%oO!#rQbO7+%oO!#wQbO7+%oO!$PQbO7+%sOOQa<tAN>tOOQ`AN>TAN>TO!&pQbOAN>TO!&uQbOAN>TOOQ`-E8^-E8^OOQ`AN>jAN>jO!&}QbOAN>jO3YQbO,5:aO;sQbO,5:cOOQ`AN>uAN>uPJjQbO'#EXOOQ`7+%[7+%[OOQ`G23oG23oO!'SQbOG23oP!&SQbO'#DuOOQ`G24UG24UO!'XQQO1G/{OOQ`1G/}1G/}OOQ`LD)ZLD)ZO;sQbO7+%gOOQ`<gO!]$SO~O!]$TO~P>gOT!POU!QOj!RO!]$TO~Ou!sa#`!sa#q!sa!_!sa!b!sa!c!sa#m!sa!j!sa~P)aOP{OQ{OR|OS|Od}Oe}Of}Og}Oh}Oi}O~Ou!sa#`!sa#q!sa!_!sa!b!sa!c!sa#m!sa!j!sa~P@eOT!POU!QOj!ROu!sa#`!sa#q!sa!_!sa!b!sa!c!sa#m!sa!j!sa~Ol!jO!SoOu!ua#`!ua#q!ua!_!ua!b!ua!c!ua#m!ua!j!ua~O^zOR!liS!lid!lie!lif!lig!lih!lii!liu!li#`!li#q!li#m!li!_!li!b!li!c!li!j!li~OP!liQ!li~PCYOP{OQ{O~PCYOP{OQ{Od!lie!lif!lig!lih!lii!liu!li#`!li#q!li#m!li!_!li!b!li!c!li!j!li~OR!liS!li~PDuOR|OS|O^zO~PDuOR|OS|O~PDuOW!OOX!OOY!OOZ!OO[!OO]!OOTxijxiuxi#`xi#qxi#mxi!]xi!_xi!bxi!cxi!jxi~OU!QO~PFlOU!QO~PGOOUxi~PFlOT!POU!QOjxiuxi#`xi#qxi#mxi!]xi!_xi!bxi!cxi!jxi~OW!OOX!OOY!OOZ!OO[!OO]!OO~PHYO#`!SO#m$ZO~P*qO#m$ZO~O#m$ZOu#[X~O!]!eO#m$ZOu#[X~O#m$ZO~P/]O#m$ZO~P9TOqgO!dnO~P-gO#`!SO#m$ZO~O!SoO#`#qO#p$^O~O#`#tO#p$`O~P4WOu!hO#`!xi#q!xi!_!xi!b!xi!c!xi#m!xi!j!xi~Ou!hO#`!wi#q!wi!_!wi!b!wi!c!wi#m!wi!j!wi~Ou!hO!_!`X!b!`X!c!`X!j!`X~O#`$cO!_#jP!b#jP!c#jP!j#jP~P:`O!_$gO!b$hO!c$iO~O!S!lO!]!Qa~O#`$mO~P:`O!_$gO!b$hO!c$pO~O!SoOu!ui#`!ui#q!ui!_!ui!b!ui!c!ui#m!ui!j!ui~Ol!jO~PNUO#`!SO#m$tO~O#`!SO#mzi~O!SoO#`#qO#p$wO~O#`#tO#p$xO~P4WOu!hO#`$yO~O#`$cO!_#jX!b#jX!c#jX!j#jX~P:`Ol${O~O!]$|O~O!c$}O~O!b$hO!c$}O~Ou!hO!_$gO!b$hO!c%PO~O#`$cO!_#jP!b#jP!c#jP~P:`O!c%WO!j%VO~O!c%YO~O!c%ZO~O!b$hO!c%ZO~O!SoOu!uq#`!uq#q!uq!_!uq!b!uq!c!uq#m!uq!j!uq~OqgO!dnO#mzq~P-gO#`!SO#mzq~O!]%`O~O!c%bO~O!c%cO~O!b$hO!c%cO~O!_$gO!b$hO!c%cO~O!c%gO!j%VO~O!]%jO!g%iO~O!c%gO~O!c%kO~OqgO!dnO#mzy~P-gO!c%nO~O!b$hO!c%nO~O!c%qO~O!c%tO~O!]%uO~Ol#OOo%xO|#OO}%xO#_XO~O#a%wO~O|!m~", + goto: "9g#mPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP#nP$XP$n%l&{'RPP(d(p)j)mP)sP*z+O*zPPPP+hP+t,^PPP,t#nP-f.PP.T.ZP/Q0U$X$XP$XP$XP$X$X1[1b1n2b2p2z3Q3X3_3i3o3y4TPPP4c4g5[7QPPP8[P8lPPPPP8p8v8|raOf!c!d!e!h!w#y$R$S$T$e$m$|%`%j%uQ!YXR#g!TwaOXf!T!c!d!e!h!w#y$R$S$T$e$m$|%`%j%ur_Of!c!d!e!h!w#y$R$S$T$e$m$|%`%j%uQ!]XS!qh%iQ!viQ!zkQ#^!QQ#`!PQ#c!RR#j!TvTOfh!c!d!e!h!w#y$R$S$T$e$m$|%`%i%j%u!W[QTZikorz{|}!O!P!Q!R!U!V!_!b!p#k#p#u$_$u%^%lS!VX!TS#Om%wR#StQ!XXR#f!TrQOf!c!d!e!h!w#y$R$S$T$e$m$|%`%j%u!WsQTZikorz{|}!O!P!Q!R!U!V!_!b!p#k#p#u$_$u%^%lS!UX!TS!ph%iS#Om%wR#RtepQTr!U!V!p#k$u%^%lraOf!c!d!e!h!w#y$R$S$T$e$m$|%`%j%udnQTr!U!V!p#k$u%^%lQ!YXQ#PoR#g!TR!ogX!mg!k!n$O#S[OQTXZfhikorz{|}!O!P!Q!R!T!U!V!_!b!c!d!e!h!p!w#k#p#u#y$R$S$T$_$e$m$u$|%^%`%i%j%l%uR$P!lTvRxvUOXf!T!c!d!e!h!w#y$R$S$T$e$m$|%`%j%uR#StQ$k#zQ$r$UQ%R$lR%e%SQ#z!eQ$U!wQ$n$SQ$o$TQ%a$|Q%m%`Q%s%jR%v%uQ$j#zQ$q$UQ%O$kQ%Q$lQ%[$rS%d%R%SR%o%edpQTr!U!V!p#k$u%^%lQ!`Z[!|l!{!}$V$W$sQ#n!_X#q!`#n#r$]vUOXf!T!c!d!e!h!w#y$R$S$T$e$m$|%`%j%uT!sh%iT%T$n%UQ%X$nR%h%UrWOf!c!d!e!h!w#y$R$S$T$e$m$|%`%j%uQ!WXQ!ykQ#W{Q#Z|Q#]}R#e!T#T[OQTXZfhikorz{|}!O!P!Q!R!T!U!V!_!b!c!d!e!h!p!w#k#p#u#y$R$S$T$_$e$m$u$|%^%`%i%j%l%u![[QTZhikorz{|}!O!P!Q!R!U!V!_!b!p#k#p#u$_$u%^%i%lw]OXf!T!c!d!e!h!w#y$R$S$T$e$m$|%`%j%uQfOR!if^!fc!^#v#w#x$d$lR#{!fQ!TXQ!_Z`#d!T!_#k#l$Y$u%^%lS#k!U!VS#l!W!]S$Y#e#jQ$u$[R%^$vQ!kgQ!{lU#}!k!{$WR$W!}Q!ngQ$O!kT$Q!n$OQxRR#UxS$e#y$mR$z$eQ$v$[R%_$vYrQT!U!V!pR#QrQ%U$nR%f%UQ#r!`Q$]#nT$a#r$]Q#u!bQ$_#pT$b#u$_Q!}lQ$V!{U$X!}$V$sR$s$WTeOfScOfS!^X!TQ#v!cQ#w!d`#x!e!w$S$T$|%`%j%uQ#|!hU$d#y$e$mR$l$RvVOXf!T!c!d!e!h!w#y$R$S$T$e$m$|%`%j%udnQTr!U!V!p#k$u%^%lQ!bZS!rh%iQ!uiQ!xkQ#PoQ#WzQ#X{Q#Y|Q#[}Q#^!OQ#_!PQ#a!QQ#b!RQ#p!_X#t!b#p#u$_r^Of!c!d!e!h!w#y$R$S$T$e$m$|%`%j%u![sQTZhikorz{|}!O!P!Q!R!U!V!_!b!p#k#p#u$_$u%^%i%lQ![XR#i!T[qQTr!U!V!pQ$[#kV%]$u%^%lTwRxQ$f#yR%S$mQ!thR%r%irbOf!c!d!e!h!w#y$R$S$T$e$m$|%`%j%uQ!ZXR#h!T", + 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 Dollar PositionalArg FunctionDef Params NamedParam NamedArgPrefix String StringFragment Interpolation FunctionCallOrIdentifier EscapeSeq DoubleQuote Boolean Null colon CatchExpr keyword Block FinallyExpr keyword keyword Underscore NamedArg IfExpr keyword FunctionCall ElseIfExpr keyword ElseExpr BinOp Regex Dict Array FunctionCallWithBlock TryExpr keyword Throw keyword Import keyword CompoundAssign Assign", + maxTerm: 125, context: trackScope, nodeProps: [ - ["closedBy", 57,"end"] + ["closedBy", 59,"end"] ], propSources: [highlighting], skippedNodes: [0,34], repeatNodeCount: 13, - tokenData: "Lq~R!OOX$RXY$pYZ%ZZp$Rpq$pqr$Rrs%tst'ztu)cuw$Rwx)hxy)myz*Wz{$R{|*q|}$R}!O*q!O!P$R!P!Q4^!Q!R+c!R![.W![!]T!`#O$R#P;'S$R;'S;=`$j<%lO$RU>YV!TSOt$Ruw$Rx#O$R#P#Q>o#Q;'S$R;'S;=`$j<%lO$RU>vU#mQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$R~?_O#e~U?fU#oQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU@PU!TS!bQOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU@h^!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#o@c#o;'S$R;'S;=`$j<%lO$RUAkU!RQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RUBS_!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#UCR#U#o@c#o;'S$R;'S;=`$j<%lO$RUCW`!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#`@c#`#aDY#a#o@c#o;'S$R;'S;=`$j<%lO$RUD_`!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#g@c#g#hEa#h#o@c#o;'S$R;'S;=`$j<%lO$RUEf`!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#X@c#X#YFh#Y#o@c#o;'S$R;'S;=`$j<%lO$RUFo^!XQ!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#o@c#o;'S$R;'S;=`$j<%lO$R^Gr^#fW!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#o@c#o;'S$R;'S;=`$j<%lO$R^Hu^#hW!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#o@c#o;'S$R;'S;=`$j<%lO$R^Ix`#gW!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#f@c#f#gJz#g#o@c#o;'S$R;'S;=`$j<%lO$RUKP`!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#i@c#i#jEa#j#o@c#o;'S$R;'S;=`$j<%lO$RULYUuQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$R~LqO#p~", - tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO#`~~", 11)], + tokenData: "Ls~R!OOX$RXY$pYZ%ZZp$Rpq$pqr$Rrs%tst'ztu)cuw$Rwx)jxy)oyz*Yz{$R{|*s|}$R}!O*s!O!P$R!P!Q4`!Q!R+e!R![.Y![!]<{!]!^%Z!^!}$R!}#O=f#O#P?[#P#Q?a#Q#R$R#R#S?z#S#T$R#T#Y@e#Y#ZBP#Z#b@e#b#cGm#c#f@e#f#gHp#g#h@e#h#iIs#i#o@e#o#p$R#p#qLT#q;'S$R;'S;=`$j<%l~$R~O$R~~LnS$WU!USOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RS$mP;=`<%l$R^$wU!US#YYOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU%bU!US#`QOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU%yZ!USOr%trs&lst%ttu'Vuw%twx'Vx#O%t#O#P'V#P;'S%t;'S;=`'t<%lO%tU&sU!YQ!USOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RQ'YTOr'Vrs'is;'S'V;'S;=`'n<%lO'VQ'nO!YQQ'qP;=`<%l'VU'wP;=`<%l%t^(RZrY!USOY'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^)jO#e[}Q~)oO#c~U)vU!US#_QOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU*aU!US#mQOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU*xX!USOt$Ruw$Rx!Q$R!Q!R+e!R![.Y![#O$R#P;'S$R;'S;=`$j<%lO$RU+lb!US|QOt$Ruw$Rx!O$R!O!P,t!P!Q$R!Q![.Y![#O$R#P#R$R#R#S/V#S#U$R#U#V/t#V#c$R#c#d1Y#d#l$R#l#m2h#m;'S$R;'S;=`$j<%lO$RU,yW!USOt$Ruw$Rx!Q$R!Q![-c![#O$R#P;'S$R;'S;=`$j<%lO$RU-jY!US|QOt$Ruw$Rx!Q$R!Q![-c![#O$R#P#R$R#R#S,t#S;'S$R;'S;=`$j<%lO$RU.a[!US|QOt$Ruw$Rx!O$R!O!P,t!P!Q$R!Q![.Y![#O$R#P#R$R#R#S/V#S;'S$R;'S;=`$j<%lO$RU/[W!USOt$Ruw$Rx!Q$R!Q![.Y![#O$R#P;'S$R;'S;=`$j<%lO$RU/yX!USOt$Ruw$Rx!Q$R!Q!R0f!R!S0f!S#O$R#P;'S$R;'S;=`$j<%lO$RU0mX!US|QOt$Ruw$Rx!Q$R!Q!R0f!R!S0f!S#O$R#P;'S$R;'S;=`$j<%lO$RU1_W!USOt$Ruw$Rx!Q$R!Q!Y1w!Y#O$R#P;'S$R;'S;=`$j<%lO$RU2OW!US|QOt$Ruw$Rx!Q$R!Q!Y1w!Y#O$R#P;'S$R;'S;=`$j<%lO$RU2m[!USOt$Ruw$Rx!Q$R!Q![3c![!c$R!c!i3c!i#O$R#P#T$R#T#Z3c#Z;'S$R;'S;=`$j<%lO$RU3j[!US|QOt$Ruw$Rx!Q$R!Q![3c![!c$R!c!i3c!i#O$R#P#T$R#T#Z3c#Z;'S$R;'S;=`$j<%lO$RU4eW!USOt$Ruw$Rx!P$R!P!Q4}!Q#O$R#P;'S$R;'S;=`$j<%lO$RU5S^!USOY6OYZ$RZt6Otu7Ruw6Owx7Rx!P6O!P!Q$R!Q!}6O!}#O;t#O#P9a#P;'S6O;'S;=`V!`#O$R#P;'S$R;'S;=`$j<%lO$RU>[V!USOt$Ruw$Rx#O$R#P#Q>q#Q;'S$R;'S;=`$j<%lO$RU>xU#nQ!USOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$R~?aO#f~U?hU#pQ!USOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU@RU!US!dQOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU@j^!USOt$Ruw$Rx}$R}!O@e!O!Q$R!Q![@e![!_$R!_!`Af!`#O$R#P#T$R#T#o@e#o;'S$R;'S;=`$j<%lO$RUAmU!SQ!USOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RUBU_!USOt$Ruw$Rx}$R}!O@e!O!Q$R!Q![@e![!_$R!_!`Af!`#O$R#P#T$R#T#UCT#U#o@e#o;'S$R;'S;=`$j<%lO$RUCY`!USOt$Ruw$Rx}$R}!O@e!O!Q$R!Q![@e![!_$R!_!`Af!`#O$R#P#T$R#T#`@e#`#aD[#a#o@e#o;'S$R;'S;=`$j<%lO$RUDa`!USOt$Ruw$Rx}$R}!O@e!O!Q$R!Q![@e![!_$R!_!`Af!`#O$R#P#T$R#T#g@e#g#hEc#h#o@e#o;'S$R;'S;=`$j<%lO$RUEh`!USOt$Ruw$Rx}$R}!O@e!O!Q$R!Q![@e![!_$R!_!`Af!`#O$R#P#T$R#T#X@e#X#YFj#Y#o@e#o;'S$R;'S;=`$j<%lO$RUFq^!ZQ!USOt$Ruw$Rx}$R}!O@e!O!Q$R!Q![@e![!_$R!_!`Af!`#O$R#P#T$R#T#o@e#o;'S$R;'S;=`$j<%lO$R^Gt^#gW!USOt$Ruw$Rx}$R}!O@e!O!Q$R!Q![@e![!_$R!_!`Af!`#O$R#P#T$R#T#o@e#o;'S$R;'S;=`$j<%lO$R^Hw^#iW!USOt$Ruw$Rx}$R}!O@e!O!Q$R!Q![@e![!_$R!_!`Af!`#O$R#P#T$R#T#o@e#o;'S$R;'S;=`$j<%lO$R^Iz`#hW!USOt$Ruw$Rx}$R}!O@e!O!Q$R!Q![@e![!_$R!_!`Af!`#O$R#P#T$R#T#f@e#f#gJ|#g#o@e#o;'S$R;'S;=`$j<%lO$RUKR`!USOt$Ruw$Rx}$R}!O@e!O!Q$R!Q![@e![!_$R!_!`Af!`#O$R#P#T$R#T#i@e#i#jEc#j#o@e#o;'S$R;'S;=`$j<%lO$RUL[UuQ!USOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$R~LsO#q~", + tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO#a~~", 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: 2373 + tokenPrec: 2428 }) diff --git a/src/parser/tests/dot-get.test.ts b/src/parser/tests/dot-get.test.ts index 4181ce6..fbcdb26 100644 --- a/src/parser/tests/dot-get.test.ts +++ b/src/parser/tests/dot-get.test.ts @@ -447,5 +447,14 @@ end`).toMatchTree(` Number 1 Identifier b `) + + test('parses $.pid just fine', () => { + expect(`$.pid`).toMatchTree(` + FunctionCallOrIdentifier + DotGet + Dollar $ + Identifier pid + `) + }) }) }) diff --git a/src/parser/tests/strings.test.ts b/src/parser/tests/strings.test.ts index 7b4a672..01fd0ac 100644 --- a/src/parser/tests/strings.test.ts +++ b/src/parser/tests/strings.test.ts @@ -8,7 +8,8 @@ describe('string interpolation', () => { String StringFragment ${'hello '} Interpolation - Identifier name + FunctionCallOrIdentifier + Identifier name `) }) @@ -44,7 +45,8 @@ describe('string interpolation', () => { String StringFragment x/ Interpolation - Identifier y + FunctionCallOrIdentifier + Identifier y StringFragment /z `) }) @@ -122,7 +124,8 @@ describe('string escape sequences', () => { String StringFragment value: Interpolation - Identifier x + FunctionCallOrIdentifier + Identifier x EscapeSeq \\n `) }) diff --git a/src/prelude/index.ts b/src/prelude/index.ts index 8233834..78c1355 100644 --- a/src/prelude/index.ts +++ b/src/prelude/index.ts @@ -1,5 +1,6 @@ // The prelude creates all the builtin Shrimp functions. +import { join, resolve } from 'path' import { type Value, type VM, toValue, extractParamInfo, isWrapped, getOriginalFunction, @@ -22,6 +23,20 @@ export const globals = { math, str, + // shrimp runtime info + $: { + args: Bun.argv.slice(3), + argv: Bun.argv.slice(1), + env: process.env, + pid: process.pid, + cwd: process.env.PWD, + script: { + name: Bun.argv[2] || '(shrimp)', + path: resolve(join('.', Bun.argv[2] ?? '')) + }, + + }, + // hello echo: (...args: any[]) => { console.log(...args.map(a => { @@ -66,7 +81,6 @@ export const globals = { }, // env - args: Bun.argv.slice(1), exit: (num: number) => process.exit(num ?? 0), // type predicates diff --git a/src/prelude/tests/info.test.ts b/src/prelude/tests/info.test.ts index 39e2545..ce97a35 100644 --- a/src/prelude/tests/info.test.ts +++ b/src/prelude/tests/info.test.ts @@ -80,15 +80,15 @@ describe('introspection', () => { describe('environment', () => { test('args is an array', async () => { - await expect(`array? args`).toEvaluateTo(true, globals) + await expect(`array? $.args`).toEvaluateTo(true, globals) }) test('args can be accessed', async () => { - await expect(`type args`).toEvaluateTo('array', globals) + await expect(`type $.args`).toEvaluateTo('array', globals) }) - test('', async () => { - await expect(`list.first args | str.ends-with? 'shrimp.test.ts'`).toEvaluateTo(true) + test('argv includes more than just the args', async () => { + await expect(`list.first $.argv | str.ends-with? 'shrimp.test.ts'`).toEvaluateTo(true) }) }) @@ -103,3 +103,38 @@ describe('ref', () => { expect(`rnd = ref math.random; rnd | type`).toEvaluateTo('number') expect(`rnd = ref math.random; ref rnd | type`).toEvaluateTo('native') }) + +describe('$ global dictionary', () => { + test('$.args is an array', async () => { + await expect(`$.args | array?`).toEvaluateTo(true, globals) + }) + + test('$.args can be accessed', async () => { + await expect(`$.args | type`).toEvaluateTo('array', globals) + }) + + test('$.script.name is a string', async () => { + await expect(`$.script.name | string?`).toEvaluateTo(true, globals) + }) + + test('$.script.path is a string', async () => { + await expect(`$.script.path | string?`).toEvaluateTo(true, globals) + }) + + test('$.env is a dict', async () => { + await expect(`$.env | dict?`).toEvaluateTo(true, globals) + }) + + test('$.pid is a number', async () => { + await expect(`$.pid | number?`).toEvaluateTo(true, globals) + await expect(`$.pid > 0`).toEvaluateTo(true, globals) + }) + + test('$.cwd is a string', async () => { + await expect(`$.cwd | string?`).toEvaluateTo(true, globals) + }) + + test('$.cwd returns current working directory', async () => { + await expect(`$.cwd`).toEvaluateTo(process.cwd(), globals) + }) +}) From 0c6ce16bcde8631f880f0570ffcb3e8e97fa1856 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Mon, 10 Nov 2025 01:00:33 -0800 Subject: [PATCH 41/48] not always --- bin/shrimp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/shrimp b/bin/shrimp index 04169bf..6e5d502 100755 --- a/bin/shrimp +++ b/bin/shrimp @@ -9,9 +9,6 @@ import { readFileSync } from 'fs' import { spawn } from 'child_process' import { join } from 'path' -const idx = Bun.argv.indexOf('--') -prelude.$.args = idx >= 0 ? Bun.argv.slice(idx + 1) : [] - function showHelp() { console.log(`${colors.bright}${colors.magenta}🦐 Shrimp${colors.reset} is a scripting language in a shell. @@ -38,6 +35,9 @@ function showVersion() { } async function evalCode(code: string, imports: string[]) { + const idx = Bun.argv.indexOf('--') + prelude.$.args = idx >= 0 ? Bun.argv.slice(idx + 1) : [] + const importStatement = imports.length > 0 ? `import ${imports.join(' ')}` : '' if (importStatement) code = `${importStatement}; ${code}` return await runCode(code) From dcf94296faebee0e8c4d685d4c3b0287b3d882a3 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Mon, 10 Nov 2025 07:09:50 -0800 Subject: [PATCH 42/48] compile spacey function calls too --- src/compiler/compiler.ts | 3 ++- src/parser/tests/basics.test.ts | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 6b98c1b..fe18a09 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -482,6 +482,7 @@ export class Compiler { PUSH 1 ; Named count CALL */ + case terms.FunctionCallWithNewlines: case terms.FunctionCall: { const { identifierNode, namedArgs, positionalArgs } = getFunctionCallParts(node, input) const instructions: ProgramItem[] = [] @@ -863,7 +864,7 @@ export class Compiler { default: throw new CompilerError( - `Compiler doesn't know how to handle a "${node.type.name}" node.`, + `Compiler doesn't know how to handle a "${node.type.name}" (${node.type.id}) node.`, node.from, node.to ) diff --git a/src/parser/tests/basics.test.ts b/src/parser/tests/basics.test.ts index 3558b5d..1f6f1a1 100644 --- a/src/parser/tests/basics.test.ts +++ b/src/parser/tests/basics.test.ts @@ -440,6 +440,26 @@ describe('Parentheses', () => { PositionalArg Identifier arg3`) }) + + test('function call with mulitline identifiers starting separate lines in parens', () => { + expect(`( + + echo + arg1 + arg2 + arg3 + + )`).toMatchTree(` + ParenExpr + FunctionCall + Identifier echo + PositionalArg + Identifier arg1 + PositionalArg + Identifier arg2 + PositionalArg + Identifier arg3`) + }) }) describe('Number literals', () => { From c4368f24fcc7aff3a8995b0569d2fc85bc10381a Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Sun, 9 Nov 2025 23:58:47 -0800 Subject: [PATCH 43/48] allow lines to start with | (pipes) --- src/compiler/tests/pipe.test.ts | 1 - src/parser/shrimp.grammar | 7 +- src/parser/shrimp.terms.ts | 2 + src/parser/shrimp.ts | 16 ++-- src/parser/tests/pipes.test.ts | 157 ++++++++++++++++++++++++++++++++ src/parser/tokenizer.ts | 33 ++++++- 6 files changed, 204 insertions(+), 12 deletions(-) diff --git a/src/compiler/tests/pipe.test.ts b/src/compiler/tests/pipe.test.ts index b8f92f1..4d1669f 100644 --- a/src/compiler/tests/pipe.test.ts +++ b/src/compiler/tests/pipe.test.ts @@ -93,7 +93,6 @@ describe('pipe expressions', () => { `).toEvaluateTo(5) }) - test('string literals can be piped', () => { expect(`'hey there' | str.to-upper`).toEvaluateTo('HEY THERE') }) diff --git a/src/parser/shrimp.grammar b/src/parser/shrimp.grammar index e8f2fa4..4f2e032 100644 --- a/src/parser/shrimp.grammar +++ b/src/parser/shrimp.grammar @@ -21,7 +21,7 @@ ("-" | "+")? $[0-9]+ ("_"? $[0-9]+)* ('.' $[0-9]+ ("_"? $[0-9]+)*)? } Boolean { "true" | "false" } - newlineOrSemicolon { "\n" | ";" } + semicolon { ";" } eof { @eof } space { " " | "\t" } Comment { "#" ![\n]* } @@ -34,6 +34,8 @@ "|"[@name=operator] } +newlineOrSemicolon { newline | semicolon } + end { @specialize[@name=keyword] } while { @specialize[@name=keyword] } if { @specialize[@name=keyword] } @@ -46,6 +48,7 @@ import { @specialize[@name=keyword] } null { @specialize[@name=Null] } @external tokens tokenizer from "./tokenizer" { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot, CurlyString } +@external tokens pipeStartsLineTokenizer from "./tokenizer" { newline, pipeStartsLine } @external specialize {Identifier} specializeKeyword from "./tokenizer" { Do } @precedence { @@ -85,7 +88,7 @@ consumeToTerminator { } PipeExpr { - pipeOperand (!pipe "|" pipeOperand)+ + pipeOperand (!pipe (pipeStartsLine? "|") newlineOrSemicolon* pipeOperand)+ } pipeOperand { diff --git a/src/parser/shrimp.terms.ts b/src/parser/shrimp.terms.ts index 2085974..cc00537 100644 --- a/src/parser/shrimp.terms.ts +++ b/src/parser/shrimp.terms.ts @@ -32,6 +32,8 @@ export const Word = 30, IdentifierBeforeDot = 31, CurlyString = 32, + newline = 101, + pipeStartsLine = 102, Do = 33, Comment = 34, Program = 35, diff --git a/src/parser/shrimp.ts b/src/parser/shrimp.ts index 8b7359e..7064a86 100644 --- a/src/parser/shrimp.ts +++ b/src/parser/shrimp.ts @@ -1,17 +1,17 @@ // This file was generated by lezer-generator. You probably shouldn't edit it. import {LRParser, LocalTokenGroup} from "@lezer/lr" import {operatorTokenizer} from "./operatorTokenizer" -import {tokenizer, specializeKeyword} from "./tokenizer" +import {tokenizer, pipeStartsLineTokenizer, specializeKeyword} from "./tokenizer" import {trackScope} from "./parserScopeContext" import {highlighting} from "./highlight" const spec_Identifier = {__proto__:null,while:78, null:116, catch:122, finally:128, end:130, if:138, else:144, try:160, throw:164, import:168} export const parser = LRParser.deserialize({ version: 14, - states: ">`QYQbOOO!pOpO'#DXO%eQcO'#DdO%{OSO'#DaOOQa'#Da'#DaO(vQcO'#EjOOQ`'#Ex'#ExO)aQRO'#DxO+fQcO'#EhO,PQbO'#DVOOQa'#Dz'#DzO.kQbO'#D{OOQa'#Ej'#EjO.rQcO'#EjO0pQcO'#EiO1uQcO'#EhO2SQRO'#ETOOQ`'#Eh'#EhO2kQbO'#EhO2rQQO'#EgOOQ`'#Eg'#EgOOQ`'#EV'#EVQYQbOOO2}QbO'#D]O3YQbO'#DrO4WQbO'#DSO5UQQO'#D}O4WQbO'#EPO5ZQbO'#ERO5cObO,59sOOQ`'#D['#D[O5tQbO'#DqOOQ`'#En'#EnOOQ`'#E_'#E_O6OQbO,5:`OOQa'#Ei'#EiO6xQbO'#DcO7WQWO'#DeOOOO'#Ep'#EpOOOO'#E['#E[O7lOSO,59{OOQa,59{,59{O4WQbO,5:dO4WQbO,5:dO4WQbO,5:dO4WQbO,5:dO4WQbO,59pO4WQbO,59pO4WQbO,59pO4WQbO,59pOOQ`'#EX'#EXO,PQbO,59qO7zQcO'#DdO8RQcO'#EjO8YQRO,59qO8dQQO,59qO8iQQO,59qO8qQQO,59qO8|QRO,59qO9fQRO,59qO9mQQO'#DQO9rQbO,5:gO9yQQO,5:fOOQa,5:g,5:gO:UQbO,5:gO:`QbO,5:pO:`QbO,5:oO;sQbO,5:hO;zQbO,59lOOQ`,5;R,5;RO:`QbO'#EWOOQ`-E8T-E8TOOQ`'#EY'#EYOXQRO'#EwO?UQRO'#EwOOQO'#Ew'#EwO?]QQO,5:^O?bQRO,59nO?iQRO,59nO;sQbO,5:iO?wQcO,5:kOAVQcO,5:kOAsQcO,5:kOBhQbO,5:mOOQ`'#Ec'#EcO5ZQbO,5:mOOQa1G/_1G/_OOQ`,5:],5:]OOQ`-E8]-E8]OOOO'#Dd'#DdOOOO,59},59}OOOO,5:P,5:POOOO-E8Y-E8YOOQa1G/g1G/gOOQa1G0O1G0OODaQcO1G0OODkQcO1G0OOEyQcO1G0OOFTQcO1G0OOFbQcO1G0OOOQa1G/[1G/[OGsQcO1G/[OGzQcO1G/[OHRQcO1G/[OIQQcO1G/[OHYQcO1G/[OOQ`-E8V-E8VOIhQRO1G/]OIrQQO1G/]OIwQQO1G/]OJPQQO1G/]OJ[QRO1G/]OJcQRO1G/]OJjQbO,59rOJtQQO1G/]OOQa1G/]1G/]OJ|QQO1G0QOOQa1G0R1G0ROKXQbO1G0ROOQO'#Ea'#EaOJ|QQO1G0QOOQa1G0Q1G0QOOQ`'#Eb'#EbOKXQbO1G0ROKcQbO1G0[OK}QbO1G0ZOLiQbO'#DlOLzQbO'#DlOM_QbO1G0SOOQ`-E8U-E8UOOQ`,5:r,5:rOOQ`-E8W-E8WOMjQQO,59xOOQO,59y,59yOOQO-E8X-E8XOMrQbO1G/cO;sQbO1G/xO;sQbO1G/YOMyQbO1G0TONUQbO1G0XONsQbO1G0XOOQ`-E8a-E8aONzQQO7+$wOOQa7+$w7+$wO! SQQO1G/^O! [QQO7+%lOOQa7+%l7+%lO! gQbO7+%mOOQa7+%m7+%mOOQO-E8_-E8_OOQ`-E8`-E8`OOQ`'#E]'#E]O! qQQO'#E]O! yQbO'#EvOOQ`,5:W,5:WO!!^QbO'#DjO!!cQQO'#DmOOQ`7+%n7+%nO!!hQbO7+%nO!!mQbO7+%nO!!uQbO7+$}O!#TQbO7+$}O!#eQbO7+%dO!#mQbO7+$tOOQ`7+%o7+%oO!#rQbO7+%oO!#wQbO7+%oO!$PQbO7+%sOOQa<tAN>tOOQ`AN>TAN>TO!&pQbOAN>TO!&uQbOAN>TOOQ`-E8^-E8^OOQ`AN>jAN>jO!&}QbOAN>jO3YQbO,5:aO;sQbO,5:cOOQ`AN>uAN>uPJjQbO'#EXOOQ`7+%[7+%[OOQ`G23oG23oO!'SQbOG23oP!&SQbO'#DuOOQ`G24UG24UO!'XQQO1G/{OOQ`1G/}1G/}OOQ`LD)ZLD)ZO;sQbO7+%gOOQ`<gO!]$SO~O!]$TO~P>gOT!POU!QOj!RO!]$TO~Ou!sa#`!sa#q!sa!_!sa!b!sa!c!sa#m!sa!j!sa~P)aOP{OQ{OR|OS|Od}Oe}Of}Og}Oh}Oi}O~Ou!sa#`!sa#q!sa!_!sa!b!sa!c!sa#m!sa!j!sa~P@eOT!POU!QOj!ROu!sa#`!sa#q!sa!_!sa!b!sa!c!sa#m!sa!j!sa~Ol!jO!SoOu!ua#`!ua#q!ua!_!ua!b!ua!c!ua#m!ua!j!ua~O^zOR!liS!lid!lie!lif!lig!lih!lii!liu!li#`!li#q!li#m!li!_!li!b!li!c!li!j!li~OP!liQ!li~PCYOP{OQ{O~PCYOP{OQ{Od!lie!lif!lig!lih!lii!liu!li#`!li#q!li#m!li!_!li!b!li!c!li!j!li~OR!liS!li~PDuOR|OS|O^zO~PDuOR|OS|O~PDuOW!OOX!OOY!OOZ!OO[!OO]!OOTxijxiuxi#`xi#qxi#mxi!]xi!_xi!bxi!cxi!jxi~OU!QO~PFlOU!QO~PGOOUxi~PFlOT!POU!QOjxiuxi#`xi#qxi#mxi!]xi!_xi!bxi!cxi!jxi~OW!OOX!OOY!OOZ!OO[!OO]!OO~PHYO#`!SO#m$ZO~P*qO#m$ZO~O#m$ZOu#[X~O!]!eO#m$ZOu#[X~O#m$ZO~P/]O#m$ZO~P9TOqgO!dnO~P-gO#`!SO#m$ZO~O!SoO#`#qO#p$^O~O#`#tO#p$`O~P4WOu!hO#`!xi#q!xi!_!xi!b!xi!c!xi#m!xi!j!xi~Ou!hO#`!wi#q!wi!_!wi!b!wi!c!wi#m!wi!j!wi~Ou!hO!_!`X!b!`X!c!`X!j!`X~O#`$cO!_#jP!b#jP!c#jP!j#jP~P:`O!_$gO!b$hO!c$iO~O!S!lO!]!Qa~O#`$mO~P:`O!_$gO!b$hO!c$pO~O!SoOu!ui#`!ui#q!ui!_!ui!b!ui!c!ui#m!ui!j!ui~Ol!jO~PNUO#`!SO#m$tO~O#`!SO#mzi~O!SoO#`#qO#p$wO~O#`#tO#p$xO~P4WOu!hO#`$yO~O#`$cO!_#jX!b#jX!c#jX!j#jX~P:`Ol${O~O!]$|O~O!c$}O~O!b$hO!c$}O~Ou!hO!_$gO!b$hO!c%PO~O#`$cO!_#jP!b#jP!c#jP~P:`O!c%WO!j%VO~O!c%YO~O!c%ZO~O!b$hO!c%ZO~O!SoOu!uq#`!uq#q!uq!_!uq!b!uq!c!uq#m!uq!j!uq~OqgO!dnO#mzq~P-gO#`!SO#mzq~O!]%`O~O!c%bO~O!c%cO~O!b$hO!c%cO~O!_$gO!b$hO!c%cO~O!c%gO!j%VO~O!]%jO!g%iO~O!c%gO~O!c%kO~OqgO!dnO#mzy~P-gO!c%nO~O!b$hO!c%nO~O!c%qO~O!c%tO~O!]%uO~Ol#OOo%xO|#OO}%xO#_XO~O#a%wO~O|!m~", - goto: "9g#mPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP#nP$XP$n%l&{'RPP(d(p)j)mP)sP*z+O*zPPPP+hP+t,^PPP,t#nP-f.PP.T.ZP/Q0U$X$XP$XP$XP$X$X1[1b1n2b2p2z3Q3X3_3i3o3y4TPPP4c4g5[7QPPP8[P8lPPPPP8p8v8|raOf!c!d!e!h!w#y$R$S$T$e$m$|%`%j%uQ!YXR#g!TwaOXf!T!c!d!e!h!w#y$R$S$T$e$m$|%`%j%ur_Of!c!d!e!h!w#y$R$S$T$e$m$|%`%j%uQ!]XS!qh%iQ!viQ!zkQ#^!QQ#`!PQ#c!RR#j!TvTOfh!c!d!e!h!w#y$R$S$T$e$m$|%`%i%j%u!W[QTZikorz{|}!O!P!Q!R!U!V!_!b!p#k#p#u$_$u%^%lS!VX!TS#Om%wR#StQ!XXR#f!TrQOf!c!d!e!h!w#y$R$S$T$e$m$|%`%j%u!WsQTZikorz{|}!O!P!Q!R!U!V!_!b!p#k#p#u$_$u%^%lS!UX!TS!ph%iS#Om%wR#RtepQTr!U!V!p#k$u%^%lraOf!c!d!e!h!w#y$R$S$T$e$m$|%`%j%udnQTr!U!V!p#k$u%^%lQ!YXQ#PoR#g!TR!ogX!mg!k!n$O#S[OQTXZfhikorz{|}!O!P!Q!R!T!U!V!_!b!c!d!e!h!p!w#k#p#u#y$R$S$T$_$e$m$u$|%^%`%i%j%l%uR$P!lTvRxvUOXf!T!c!d!e!h!w#y$R$S$T$e$m$|%`%j%uR#StQ$k#zQ$r$UQ%R$lR%e%SQ#z!eQ$U!wQ$n$SQ$o$TQ%a$|Q%m%`Q%s%jR%v%uQ$j#zQ$q$UQ%O$kQ%Q$lQ%[$rS%d%R%SR%o%edpQTr!U!V!p#k$u%^%lQ!`Z[!|l!{!}$V$W$sQ#n!_X#q!`#n#r$]vUOXf!T!c!d!e!h!w#y$R$S$T$e$m$|%`%j%uT!sh%iT%T$n%UQ%X$nR%h%UrWOf!c!d!e!h!w#y$R$S$T$e$m$|%`%j%uQ!WXQ!ykQ#W{Q#Z|Q#]}R#e!T#T[OQTXZfhikorz{|}!O!P!Q!R!T!U!V!_!b!c!d!e!h!p!w#k#p#u#y$R$S$T$_$e$m$u$|%^%`%i%j%l%u![[QTZhikorz{|}!O!P!Q!R!U!V!_!b!p#k#p#u$_$u%^%i%lw]OXf!T!c!d!e!h!w#y$R$S$T$e$m$|%`%j%uQfOR!if^!fc!^#v#w#x$d$lR#{!fQ!TXQ!_Z`#d!T!_#k#l$Y$u%^%lS#k!U!VS#l!W!]S$Y#e#jQ$u$[R%^$vQ!kgQ!{lU#}!k!{$WR$W!}Q!ngQ$O!kT$Q!n$OQxRR#UxS$e#y$mR$z$eQ$v$[R%_$vYrQT!U!V!pR#QrQ%U$nR%f%UQ#r!`Q$]#nT$a#r$]Q#u!bQ$_#pT$b#u$_Q!}lQ$V!{U$X!}$V$sR$s$WTeOfScOfS!^X!TQ#v!cQ#w!d`#x!e!w$S$T$|%`%j%uQ#|!hU$d#y$e$mR$l$RvVOXf!T!c!d!e!h!w#y$R$S$T$e$m$|%`%j%udnQTr!U!V!p#k$u%^%lQ!bZS!rh%iQ!uiQ!xkQ#PoQ#WzQ#X{Q#Y|Q#[}Q#^!OQ#_!PQ#a!QQ#b!RQ#p!_X#t!b#p#u$_r^Of!c!d!e!h!w#y$R$S$T$e$m$|%`%j%u![sQTZhikorz{|}!O!P!Q!R!U!V!_!b!p#k#p#u$_$u%^%i%lQ![XR#i!T[qQTr!U!V!pQ$[#kV%]$u%^%lTwRxQ$f#yR%S$mQ!thR%r%irbOf!c!d!e!h!w#y$R$S$T$e$m$|%`%j%uQ!ZXR#h!T", + states: "?[QYQ!SOOOOQ!Q'#Ek'#EkO!sO!bO'#DXO%kQ!TO'#DdO&UOSO'#DaOOQ!R'#Da'#DaO)SQ!TO'#EnOOQ!Q'#E{'#E{O)pQRO'#DxO+xQ!TO'#EjO,fQ!SO'#DVOOQ!R'#Dz'#DzO/WQ!SO'#D{OOQ!R'#En'#EnO/_Q!TO'#EnO1cQ!TO'#EmO2qQ!TO'#EjO3OQRO'#ETOOQ!Q'#Ej'#EjO3gQ!SO'#EjO3nQrO'#EiOOQ!Q'#Ei'#EiOOQ!Q'#EV'#EVQYQ!SOOO4PQbO'#D]O4[QbO'#DrO5YQbO'#DSO6WQQO'#D}O5YQbO'#EPO6]QbO'#ERO6eObO,59sOOQ!Q'#D['#D[O6vQbO'#DqOOQ!Q'#Eq'#EqOOQ!Q'#E_'#E_O7QQ!SO,5:`OOQ!R'#Em'#EmO8QQbO'#DcO8`QWO'#DeOOOO'#Es'#EsOOOO'#E['#E[O8tOSO,59{OOQ!R,59{,59{O5YQbO,5:dO5YQbO,5:dO5YQbO,5:dO5YQbO,5:dO5YQbO,59pO5YQbO,59pO5YQbO,59pO5YQbO,59pOOQ!Q'#EX'#EXO,fQ!SO,59qO9SQ!TO'#DdO9^Q!TO'#EnO9hQsO,59qO9uQQO,59qO9zQrO,59qO:VQrO,59qO:eQsO,59qO;TQsO,59qO;[QrO'#DQO;dQ!SO,5:gO;kQrO,5:fOOQ!R,5:g,5:gO;yQ!SO,5:gO]QQO'#EWOOQ!Q-E8T-E8TOOQ!Q'#EY'#EYO>bQbO'#D^O>mQbO'#D_OOQO'#EZ'#EZO>eQQO'#D^O?RQQO,59wO?WQcO'#EmO@TQRO'#EzOAQQRO'#EzOOQO'#Ez'#EzOAXQQO,5:^OA^QRO,59nOAeQRO,59nOYQ!SO,5:iOAsQ!TO,5:kOCXQ!TO,5:kOC{Q!TO,5:kODYQ!SO,5:mOOQ!Q'#Ec'#EcO6]QbO,5:mOOQ!R1G/_1G/_OOQ!Q,5:],5:]OOQ!Q-E8]-E8]OOOO'#Dd'#DdOOOO,59},59}OOOO,5:P,5:POOOO-E8Y-E8YOOQ!R1G/g1G/gOOQ!R1G0O1G0OOF_Q!TO1G0OOFiQ!TO1G0OOG}Q!TO1G0OOHXQ!TO1G0OOHfQ!TO1G0OOOQ!R1G/[1G/[OI}Q!TO1G/[OJUQ!TO1G/[OJ]Q!TO1G/[OKbQ!TO1G/[OJdQ!TO1G/[OOQ!Q-E8V-E8VOKxQsO1G/]OLVQQO1G/]OL[QrO1G/]OLgQrO1G/]OLuQsO1G/]OL|QsO1G/]OMTQ!SO,59rOM_QrO1G/]OOQ!R1G/]1G/]OMjQrO1G0QOOQ!R1G0R1G0ROMxQ!SO1G0ROOQp'#Ea'#EaOMjQrO1G0QOOQ!R1G0Q1G0QOOQ!Q'#Eb'#EbOMxQ!SO1G0RONVQ!SO1G0[ONwQ!SO1G0ZO! iQ!SO'#DlO! }Q!SO'#DlO!!_QbO1G0SOOQ!Q-E8U-E8UOYQ!SO,5:rOOQ!Q,5:r,5:rOYQ!SO,5:rOOQ!Q-E8W-E8WO!!jQQO,59xOOQO,59y,59yOOQO-E8X-E8XOYQ!SO1G/cOYQ!SO1G/xOYQ!SO1G/YO!!rQbO1G0TO!!}Q!SO1G0XO!#rQ!SO1G0XOOQ!Q-E8a-E8aO!#yQrO7+$wOOQ!R7+$w7+$wO!$UQrO1G/^O!$aQrO7+%lOOQ!R7+%l7+%lO!$oQ!SO7+%mOOQ!R7+%m7+%mOOQp-E8_-E8_OOQ!Q-E8`-E8`OOQ!Q'#E]'#E]O!$|QrO'#E]O!%[Q!SO'#EyOOQ`,5:W,5:WO!%lQbO'#DjO!%qQQO'#DmOOQ!Q7+%n7+%nO!%vQbO7+%nO!%{QbO7+%nOOQ!Q1G0^1G0^OYQ!SO1G0^O!&TQ!SO7+$}O!&fQ!SO7+$}O!&sQbO7+%dO!&{QbO7+$tOOQ!Q7+%o7+%oO!'QQbO7+%oO!'VQbO7+%oO!'_Q!SO7+%sOOQ!R<tAN>tOOQ!QAN>TAN>TO!*XQbOAN>TO!*^QbOAN>TOOQ`-E8^-E8^OOQ!QAN>jAN>jO!*fQbOAN>jO4[QbO,5:aOYQ!SO,5:cOOQ!QAN>uAN>uPMTQ!SO'#EXOOQ`7+%[7+%[OOQ!QG23oG23oO!*kQbOG23oP!)kQbO'#DuOOQ!QG24UG24UO!*pQQO1G/{OOQ`1G/}1G/}OOQ!QLD)ZLD)ZOYQ!SO7+%gOOQ`<V!`#O$R#P;'S$R;'S;=`$j<%lO$RU>[V!USOt$Ruw$Rx#O$R#P#Q>q#Q;'S$R;'S;=`$j<%lO$RU>xU#nQ!USOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$R~?aO#f~U?hU#pQ!USOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU@RU!US!dQOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU@j^!USOt$Ruw$Rx}$R}!O@e!O!Q$R!Q![@e![!_$R!_!`Af!`#O$R#P#T$R#T#o@e#o;'S$R;'S;=`$j<%lO$RUAmU!SQ!USOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RUBU_!USOt$Ruw$Rx}$R}!O@e!O!Q$R!Q![@e![!_$R!_!`Af!`#O$R#P#T$R#T#UCT#U#o@e#o;'S$R;'S;=`$j<%lO$RUCY`!USOt$Ruw$Rx}$R}!O@e!O!Q$R!Q![@e![!_$R!_!`Af!`#O$R#P#T$R#T#`@e#`#aD[#a#o@e#o;'S$R;'S;=`$j<%lO$RUDa`!USOt$Ruw$Rx}$R}!O@e!O!Q$R!Q![@e![!_$R!_!`Af!`#O$R#P#T$R#T#g@e#g#hEc#h#o@e#o;'S$R;'S;=`$j<%lO$RUEh`!USOt$Ruw$Rx}$R}!O@e!O!Q$R!Q![@e![!_$R!_!`Af!`#O$R#P#T$R#T#X@e#X#YFj#Y#o@e#o;'S$R;'S;=`$j<%lO$RUFq^!ZQ!USOt$Ruw$Rx}$R}!O@e!O!Q$R!Q![@e![!_$R!_!`Af!`#O$R#P#T$R#T#o@e#o;'S$R;'S;=`$j<%lO$R^Gt^#gW!USOt$Ruw$Rx}$R}!O@e!O!Q$R!Q![@e![!_$R!_!`Af!`#O$R#P#T$R#T#o@e#o;'S$R;'S;=`$j<%lO$R^Hw^#iW!USOt$Ruw$Rx}$R}!O@e!O!Q$R!Q![@e![!_$R!_!`Af!`#O$R#P#T$R#T#o@e#o;'S$R;'S;=`$j<%lO$R^Iz`#hW!USOt$Ruw$Rx}$R}!O@e!O!Q$R!Q![@e![!_$R!_!`Af!`#O$R#P#T$R#T#f@e#f#gJ|#g#o@e#o;'S$R;'S;=`$j<%lO$RUKR`!USOt$Ruw$Rx}$R}!O@e!O!Q$R!Q![@e![!_$R!_!`Af!`#O$R#P#T$R#T#i@e#i#jEc#j#o@e#o;'S$R;'S;=`$j<%lO$RUL[UuQ!USOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$R~LsO#q~", - tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO#a~~", 11)], + tokenData: "Lp~R}OX$OXY$mYp$Opq$mqr$Ors%Wst'^tu(uuw$Owx(|xy)Ryz)lz{$O{|*V|}$O}!O*V!O!P$O!P!Q3r!Q!R*w!R![-l![!]<_!]!^S!`#O$O#P;'S$O;'S;=`$g<%lO$OU>XV!USOt$Ouw$Ox#O$O#P#Q>n#Q;'S$O;'S;=`$g<%lO$OU>uU#qQ!USOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$O~?^O#i~U?eU#sQ!USOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU@OU!US!dQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU@g^!USOt$Ouw$Ox}$O}!O@b!O!Q$O!Q![@b![!_$O!_!`Ac!`#O$O#P#T$O#T#o@b#o;'S$O;'S;=`$g<%lO$OUAjU!SQ!USOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OUBR_!USOt$Ouw$Ox}$O}!O@b!O!Q$O!Q![@b![!_$O!_!`Ac!`#O$O#P#T$O#T#UCQ#U#o@b#o;'S$O;'S;=`$g<%lO$OUCV`!USOt$Ouw$Ox}$O}!O@b!O!Q$O!Q![@b![!_$O!_!`Ac!`#O$O#P#T$O#T#`@b#`#aDX#a#o@b#o;'S$O;'S;=`$g<%lO$OUD^`!USOt$Ouw$Ox}$O}!O@b!O!Q$O!Q![@b![!_$O!_!`Ac!`#O$O#P#T$O#T#g@b#g#hE`#h#o@b#o;'S$O;'S;=`$g<%lO$OUEe`!USOt$Ouw$Ox}$O}!O@b!O!Q$O!Q![@b![!_$O!_!`Ac!`#O$O#P#T$O#T#X@b#X#YFg#Y#o@b#o;'S$O;'S;=`$g<%lO$OUFn^!ZQ!USOt$Ouw$Ox}$O}!O@b!O!Q$O!Q![@b![!_$O!_!`Ac!`#O$O#P#T$O#T#o@b#o;'S$O;'S;=`$g<%lO$O^Gq^#jW!USOt$Ouw$Ox}$O}!O@b!O!Q$O!Q![@b![!_$O!_!`Ac!`#O$O#P#T$O#T#o@b#o;'S$O;'S;=`$g<%lO$O^Ht^#lW!USOt$Ouw$Ox}$O}!O@b!O!Q$O!Q![@b![!_$O!_!`Ac!`#O$O#P#T$O#T#o@b#o;'S$O;'S;=`$g<%lO$O^Iw`#kW!USOt$Ouw$Ox}$O}!O@b!O!Q$O!Q![@b![!_$O!_!`Ac!`#O$O#P#T$O#T#f@b#f#gJy#g#o@b#o;'S$O;'S;=`$g<%lO$OUKO`!USOt$Ouw$Ox}$O}!O@b!O!Q$O!Q![@b![!_$O!_!`Ac!`#O$O#P#T$O#T#i@b#i#jE`#j#o@b#o;'S$O;'S;=`$g<%lO$OULXUuQ!USOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$O~LpO#t~", + tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, pipeStartsLineTokenizer, new LocalTokenGroup("[~RP!O!PU~ZO#d~~", 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: 2428 + tokenPrec: 2589 }) diff --git a/src/parser/tests/pipes.test.ts b/src/parser/tests/pipes.test.ts index 0d5da6a..e1ed0a9 100644 --- a/src/parser/tests/pipes.test.ts +++ b/src/parser/tests/pipes.test.ts @@ -1,4 +1,5 @@ import { expect, describe, test } from 'bun:test' +import { parser } from '../shrimp' import '../shrimp.grammar' // Importing this so changes cause it to retest! @@ -176,3 +177,159 @@ describe('pipe expressions', () => { `) }) }) + +describe('pipe continuation', () => { + test('pipe on next line', () => { + expect(`hello +| echo`).toMatchTree(` + PipeExpr + FunctionCallOrIdentifier + Identifier hello + operator | + FunctionCallOrIdentifier + Identifier echo + `) + + expect(`echo hello +| grep h`).toMatchTree(` + PipeExpr + FunctionCall + Identifier echo + PositionalArg + Identifier hello + operator | + FunctionCall + Identifier grep + PositionalArg + Identifier h + `) + }) + + test('pipe on next non-empty line', () => { + expect(`hello + + +| echo`).toMatchTree(` + PipeExpr + FunctionCallOrIdentifier + Identifier hello + operator | + FunctionCallOrIdentifier + Identifier echo + `) + }) + + test('multi-line pipe chain', () => { + expect(`echo hello +| grep h +| sort`).toMatchTree(` + PipeExpr + FunctionCall + Identifier echo + PositionalArg + Identifier hello + operator | + FunctionCall + Identifier grep + PositionalArg + Identifier h + operator | + FunctionCallOrIdentifier + Identifier sort + `) + }) + + test('pipe with indentation', () => { + expect(`echo hello + | grep h + | sort`).toMatchTree(` + PipeExpr + FunctionCall + Identifier echo + PositionalArg + Identifier hello + operator | + FunctionCall + Identifier grep + PositionalArg + Identifier h + operator | + FunctionCallOrIdentifier + Identifier sort + `) + }) + + test('pipe after operand on next line (trailing pipe style)', () => { + expect(`echo hello | +grep h`).toMatchTree(` + PipeExpr + FunctionCall + Identifier echo + PositionalArg + Identifier hello + operator | + FunctionCall + Identifier grep + PositionalArg + Identifier h + `) + }) + + test('same-line pipes still work', () => { + expect('echo hello | grep h | sort').toMatchTree(` + PipeExpr + FunctionCall + Identifier echo + PositionalArg + Identifier hello + operator | + FunctionCall + Identifier grep + PositionalArg + Identifier h + operator | + FunctionCallOrIdentifier + Identifier sort + `) + }) + + test('lots of pipes', () => { + expect(` +'this should help readability in long chains' + | split ' ' + | map (ref str.to-upper) + | join '-' + | echo +`).toMatchTree(` + PipeExpr + String + StringFragment this should help readability in long chains + operator | + FunctionCall + Identifier split + PositionalArg + String + StringFragment + operator | + FunctionCall + Identifier map + PositionalArg + ParenExpr + FunctionCall + Identifier ref + PositionalArg + DotGet + IdentifierBeforeDot str + Identifier to-upper + operator | + FunctionCall + Identifier join + PositionalArg + String + StringFragment - + operator | + FunctionCallOrIdentifier + Identifier echo + `) + }) +}) diff --git a/src/parser/tokenizer.ts b/src/parser/tokenizer.ts index 75c027a..cbecdb7 100644 --- a/src/parser/tokenizer.ts +++ b/src/parser/tokenizer.ts @@ -1,5 +1,5 @@ import { ExternalTokenizer, InputStream, Stack } from '@lezer/lr' -import { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot, Do, CurlyString, DotGet } from './shrimp.terms' +import { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot, Do, CurlyString, DotGet, newline, pipeStartsLine } from './shrimp.terms' // doobie doobie do (we need the `do` keyword to know when we're defining params) export function specializeKeyword(ident: string) { @@ -356,3 +356,34 @@ const isEmojiOrUnicode = (ch: number): boolean => { } const getCharSize = (ch: number) => (ch > 0xffff ? 2 : 1) // emoji takes 2 UTF-16 code units + +export const pipeStartsLineTokenizer = new ExternalTokenizer((input: InputStream, stack: Stack) => { + const ch = input.peek(0) + + if (ch !== 10 /* \n */) return + + // ignore whitespace + let offset = 1 + let lastNewlineOffset = 0 + + while (true) { + const ch = input.peek(offset) + if (ch === 10 /* \n */) { + lastNewlineOffset = offset + offset++ + } else if (isWhiteSpace(ch)) { + offset++ + } else { + break + } + } + + // look for pipe after skipping empty lines + if (input.peek(offset) === 124 /* | */) { + input.advance(lastNewlineOffset + 1) + input.acceptToken(pipeStartsLine) + } else { + input.advance(1) + input.acceptToken(newline) + } +}) From f13be7817ca3486baeb018c522bcf90caa9b6a0a Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Mon, 10 Nov 2025 07:34:16 -0800 Subject: [PATCH 44/48] list.has? --- src/prelude/list.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/prelude/list.ts b/src/prelude/list.ts index 86320e0..9f0517d 100644 --- a/src/prelude/list.ts +++ b/src/prelude/list.ts @@ -36,6 +36,8 @@ export const list = { // predicates 'empty?': (list: any[]) => list.length === 0, 'contains?': (list: any[], item: any) => list.includes(item), + 'includes?': (list: any[], item: any) => list.includes(item), + 'has?': (list: any[], item: any) => list.includes(item), 'any?': async (list: any[], cb: Function) => { for (const value of list) { if (await cb(value)) return true From 4a27a8b474fc7f92c771b88d7eef39f524501390 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Mon, 24 Nov 2025 09:42:24 -0800 Subject: [PATCH 45/48] Delete shrimp-0.0.1.vsix --- today.md | 0 vscode-extension/shrimp-0.0.1.vsix | Bin 451594 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 today.md delete mode 100644 vscode-extension/shrimp-0.0.1.vsix diff --git a/today.md b/today.md deleted file mode 100644 index e69de29..0000000 diff --git a/vscode-extension/shrimp-0.0.1.vsix b/vscode-extension/shrimp-0.0.1.vsix deleted file mode 100644 index 730cc92292b1e0a9bc41a2277c76a4a62d01b733..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 451594 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 z%0D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