From 34305b473ef5bf37cc065dafae7767c6347825e4 Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Wed, 29 Oct 2025 11:18:47 -0700 Subject: [PATCH] switch back to using == --- src/compiler/compiler.ts | 2 +- src/compiler/tests/compiler.test.ts | 2 +- src/parser/operatorTokenizer.ts | 1 + src/parser/shrimp.grammar | 4 +- src/parser/shrimp.terms.ts | 87 ++++++++++++++------------- src/parser/shrimp.ts | 22 +++---- src/parser/tests/basics.test.ts | 16 ++--- src/parser/tests/control-flow.test.ts | 4 +- 8 files changed, 70 insertions(+), 68 deletions(-) diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index 227cc8b..e6b006b 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -366,7 +366,7 @@ export class Compiler { const opValue = input.slice(op.from, op.to) switch (opValue) { - case '=': + case '==': instructions.push(...leftInstructions, ...rightInstructions, ['EQ']) break diff --git a/src/compiler/tests/compiler.test.ts b/src/compiler/tests/compiler.test.ts index 5e58c11..ef02035 100644 --- a/src/compiler/tests/compiler.test.ts +++ b/src/compiler/tests/compiler.test.ts @@ -111,7 +111,7 @@ describe('compiler', () => { expect(`(10 > 20)`).toEvaluateTo(false) expect(`(4 <= 9)`).toEvaluateTo(true) expect(`(15 >= 20)`).toEvaluateTo(false) - expect(`(7 = 7)`).toEvaluateTo(true) + expect(`(7 == 7)`).toEvaluateTo(true) expect(`(5 != 5)`).toEvaluateTo(false) expect(`('shave' and 'haircut')`).toEvaluateTo('haircut') expect(`(false and witness)`).toEvaluateTo(false) diff --git a/src/parser/operatorTokenizer.ts b/src/parser/operatorTokenizer.ts index d15318c..ee1dc44 100644 --- a/src/parser/operatorTokenizer.ts +++ b/src/parser/operatorTokenizer.ts @@ -8,6 +8,7 @@ const operators: Array = [ { str: '>=', tokenName: 'Gte' }, { str: '<=', tokenName: 'Lte' }, { str: '!=', tokenName: 'Neq' }, + { str: '==', tokenName: 'EqEq' }, // // Single-char operators { str: '*', tokenName: 'Star' }, diff --git a/src/parser/shrimp.grammar b/src/parser/shrimp.grammar index 794dc29..113cf01 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, Neq, Lt, Lte, Gt, Gte, Modulo } +@external tokens operatorTokenizer from "./operatorTokenizer" { Star, Slash, Plus, Minus, And, Or, Eq, EqEq, Neq, Lt, Lte, Gt, Gte, Modulo } @tokens { @precedence { Number Regex } @@ -132,7 +132,7 @@ SingleLineThenBlock { } ConditionalOp { - expression !comparison Eq expression | + expression !comparison EqEq expression | expression !comparison Neq expression | expression !comparison Lt expression | expression !comparison Lte expression | diff --git a/src/parser/shrimp.terms.ts b/src/parser/shrimp.terms.ts index eb818df..144d69b 100644 --- a/src/parser/shrimp.terms.ts +++ b/src/parser/shrimp.terms.ts @@ -7,46 +7,47 @@ export const And = 5, Or = 6, Eq = 7, - Neq = 8, - Lt = 9, - Lte = 10, - Gt = 11, - Gte = 12, - Modulo = 13, - Identifier = 14, - AssignableIdentifier = 15, - Word = 16, - IdentifierBeforeDot = 17, - Do = 18, - Program = 19, - PipeExpr = 20, - FunctionCall = 21, - DotGet = 22, - Number = 23, - ParenExpr = 24, - FunctionCallOrIdentifier = 25, - BinOp = 26, - String = 27, - StringFragment = 28, - Interpolation = 29, - EscapeSeq = 30, - Boolean = 31, - Regex = 32, - Dict = 33, - NamedArg = 34, - NamedArgPrefix = 35, - FunctionDef = 36, - Params = 37, - colon = 38, - keyword = 53, - Underscore = 40, - Array = 41, - Null = 42, - ConditionalOp = 43, - PositionalArg = 44, - IfExpr = 46, - SingleLineThenBlock = 48, - ThenBlock = 49, - ElseIfExpr = 50, - ElseExpr = 52, - Assign = 54 + EqEq = 8, + Neq = 9, + Lt = 10, + Lte = 11, + Gt = 12, + Gte = 13, + Modulo = 14, + Identifier = 15, + AssignableIdentifier = 16, + Word = 17, + IdentifierBeforeDot = 18, + Do = 19, + Program = 20, + PipeExpr = 21, + FunctionCall = 22, + DotGet = 23, + Number = 24, + ParenExpr = 25, + FunctionCallOrIdentifier = 26, + BinOp = 27, + String = 28, + StringFragment = 29, + Interpolation = 30, + EscapeSeq = 31, + Boolean = 32, + Regex = 33, + Dict = 34, + NamedArg = 35, + NamedArgPrefix = 36, + FunctionDef = 37, + Params = 38, + colon = 39, + keyword = 54, + Underscore = 41, + Array = 42, + Null = 43, + ConditionalOp = 44, + PositionalArg = 45, + IfExpr = 47, + SingleLineThenBlock = 49, + ThenBlock = 50, + ElseIfExpr = 51, + ElseExpr = 53, + Assign = 55 diff --git a/src/parser/shrimp.ts b/src/parser/shrimp.ts index 642c46b..a8cf6ee 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 "./scopeTracker" import {highlighting} from "./highlight" -const spec_Identifier = {__proto__:null,end:78, null:84, if:94, elseif:102, else:106} +const spec_Identifier = {__proto__:null,end:80, null:86, if:96, elseif:104, else:108} export const parser = LRParser.deserialize({ version: 14, - states: "3UQYQbOOO!ZOpO'#CrO#mQcO'#CuO$jOSO'#CwO$xQbO'#EUOOQ`'#DQ'#DQOOQa'#C}'#C}O%{QbO'#DVO'QQcO'#DyOOQa'#Dy'#DyO(lQcO'#DxO(yQRO'#CvO)[QcO'#DtO)sQbO'#CtOOQ`'#Du'#DuO*kQbO'#DtO*yQbO'#E[OOQ`'#D['#D[O+nQRO'#DdOOQ`'#Dt'#DtO+sQQO'#DsOOQ`'#Ds'#DsOOQ`'#De'#DeQYQbOOO+{ObO,59^O,TQbO'#DOOOQa'#Dx'#DxOOQ`'#DY'#DYOOQ`'#EZ'#EZOOQ`'#Dl'#DlO,_QbO,59]O,rQbO'#CyO,zQWO'#CzOOOO'#D{'#D{OOOO'#Df'#DfO-`OSO,59cOOQa,59c,59cOOQ`'#Dh'#DhO-nQbO'#DRO-vQQO,5:pOOQ`'#Dg'#DgO-{QbO,59qO.SQQO,59iOOQa,59q,59qO._QbO,59qO*yQbO,59bO*yQbO,59bO*yQbO,59bO.iQRO,59`O/YQRO'#CvO/vQRO,59`O0XQRO,59`O0SQQO,59`O0dQQO,59`O0lQbO'#DmO0wQbO,59[O1tQRO,5:vO1{QRO,5:vO2WQbO,5:OOOQ`,5:_,5:_OOQ`-E7c-E7cOOQa1G.x1G.xOOQ`,59j,59jOOQ`-E7j-E7jOOOO,59e,59eOOOO,59f,59fOOOO-E7d-E7dOOQa1G.}1G.}OOQ`-E7f-E7fO2bQbO1G0[OOQ`-E7e-E7eO2oQQO1G/TOOQa1G/]1G/]O2zQbO1G/]OOQO'#Dj'#DjO2oQQO1G/TOOQa1G/T1G/TOOQ`'#Dk'#DkO2zQbO1G/]OOQa1G.|1G.|O3mQcO1G.|O3wQcO1G.|O4RQcO1G.|OOQa1G.z1G.zO*yQbO,59sO*yQbO,59sO*yQbO,59sO!`QbO'#CuO&SQbO'#CqOOQ`,5:X,5:XOOQ`-E7k-E7kO4mQbO1G0bOOQ`1G/j1G/jO4zQbO7+%vO5PQbO7+%wO5aQQO7+$oOOQa7+$o7+$oO5lQbO7+$wOOQa7+$w7+$wOOQO-E7h-E7hOOQ`-E7i-E7iOOQP1G/_1G/_O6eQRO1G/_O6lQRO1G/_O6zQRO1G/_OOQ`'#D^'#D^O7RQbO7+%|O7WQbO7+%}OOQ`<}AN>}O*yQbO'#D`OOQ`'#Dn'#DnO8kQbOAN?TO8vQQO'#DbOOQ`AN?TAN?TO8{QbOAN?TO9QQRO,59zO9XQRO,59zOOQ`-E7l-E7lOOQ`G24oG24oO9dQbOG24oO9iQQO,59|O9nQQO1G/fOOQ`LD*ZLD*ZO5PQbO1G/hO7WQbO7+%QOOQ`7+%S7+%SOOQ`<h#i#o7^#o#p#{#p#q@`#q;'S#{;'S;=`$d<%l~#{~O#{~~@yS$QUlSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{S$gP;=`<%l#{^$qUlS!eYOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U%[UlS!wQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{^%uZlS!fYOY%nYZ#{Zt%ntu&huw%nwx&hx#O%n#O#P&h#P;'S%n;'S;=`'P<%lO%nY&mS!fYOY&hZ;'S&h;'S;=`&y<%lO&hY&|P;=`<%l&h^'SP;=`<%l%n~'[O!p~~'aO!n~U'hUlS!kQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(RUlS!|QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(jWlSOt#{uw#{x!Q#{!Q![)S![#O#{#P;'S#{;'S;=`$d<%lO#{U)ZYlSgQOt#{uw#{x!O#{!O!P)y!P!Q#{!Q![)S![#O#{#P;'S#{;'S;=`$d<%lO#{U*OWlSOt#{uw#{x!Q#{!Q![*h![#O#{#P;'S#{;'S;=`$d<%lO#{U*oWlSgQOt#{uw#{x!Q#{!Q![*h![#O#{#P;'S#{;'S;=`$d<%lO#{U+^WlSOt#{uw#{x!P#{!P!Q+v!Q#O#{#P;'S#{;'S;=`$d<%lO#{U+{^lSOY,wYZ#{Zt,wtu-zuw,wwx-zx!P,w!P!Q#{!Q!},w!}#O2m#O#P0Y#P;'S,w;'S;=`3n<%lO,wU-O^lSpQOY,wYZ#{Zt,wtu-zuw,wwx-zx!P,w!P!Q0o!Q!},w!}#O2m#O#P0Y#P;'S,w;'S;=`3n<%lO,wQ.PXpQOY-zZ!P-z!P!Q.l!Q!}-z!}#O/Z#O#P0Y#P;'S-z;'S;=`0i<%lO-zQ.oP!P!Q.rQ.wUpQ#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-zU0tWlSOt#{uw#{x!P#{!P!Q1^!Q#O#{#P;'S#{;'S;=`$d<%lO#{U1eblSpQOt#{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[lSOY2mYZ#{Zt2mtu/Zuw2mwx/Zx#O2m#O#P/s#P#Q,w#Q;'S2m;'S;=`3h<%lO2mU3kP;=`<%l2mU3qP;=`<%l,wU3{UlSvQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U4fW!vQlSOt#{uw#{x!_#{!_!`5O!`#O#{#P;'S#{;'S;=`$d<%lO#{U5TVlSOt#{uw#{x#O#{#P#Q5j#Q;'S#{;'S;=`$d<%lO#{U5qU!uQlSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~6YO!q~U6aU!{QlSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U6zUlSxQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U7cYlSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#o7^#o;'S#{;'S;=`$d<%lO#{U8YUsQlSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U8qZlSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#U9d#U#o7^#o;'S#{;'S;=`$d<%lO#{U9i[lSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#`7^#`#a:_#a#o7^#o;'S#{;'S;=`$d<%lO#{U:d[lSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#g7^#g#h;Y#h#o7^#o;'S#{;'S;=`$d<%lO#{U;_[lSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#X7^#X#Yo[!sWlSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#f7^#f#g?e#g#o7^#o;'S#{;'S;=`$d<%lO#{U?j[lSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#i7^#i#j;Y#j#o7^#o;'S#{;'S;=`$d<%lO#{U@gU}QlSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~AOO#Q~", - tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO!j~~", 11)], - topRules: {"Program":[0,19]}, - specialized: [{term: 14, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 14, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], + tokenData: "AO~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#Z8l#Z#b7^#b#ch#i#o7^#o#p#{#p#q@`#q;'S#{;'S;=`$d<%l~#{~O#{~~@yS$QUmSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{S$gP;=`<%l#{^$qUmS!fYOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U%[UmS!xQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{^%uZmS!gYOY%nYZ#{Zt%ntu&huw%nwx&hx#O%n#O#P&h#P;'S%n;'S;=`'P<%lO%nY&mS!gYOY&hZ;'S&h;'S;=`&y<%lO&hY&|P;=`<%l&h^'SP;=`<%l%n~'[O!q~~'aO!o~U'hUmS!lQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(RUmS!}QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(jWmSOt#{uw#{x!Q#{!Q![)S![#O#{#P;'S#{;'S;=`$d<%lO#{U)ZYmShQOt#{uw#{x!O#{!O!P)y!P!Q#{!Q![)S![#O#{#P;'S#{;'S;=`$d<%lO#{U*OWmSOt#{uw#{x!Q#{!Q![*h![#O#{#P;'S#{;'S;=`$d<%lO#{U*oWmShQOt#{uw#{x!Q#{!Q![*h![#O#{#P;'S#{;'S;=`$d<%lO#{U+^WmSOt#{uw#{x!P#{!P!Q+v!Q#O#{#P;'S#{;'S;=`$d<%lO#{U+{^mSOY,wYZ#{Zt,wtu-zuw,wwx-zx!P,w!P!Q#{!Q!},w!}#O2m#O#P0Y#P;'S,w;'S;=`3n<%lO,wU-O^mSqQOY,wYZ#{Zt,wtu-zuw,wwx-zx!P,w!P!Q0o!Q!},w!}#O2m#O#P0Y#P;'S,w;'S;=`3n<%lO,wQ.PXqQOY-zZ!P-z!P!Q.l!Q!}-z!}#O/Z#O#P0Y#P;'S-z;'S;=`0i<%lO-zQ.oP!P!Q.rQ.wUqQ#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-zU0tWmSOt#{uw#{x!P#{!P!Q1^!Q#O#{#P;'S#{;'S;=`$d<%lO#{U1ebmSqQOt#{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[mSOY2mYZ#{Zt2mtu/Zuw2mwx/Zx#O2m#O#P/s#P#Q,w#Q;'S2m;'S;=`3h<%lO2mU3kP;=`<%l2mU3qP;=`<%l,wU3{UmSwQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U4fW!wQmSOt#{uw#{x!_#{!_!`5O!`#O#{#P;'S#{;'S;=`$d<%lO#{U5TVmSOt#{uw#{x#O#{#P#Q5j#Q;'S#{;'S;=`$d<%lO#{U5qU!vQmSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~6YO!r~U6aU!|QmSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U6zUmSyQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U7cYmSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#o7^#o;'S#{;'S;=`$d<%lO#{U8YUtQmSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U8qZmSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#U9d#U#o7^#o;'S#{;'S;=`$d<%lO#{U9i[mSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#`7^#`#a:_#a#o7^#o;'S#{;'S;=`$d<%lO#{U:d[mSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#g7^#g#h;Y#h#o7^#o;'S#{;'S;=`$d<%lO#{U;_[mSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#X7^#X#Yo[!tWmSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#f7^#f#g?e#g#o7^#o;'S#{;'S;=`$d<%lO#{U?j[mSOt#{uw#{x!_#{!_!`8R!`#O#{#P#T#{#T#i7^#i#j;Y#j#o7^#o;'S#{;'S;=`$d<%lO#{U@gU!OQmSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~AOO#R~", + tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO!k~~", 11)], + topRules: {"Program":[0,20]}, + specialized: [{term: 15, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 15, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], tokenPrec: 1139 }) diff --git a/src/parser/tests/basics.test.ts b/src/parser/tests/basics.test.ts index 80c3a07..da6d4bb 100644 --- a/src/parser/tests/basics.test.ts +++ b/src/parser/tests/basics.test.ts @@ -598,7 +598,7 @@ describe('Conditional ops', () => { test('or can be chained', () => { expect(` is-positive = do x: - if x = 3 or x = 4 or x = 5: + if x == 3 or x == 4 or x == 5: true end end @@ -617,17 +617,17 @@ Assign ConditionalOp ConditionalOp Identifier x - Eq = + EqEq == Number 3 Or or ConditionalOp Identifier x - Eq = + EqEq == Number 4 Or or ConditionalOp Identifier x - Eq = + EqEq == Number 5 colon : ThenBlock @@ -640,7 +640,7 @@ Assign test('and can be chained', () => { expect(` is-positive = do x: - if x = 3 and x = 4 and x = 5: + if x == 3 and x == 4 and x == 5: true end end @@ -659,17 +659,17 @@ Assign ConditionalOp ConditionalOp Identifier x - Eq = + EqEq == Number 3 And and ConditionalOp Identifier x - Eq = + EqEq == Number 4 And and ConditionalOp Identifier x - Eq = + EqEq == Number 5 colon : ThenBlock diff --git a/src/parser/tests/control-flow.test.ts b/src/parser/tests/control-flow.test.ts index 11c81d0..70efddb 100644 --- a/src/parser/tests/control-flow.test.ts +++ b/src/parser/tests/control-flow.test.ts @@ -4,12 +4,12 @@ import '../shrimp.grammar' // Importing this so changes cause it to retest! describe('if/elseif/else', () => { test('parses single line if', () => { - expect(`if y = 1: 'cool' end`).toMatchTree(` + expect(`if y == 1: 'cool' end`).toMatchTree(` IfExpr keyword if ConditionalOp Identifier y - Eq = + EqEq == Number 1 colon : SingleLineThenBlock