From fd197a2dfc8138a3f638e66939e33231a1e0675c Mon Sep 17 00:00:00 2001 From: Chris Wanstrath Date: Wed, 29 Oct 2025 11:13:30 -0700 Subject: [PATCH] fix or/and chaining --- src/parser/shrimp.grammar | 19 +++++--- src/parser/shrimp.ts | 8 +-- src/parser/tests/basics.test.ts | 86 +++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 12 deletions(-) diff --git a/src/parser/shrimp.grammar b/src/parser/shrimp.grammar index 9147110..794dc29 100644 --- a/src/parser/shrimp.grammar +++ b/src/parser/shrimp.grammar @@ -33,6 +33,9 @@ @precedence { pipe @left, + or @left, + and @left, + comparison @left, multiplicative @left, additive @left, call @@ -129,14 +132,14 @@ SingleLineThenBlock { } ConditionalOp { - expression Eq expression | - expression Neq expression | - expression Lt expression | - expression Lte expression | - expression Gt expression | - expression Gte expression | - expression And (expression | ConditionalOp) | - expression Or (expression | ConditionalOp) + expression !comparison Eq expression | + expression !comparison Neq expression | + expression !comparison Lt expression | + expression !comparison Lte expression | + expression !comparison Gt expression | + expression !comparison Gte expression | + (expression | ConditionalOp) !and And (expression | ConditionalOp) | + (expression | ConditionalOp) !or Or (expression | ConditionalOp) } Params { diff --git a/src/parser/shrimp.ts b/src/parser/shrimp.ts index 5b4e228..642c46b 100644 --- a/src/parser/shrimp.ts +++ b/src/parser/shrimp.ts @@ -7,9 +7,9 @@ import {highlighting} from "./highlight" const spec_Identifier = {__proto__:null,end:78, null:84, if:94, elseif:102, else:106} export const parser = LRParser.deserialize({ version: 14, - states: "2lQYQbOOO!ZOpO'#CrO#mQcO'#CuO$jOSO'#CwO$xQbO'#EUOOQ`'#DQ'#DQOOQa'#C}'#C}O%{QbO'#DVO'QQcO'#DyOOQa'#Dy'#DyO(lQcO'#DxO(yQRO'#CvO)[QcO'#DtO)sQbO'#CtOOQ`'#Du'#DuO*kQbO'#DtO*yQbO'#E[OOQ`'#D['#D[O+nQRO'#DdOOQ`'#Dt'#DtO+sQQO'#DsOOQ`'#Ds'#DsOOQ`'#De'#DeQYQbOOO+{ObO,59^O,TQbO'#DOOOQa'#Dx'#DxOOQ`'#DY'#DYOOQ`'#EZ'#EZOOQ`'#Dl'#DlO,_QbO,59]O,rQbO'#CyO,zQWO'#CzOOOO'#D{'#D{OOOO'#Df'#DfO-`OSO,59cOOQa,59c,59cOOQ`'#Dh'#DhO-nQbO'#DRO-vQQO,5:pOOQ`'#Dg'#DgO-{QbO,59qO.SQQO,59iOOQa,59q,59qO._QbO,59qO*yQbO,59bO*yQbO,59bO*yQbO,59bO.iQRO,59`O/YQRO'#CvO/vQRO,59`O0SQQO,59`O0XQQO,59`O0aQbO'#DmO0lQbO,59[O1iQRO,5:vO1pQQO,5:vO1uQbO,5:OOOQ`,5:_,5:_OOQ`-E7c-E7cOOQa1G.x1G.xOOQ`,59j,59jOOQ`-E7j-E7jOOOO,59e,59eOOOO,59f,59fOOOO-E7d-E7dOOQa1G.}1G.}OOQ`-E7f-E7fO2PQbO1G0[OOQ`-E7e-E7eO2^QQO1G/TOOQa1G/]1G/]O2iQbO1G/]OOQO'#Dj'#DjO2^QQO1G/TOOQa1G/T1G/TOOQ`'#Dk'#DkO2iQbO1G/]OOQa1G.|1G.|O3[QcO1G.|O3fQcO1G.|O3pQcO1G.|OOQa1G.z1G.zO*yQbO,59sO*yQbO,59sO!`QbO'#CuO&SQbO'#CqOOQ`,5:X,5:XOOQ`-E7k-E7kO4[QbO1G0bOOQ`1G/j1G/jO4iQbO7+%vO4nQbO7+%wO5OQQO7+$oOOQa7+$o7+$oO5ZQbO7+$wOOQa7+$w7+$wOOQO-E7h-E7hOOQ`-E7i-E7iOOQO1G/_1G/_O5eQRO1G/_OOQ`'#D^'#D^O5oQbO7+%|O5tQbO7+%}OOQ`<}AN>}O*yQbO'#D`OOQ`'#Dn'#DnO7XQbOAN?TO7dQQO'#DbOOQ`AN?TAN?TO7iQbOAN?TO7nQRO,59zO7uQQO,59zOOQ`-E7l-E7lOOQ`G24oG24oO7zQbOG24oO8PQQO,59|O8UQQO1G/fOOQ`LD*ZLD*ZO4nQbO1G/hO5tQbO7+%QOOQ`7+%S7+%SOOQ`<}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`< (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 14, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], - tokenPrec: 1069 + tokenPrec: 1139 }) diff --git a/src/parser/tests/basics.test.ts b/src/parser/tests/basics.test.ts index f5e03f2..80c3a07 100644 --- a/src/parser/tests/basics.test.ts +++ b/src/parser/tests/basics.test.ts @@ -592,4 +592,90 @@ describe('Comments', () => { Slash / Identifier prop`) }) +}) + +describe('Conditional ops', () => { + test('or can be chained', () => { + expect(` + is-positive = do x: + if x = 3 or x = 4 or x = 5: + true + end + end + `).toMatchTree(` +Assign + AssignableIdentifier is-positive + Eq = + FunctionDef + Do do + Params + Identifier x + colon : + IfExpr + keyword if + ConditionalOp + ConditionalOp + ConditionalOp + Identifier x + Eq = + Number 3 + Or or + ConditionalOp + Identifier x + Eq = + Number 4 + Or or + ConditionalOp + Identifier x + Eq = + Number 5 + colon : + ThenBlock + Boolean true + keyword end + keyword end + `) + }) + + test('and can be chained', () => { + expect(` + is-positive = do x: + if x = 3 and x = 4 and x = 5: + true + end + end + `).toMatchTree(` +Assign + AssignableIdentifier is-positive + Eq = + FunctionDef + Do do + Params + Identifier x + colon : + IfExpr + keyword if + ConditionalOp + ConditionalOp + ConditionalOp + Identifier x + Eq = + Number 3 + And and + ConditionalOp + Identifier x + Eq = + Number 4 + And and + ConditionalOp + Identifier x + Eq = + Number 5 + colon : + ThenBlock + Boolean true + keyword end + keyword end + `) + }) }) \ No newline at end of file