From 5234ad9a7342978ad7c5c2de6c6c59e242001963 Mon Sep 17 00:00:00 2001 From: Corey Johnson Date: Sat, 25 Oct 2025 09:53:45 -0700 Subject: [PATCH] better stuff --- src/compiler/tests/compiler.test.ts | 12 +++---- src/compiler/utils.ts | 4 +-- src/parser/shrimp.grammar | 9 +++--- src/parser/shrimp.terms.ts | 5 ++- src/parser/shrimp.ts | 46 +++++++++++++++++---------- src/parser/tests/basics.test.ts | 8 +++++ src/parser/tests/control-flow.test.ts | 32 +++++++++---------- 7 files changed, 67 insertions(+), 49 deletions(-) diff --git a/src/compiler/tests/compiler.test.ts b/src/compiler/tests/compiler.test.ts index a864828..35da324 100644 --- a/src/compiler/tests/compiler.test.ts +++ b/src/compiler/tests/compiler.test.ts @@ -112,18 +112,18 @@ describe('compiler', () => { end`).toEvaluateTo('white') }) - test('if elsif', () => { + test('if elseif', () => { expect(`if false: boromir - elsif true: + elseif true: frodo end`).toEvaluateTo('frodo') }) - test('if elsif else', () => { + test('if elseif else', () => { expect(`if false: destroyed - elsif true: + elseif true: fire else: darkness @@ -131,9 +131,9 @@ describe('compiler', () => { expect(`if false: king - elsif false: + elseif false: elf - elsif true: + elseif true: dwarf else: scattered diff --git a/src/compiler/utils.ts b/src/compiler/utils.ts index 937efe5..312962f 100644 --- a/src/compiler/utils.ts +++ b/src/compiler/utils.ts @@ -140,11 +140,11 @@ export const getIfExprParts = (node: SyntaxNode, input: string) => { throw new CompilerError(message, child.from, child.to) } elseThenBlock = parts.at(-1) - } else if (child.type.id === terms.ElsifExpr) { + } else if (child.type.id === terms.ElseIfExpr) { const [_keyword, conditional, _colon, thenBlock] = parts if (!conditional || !thenBlock) { const names = parts.map((p) => p.type.name).join(', ') - const message = `ElsifExpr expected conditional and thenBlock, got ${names}` + const message = `ElseIfExpr expected conditional and thenBlock, got ${names}` throw new CompilerError(message, child.from, child.to) } diff --git a/src/parser/shrimp.grammar b/src/parser/shrimp.grammar index b840492..9a8790f 100644 --- a/src/parser/shrimp.grammar +++ b/src/parser/shrimp.grammar @@ -22,7 +22,6 @@ rightParen { ")" } colon[closedBy="end", @name="colon"] { ":" } Underscore { "_" } - Null { "null" } Regex { "//" (![/\\\n[] | "\\" ![\n] | "[" (![\n\\\]] | "\\" ![\n])* "]")+ ("//" $[gimsuy]*)? } // Stolen from the lezer JavaScript grammar "|"[@name=operator] @@ -109,11 +108,11 @@ singleLineIf { } multilineIf { - @specialize[@name=keyword] (ConditionalOp | expression) colon newlineOrSemicolon ThenBlock ElsifExpr* ElseExpr? @specialize[@name=keyword] + @specialize[@name=keyword] (ConditionalOp | expression) colon newlineOrSemicolon ThenBlock ElseIfExpr* ElseExpr? @specialize[@name=keyword] } -ElsifExpr { - @specialize[@name=keyword] (ConditionalOp | expression) colon newlineOrSemicolon ThenBlock +ElseIfExpr { + @specialize[@name=keyword] (ConditionalOp | expression) colon newlineOrSemicolon ThenBlock } ElseExpr { @@ -196,7 +195,7 @@ EscapeSeq { // to go through ambiguousFunctionCall (which is what we want semantically). // Yes, it is annoying and I gave up trying to use GLR to fix it. expressionWithoutIdentifier { - ParenExpr | Word | String | Number | Boolean | Regex | Null + ParenExpr | Word | String | Number | Boolean | Regex | @specialize[@name=Null] } block { diff --git a/src/parser/shrimp.terms.ts b/src/parser/shrimp.terms.ts index cef00ea..b39e11d 100644 --- a/src/parser/shrimp.terms.ts +++ b/src/parser/shrimp.terms.ts @@ -1,6 +1,5 @@ // This file was generated by lezer-generator. You probably shouldn't edit it. -export const - Star = 1, +export const Star = 1, Slash = 2, Plus = 3, Minus = 4, @@ -42,6 +41,6 @@ export const NamedArgPrefix = 41, IfExpr = 43, ThenBlock = 46, - ElsifExpr = 47, + ElseIfExpr = 47, ElseExpr = 49, Assign = 51 diff --git a/src/parser/shrimp.ts b/src/parser/shrimp.ts index 5e6b2e9..548273d 100644 --- a/src/parser/shrimp.ts +++ b/src/parser/shrimp.ts @@ -1,27 +1,39 @@ // 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} from "./tokenizer" -import {trackScope} from "./scopeTracker" -import {highlighting} from "./highlight" -const spec_Identifier = {__proto__:null,do:70, end:76, if:88, elsif:96, else:100} +import { LRParser, LocalTokenGroup } from '@lezer/lr' +import { operatorTokenizer } from './operatorTokenizer' +import { tokenizer } from './tokenizer' +import { trackScope } from './scopeTracker' +import { highlighting } from './highlight' +const spec_Identifier = { + __proto__: null, + null: 64, + do: 70, + end: 76, + if: 88, + elseif: 96, + else: 100, +} export const parser = LRParser.deserialize({ version: 14, - states: ".jQVQbOOO#XQcO'#CrO$RQRO'#CsO$aQcO'#DmO$xQbO'#CqO%gOSO'#CuOOQa'#Dq'#DqO%uOpO'#C}O%zQcO'#DpO&cQbO'#D|OOQ`'#DO'#DOOOQ`'#Dn'#DnO&kQbO'#DmO&yQbO'#EQOOQ`'#DX'#DXO'hQRO'#DaOOQ`'#Dm'#DmO'mQQO'#DlOOQ`'#Dl'#DlOOQ`'#Db'#DbQVQbOOOOQa'#Dp'#DpOOQ`'#Cp'#CpO'uQbO'#DUOOQ`'#Do'#DoOOQ`'#Dc'#DcO(PQbO,59ZO&yQbO,59_O&yQbO,59_O)XQRO'#CsO)iQRO,59]O)zQRO,59]O)uQQO,59]O*uQQO,59]O*}QbO'#CwO+VQWO'#CxOOOO'#Du'#DuOOOO'#Dd'#DdO+kOSO,59aOOQa,59a,59aO+yO`O,59iOOQ`'#De'#DeO,OQbO'#DQO,WQQO,5:hO,]QbO'#DgO,bQbO,59YO,sQRO,5:lO,zQQO,5:lO-PQbO,59{OOQ`,5:W,5:WOOQ`-E7`-E7`OOQ`,59p,59pOOQ`-E7a-E7aOOQa1G.y1G.yO-^QcO1G.yO&yQbO,59`O&yQbO,59`OOQa1G.w1G.wOOOO,59c,59cOOOO,59d,59dOOOO-E7b-E7bOOQa1G.{1G.{OOQa1G/T1G/TOOQ`-E7c-E7cO-xQbO1G0SO!QQbO'#CrOOQ`,5:R,5:ROOQ`-E7e-E7eO.YQbO1G0WOOQ`1G/g1G/gOOQO1G.z1G.zO.jQRO1G.zO.tQbO7+%nO.yQbO7+%oOOQ`'#DZ'#DZOOQ`7+%r7+%rO/ZQbO7+%sOOQ`<uAN>uO&yQbO'#D]OOQ`'#Dh'#DhO0nQbOAN>yO0yQQO'#D_OOQ`AN>yAN>yO1OQbOAN>yO1TQRO,59wO1[QQO,59wOOQ`-E7f-E7fOOQ`G24eG24eO1aQbOG24eO1fQQO,59yO1kQQO1G/cOOQ`LD*PLD*PO.yQbO1G/eO/ZQbO7+$}OOQ`7+%P7+%POOQ`<uAN>uO&yQbO'#D]OOQ`'#Dh'#DhO0nQbOAN>yO0yQQO'#D_OOQ`AN>yAN>yO1OQbOAN>yO1TQRO,59wO1[QQO,59wOOQ`-E7f-E7fOOQ`G24eG24eO1aQbOG24eO1fQQO,59yO1kQQO1G/cOOQ`LD*PLD*PO.yQbO1G/eO/ZQbO7+$}OOQ`7+%P7+%POOQ`<V#g#o3]#o;'S#r;'S;=`$Z<%lO#rU>[[jSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#i3]#i#j7X#j#o3]#o;'S#r;'S;=`$Z<%lO#rU?XUzQjSOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#r~?pO!v~", - tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO!o~~", 11)], - topRules: {"Program":[0,17]}, - specialized: [{term: 13, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], - tokenPrec: 768 + tokenData: + "<}~RyOX#rXY$aYZ$zZp#rpq$aqt#rtu%euw#rwx%jxy%oyz&Yz{#r{|&s|}#r}!O&s!O!P#r!P!Q)g!Q!['b![!]2S!]!^$z!^#O#r#O#P2m#P#R#r#R#S2r#S#T#r#T#Y3]#Y#Z4k#Z#b3]#b#c8y#c#f3]#f#g9p#g#h3]#h#i:g#i#o3]#o#p#r#p#q<_#q;'S#r;'S;=`$Z<%l~#r~O#r~~ spec_Identifier[value] || -1 }, + ], + tokenPrec: 768, }) diff --git a/src/parser/tests/basics.test.ts b/src/parser/tests/basics.test.ts index 86e4efa..3299ff3 100644 --- a/src/parser/tests/basics.test.ts +++ b/src/parser/tests/basics.test.ts @@ -14,6 +14,14 @@ describe('null', () => { Eq = Null null`) }) + + test('does not parse null in identifier', () => { + expect('null-jk = 5').toMatchTree(` + Assign + AssignableIdentifier null-jk + Eq = + Number 5`) + }) }) describe('Identifier', () => { diff --git a/src/parser/tests/control-flow.test.ts b/src/parser/tests/control-flow.test.ts index 33cf734..df1bc3c 100644 --- a/src/parser/tests/control-flow.test.ts +++ b/src/parser/tests/control-flow.test.ts @@ -2,7 +2,7 @@ import { expect, describe, test } from 'bun:test' import '../shrimp.grammar' // Importing this so changes cause it to retest! -describe('if/elsif/else', () => { +describe('if/elseif/else', () => { test('parses single line if', () => { expect(`if y = 1: 'cool'`).toMatchTree(` IfExpr @@ -72,21 +72,21 @@ describe('if/elsif/else', () => { `) }) - test('parses multiline if with elsif', () => { - expect(`if with-elsif: + test('parses multiline if with elseif', () => { + expect(`if with-elseif: x - elsif another-condition: + elseif another-condition: y end`).toMatchTree(` IfExpr keyword if - Identifier with-elsif + Identifier with-elseif colon : ThenBlock FunctionCallOrIdentifier Identifier x - ElsifExpr - keyword elsif + ElseIfExpr + keyword elseif Identifier another-condition colon : ThenBlock @@ -96,32 +96,32 @@ describe('if/elsif/else', () => { `) }) - test('parses multiline if with multiple elsif and else', () => { - expect(`if with-elsif-else: + test('parses multiline if with multiple elseif and else', () => { + expect(`if with-elseif-else: x - elsif another-condition: + elseif another-condition: y - elsif yet-another-condition: + elseif yet-another-condition: z else: oh-no end`).toMatchTree(` IfExpr keyword if - Identifier with-elsif-else + Identifier with-elseif-else colon : ThenBlock FunctionCallOrIdentifier Identifier x - ElsifExpr - keyword elsif + ElseIfExpr + keyword elseif Identifier another-condition colon : ThenBlock FunctionCallOrIdentifier Identifier y - ElsifExpr - keyword elsif + ElseIfExpr + keyword elseif Identifier yet-another-condition colon : ThenBlock