Compare commits

..

2 Commits

Author SHA1 Message Date
d306d58b2f now it is do 2025-10-24 14:04:50 -07:00
7077762738 great stuff 2025-10-24 14:02:29 -07:00
12 changed files with 137 additions and 93 deletions

View File

@ -63,26 +63,26 @@ describe('compiler', () => {
})
test('function', () => {
expect(`fn a b: a + b end`).toEvaluateTo(Function)
expect(`do a b: a + b end`).toEvaluateTo(Function)
})
test('function call', () => {
expect(`add = fn a b: a + b end; add 2 9`).toEvaluateTo(11)
expect(`add = do a b: a + b end; add 2 9`).toEvaluateTo(11)
})
test('function call with named args', () => {
expect(`minus = fn a b: a - b end; minus b=2 a=9`).toEvaluateTo(7)
expect(`minus = do a b: a - b end; minus b=2 a=9`).toEvaluateTo(7)
})
test('function call with named and positional args', () => {
expect(`minus = fn a b: a - b end; minus b=2 9`).toEvaluateTo(7)
expect(`minus = fn a b: a - b end; minus 90 b=20`).toEvaluateTo(70)
expect(`minus = fn a b: a - b end; minus a=900 200`).toEvaluateTo(700)
expect(`minus = fn a b: a - b end; minus 2000 a=9000`).toEvaluateTo(7000)
expect(`minus = do a b: a - b end; minus b=2 9`).toEvaluateTo(7)
expect(`minus = do a b: a - b end; minus 90 b=20`).toEvaluateTo(70)
expect(`minus = do a b: a - b end; minus a=900 200`).toEvaluateTo(700)
expect(`minus = do a b: a - b end; minus 2000 a=9000`).toEvaluateTo(7000)
})
test('function call with no args', () => {
expect(`bloop = fn: 'bloop' end; bloop`).toEvaluateTo('bloop')
expect(`bloop = do: 'bloop' end; bloop`).toEvaluateTo('bloop')
})
test('simple conditionals', () => {
@ -150,7 +150,7 @@ describe('errors', () => {
describe('multiline tests', () => {
test('multiline function', () => {
expect(`
add = fn a b:
add = do a b:
result = a + b
result
end

View File

@ -3,7 +3,7 @@ import { describe, test, expect } from 'bun:test'
describe('pipe expressions', () => {
test('simple pipe passes result as first argument', () => {
const code = `
double = fn x: x * 2 end
double = do x: x * 2 end
double 2 | double`
expect(code).toEvaluateTo(8)
@ -11,9 +11,9 @@ describe('pipe expressions', () => {
test('pipe chain with three stages', () => {
const code = `
add-one = fn x: x + 1 end
double = fn x: x * 2 end
minus-point-one = fn x: x - 0.1 end
add-one = do x: x + 1 end
double = do x: x * 2 end
minus-point-one = do x: x - 0.1 end
add-one 3 | double | minus-point-one`
// 4 8 7.9
expect(code).toEvaluateTo(7.9)
@ -21,8 +21,8 @@ describe('pipe expressions', () => {
test('pipe with function that has additional arguments', () => {
const code = `
multiply = fn a b: a * b end
get-five = fn: 5 end
multiply = do a b: a * b end
get-five = do: 5 end
get-five | multiply 3`
expect(code).toEvaluateTo(15)
@ -31,7 +31,7 @@ describe('pipe expressions', () => {
test('pipe with bare identifier', () => {
const code = `
get-value = 42
process = fn x: x + 10 end
process = do x: x + 10 end
get-value | process`
expect(code).toEvaluateTo(52)
@ -39,7 +39,7 @@ describe('pipe expressions', () => {
test('pipe in assignment', () => {
const code = `
add-ten = fn x: x + 10 end
add-ten = do x: x + 10 end
result = add-ten 5 | add-ten
result`
@ -49,23 +49,23 @@ describe('pipe expressions', () => {
test('pipe with named underscore arg', () => {
expect(`
divide = fn a b: a / b end
get-ten = fn: 10 end
divide = do a b: a / b end
get-ten = do: 10 end
get-ten | divide 2 b=_`).toEvaluateTo(0.2)
expect(`
divide = fn a b: a / b end
get-ten = fn: 10 end
divide = do a b: a / b end
get-ten = do: 10 end
get-ten | divide b=_ 2`).toEvaluateTo(0.2)
expect(`
divide = fn a b: a / b end
get-ten = fn: 10 end
divide = do a b: a / b end
get-ten = do: 10 end
get-ten | divide 2 a=_`).toEvaluateTo(5)
expect(`
divide = fn a b: a / b end
get-ten = fn: 10 end
divide = do a b: a / b end
get-ten = do: 10 end
get-ten | divide a=_ 2`).toEvaluateTo(5)
})
@ -74,8 +74,8 @@ describe('pipe expressions', () => {
// handling logic works correctly when there are multiple pipe stages
// in a single expression.
expect(`
sub = fn a b: a - b end
div = fn a b: a / b end
sub = do a b: a - b end
div = do a b: a / b end
sub 3 1 | div (sub 110 9 | sub 1) _ | div 5`).toEvaluateTo(10)
})
})

View File

@ -245,7 +245,7 @@ const commandShapes: CommandShape[] = [
] as const
let commandSource = () => commandShapes
export const setCommandSource = (fn: () => CommandShape[]) => {
export const setCommandSource = (do: () => CommandShape[]) => {
commandSource = fn
}

View File

@ -21,14 +21,9 @@
leftParen { "(" }
rightParen { ")" }
colon[closedBy="end", @name="colon"] { ":" }
end[openedBy="colon", @name="end"] { "end" }
Underscore { "_" }
Null { "null" }
Regex { "//" (![/\\\n[] | "\\" ![\n] | "[" (![\n\\\]] | "\\" ![\n])* "]")+ ("//" $[gimsuy]*)? } // Stolen from the lezer JavaScript grammar
Fn[@name=keyword] { "fn" }
"if" [@name=keyword]
"elsif" [@name=keyword]
"else" [@name=keyword]
"|"[@name=operator]
}
@ -98,11 +93,11 @@ FunctionDef {
}
singleLineFunctionDef {
Fn Params colon consumeToTerminator end
@specialize[@name=keyword]<Identifier, "do"> Params colon consumeToTerminator @specialize[@name=keyword]<Identifier, "end">
}
multilineFunctionDef {
Fn Params colon newlineOrSemicolon block end
@specialize[@name=keyword]<Identifier, "do"> Params colon newlineOrSemicolon block @specialize[@name=keyword]<Identifier, "end">
}
IfExpr {
@ -110,19 +105,19 @@ IfExpr {
}
singleLineIf {
"if" (ConditionalOp | expression) colon ThenBlock { consumeToTerminator }
@specialize[@name=keyword]<Identifier, "if"> (ConditionalOp | expression) colon ThenBlock { consumeToTerminator }
}
multilineIf {
"if" (ConditionalOp | expression) colon newlineOrSemicolon ThenBlock ElsifExpr* ElseExpr? end
@specialize[@name=keyword]<Identifier, "if"> (ConditionalOp | expression) colon newlineOrSemicolon ThenBlock ElsifExpr* ElseExpr? @specialize[@name=keyword]<Identifier, "end">
}
ElsifExpr {
"elsif" (ConditionalOp | expression) colon newlineOrSemicolon ThenBlock
@specialize[@name=keyword]<Identifier, "elsif"> (ConditionalOp | expression) colon newlineOrSemicolon ThenBlock
}
ElseExpr {
"else" colon newlineOrSemicolon ThenBlock
@specialize[@name=keyword]<Identifier, "else"> colon newlineOrSemicolon ThenBlock
}
ThenBlock {
@ -163,9 +158,14 @@ expression {
expressionWithoutIdentifier | DotGet | Identifier
}
@local tokens {
dot { "." }
}
@skip {} {
DotGet {
IdentifierBeforeDot "." Identifier
IdentifierBeforeDot dot Identifier
}
String { "'" stringContent* "'" }

View File

@ -34,10 +34,9 @@ export const
Null = 32,
DotGet = 33,
FunctionDef = 34,
Fn = 35,
keyword = 50,
Params = 36,
colon = 37,
end = 38,
Underscore = 39,
NamedArg = 40,
NamedArgPrefix = 41,

View File

@ -1,26 +1,27 @@
// This file was generated by lezer-generator. You probably shouldn't edit it.
import {LRParser} from "@lezer/lr"
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}
export const parser = LRParser.deserialize({
version: 14,
states: ".jQVQrOOO#XQuO'#CrO$RQRO'#CsO$aQRO'#DmO$xQrO'#CqO%gOWO'#CuOOQq'#Dq'#DqO%uOQO'#C}O%zQRO'#DpO&cQrO'#D|OOQp'#DO'#DOOOQO'#Dn'#DnO&kQQO'#DmO&yQrO'#EQOOQO'#DX'#DXO'hQRO'#DaOOQO'#Dm'#DmO'mQQO'#DlOOQp'#Dl'#DlOOQp'#Db'#DbQVQrOOOOQq'#Dp'#DpOOQp'#Cp'#CpO'uQrO'#DUOOQp'#Do'#DoOOQp'#Dc'#DcO(PQtO,59ZO&yQrO,59_O&yQrO,59_O)XQRO'#CsO)iQRO,59]O)zQRO,59]O)uQQO,59]O*uQQO,59]O*}QrO'#CwO+VQ`O'#CxOOOO'#Du'#DuOOOO'#Dd'#DdO+kOWO,59aOOQq,59a,59aO+yOpO,59iOOQp'#De'#DeO,OQrO'#DQO,WQQO,5:hO,]QrO'#DgO,bQQO,59YO,sQRO,5:lO,zQQO,5:lO-PQrO,59{OOQp,5:W,5:WOOQp-E7`-E7`OOQp,59p,59pOOQp-E7a-E7aOOQP1G.y1G.yO-^QRO1G.yO&yQrO,59`O&yQrO,59`OOQq1G.w1G.wOOOO,59c,59cOOOO,59d,59dOOOO-E7b-E7bOOQq1G.{1G.{OOQq1G/T1G/TOOQp-E7c-E7cO-xQrO1G0SO!QQtO'#CrOOQO,5:R,5:ROOQO-E7e-E7eO.YQrO1G0WOOQO1G/g1G/gOOQO1G.z1G.zO.jQRO1G.zO.tQQO7+%nO.yQrO7+%oOOQO'#DZ'#DZOOQO7+%r7+%rO/ZQrO7+%sOOQp<<IY<<IYO/qQQO'#DfO/vQrO'#EPO0^QQO<<IZOOQO'#D['#D[O0cQQO<<I_OOQp,5:Q,5:QOOQp-E7d-E7dOOQpAN>uAN>uO&yQrO'#D]OOQO'#Dh'#DhO0nQQOAN>yO0yQQO'#D_OOQOAN>yAN>yO1OQQOAN>yO1TQRO,59wO1[QQO,59wOOQO-E7f-E7fOOQOG24eG24eO1aQQOG24eO1fQQO,59yO1kQQO1G/cOOQOLD*PLD*PO.yQrO1G/eO/ZQrO7+$}OOQO7+%P7+%POOQO<<Hi<<Hi",
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`<<IY<<IYO/qQQO'#DfO/vQbO'#EPO0^QbO<<IZOOQ`'#D['#D[O0cQbO<<I_OOQ`,5:Q,5:QOOQ`-E7d-E7dOOQ`AN>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`<<Hi<<Hi",
stateData: "1s~O!_OS~O]PO^_O_UO`VOmUOnUOoUOpUOsXO|]O!fSO!hTO!rbO~O]eO_UO`VOmUOnUOoUOpUOsXOwfOygO!fSO!hTOzfX!rfX!vfX!gfXvfX~OP!dXQ!dXR!dXS!dXT!dXU!dXV!dXW!dXX!dXY!dXZ!dX[!dX~P!QOPkOQkORlOSlO~OPkOQkORlOSlO!r!aX!v!aXv!aX~O]PO_UO`VOmUOnUOoUOpUO!fSO!hTO~OjtO!hwO!jrO!ksO~O!oxO~OP!dXQ!dXR!dXS!dX!r!aX!v!aXv!aX~O^yOutP~Oz|O!r!aX!v!aXv!aX~O]eO_UO`VOmUOnUOoUOpUO!fSO!hTO~OV!QO~O!r!RO!v!RO~OsXOw!TO~P&yOsXOwfOygOzca!rca!vca!gcavca~P&yOT!YOU!YOV!XOW!XOX!XOY!XOZ!XO[!XO~OPkOQkORlOSlO~P(mOPkOQkORlOSlO!g!ZO~O!g!ZOP!dXQ!dXR!dXS!dXT!dXU!dXV!dXW!dXX!dXY!dXZ!dX[!dX~Oz|O!g!ZO~O]![O!fSO~O!h!]O!j!]O!k!]O!l!]O!m!]O!n!]O~OjtO!h!_O!jrO!ksO~O]!`O~O^yOutX~Ou!bO~O]!cO~Oz|O!rba!vba!gbavba~Ou!fO~P(mOu!fO~O^_OsXO|]O~P$xOPkOQkORgiSgi!rgi!vgi!ggivgi~O^_OsXO|]O!r!kO~P$xO^_OsXO|]O!r!nO~P$xO!ghiuhi~P(mOv!oO~O^_OsXO|]Ov!sP~P$xO^_OsXO|]Ov!sP!Q!sP!S!sP~P$xO!r!uO~O^_OsXO|]Ov!sX!Q!sX!S!sX~P$xOv!wO~Ov!|O!Q!xO!S!{O~Ov#RO!Q!xO!S!{O~Ou#TO~Ov#RO~Ou#UO~P(mOu#UO~Ov#VO~O!r#WO~O!r#XO~Omo~",
goto: "+m!vPPPPPPPPPPPPPPPPPP!w#W#f#k#W$V$l$xP%a%aPPPP%e&OP&dPPP#fPP&gP&s&v'PP'TP&g'Z'a'h'n't'}(UPPP([(`(t)W)]*WPPP*sPPPPPP*w*wP+X+a+ad`Od!Q!b!f!k!n!q#W#XRpSiZOSd|!Q!b!f!k!n!q#W#XVhPj!czUOPS]dgjkl!Q!X!Y!b!c!f!k!n!q!x#W#XR![rdROd!Q!b!f!k!n!q#W#XQnSQ!VkR!WlQpSQ!P]Q!h!YR#P!x{UOPS]dgjkl!Q!X!Y!b!c!f!k!n!q!x#W#XTtTvdWOd!Q!b!f!k!n!q#W#XgePS]gjkl!X!Y!c!xd`Od!Q!b!f!k!n!q#W#XUfPj!cR!TgR{Xe`Od!Q!b!f!k!n!q#W#XR!m!fQ!t!nQ#Y#WR#Z#XT!y!t!zQ!}!tR#S!zQdOR!SdSjP!cR!UjQvTR!^vQzXR!azW!q!k!n#W#XR!v!qS}[qR!e}Q!z!tR#Q!zTcOdSaOdQ!g!QQ!j!bQ!l!fZ!p!k!n!q#W#Xd[Od!Q!b!f!k!n!q#W#XQqSR!d|ViPj!cdQOd!Q!b!f!k!n!q#W#XUfPj!cQmSQ!O]Q!TgQ!VkQ!WlQ!h!XQ!i!YR#O!xdWOd!Q!b!f!k!n!q#W#XdeP]gjkl!X!Y!c!xRoSTuTvmYOPdgj!Q!b!c!f!k!n!q#W#XQ!r!kV!s!n#W#Xe^Od!Q!b!f!k!n!q#W#X",
nodeNames: "⚠ Star Slash Plus Minus And Or Eq Neq Lt Lte Gt Gte Identifier AssignableIdentifier Word IdentifierBeforeDot Program PipeExpr FunctionCall PositionalArg ParenExpr FunctionCallOrIdentifier BinOp ConditionalOp String StringFragment Interpolation EscapeSeq Number Boolean Regex Null DotGet FunctionDef keyword Params colon end Underscore NamedArg NamedArgPrefix operator IfExpr keyword ThenBlock ThenBlock ElsifExpr keyword ElseExpr keyword Assign",
nodeNames: "⚠ Star Slash Plus Minus And Or Eq Neq Lt Lte Gt Gte Identifier AssignableIdentifier Word IdentifierBeforeDot Program PipeExpr FunctionCall PositionalArg ParenExpr FunctionCallOrIdentifier BinOp ConditionalOp String StringFragment Interpolation EscapeSeq Number Boolean Regex Null DotGet FunctionDef keyword Params colon keyword Underscore NamedArg NamedArgPrefix operator IfExpr keyword ThenBlock ThenBlock ElsifExpr keyword ElseExpr keyword Assign",
maxTerm: 84,
context: trackScope,
nodeProps: [
["closedBy", 37,"end"],
["openedBy", 38,"colon"]
["closedBy", 37,"end"]
],
propSources: [highlighting],
skippedNodes: [0],
repeatNodeCount: 7,
tokenData: "JX~R|OX#{XY$jYZ%TZp#{pq$jqt#{tu%nuw#{wx%sxy%xyz&cz{#{{|&||}#{}!O&|!O!P)p!P!Q*Z!Q!['k![!]2v!]!^%T!^#O#{#O#P3a#P#R#{#R#S3f#S#T#{#T#X4P#X#Y5_#Y#Z<e#Z#]4P#]#^Ap#^#b4P#b#cCb#c#f4P#f#gFz#g#h4P#h#iGq#i#o4P#o#p#{#p#qIi#q;'S#{;'S;=`$d<%l~#{~O#{~~JSS$QUjSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{S$gP;=`<%l#{_$qUjS!_ZOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{V%[UjS!rROt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~%sO!j~~%xO!h~V&PUjS!fROt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{V&jUjS!gROt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{V'RWjSOt#{uw#{x!Q#{!Q!['k![#O#{#P;'S#{;'S;=`$d<%lO#{V'rYjSmROt#{uw#{x!O#{!O!P(b!P!Q#{!Q!['k![#O#{#P;'S#{;'S;=`$d<%lO#{V(gWjSOt#{uw#{x!Q#{!Q![)P![#O#{#P;'S#{;'S;=`$d<%lO#{V)WWjSmROt#{uw#{x!Q#{!Q![)P![#O#{#P;'S#{;'S;=`$d<%lO#{T)wU!oPjSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{V*`WjSOt#{uw#{x!P#{!P!Q*x!Q#O#{#P;'S#{;'S;=`$d<%lO#{V*}^jSOY+yYZ#{Zt+ytu,|uw+ywx,|x!P+y!P!Q#{!Q!}+y!}#O1o#O#P/[#P;'S+y;'S;=`2p<%lO+yV,Q^jSoROY+yYZ#{Zt+ytu,|uw+ywx,|x!P+y!P!Q/q!Q!}+y!}#O1o#O#P/[#P;'S+y;'S;=`2p<%lO+yR-RXoROY,|Z!P,|!P!Q-n!Q!},|!}#O.]#O#P/[#P;'S,|;'S;=`/k<%lO,|R-qP!P!Q-tR-yUoR#Z#[-t#]#^-t#a#b-t#g#h-t#i#j-t#m#n-tR.`VOY.]Z#O.]#O#P.u#P#Q,|#Q;'S.];'S;=`/U<%lO.]R.xSOY.]Z;'S.];'S;=`/U<%lO.]R/XP;=`<%l.]R/_SOY,|Z;'S,|;'S;=`/k<%lO,|R/nP;=`<%l,|V/vWjSOt#{uw#{x!P#{!P!Q0`!Q#O#{#P;'S#{;'S;=`$d<%lO#{V0gbjSoROt#{uw#{x#O#{#P#Z#{#Z#[0`#[#]#{#]#^0`#^#a#{#a#b0`#b#g#{#g#h0`#h#i#{#i#j0`#j#m#{#m#n0`#n;'S#{;'S;=`$d<%lO#{V1t[jSOY1oYZ#{Zt1otu.]uw1owx.]x#O1o#O#P.u#P#Q+y#Q;'S1o;'S;=`2j<%lO1oV2mP;=`<%l1oV2sP;=`<%l+yT2}UjSuPOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~3fO!k~V3mUjSwROt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U4UYjSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#o4P#o;'S#{;'S;=`$d<%lO#{U4{UyQjSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{V5d^jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#`4P#`#a6`#a#b4P#b#c:s#c#o4P#o;'S#{;'S;=`$d<%lO#{V6e[jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#g4P#g#h7Z#h#o4P#o;'S#{;'S;=`$d<%lO#{V7`^jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#X4P#X#Y8[#Y#]4P#]#^9R#^#o4P#o;'S#{;'S;=`$d<%lO#{V8cY!SPjSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#o4P#o;'S#{;'S;=`$d<%lO#{V9W[jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#Y4P#Y#Z9|#Z#o4P#o;'S#{;'S;=`$d<%lO#{V:TY!QPjSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#o4P#o;'S#{;'S;=`$d<%lO#{V:x[jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#W4P#W#X;n#X#o4P#o;'S#{;'S;=`$d<%lO#{V;uYjSvROt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#o4P#o;'S#{;'S;=`$d<%lO#{V<j]jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#U=c#U#b4P#b#c@y#c#o4P#o;'S#{;'S;=`$d<%lO#{V=h[jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#`4P#`#a>^#a#o4P#o;'S#{;'S;=`$d<%lO#{V>c[jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#g4P#g#h?X#h#o4P#o;'S#{;'S;=`$d<%lO#{V?^[jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#X4P#X#Y@S#Y#o4P#o;'S#{;'S;=`$d<%lO#{V@ZYnRjSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#o4P#o;'S#{;'S;=`$d<%lO#{VAQYsRjSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#o4P#o;'S#{;'S;=`$d<%lO#{VAu[jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#Y4P#Y#ZBk#Z#o4P#o;'S#{;'S;=`$d<%lO#{VBrY|PjSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#o4P#o;'S#{;'S;=`$d<%lO#{_Ci[!lWjSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#i4P#i#jD_#j#o4P#o;'S#{;'S;=`$d<%lO#{VDd[jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#`4P#`#aEY#a#o4P#o;'S#{;'S;=`$d<%lO#{VE_[jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#`4P#`#aFT#a#o4P#o;'S#{;'S;=`$d<%lO#{VF[YpRjSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#o4P#o;'S#{;'S;=`$d<%lO#{^GRY!nWjSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#o4P#o;'S#{;'S;=`$d<%lO#{_Gx[!mWjSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#f4P#f#gHn#g#o4P#o;'S#{;'S;=`$d<%lO#{VHs[jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#i4P#i#j?X#j#o4P#o;'S#{;'S;=`$d<%lO#{VIpUzRjSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~JXO!v~",
tokenizers: [operatorTokenizer, 0, 1, 2, 3, tokenizer],
tokenData: "?p~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#g<c#g#h3]#h#i=Y#i#o3]#o#p#r#p#q?Q#q;'S#r;'S;=`$Z<%l~#r~O#r~~?kS#wUjSOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#rS$^P;=`<%l#r^$hUjS!_YOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#rU%RUjS!rQOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#r~%jO!j~~%oO!h~U%vUjS!fQOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#rU&aUjS!gQOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#rU&xWjSOt#ruw#rx!Q#r!Q!['b![#O#r#P;'S#r;'S;=`$Z<%lO#rU'iYjSmQOt#ruw#rx!O#r!O!P(X!P!Q#r!Q!['b![#O#r#P;'S#r;'S;=`$Z<%lO#rU(^WjSOt#ruw#rx!Q#r!Q![(v![#O#r#P;'S#r;'S;=`$Z<%lO#rU(}WjSmQOt#ruw#rx!Q#r!Q![(v![#O#r#P;'S#r;'S;=`$Z<%lO#rU)lWjSOt#ruw#rx!P#r!P!Q*U!Q#O#r#P;'S#r;'S;=`$Z<%lO#rU*Z^jSOY+VYZ#rZt+Vtu,Yuw+Vwx,Yx!P+V!P!Q#r!Q!}+V!}#O0{#O#P.h#P;'S+V;'S;=`1|<%lO+VU+^^jSoQOY+VYZ#rZt+Vtu,Yuw+Vwx,Yx!P+V!P!Q.}!Q!}+V!}#O0{#O#P.h#P;'S+V;'S;=`1|<%lO+VQ,_XoQOY,YZ!P,Y!P!Q,z!Q!},Y!}#O-i#O#P.h#P;'S,Y;'S;=`.w<%lO,YQ,}P!P!Q-QQ-VUoQ#Z#[-Q#]#^-Q#a#b-Q#g#h-Q#i#j-Q#m#n-QQ-lVOY-iZ#O-i#O#P.R#P#Q,Y#Q;'S-i;'S;=`.b<%lO-iQ.USOY-iZ;'S-i;'S;=`.b<%lO-iQ.eP;=`<%l-iQ.kSOY,YZ;'S,Y;'S;=`.w<%lO,YQ.zP;=`<%l,YU/SWjSOt#ruw#rx!P#r!P!Q/l!Q#O#r#P;'S#r;'S;=`$Z<%lO#rU/sbjSoQOt#ruw#rx#O#r#P#Z#r#Z#[/l#[#]#r#]#^/l#^#a#r#a#b/l#b#g#r#g#h/l#h#i#r#i#j/l#j#m#r#m#n/l#n;'S#r;'S;=`$Z<%lO#rU1Q[jSOY0{YZ#rZt0{tu-iuw0{wx-ix#O0{#O#P.R#P#Q+V#Q;'S0{;'S;=`1v<%lO0{U1yP;=`<%l0{U2PP;=`<%l+VU2ZUjSuQOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#r~2rO!k~U2yUjSwQOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#rU3bYjSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#o3]#o;'S#r;'S;=`$Z<%lO#rU4XUyQjSOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#rU4pZjSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#U5c#U#o3]#o;'S#r;'S;=`$Z<%lO#rU5h[jSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#`3]#`#a6^#a#o3]#o;'S#r;'S;=`$Z<%lO#rU6c[jSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#g3]#g#h7X#h#o3]#o;'S#r;'S;=`$Z<%lO#rU7^[jSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#X3]#X#Y8S#Y#o3]#o;'S#r;'S;=`$Z<%lO#rU8ZYnQjSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#o3]#o;'S#r;'S;=`$Z<%lO#r^9Q[!lWjSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#i3]#i#j9v#j#o3]#o;'S#r;'S;=`$Z<%lO#rU9{[jSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#`3]#`#a:q#a#o3]#o;'S#r;'S;=`$Z<%lO#rU:v[jSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#`3]#`#a;l#a#o3]#o;'S#r;'S;=`$Z<%lO#rU;sYpQjSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#o3]#o;'S#r;'S;=`$Z<%lO#r^<jY!nWjSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#o3]#o;'S#r;'S;=`$Z<%lO#r^=a[!mWjSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#f3]#f#g>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
})

View File

@ -286,12 +286,12 @@ describe('Assign', () => {
})
test('parses assignment with functions', () => {
expect('add = fn a b: a + b end').toMatchTree(`
expect('add = do a b: a + b end').toMatchTree(`
Assign
AssignableIdentifier add
Eq =
FunctionDef
keyword fn
keyword do
Params
AssignableIdentifier a
AssignableIdentifier b
@ -300,7 +300,7 @@ describe('Assign', () => {
Identifier a
Plus +
Identifier b
end end`)
keyword end`)
})
})

View File

@ -45,7 +45,7 @@ describe('if/elsif/else', () => {
ThenBlock
FunctionCallOrIdentifier
Identifier yes
end end
keyword end
`)
})
@ -68,7 +68,7 @@ describe('if/elsif/else', () => {
ThenBlock
FunctionCallOrIdentifier
Identifier y
end end
keyword end
`)
})
@ -92,7 +92,7 @@ describe('if/elsif/else', () => {
ThenBlock
FunctionCallOrIdentifier
Identifier y
end end
keyword end
`)
})
@ -133,7 +133,21 @@ describe('if/elsif/else', () => {
ThenBlock
FunctionCallOrIdentifier
Identifier oh-no
end end
keyword end
`)
})
test('does not parse identifiers that start with if', () => {
expect('iffy = if true: 2').toMatchTree(`
Assign
AssignableIdentifier iffy
Eq =
IfExpr
keyword if
Boolean true
colon :
ThenBlock
Number 2
`)
})
})

View File

@ -27,41 +27,41 @@ describe('DotGet', () => {
})
test('function parameters are in scope within function body', () => {
expect('fn config: config.path end').toMatchTree(`
expect('do config: config.path end').toMatchTree(`
FunctionDef
keyword fn
keyword do
Params
AssignableIdentifier config
colon :
DotGet
IdentifierBeforeDot config
Identifier path
end end
keyword end
`)
})
test('parameters out of scope outside function', () => {
expect('fn x: x.prop end; x.prop').toMatchTree(`
expect('do x: x.prop end; x.prop').toMatchTree(`
FunctionDef
keyword fn
keyword do
Params
AssignableIdentifier x
colon :
DotGet
IdentifierBeforeDot x
Identifier prop
end end
keyword end
Word x.prop
`)
})
test('multiple parameters work correctly', () => {
expect(`fn x y:
expect(`do x y:
x.foo
y.bar
end`).toMatchTree(`
FunctionDef
keyword fn
keyword do
Params
AssignableIdentifier x
AssignableIdentifier y
@ -72,17 +72,17 @@ end`).toMatchTree(`
DotGet
IdentifierBeforeDot y
Identifier bar
end end
keyword end
`)
})
test('nested functions with scope isolation', () => {
expect(`fn x:
expect(`do x:
x.outer
fn y: y.inner end
do y: y.inner end
end`).toMatchTree(`
FunctionDef
keyword fn
keyword do
Params
AssignableIdentifier x
colon :
@ -90,15 +90,15 @@ end`).toMatchTree(`
IdentifierBeforeDot x
Identifier outer
FunctionDef
keyword fn
keyword do
Params
AssignableIdentifier y
colon :
DotGet
IdentifierBeforeDot y
Identifier inner
end end
end end
keyword end
keyword end
`)
})

View File

@ -56,21 +56,21 @@ describe('calling functions', () => {
})
})
describe('Fn', () => {
describe('Do', () => {
test('parses function no parameters', () => {
expect('fn: 1 end').toMatchTree(`
expect('do: 1 end').toMatchTree(`
FunctionDef
keyword fn
keyword do
Params
colon :
Number 1
end end`)
keyword end`)
})
test('parses function with single parameter', () => {
expect('fn x: x + 1 end').toMatchTree(`
expect('do x: x + 1 end').toMatchTree(`
FunctionDef
keyword fn
keyword do
Params
AssignableIdentifier x
colon :
@ -78,13 +78,13 @@ describe('Fn', () => {
Identifier x
Plus +
Number 1
end end`)
keyword end`)
})
test('parses function with multiple parameters', () => {
expect('fn x y: x * y end').toMatchTree(`
expect('do x y: x * y end').toMatchTree(`
FunctionDef
keyword fn
keyword do
Params
AssignableIdentifier x
AssignableIdentifier y
@ -93,16 +93,16 @@ describe('Fn', () => {
Identifier x
Star *
Identifier y
end end`)
keyword end`)
})
test('parses multiline function with multiple statements', () => {
expect(`fn x y:
expect(`do x y:
x * y
x + 9
end`).toMatchTree(`
FunctionDef
keyword fn
keyword do
Params
AssignableIdentifier x
AssignableIdentifier y
@ -115,6 +115,36 @@ end`).toMatchTree(`
Identifier x
Plus +
Number 9
end end`)
keyword end`)
})
test('does not parse identifiers that start with fn', () => {
expect('fnnn = do x: x end').toMatchTree(`
Assign
AssignableIdentifier fnnn
Eq =
FunctionDef
keyword do
Params
AssignableIdentifier x
colon :
FunctionCallOrIdentifier
Identifier x
keyword end`)
})
test('does not parse identifiers that start with end', () => {
expect('enddd = do x: x end').toMatchTree(`
Assign
AssignableIdentifier enddd
Eq =
FunctionDef
keyword do
Params
AssignableIdentifier x
colon :
FunctionCallOrIdentifier
Identifier x
keyword end`)
})
})

View File

@ -13,7 +13,7 @@ describe('multiline', () => {
test('parses multiline functions', () => {
expect(`
add = fn a b:
add = do a b:
result = a + b
result
end
@ -24,7 +24,7 @@ describe('multiline', () => {
AssignableIdentifier add
Eq =
FunctionDef
keyword fn
keyword do
Params
AssignableIdentifier a
AssignableIdentifier b
@ -39,7 +39,7 @@ describe('multiline', () => {
FunctionCallOrIdentifier
Identifier result
end end
keyword end
FunctionCall
Identifier add
PositionalArg
@ -53,7 +53,7 @@ describe('multiline', () => {
3
fn x y:
do x y:
x
end
@ -61,14 +61,14 @@ end
Number 3
FunctionDef
keyword fn
keyword do
Params
AssignableIdentifier x
AssignableIdentifier y
colon :
FunctionCallOrIdentifier
Identifier x
end end
keyword end
`)
})
})

View File

@ -66,7 +66,7 @@ describe('pipe expressions', () => {
})
test('pipe with inline function', () => {
expect('items | each fn x: x end').toMatchTree(`
expect('items | each do x: x end').toMatchTree(`
PipeExpr
FunctionCallOrIdentifier
Identifier items
@ -75,13 +75,13 @@ describe('pipe expressions', () => {
Identifier each
PositionalArg
FunctionDef
keyword fn
keyword do
Params
AssignableIdentifier x
colon :
FunctionCallOrIdentifier
Identifier x
end end
keyword end
`)
})
})