Compare commits

...

6 Commits

11 changed files with 270 additions and 116 deletions

View File

@ -70,9 +70,9 @@ export const getFunctionDefParts = (node: SyntaxNode, input: string) => {
} }
const paramNames = getAllChildren(paramsNode).map((param) => { const paramNames = getAllChildren(paramsNode).map((param) => {
if (param.type.id !== terms.AssignableIdentifier) { if (param.type.id !== terms.Identifier) {
throw new CompilerError( throw new CompilerError(
`FunctionDef params must be AssignableIdentifiers, got ${param.type.name}`, `FunctionDef params must be Identifier, got ${param.type.name}`,
param.from, param.from,
param.to param.to
) )
@ -219,9 +219,9 @@ export const getDotGetParts = (node: SyntaxNode, input: string) => {
) )
} }
if (property.type.id !== terms.Identifier) { if (property.type.id !== terms.Identifier && property.type.id !== terms.Number) {
throw new CompilerError( throw new CompilerError(
`DotGet property must be an Identifier, got ${property.type.name}`, `DotGet property must be an Identifier or Number, got ${property.type.name}`,
property.from, property.from,
property.to property.to
) )

View File

@ -57,26 +57,30 @@ const readIdentifierText = (input: InputStream, start: number, end: number): str
return text return text
} }
let inParams = false
export const trackScope = new ContextTracker<TrackerContext>({ export const trackScope = new ContextTracker<TrackerContext>({
start: new TrackerContext(new Scope(null, new Set())), start: new TrackerContext(new Scope(null, new Set())),
shift(context, term, stack, input) { shift(context, term, stack, input) {
if (term !== terms.AssignableIdentifier) return context if (term == terms.Do) inParams = true
const text = readIdentifierText(input, input.pos, stack.pos) if (term === terms.AssignableIdentifier) {
return new TrackerContext(context.scope, [...context.pendingIds, text]) const text = readIdentifierText(input, input.pos, stack.pos)
return new TrackerContext(Scope.add(context.scope, text), context.pendingIds)
}
if (inParams && term === terms.Identifier) {
const text = readIdentifierText(input, input.pos, stack.pos)
return new TrackerContext(context.scope, [...context.pendingIds, text])
}
return context
}, },
reduce(context, term) { reduce(context, term) {
// Add assignment variable to scope
if (term === terms.Assign) {
const varName = context.pendingIds.at(-1)
if (!varName) return context
return new TrackerContext(Scope.add(context.scope, varName), context.pendingIds.slice(0, -1))
}
// Push new scope and add all parameters
if (term === terms.Params) { if (term === terms.Params) {
inParams = false
let newScope = context.scope.push() let newScope = context.scope.push()
if (context.pendingIds.length > 0) { if (context.pendingIds.length > 0) {
newScope = Scope.add(newScope, ...context.pendingIds) newScope = Scope.add(newScope, ...context.pendingIds)

View File

@ -29,6 +29,7 @@
} }
@external tokens tokenizer from "./tokenizer" { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot } @external tokens tokenizer from "./tokenizer" { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot }
@external specialize {Identifier} specializeKeyword from "./tokenizer" { Do }
@precedence { @precedence {
pipe @left, pipe @left,
@ -47,7 +48,6 @@ item {
consumeToTerminator { consumeToTerminator {
PipeExpr | PipeExpr |
ambiguousFunctionCall | ambiguousFunctionCall |
DotGet |
IfExpr | IfExpr |
FunctionDef | FunctionDef |
Assign | Assign |
@ -64,7 +64,7 @@ pipeOperand {
} }
FunctionCallOrIdentifier { FunctionCallOrIdentifier {
Identifier DotGet | Identifier
} }
ambiguousFunctionCall { ambiguousFunctionCall {
@ -72,7 +72,7 @@ ambiguousFunctionCall {
} }
FunctionCall { FunctionCall {
Identifier arg+ (DotGet | Identifier) arg+
} }
arg { arg {
@ -93,11 +93,11 @@ FunctionDef {
} }
singleLineFunctionDef { singleLineFunctionDef {
@specialize[@name=keyword]<Identifier, "do"> Params colon consumeToTerminator @specialize[@name=keyword]<Identifier, "end"> Do Params colon consumeToTerminator @specialize[@name=keyword]<Identifier, "end">
} }
multilineFunctionDef { multilineFunctionDef {
@specialize[@name=keyword]<Identifier, "do"> Params colon newlineOrSemicolon block @specialize[@name=keyword]<Identifier, "end"> Do Params colon newlineOrSemicolon block @specialize[@name=keyword]<Identifier, "end">
} }
IfExpr { IfExpr {
@ -140,7 +140,7 @@ ConditionalOp {
} }
Params { Params {
AssignableIdentifier* Identifier*
} }
Assign { Assign {
@ -169,7 +169,7 @@ expression {
@skip {} { @skip {} {
DotGet { DotGet {
IdentifierBeforeDot dot Identifier IdentifierBeforeDot dot (Number | Identifier)
} }
String { "'" stringContent* "'" } String { "'" stringContent* "'" }

View File

@ -16,27 +16,28 @@ export const
AssignableIdentifier = 14, AssignableIdentifier = 14,
Word = 15, Word = 15,
IdentifierBeforeDot = 16, IdentifierBeforeDot = 16,
Program = 17, Do = 17,
PipeExpr = 18, Program = 18,
FunctionCall = 19, PipeExpr = 19,
PositionalArg = 20, FunctionCall = 20,
ParenExpr = 21, DotGet = 21,
FunctionCallOrIdentifier = 22, Number = 22,
BinOp = 23, PositionalArg = 23,
ConditionalOp = 24, ParenExpr = 24,
FunctionDef = 25, FunctionCallOrIdentifier = 25,
BinOp = 26,
ConditionalOp = 27,
FunctionDef = 28,
Params = 29,
colon = 30,
keyword = 50, keyword = 50,
Params = 27, String = 32,
colon = 28, StringFragment = 33,
String = 30, Interpolation = 34,
StringFragment = 31, EscapeSeq = 35,
Interpolation = 32, Boolean = 36,
EscapeSeq = 33, Regex = 37,
Number = 34, Null = 38,
Boolean = 35,
Regex = 36,
Null = 37,
DotGet = 38,
Underscore = 39, Underscore = 39,
NamedArg = 40, NamedArg = 40,
NamedArgPrefix = 41, NamedArgPrefix = 41,

View File

@ -1,27 +1,27 @@
// This file was generated by lezer-generator. You probably shouldn't edit it. // This file was generated by lezer-generator. You probably shouldn't edit it.
import {LRParser, LocalTokenGroup} from "@lezer/lr" import {LRParser, LocalTokenGroup} from "@lezer/lr"
import {operatorTokenizer} from "./operatorTokenizer" import {operatorTokenizer} from "./operatorTokenizer"
import {tokenizer} from "./tokenizer" import {tokenizer, specializeKeyword} from "./tokenizer"
import {trackScope} from "./scopeTracker" import {trackScope} from "./scopeTracker"
import {highlighting} from "./highlight" import {highlighting} from "./highlight"
const spec_Identifier = {__proto__:null,do:52, end:58, null:74, if:88, elseif:96, else:100} const spec_Identifier = {__proto__:null,end:62, null:76, if:88, elseif:96, else:100}
export const parser = LRParser.deserialize({ export const parser = LRParser.deserialize({
version: 14, version: 14,
states: ".vQYQbOOO#[QcO'#CrO$UQRO'#CsO$dQcO'#DnO${QbO'#DtOOQ`'#Cu'#CuO%TQbO'#CqO%uOSO'#CzOOQa'#Dr'#DrO&TOpO'#DSO&YQcO'#DqOOQ`'#Do'#DoO&qQbO'#DnO'PQbO'#EROOQ`'#DX'#DXO'nQRO'#DaOOQ`'#Dn'#DnO'sQQO'#DmOOQ`'#Dm'#DmOOQ`'#Db'#DbQYQbOOOOQa'#Dq'#DqOOQ`'#Cp'#CpO'{QbO'#DUOOQ`'#Dp'#DpOOQ`'#Dc'#DcO(VQbO,59ZO'PQbO,59_O'PQbO,59_OOQ`'#Dd'#DdO(sQbO'#CwO({QQO,5:`O)lQRO'#CsO)|QRO,59]O*_QRO,59]O*YQQO,59]O+YQQO,59]O+bQbO'#C|O+jQWO'#C}OOOO'#Dz'#DzOOOO'#Df'#DfO,OOSO,59fOOQa,59f,59fO,^O`O,59nO,cQbO'#DgO,hQbO,59YO,yQRO,5:mO-QQQO,5:mO-VQbO,59{OOQ`,5:X,5:XOOQ`-E7`-E7`OOQ`,59p,59pOOQ`-E7a-E7aOOQa1G.y1G.yO-aQcO1G.yOOQ`-E7b-E7bO-{QbO1G/zO'PQbO,59`O'PQbO,59`OOQa1G.w1G.wOOOO,59h,59hOOOO,59i,59iOOOO-E7d-E7dOOQa1G/Q1G/QOOQa1G/Y1G/YO!TQbO'#CrOOQ`,5:R,5:ROOQ`-E7e-E7eO.YQbO1G0XOOQ`1G/g1G/gO.gQbO7+%fO.lQbO7+%gOOQO1G.z1G.zO.|QRO1G.zOOQ`'#DZ'#DZO/WQbO7+%sO/]QbO7+%tOOQ`<<IQ<<IQOOQ`'#De'#DeO/sQQO'#DeO/xQbO'#DwO0`QbO<<IROOQ`<<I_<<I_OOQ`'#D['#D[O0eQbO<<I`OOQ`,5:P,5:POOQ`-E7c-E7cOOQ`AN>mAN>mO'PQbO'#D]OOQ`'#Dh'#DhO0pQbOAN>zO0{QQO'#D_OOQ`AN>zAN>zO1QQbOAN>zO1VQRO,59wO1^QQO,59wOOQ`-E7f-E7fOOQ`G24fG24fO1cQbOG24fO1hQQO,59yO1mQQO1G/cOOQ`LD*QLD*QO.lQbO1G/eO/]QbO7+$}OOQ`7+%P7+%POOQ`<<Hi<<Hi", states: ".vQYQbOOO!TOpO'#CqO#aQcO'#CuO$ZQRO'#CvO$iQcO'#DnO%QQbO'#DuOOQ`'#Cx'#CxO%YQbO'#CtO%zOSO'#C|OOQa'#Ds'#DsO&YQcO'#DrOOQ`'#Do'#DoO&qQbO'#DnO'PQbO'#EROOQ`'#DX'#DXO'nQRO'#DaOOQ`'#Dn'#DnO'sQQO'#DmOOQ`'#Dm'#DmOOQ`'#Db'#DbQYQbOOO'{ObO,59]OOQa'#Dr'#DrOOQ`'#Cs'#CsO(TQbO'#DUOOQ`'#Dq'#DqOOQ`'#Dc'#DcO(_QbO,59[O'PQbO,59bO'PQbO,59bOOQ`'#Dd'#DdO({QbO'#CyO)TQQO,5:aO)tQRO'#CvO*UQRO,59`O*gQRO,59`O*bQQO,59`O+bQQO,59`O+jQbO'#DOO+rQWO'#DPOOOO'#D{'#D{OOOO'#Df'#DfO,WOSO,59hOOQa,59h,59hO,fQbO'#DgO,nQbO,59ZO-PQRO,5:mO-WQQO,5:mO-]QbO,59{OOQ`,5:X,5:XOOQ`-E7`-E7`OOQa1G.w1G.wOOQ`,59p,59pOOQ`-E7a-E7aOOQa1G.|1G.|O-gQcO1G.|OOQ`-E7b-E7bO.RQbO1G/{O'PQbO,59cO'PQbO,59cOOQa1G.z1G.zOOOO,59j,59jOOOO,59k,59kOOOO-E7d-E7dOOQa1G/S1G/SO!YQbO'#CuOOQ`,5:R,5:ROOQ`-E7e-E7eO.`QbO1G0XOOQ`1G/g1G/gO.mQbO7+%gO.rQbO7+%hOOQO1G.}1G.}O/SQRO1G.}OOQ`'#DZ'#DZO/^QbO7+%sO/cQbO7+%tOOQ`<<IR<<IROOQ`'#De'#DeO/yQQO'#DeO0OQbO'#DxO0fQbO<<ISOOQ`<<I_<<I_OOQ`'#D['#D[O0kQbO<<I`OOQ`,5:P,5:POOQ`-E7c-E7cOOQ`AN>nAN>nO'PQbO'#D]OOQ`'#Dh'#DhO0vQbOAN>zO1RQQO'#D_OOQ`AN>zAN>zO1WQbOAN>zO1]QRO,59wO1dQQO,59wOOQ`-E7f-E7fOOQ`G24fG24fO1iQbOG24fO1nQQO,59yO1sQQO1G/cOOQ`LD*QLD*QO.rQbO1G/eO/cQbO7+$}OOQ`7+%P7+%POOQ`<<Hi<<Hi",
stateData: "1u~O!_OS!`OS~O]PO^_O_WO`XOjSOrWOsWOtWOuWO|]O!gUO!jbO!mVO~O]eO_WO`XOjSOrWOsWOtWOuWOwfOygO!gUO!mVOzfX!jfX!wfX!lfXmfX~OP!eXQ!eXR!eXS!eXT!eXU!eXV!eXW!eXX!eXY!eXZ!eX[!eX~P!TOPkOQkORlOSlO~OPkOQkORlOSlO!j!bX!w!bXm!bX~O^mOlkP~O]PO_WO`XOjSOrWOsWOtWOuWO!gUO!mVO~OowO!mzO!ouO!pvO~O!t{O~OP!eXQ!eXR!eXS!eX!j!bX!w!bXm!bX~Oz|O!j!bX!w!bXm!bX~O]eO_WO`XOrWOsWOtWOuWO!gUO!mVO~OV!QO~O!j!RO!w!RO~OjSOw!TO~P'POjSOwfOygOzca!jca!wca!lcamca~P'PO^mOlkX~Ol!YO~OT![OU![OV!ZOW!ZOX!ZOY!ZOZ!ZO[!ZO~OPkOQkORlOSlO~P)QOPkOQkORlOSlO!l!]O~O!l!]OP!eXQ!eXR!eXS!eXT!eXU!eXV!eXW!eXX!eXY!eXZ!eX[!eX~Oz|O!l!]O~O]!^O!gUO~O!m!_O!o!_O!p!_O!q!_O!r!_O!s!_O~OowO!m!aO!ouO!pvO~O]!bO~O]!cO~Oz|O!jba!wba!lbamba~Ol!fO~P)QOl!fO~O^_O|]O~P%TOPkOQkORgiSgi!jgi!wgi!lgimgi~O^_O|]O!j!iO~P%TO^_O|]O!j!nO~P%TOm!oO~O^_O|]O!j!pOm!kP~P%TO!lhilhi~P)QOm!tO~O^_O|]O!j!pOm!kP!Q!kP!S!kP~P%TO!j!wO~O^_O|]O!j!pOm!kX!Q!kX!S!kX~P%TOm!yO~Om#OO!Q!zO!S!}O~Om#TO!Q!zO!S!}O~Ol#VO~Om#TO~Ol#WO~P)QOl#WO~Om#XO~O!j#YO~O!j#ZO~Ort~", stateData: "1{~O!_OS!`OS~O]QO^_O_XO`POaTOfXOtXOuXOvXO|]O!hVO!kbO!nWO~O!deO~O]fO_XO`POaTOfXOtXOuXOvXOwgOyhO!hVO!nWOziX!kiX!wiX!miXoiX~OP!fXQ!fXR!fXS!fXT!fXU!fXV!fXW!fXX!fXY!fXZ!fX[!fX~P!YOPlOQlORmOSmO~OPlOQlORmOSmO!k!bX!w!bXo!bX~O]nOnmP~O]QO_XO`POaTOfXOtXOuXOvXO!hVO!nWO~OqxO!n{O!pvO!qwO~OP!fXQ!fXR!fXS!fX!k!bX!w!bXo!bX~Oz|O!k!bX!w!bXo!bX~O]fO_XO`POfXOtXOuXOvXO!hVO!nWO~OV!QO~O!k!RO!w!RO~O]!TOf!TO~OaTOw!UO~P'POaTOwgOyhOzda!kda!wda!mdaoda~P'PO]nOnmX~On!ZO~OT!]OU!]OV![OW![OX![OY![OZ![O[![O~OPlOQlORmOSmO~P)YOPlOQlORmOSmO!m!^O~O!m!^OP!fXQ!fXR!fXS!fXT!fXU!fXV!fXW!fXX!fXY!fXZ!fX[!fX~Oz|O!m!^O~O]!_O!hVO~O!n!`O!p!`O!q!`O!r!`O!s!`O!t!`O~OqxO!n!bO!pvO!qwO~O]!cO`PO~Oz|O!kca!wca!mcaoca~On!fO~P)YOn!fO~O^_O|]O~P%YOPlOQlORjiSji!kji!wji!mjioji~O^_O|]O!k!iO~P%YO^_O|]O!k!nO~P%YOo!oO~O^_O|]O!k!pOo!lP~P%YO!mkinki~P)YOo!tO~O^_O|]O!k!pOo!lP!Q!lP!S!lP~P%YO!k!wO~O^_O|]O!k!pOo!lX!Q!lX!S!lX~P%YOo!yO~Oo#OO!Q!zO!S!}O~Oo#TO!Q!zO!S!}O~On#VO~Oo#TO~On#WO~P)YOn#WO~Oo#XO~O!k#YO~O!k#ZO~Ofu~",
goto: "+r!wPPPPPPPPPPPPPPPPPP!x#X#g#l#X$W$m$yP%bPP%eP%|%|PPPP&QP#gPP&kP&w&z'TP'XP&k'_'e'l'r'{(R(YPPPP(`(d(x)[)a*[P*w*wP+YPP+bPPPPPP+f+fd`Od!Q!Y!f!i!n!r#Y#ZRsUiZOUd|!Q!Y!f!i!n!r#Y#ZVhPj!czWOPU]dgjkl!Q!Y!Z![!c!f!i!n!r!z#Y#ZR!^udROd!Q!Y!f!i!n!r#Y#ZQqUQ!VkR!WlQsUQ!P]Q!j![R#R!zd`Od!Q!Y!f!i!n!r#Y#ZUfPj!cQsUR!TgRoS{WOPU]dgjkl!Q!Y!Z![!c!f!i!n!r!z#Y#ZTwVydYOd!Q!Y!f!i!n!r#Y#ZgePU]gjkl!Z![!c!ze`Od!Q!Y!f!i!n!r#Y#ZR!m!fQ!v!nQ#[#YR#]#ZT!{!v!|Q#P!vR#U!|QdOR!SdSjP!cR!UjQnSR!XnW!r!i!n#Y#ZR!x!rQyVR!`yS}[tR!e}Q!|!vR#S!|TcOdSaOdQ!g!QQ!h!YQ!l!fZ!q!i!n!r#Y#Zd[Od!Q!Y!f!i!n!r#Y#ZQtUR!d|ViPj!cdQOd!Q!Y!f!i!n!r#Y#ZUfPj!cQpUQ!O]Q!TgQ!VkQ!WlQ!j!ZQ!k![R#Q!zdYOd!Q!Y!f!i!n!r#Y#ZdeP]gjkl!Z![!c!zRrUoTOPUdgj!Q!Y!c!f!i!n!r#Y#ZQ!s!iV!u!n#Y#ZTxVye^Od!Q!Y!f!i!n!r#Y#Z", goto: "+u!wPPPPPPPPPPPPPPPPPPP!x#X#gP$T$Y#X$t%Z%g&OPP&RP&j&jPPPP$TPP&nP&z&}'WP'[P&n'b'h'o'u(O(U(]PPPP(c(g({P)_)d*_P*z*zP+]PP+ePPPPP+i+id`Od!Q!Z!f!i!n!r#Y#ZRtViZOVd|!Q!Z!f!i!n!r#Y#ZfQOVd!Q!Z!f!i!n!r#Y#ZdfQ]hklm![!]!c!zR!c|ViQk!czXOQV]dhklm!Q!Z![!]!c!f!i!n!r!z#Y#ZR!_vdSOd!Q!Z!f!i!n!r#Y#ZQrVQ!WlR!XmQtVQ!P]Q!j!]R#R!zd`Od!Q!Z!f!i!n!r#Y#ZUgQk!cQtVR!UhRpT{XOQV]dhklm!Q!Z![!]!c!f!i!n!r!z#Y#ZTxWze`Od!Q!Z!f!i!n!r#Y#ZR!m!fQ!v!nQ#[#YR#]#ZT!{!v!|Q#P!vR#U!|QdOR!SdSkQ!cR!VkQoTR!YoW!r!i!n#Y#ZR!x!rQzWR!azS}[uR!e}Q!|!vR#S!|TcOdSaOdQ!g!QQ!h!ZQ!l!fZ!q!i!n!r#Y#Zd[Od!Q!Z!f!i!n!r#Y#ZQuVR!d|VjQk!cdROd!Q!Z!f!i!n!r#Y#ZUgQk!cQqVQ!O]Q!UhQ!WlQ!XmQ!j![Q!k!]R#Q!zdYOd!Q!Z!f!i!n!r#Y#ZdfQ]hklm![!]!c!zRsVoUOQVdhk!Q!Z!c!f!i!n!r#Y#ZQ!s!iV!u!n#Y#ZTyWze^Od!Q!Z!f!i!n!r#Y#Z",
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 FunctionDef keyword Params colon keyword String StringFragment Interpolation EscapeSeq Number Boolean Regex Null DotGet Underscore NamedArg NamedArgPrefix operator IfExpr keyword SingleLineThenBlock ThenBlock ElseIfExpr keyword ElseExpr keyword Assign", nodeNames: "⚠ Star Slash Plus Minus And Or Eq Neq Lt Lte Gt Gte Identifier AssignableIdentifier Word IdentifierBeforeDot Do Program PipeExpr FunctionCall DotGet Number PositionalArg ParenExpr FunctionCallOrIdentifier BinOp ConditionalOp FunctionDef Params colon keyword String StringFragment Interpolation EscapeSeq Boolean Regex Null Underscore NamedArg NamedArgPrefix operator IfExpr keyword SingleLineThenBlock ThenBlock ElseIfExpr keyword ElseExpr keyword Assign",
maxTerm: 85, maxTerm: 85,
context: trackScope, context: trackScope,
nodeProps: [ nodeProps: [
["closedBy", 28,"end"] ["closedBy", 30,"end"]
], ],
propSources: [highlighting], propSources: [highlighting],
skippedNodes: [0], skippedNodes: [0],
repeatNodeCount: 7, repeatNodeCount: 7,
tokenData: ">i~RzOX#uXY$dYZ$}Zp#upq$dqs#ust%htu'Puw#uwx'Uxy'Zyz'tz{#u{|(_|}#u}!O(_!O!P#u!P!Q+R!Q![(|![!]3n!]!^$}!^#O#u#O#P4X#P#R#u#R#S4^#S#T#u#T#Y4w#Y#Z6V#Z#b4w#b#c:e#c#f4w#f#g;[#g#h4w#h#i<R#i#o4w#o#p#u#p#q=y#q;'S#u;'S;=`$^<%l~#u~O#u~~>dS#zUoSOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#uS$aP;=`<%l#u^$kUoS!_YOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#uU%UUoS!jQOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#u^%oZoS!`YOY%hYZ#uZt%htu&buw%hwx&bx#O%h#O#P&b#P;'S%h;'S;=`&y<%lO%hY&gS!`YOY&bZ;'S&b;'S;=`&s<%lO&bY&vP;=`<%l&b^&|P;=`<%l%h~'UO!o~~'ZO!m~U'bUoS!gQOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#uU'{UoS!lQOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#uU(dWoSOt#uuw#ux!Q#u!Q![(|![#O#u#P;'S#u;'S;=`$^<%lO#uU)TYoSrQOt#uuw#ux!O#u!O!P)s!P!Q#u!Q![(|![#O#u#P;'S#u;'S;=`$^<%lO#uU)xWoSOt#uuw#ux!Q#u!Q![*b![#O#u#P;'S#u;'S;=`$^<%lO#uU*iWoSrQOt#uuw#ux!Q#u!Q![*b![#O#u#P;'S#u;'S;=`$^<%lO#uU+WWoSOt#uuw#ux!P#u!P!Q+p!Q#O#u#P;'S#u;'S;=`$^<%lO#uU+u^oSOY,qYZ#uZt,qtu-tuw,qwx-tx!P,q!P!Q#u!Q!},q!}#O2g#O#P0S#P;'S,q;'S;=`3h<%lO,qU,x^oStQOY,qYZ#uZt,qtu-tuw,qwx-tx!P,q!P!Q0i!Q!},q!}#O2g#O#P0S#P;'S,q;'S;=`3h<%lO,qQ-yXtQOY-tZ!P-t!P!Q.f!Q!}-t!}#O/T#O#P0S#P;'S-t;'S;=`0c<%lO-tQ.iP!P!Q.lQ.qUtQ#Z#[.l#]#^.l#a#b.l#g#h.l#i#j.l#m#n.lQ/WVOY/TZ#O/T#O#P/m#P#Q-t#Q;'S/T;'S;=`/|<%lO/TQ/pSOY/TZ;'S/T;'S;=`/|<%lO/TQ0PP;=`<%l/TQ0VSOY-tZ;'S-t;'S;=`0c<%lO-tQ0fP;=`<%l-tU0nWoSOt#uuw#ux!P#u!P!Q1W!Q#O#u#P;'S#u;'S;=`$^<%lO#uU1_boStQOt#uuw#ux#O#u#P#Z#u#Z#[1W#[#]#u#]#^1W#^#a#u#a#b1W#b#g#u#g#h1W#h#i#u#i#j1W#j#m#u#m#n1W#n;'S#u;'S;=`$^<%lO#uU2l[oSOY2gYZ#uZt2gtu/Tuw2gwx/Tx#O2g#O#P/m#P#Q,q#Q;'S2g;'S;=`3b<%lO2gU3eP;=`<%l2gU3kP;=`<%l,qU3uUoSlQOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#u~4^O!p~U4eUoSwQOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#uU4|YoSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#o4w#o;'S#u;'S;=`$^<%lO#uU5sUyQoSOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#uU6[ZoSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#U6}#U#o4w#o;'S#u;'S;=`$^<%lO#uU7S[oSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#`4w#`#a7x#a#o4w#o;'S#u;'S;=`$^<%lO#uU7}[oSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#g4w#g#h8s#h#o4w#o;'S#u;'S;=`$^<%lO#uU8x[oSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#X4w#X#Y9n#Y#o4w#o;'S#u;'S;=`$^<%lO#uU9uYsQoSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#o4w#o;'S#u;'S;=`$^<%lO#u^:lY!qWoSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#o4w#o;'S#u;'S;=`$^<%lO#u^;cY!sWoSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#o4w#o;'S#u;'S;=`$^<%lO#u^<Y[!rWoSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#f4w#f#g=O#g#o4w#o;'S#u;'S;=`$^<%lO#uU=T[oSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#i4w#i#j8s#j#o4w#o;'S#u;'S;=`$^<%lO#uU>QUzQoSOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#u~>iO!w~", tokenData: ">i~RzOX#uXY$dYZ$}Zp#upq$dqs#ust%htu'Puw#uwx'Uxy'Zyz'tz{#u{|(_|}#u}!O(_!O!P#u!P!Q+R!Q![(|![!]3n!]!^$}!^#O#u#O#P4X#P#R#u#R#S4^#S#T#u#T#Y4w#Y#Z6V#Z#b4w#b#c:e#c#f4w#f#g;[#g#h4w#h#i<R#i#o4w#o#p#u#p#q=y#q;'S#u;'S;=`$^<%l~#u~O#u~~>dS#zUqSOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#uS$aP;=`<%l#u^$kUqS!_YOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#uU%UUqS!kQOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#u^%oZqS!`YOY%hYZ#uZt%htu&buw%hwx&bx#O%h#O#P&b#P;'S%h;'S;=`&y<%lO%hY&gS!`YOY&bZ;'S&b;'S;=`&s<%lO&bY&vP;=`<%l&b^&|P;=`<%l%h~'UO!p~~'ZO!n~U'bUqS!hQOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#uU'{UqS!mQOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#uU(dWqSOt#uuw#ux!Q#u!Q![(|![#O#u#P;'S#u;'S;=`$^<%lO#uU)TYqSfQOt#uuw#ux!O#u!O!P)s!P!Q#u!Q![(|![#O#u#P;'S#u;'S;=`$^<%lO#uU)xWqSOt#uuw#ux!Q#u!Q![*b![#O#u#P;'S#u;'S;=`$^<%lO#uU*iWqSfQOt#uuw#ux!Q#u!Q![*b![#O#u#P;'S#u;'S;=`$^<%lO#uU+WWqSOt#uuw#ux!P#u!P!Q+p!Q#O#u#P;'S#u;'S;=`$^<%lO#uU+u^qSOY,qYZ#uZt,qtu-tuw,qwx-tx!P,q!P!Q#u!Q!},q!}#O2g#O#P0S#P;'S,q;'S;=`3h<%lO,qU,x^qSuQOY,qYZ#uZt,qtu-tuw,qwx-tx!P,q!P!Q0i!Q!},q!}#O2g#O#P0S#P;'S,q;'S;=`3h<%lO,qQ-yXuQOY-tZ!P-t!P!Q.f!Q!}-t!}#O/T#O#P0S#P;'S-t;'S;=`0c<%lO-tQ.iP!P!Q.lQ.qUuQ#Z#[.l#]#^.l#a#b.l#g#h.l#i#j.l#m#n.lQ/WVOY/TZ#O/T#O#P/m#P#Q-t#Q;'S/T;'S;=`/|<%lO/TQ/pSOY/TZ;'S/T;'S;=`/|<%lO/TQ0PP;=`<%l/TQ0VSOY-tZ;'S-t;'S;=`0c<%lO-tQ0fP;=`<%l-tU0nWqSOt#uuw#ux!P#u!P!Q1W!Q#O#u#P;'S#u;'S;=`$^<%lO#uU1_bqSuQOt#uuw#ux#O#u#P#Z#u#Z#[1W#[#]#u#]#^1W#^#a#u#a#b1W#b#g#u#g#h1W#h#i#u#i#j1W#j#m#u#m#n1W#n;'S#u;'S;=`$^<%lO#uU2l[qSOY2gYZ#uZt2gtu/Tuw2gwx/Tx#O2g#O#P/m#P#Q,q#Q;'S2g;'S;=`3b<%lO2gU3eP;=`<%l2gU3kP;=`<%l,qU3uUqSnQOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#u~4^O!q~U4eUqSwQOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#uU4|YqSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#o4w#o;'S#u;'S;=`$^<%lO#uU5sUyQqSOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#uU6[ZqSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#U6}#U#o4w#o;'S#u;'S;=`$^<%lO#uU7S[qSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#`4w#`#a7x#a#o4w#o;'S#u;'S;=`$^<%lO#uU7}[qSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#g4w#g#h8s#h#o4w#o;'S#u;'S;=`$^<%lO#uU8x[qSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#X4w#X#Y9n#Y#o4w#o;'S#u;'S;=`$^<%lO#uU9uYtQqSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#o4w#o;'S#u;'S;=`$^<%lO#u^:lY!rWqSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#o4w#o;'S#u;'S;=`$^<%lO#u^;cY!tWqSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#o4w#o;'S#u;'S;=`$^<%lO#u^<Y[!sWqSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#f4w#f#g=O#g#o4w#o;'S#u;'S;=`$^<%lO#uU=T[qSOt#uuw#ux!_#u!_!`5l!`#O#u#P#T#u#T#i4w#i#j8s#j#o4w#o;'S#u;'S;=`$^<%lO#uU>QUzQqSOt#uuw#ux#O#u#P;'S#u;'S;=`$^<%lO#u~>iO!w~",
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO!t~~", 11)], tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO!d~~", 11)],
topRules: {"Program":[0,17]}, topRules: {"Program":[0,18]},
specialized: [{term: 13, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], specialized: [{term: 13, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 13, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}],
tokenPrec: 770 tokenPrec: 776
}) })

View File

@ -497,10 +497,10 @@ describe('Assign', () => {
AssignableIdentifier add AssignableIdentifier add
Eq = Eq =
FunctionDef FunctionDef
keyword do Do do
Params Params
AssignableIdentifier a Identifier a
AssignableIdentifier b Identifier b
colon : colon :
BinOp BinOp
Identifier a Identifier a
@ -517,9 +517,10 @@ describe('DotGet whitespace sensitivity', () => {
AssignableIdentifier basename AssignableIdentifier basename
Eq = Eq =
Number 5 Number 5
DotGet FunctionCallOrIdentifier
IdentifierBeforeDot basename DotGet
Identifier prop`) IdentifierBeforeDot basename
Identifier prop`)
}) })
test('space before dot - NOT DotGet, parses as division', () => { test('space before dot - NOT DotGet, parses as division', () => {

View File

@ -20,22 +20,24 @@ describe('DotGet', () => {
AssignableIdentifier obj AssignableIdentifier obj
Eq = Eq =
Number 5 Number 5
DotGet FunctionCallOrIdentifier
IdentifierBeforeDot obj DotGet
Identifier prop IdentifierBeforeDot obj
Identifier prop
`) `)
}) })
test('function parameters are in scope within function body', () => { test('function parameters are in scope within function body', () => {
expect('do config: config.path end').toMatchTree(` expect('do config: config.path end').toMatchTree(`
FunctionDef FunctionDef
keyword do Do do
Params Params
AssignableIdentifier config Identifier config
colon : colon :
DotGet FunctionCallOrIdentifier
IdentifierBeforeDot config DotGet
Identifier path IdentifierBeforeDot config
Identifier path
keyword end keyword end
`) `)
}) })
@ -43,13 +45,14 @@ describe('DotGet', () => {
test('parameters out of scope outside function', () => { test('parameters out of scope outside function', () => {
expect('do x: x.prop end; x.prop').toMatchTree(` expect('do x: x.prop end; x.prop').toMatchTree(`
FunctionDef FunctionDef
keyword do Do do
Params Params
AssignableIdentifier x Identifier x
colon : colon :
DotGet FunctionCallOrIdentifier
IdentifierBeforeDot x DotGet
Identifier prop IdentifierBeforeDot x
Identifier prop
keyword end keyword end
Word x.prop Word x.prop
`) `)
@ -61,17 +64,19 @@ describe('DotGet', () => {
y.bar y.bar
end`).toMatchTree(` end`).toMatchTree(`
FunctionDef FunctionDef
keyword do Do do
Params Params
AssignableIdentifier x Identifier x
AssignableIdentifier y Identifier y
colon : colon :
DotGet FunctionCallOrIdentifier
IdentifierBeforeDot x DotGet
Identifier foo IdentifierBeforeDot x
DotGet Identifier foo
IdentifierBeforeDot y FunctionCallOrIdentifier
Identifier bar DotGet
IdentifierBeforeDot y
Identifier bar
keyword end keyword end
`) `)
}) })
@ -82,21 +87,23 @@ end`).toMatchTree(`
do y: y.inner end do y: y.inner end
end`).toMatchTree(` end`).toMatchTree(`
FunctionDef FunctionDef
keyword do Do do
Params Params
AssignableIdentifier x Identifier x
colon : colon :
DotGet FunctionCallOrIdentifier
IdentifierBeforeDot x
Identifier outer
FunctionDef
keyword do
Params
AssignableIdentifier y
colon :
DotGet DotGet
IdentifierBeforeDot y IdentifierBeforeDot x
Identifier inner Identifier outer
FunctionDef
Do do
Params
Identifier y
colon :
FunctionCallOrIdentifier
DotGet
IdentifierBeforeDot y
Identifier inner
keyword end keyword end
keyword end keyword end
`) `)
@ -117,6 +124,62 @@ end`).toMatchTree(`
`) `)
}) })
test('dot get works as bare function', () => {
expect('io = dict print=echo; io.print').toMatchTree(`
Assign
AssignableIdentifier io
Eq =
FunctionCall
Identifier dict
NamedArg
NamedArgPrefix print=
Identifier echo
FunctionCallOrIdentifier
DotGet
IdentifierBeforeDot io
Identifier print
`)
})
test('dot get works as function w/ args', () => {
expect('io = dict print=echo; io.print heya').toMatchTree(`
Assign
AssignableIdentifier io
Eq =
FunctionCall
Identifier dict
NamedArg
NamedArgPrefix print=
Identifier echo
FunctionCall
DotGet
IdentifierBeforeDot io
Identifier print
PositionalArg
Identifier heya
`)
})
test('dot get works as function in parens', () => {
expect('io = dict print=echo; (io.print heya)').toMatchTree(`
Assign
AssignableIdentifier io
Eq =
FunctionCall
Identifier dict
NamedArg
NamedArgPrefix print=
Identifier echo
ParenExpr
FunctionCall
DotGet
IdentifierBeforeDot io
Identifier print
PositionalArg
Identifier heya
`)
})
test('mixed file paths and dot get', () => { test('mixed file paths and dot get', () => {
expect('config = 42; cat readme.txt; echo config.path').toMatchTree(` expect('config = 42; cat readme.txt; echo config.path').toMatchTree(`
Assign Assign
@ -145,4 +208,70 @@ end`).toMatchTree(`
PositionalArg PositionalArg
Identifier prop`) Identifier prop`)
}) })
test('readme.1 is Word when readme not in scope', () => {
expect('readme.1').toMatchTree(`Word readme.1`)
})
test('readme.1 is Word when used in function', () => {
expect('echo readme.1').toMatchTree(`
FunctionCall
Identifier echo
PositionalArg
Word readme.1`)
})
test('obj.1 is DotGet when obj is assigned', () => {
expect('obj = 5; obj.1').toMatchTree(`
Assign
AssignableIdentifier obj
Eq =
Number 5
FunctionCallOrIdentifier
DotGet
IdentifierBeforeDot obj
Number 1
`)
})
test('obj.1 arg is DotGet when obj is assigned', () => {
expect('obj = 5; obj.1').toMatchTree(`
Assign
AssignableIdentifier obj
Eq =
Number 5
FunctionCallOrIdentifier
DotGet
IdentifierBeforeDot obj
Number 1
`)
})
test('dot get index works as function w/ args', () => {
expect(`io = list (do x: echo x end); io.0 heya`).toMatchTree(`
Assign
AssignableIdentifier io
Eq =
FunctionCall
Identifier list
PositionalArg
ParenExpr
FunctionDef
Do do
Params
Identifier x
colon :
FunctionCall
Identifier echo
PositionalArg
Identifier x
keyword end
FunctionCall
DotGet
IdentifierBeforeDot io
Number 0
PositionalArg
Identifier heya
`)
})
}) })

View File

@ -60,7 +60,7 @@ describe('Do', () => {
test('parses function no parameters', () => { test('parses function no parameters', () => {
expect('do: 1 end').toMatchTree(` expect('do: 1 end').toMatchTree(`
FunctionDef FunctionDef
keyword do Do do
Params Params
colon : colon :
Number 1 Number 1
@ -70,9 +70,9 @@ describe('Do', () => {
test('parses function with single parameter', () => { test('parses function with single parameter', () => {
expect('do x: x + 1 end').toMatchTree(` expect('do x: x + 1 end').toMatchTree(`
FunctionDef FunctionDef
keyword do Do do
Params Params
AssignableIdentifier x Identifier x
colon : colon :
BinOp BinOp
Identifier x Identifier x
@ -84,10 +84,10 @@ describe('Do', () => {
test('parses function with multiple parameters', () => { test('parses function with multiple parameters', () => {
expect('do x y: x * y end').toMatchTree(` expect('do x y: x * y end').toMatchTree(`
FunctionDef FunctionDef
keyword do Do do
Params Params
AssignableIdentifier x Identifier x
AssignableIdentifier y Identifier y
colon : colon :
BinOp BinOp
Identifier x Identifier x
@ -102,10 +102,10 @@ describe('Do', () => {
x + 9 x + 9
end`).toMatchTree(` end`).toMatchTree(`
FunctionDef FunctionDef
keyword do Do do
Params Params
AssignableIdentifier x Identifier x
AssignableIdentifier y Identifier y
colon : colon :
BinOp BinOp
Identifier x Identifier x
@ -124,9 +124,9 @@ end`).toMatchTree(`
AssignableIdentifier fnnn AssignableIdentifier fnnn
Eq = Eq =
FunctionDef FunctionDef
keyword do Do do
Params Params
AssignableIdentifier x Identifier x
colon : colon :
FunctionCallOrIdentifier FunctionCallOrIdentifier
Identifier x Identifier x
@ -139,9 +139,9 @@ end`).toMatchTree(`
AssignableIdentifier enddd AssignableIdentifier enddd
Eq = Eq =
FunctionDef FunctionDef
keyword do Do do
Params Params
AssignableIdentifier x Identifier x
colon : colon :
FunctionCallOrIdentifier FunctionCallOrIdentifier
Identifier x Identifier x

View File

@ -24,10 +24,10 @@ describe('multiline', () => {
AssignableIdentifier add AssignableIdentifier add
Eq = Eq =
FunctionDef FunctionDef
keyword do Do do
Params Params
AssignableIdentifier a Identifier a
AssignableIdentifier b Identifier b
colon : colon :
Assign Assign
AssignableIdentifier result AssignableIdentifier result
@ -61,10 +61,10 @@ end
Number 3 Number 3
FunctionDef FunctionDef
keyword do Do do
Params Params
AssignableIdentifier x Identifier x
AssignableIdentifier y Identifier y
colon : colon :
FunctionCallOrIdentifier FunctionCallOrIdentifier
Identifier x Identifier x

View File

@ -75,13 +75,27 @@ describe('pipe expressions', () => {
Identifier each Identifier each
PositionalArg PositionalArg
FunctionDef FunctionDef
keyword do Do do
Params Params
AssignableIdentifier x Identifier x
colon : colon :
FunctionCallOrIdentifier FunctionCallOrIdentifier
Identifier x Identifier x
keyword end keyword end
`) `)
}) })
test(`double trouble (do keyword isn't over eager)`, () => {
expect(`
double 2 | double`).toMatchTree(`
PipeExpr
FunctionCall
Identifier double
PositionalArg
Number 2
operator |
FunctionCallOrIdentifier
Identifier double
`)
})
}) })

View File

@ -1,5 +1,10 @@
import { ExternalTokenizer, InputStream, Stack } from '@lezer/lr' import { ExternalTokenizer, InputStream, Stack } from '@lezer/lr'
import { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot } from './shrimp.terms' import { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot, Do } from './shrimp.terms'
// doobie doobie do (we need the `do` keyword to know when we're defining params)
export function specializeKeyword(ident: string) {
return ident === 'do' ? Do : -1
}
// The only chars that can't be words are whitespace, apostrophes, closing parens, and EOF. // The only chars that can't be words are whitespace, apostrophes, closing parens, and EOF.