feat(parser): complete DotGet implementation with scope tracking

- Fixed tokenizer input.read() to use absolute positions
- Fixed FN_KEYWORD term ID (33 after DotGet added to expression)
- Added DotGet to expression for use as function argument
- All 8 DotGet tests passing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Corey Johnson 2025-10-17 07:42:07 -07:00
parent 22fba65a53
commit d894713744
6 changed files with 149 additions and 19 deletions

View File

@ -43,7 +43,7 @@ let pendingIdentifiers: string[] = []
let isInParams = false let isInParams = false
// Term ID for 'fn' keyword - verified by parsing and inspecting the tree // Term ID for 'fn' keyword - verified by parsing and inspecting the tree
const FN_KEYWORD = 32 const FN_KEYWORD = 33
export const trackScope = new ContextTracker<Scope>({ export const trackScope = new ContextTracker<Scope>({
start: new Scope(null, new Set()), start: new Scope(null, new Set()),

View File

@ -174,7 +174,7 @@ ParenExpr {
} }
expression { expression {
expressionWithoutIdentifier | Identifier expressionWithoutIdentifier | DotGet | Identifier
} }
@skip {} { @skip {} {

View File

@ -18,14 +18,14 @@ export const
Boolean = 28, Boolean = 28,
Regex = 29, Regex = 29,
Null = 30, Null = 30,
FunctionDef = 31, DotGet = 31,
Params = 33, FunctionDef = 32,
colon = 34, Params = 34,
end = 35, colon = 35,
Underscore = 36, end = 36,
NamedArg = 37, Underscore = 37,
NamedArgPrefix = 38, NamedArg = 38,
DotGet = 40, NamedArgPrefix = 39,
IfExpr = 41, IfExpr = 41,
ThenBlock = 44, ThenBlock = 44,
ElsifExpr = 45, ElsifExpr = 45,

View File

@ -5,21 +5,21 @@ import {trackScope} from "./scopeTracker"
import {highlighting} from "./highlight" import {highlighting} from "./highlight"
export const parser = LRParser.deserialize({ export const parser = LRParser.deserialize({
version: 14, version: 14,
states: ".dQVQaOOO#OQbO'#CdO#cQPO'#CeO#qQPO'#DkO$qQaO'#CcO$xOSO'#CsOOQ`'#Do'#DoO%WQPO'#DnO%oQaO'#DyOOQ`'#C{'#C{OOQO'#Dl'#DlO%wQPO'#DkO&VQaO'#EOOOQO'#DV'#DVOOQO'#Dk'#DkO&^QPO'#DjOOQ`'#Dj'#DjOOQ`'#D`'#D`QVQaOOOOQ`'#Dn'#DnOOQ`'#Cb'#CbO&fQaO'#DROOQ`'#Dm'#DmOOQ`'#Da'#DaO&sQbO,58{O'dQaO,59pO'iQaO,59yO&VQaO,59PO&VQaO,59PO'vQbO'#CdO)RQPO'#CeO)cQPO,58}O)tQPO,58}O)oQPO,58}O*oQPO,58}O*wQaO'#CuO+PQWO'#CvOOOO'#Ds'#DsOOOO'#Db'#DbO+eOSO,59_OOQ`,59_,59_OOQ`'#Dc'#DcO+sQaO'#C}O+{QPO,5:eO,QQaO'#DeO,VQPO,58zO,hQPO,5:jO,oQPO,5:jOOQ`,5:U,5:UOOQ`-E7^-E7^OOQ`,59m,59mOOQ`-E7_-E7_OOQO1G/[1G/[OOQO1G/e1G/eOOQO1G.k1G.kO,tQPO1G.kO&VQaO,59UO&VQaO,59UOOQ`1G.i1G.iOOOO,59a,59aOOOO,59b,59bOOOO-E7`-E7`OOQ`1G.y1G.yOOQ`-E7a-E7aO-`QaO1G0PO-pQbO'#CdOOQO,5:P,5:POOQO-E7c-E7cO.aQaO1G0UOOQO1G.p1G.pO.qQPO1G.pO.{QPO7+%kO/QQaO7+%lOOQO'#DX'#DXOOQO7+%p7+%pO/bQaO7+%qOOQ`<<IV<<IVO/xQPO'#DdO/}QaO'#D|O0eQPO<<IWOOQO'#DY'#DYO0jQPO<<I]OOQ`,5:O,5:OOOQ`-E7b-E7bOOQ`AN>rAN>rO&VQaO'#DZOOQO'#Df'#DfO0uQPOAN>wO1QQPO'#D]OOQOAN>wAN>wO1VQPOAN>wO1[QPO,59uO1cQPO,59uOOQO-E7d-E7dOOQOG24cG24cO1hQPOG24cO1mQPO,59wO1rQPO1G/aOOQOLD)}LD)}O/QQaO1G/cO/bQaO7+${OOQO7+$}7+$}OOQO<<Hg<<Hg", states: ".pQVQaOOO#RQbO'#CdO#cQPO'#CeO#qQPO'#DkO$qQaO'#CcO$xOSO'#CsOOQ`'#Do'#DoO%WQPO'#DnO%oQaO'#DzOOQ`'#C|'#C|OOQO'#Dl'#DlO%wQPO'#DkO&VQaO'#EOOOQO'#DV'#DVOOQO'#Dk'#DkO&^QPO'#DjOOQ`'#Dj'#DjOOQ`'#D`'#D`QVQaOOO&wQbO'#DnO'qQaO,59gOOQ`'#Dn'#DnOOQ`'#Cb'#CbO'vQaO'#DSOOQ`'#Dm'#DmOOQ`'#Da'#DaO(TQbO,58{O(tQaO,59yO&VQaO,59PO&VQaO,59PO)RQbO'#CdO*^QPO'#CeO*nQPO,58}O+wQPO,58}O*zQPO,58}O,OQPO,58}O,WQaO'#CuO,`QWO'#CvOOOO'#Ds'#DsOOOO'#Db'#DbO,tOSO,59_OOQ`,59_,59_OOQ`'#Dc'#DcO-SQaO'#DOO-[QPO,5:fO-aQaO'#DeO-fQPO,58zO-wQPO,5:jO.OQPO'#DnO.fQPO,5:jOOQ`,5:U,5:UOOQ`-E7^-E7^OOQ`1G/R1G/ROOQ`,59n,59nOOQ`-E7_-E7_OOQO1G/e1G/eOOQO1G.k1G.kO.kQPO1G.kO&VQaO,59UO&VQaO,59UOOQ`1G.i1G.iOOOO,59a,59aOOOO,59b,59bOOOO-E7`-E7`OOQ`1G.y1G.yOOQ`-E7a-E7aO/VQaO1G0QO/gQbO'#CdOOQO,5:P,5:POOQO-E7c-E7cO0WQaO1G0UOOQO1G.p1G.pO0hQPO1G.pO0rQPO7+%lO0wQaO7+%mOOQO'#DX'#DXOOQO7+%p7+%pO1XQaO7+%qOOQ`<<IW<<IWO1oQPO'#DdO1tQaO'#D}O2[QPO<<IXOOQO'#DY'#DYO2aQPO<<I]OOQ`,5:O,5:OOOQ`-E7b-E7bOOQ`AN>sAN>sO&VQaO'#DZOOQO'#Df'#DfO2lQPOAN>wO2wQPO'#D]OOQOAN>wAN>wO2|QPOAN>wO3RQPO,59uO3YQPO,59uOOQO-E7d-E7dOOQOG24cG24cO3_QPOG24cO3dQPO,59wO3iQPO1G/aOOQOLD)}LD)}O0wQaO1G/cO1XQaO7+${OOQO7+$}7+$}OOQO<<Hg<<Hg",
stateData: "1}~O!]OS~OPPOQUOkUOlUOmUOnUOpWOz[O!dSO!fTO!o`O~OPcOQUOkUOlUOmUOnUOpWOtdOveO!dSO!fTOY!bXZ!bX[!bX]!bXwWX~O_jO!qiO!oWX!tWXsWX~PzOYkOZkO[lO]lO~OYkOZkO[lO]lO!o!_X!t!_Xs!_X~OQUOkUOlUOmUOnUO!dSO!fTO~OPmO~P$YOhuO!fxO!hsO!itO~OY!bXZ!bX[!bX]!bX!o!_X!t!_Xs!_X~OPyOrqP~Ow|O!o!_X!t!_Xs!_X~OPcO~P$YO!o!QO!t!QO~OPcOpWOt!SO~P$YOPcOpWOtdOveOwTa!oTa!tTa!eTasTa~P$YOP!UO~OPPOpWOz[O~P$YO_!bX`!bXa!bXb!bXc!bXd!bXe!bXf!bX!eWX~PzO_!YO`!YOa!YOb!YOc!YOd!YOe!ZOf!ZO~OYkOZkO[lO]lO~P(gOYkOZkO[lO]lO!e![O~O!e![OY!bXZ!bX[!bX]!bX_!bX`!bXa!bXb!bXc!bXd!bXe!bXf!bX~Ow|O!e![O~OP!]O!dSO~O!f!^O!h!^O!i!^O!j!^O!k!^O!l!^O~OhuO!f!`O!hsO!itO~OPyOrqX~Or!bO~OP!cO~Ow|O!oSa!tSa!eSasSa~Or!fO~P(gOr!fO~OYkOZkO[Xi]Xi!oXi!tXi!eXisXi~OPPOpWOz[O!o!jO~P$YOPcOpWOtdOveOwWX!oWX!tWX!eWXsWX~P$YOPPOpWOz[O!o!mO~P$YO!e^ir^i~P(gOs!nO~OPPOpWOz[Os!pP~P$YOPPOpWOz[Os!pP!O!pP!Q!pP~P$YO!o!tO~OPPOpWOz[Os!pX!O!pX!Q!pX~P$YOs!vO~Os!{O!O!wO!Q!zO~Os#QO!O!wO!Q!zO~Or#SO~Os#QO~Or#TO~P(gOr#TO~Os#UO~O!o#VO~O!o#WO~Ok]mZm~", stateData: "3t~O!]OS~OPPOQUOkUOlUOmUOnUOqWOz[O!dSO!fTO!p`O~OPcOQUOkUOlUOmUOnUOqWOufOwgO!dSO!fTO!mdOY!bXZ!bX[!bX]!bXxWX~O_kO!pWX!tWXtWX~PzOYlOZlO[mO]mO~OYlOZlO[mO]mO!p!_X!t!_Xt!_X~OQUOkUOlUOmUOnUO!dSO!fTO~OPnO~P$YOhvO!fyO!htO!iuO~OY!bXZ!bX[!bX]!bX!p!_X!t!_Xt!_X~OPzOsrP~Ox}O!p!_X!t!_Xt!_X~OP!QO~P$YO!p!SO!t!SO~O!mdO!p!bX!t!bX!e!bXt!bX~OP!bXQ!bXk!bXl!bXm!bXn!bXq!bXu!bXw!bXx!bX!d!bX!f!bX~P&fOP!UO~OPcOqWOu!VO~P$YOPcOqWOufOwgOxTa!pTa!tTa!eTatTa~P$YOPPOqWOz[O~P$YO_!bX`!bXa!bXb!bXc!bXd!bXe!bXf!bX!eWX~PzO_![O`![Oa![Ob![Oc![Od![Oe!]Of!]O~OYlOZlO[mO]mO~P)rOYlOZlO[mO]mO!e!^O~OY!bXZ!bX[!bX]!bX_!bX`!bXa!bXb!bXc!bXd!bXe!bXf!bX~O!e!^O~P+POx}O!e!^O~OP!_O!dSO~O!f!`O!h!`O!i!`O!j!`O!k!`O!l!`O~OhvO!f!bO!htO!iuO~OPzOsrX~Os!dO~OP!eO~Ox}O!pSa!tSa!eSatSa~Os!hO~P)rO!mdOs!bX!p!bX!t!bX!e!bXt!bX~P+POs!hO~OYlOZlO[Xi]Xi!pXi!tXi!eXitXi~OPPOqWOz[O!p!lO~P$YOPcOqWOufOwgOxWX!pWX!tWX!eWXtWX~P$YOPPOqWOz[O!p!oO~P$YO!e^is^i~P)rOt!pO~OPPOqWOz[Ot!qP~P$YOPPOqWOz[Ot!qP!O!qP!Q!qP~P$YO!p!vO~OPPOqWOz[Ot!qX!O!qX!Q!qX~P$YOt!xO~Ot!}O!O!yO!Q!|O~Ot#SO!O!yO!Q!|O~Os#UO~Ot#SO~Os#VO~P)rOs#VO~Ot#WO~O!p#XO~O!p#YO~Ok]mZm~",
goto: "+Z!tPPPP!u#U#d#j#U$VPPPP$lPPPPPPPP$xP%b%bPPPP%fP%{PPP#dPP&O&OP&[&_&hP&lP&O&r&x'Q'W'^'g'nPPP't'x(^(p(v)rPPP*`PPPPP*d*dP*uP*}*}d^Obj!b!f!j!m!p#V#WRqSiYOSbj|!b!f!j!m!p#V#WXfPhm!c|UOPS[behjklm!Y!Z!b!c!f!j!m!p!w#V#WR!]sdRObj!b!f!j!m!p#V#WQoSQ!WkR!XlQqSQ!P[Q!g!ZR#O!w}UOPS[behjklm!Y!Z!b!c!f!j!m!p!w#V#WTuTwd^Obj!b!f!j!m!p#V#WWdPhm!cR!SeR{We^Obj!b!f!j!m!p#V#WR!l!fQ!s!mQ#X#VR#Y#WT!x!s!yQ!|!sR#R!yQbOR!RbUhPm!cR!ThQwTR!_wQzWR!azW!p!j!m#V#WR!u!pS}ZrR!e}Q!y!sR#P!yTaObS_ObQ!VjQ!i!bQ!k!fZ!o!j!m!p#V#WdZObj!b!f!j!m!p#V#WQrSR!d|XgPhm!cdQObj!b!f!j!m!p#V#WWdPhm!cQnSQ!O[Q!SeQ!WkQ!XlQ!g!YQ!h!ZR!}!wdVObj!b!f!j!m!p#V#WfcP[ehklm!Y!Z!c!wRpSTvTwoXOPbehjm!b!c!f!j!m!p#V#WQ!q!jV!r!m#V#We]Obj!b!f!j!m!p#V#W", goto: "+u!tPPPP!u#U#d#j#U$VPPPP$lPPPPPPPP$xP%b%bPPPP%f&QP&gPPP#dPP&jP&v&y'SP'WP&j'^'d'l'r'x(R(YPPP(`(d(x)[)b*^PPP*zPPPPPP+O+OP+a+i+id^Obk!d!h!l!o!r#X#YRrSiYOSbk}!d!h!l!o!r#X#YXhPjn!e|UOPS[bgjklmn![!]!d!e!h!l!o!r!y#X#YR!_tdRObk!d!h!l!o!r#X#YQpSQ!YlR!ZmQrSQ!R[Q!i!]R#Q!y}UOPS[bgjklmn![!]!d!e!h!l!o!r!y#X#YTvTxdVObk!d!h!l!o!r#X#YiePS[gjlmn![!]!e!yd^Obk!d!h!l!o!r#X#YWfPjn!eR!VgR|We^Obk!d!h!l!o!r#X#YR!n!hQ!u!oQ#Z#XR#[#YT!z!u!{Q#O!uR#T!{QbOR!TbUjPn!eR!WjQxTR!axQ{WR!c{W!r!l!o#X#YR!w!rS!OZsR!g!OQ!{!uR#R!{TaObS_ObQ!XkQ!k!dQ!m!hZ!q!l!o!r#X#YdZObk!d!h!l!o!r#X#YQsSR!f}XiPjn!edQObk!d!h!l!o!r#X#YWfPjn!eQoSQ!P[Q!VgQ!YlQ!ZmQ!i![Q!j!]R#P!ydVObk!d!h!l!o!r#X#YfeP[gjlmn![!]!e!yRqSTwTxoXOPbgjkn!d!e!h!l!o!r#X#YQ!s!lV!t!o#X#Ye]Obk!d!h!l!o!r#X#Y",
nodeNames: "⚠ Identifier Word Program PipeExpr FunctionCall PositionalArg ParenExpr FunctionCallOrIdentifier BinOp operator operator operator operator ConditionalOp operator operator operator operator operator operator operator operator String StringFragment Interpolation EscapeSeq Number Boolean Regex Null FunctionDef keyword Params colon end Underscore NamedArg NamedArgPrefix operator DotGet IfExpr keyword ThenBlock ThenBlock ElsifExpr keyword ElseExpr keyword Assign", nodeNames: "⚠ Identifier Word Program PipeExpr FunctionCall PositionalArg ParenExpr FunctionCallOrIdentifier BinOp operator operator operator operator ConditionalOp operator operator operator operator operator operator operator operator 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",
maxTerm: 82, maxTerm: 82,
context: trackScope, context: trackScope,
nodeProps: [ nodeProps: [
["closedBy", 34,"end"], ["closedBy", 35,"end"],
["openedBy", 35,"colon"] ["openedBy", 36,"colon"]
], ],
propSources: [highlighting], propSources: [highlighting],
skippedNodes: [0], skippedNodes: [0],
repeatNodeCount: 7, repeatNodeCount: 7,
tokenData: "!&X~R!SOX$_XY$|YZ%gZp$_pq$|qr&Qrt$_tu'Yuw$_wx'_xy'dyz'}z{(h{|)R|}$_}!O)l!O!P,b!P!Q,{!Q![*]![!]5j!]!^%g!^!_6T!_!`7_!`!a7x!a#O$_#O#P9S#P#R$_#R#S9X#S#T$_#T#U9r#U#X;W#X#Y=m#Y#ZDs#Z#];W#]#^JO#^#b;W#b#cKp#c#d! Y#d#f;W#f#g!!z#g#h;W#h#i!#q#i#o;W#o#p$_#p#q!%i#q;'S$_;'S;=`$v<%l~$_~O$_~~!&SS$dUhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_S$yP;=`<%l$__%TUhS!]ZOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V%nUhS!oROt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V&VWhSOt$_uw$_x!_$_!_!`&o!`#O$_#P;'S$_;'S;=`$v<%lO$_V&vU`RhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_~'_O!h~~'dO!f~V'kUhS!dROt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V(UUhS!eROt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V(oUYRhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V)YU[RhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V)sWhS]ROt$_uw$_x!Q$_!Q![*]![#O$_#P;'S$_;'S;=`$v<%lO$_V*dYhSkROt$_uw$_x!O$_!O!P+S!P!Q$_!Q![*]![#O$_#P;'S$_;'S;=`$v<%lO$_V+XWhSOt$_uw$_x!Q$_!Q![+q![#O$_#P;'S$_;'S;=`$v<%lO$_V+xWhSkROt$_uw$_x!Q$_!Q![+q![#O$_#P;'S$_;'S;=`$v<%lO$_U,iU!qQhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V-SWhSZROt$_uw$_x!P$_!P!Q-l!Q#O$_#P;'S$_;'S;=`$v<%lO$_V-q^hSOY.mYZ$_Zt.mtu/puw.mwx/px!P.m!P!Q$_!Q!}.m!}#O4c#O#P2O#P;'S.m;'S;=`5d<%lO.mV.t^hSmROY.mYZ$_Zt.mtu/puw.mwx/px!P.m!P!Q2e!Q!}.m!}#O4c#O#P2O#P;'S.m;'S;=`5d<%lO.mR/uXmROY/pZ!P/p!P!Q0b!Q!}/p!}#O1P#O#P2O#P;'S/p;'S;=`2_<%lO/pR0eP!P!Q0hR0mUmR#Z#[0h#]#^0h#a#b0h#g#h0h#i#j0h#m#n0hR1SVOY1PZ#O1P#O#P1i#P#Q/p#Q;'S1P;'S;=`1x<%lO1PR1lSOY1PZ;'S1P;'S;=`1x<%lO1PR1{P;=`<%l1PR2RSOY/pZ;'S/p;'S;=`2_<%lO/pR2bP;=`<%l/pV2jWhSOt$_uw$_x!P$_!P!Q3S!Q#O$_#P;'S$_;'S;=`$v<%lO$_V3ZbhSmROt$_uw$_x#O$_#P#Z$_#Z#[3S#[#]$_#]#^3S#^#a$_#a#b3S#b#g$_#g#h3S#h#i$_#i#j3S#j#m$_#m#n3S#n;'S$_;'S;=`$v<%lO$_V4h[hSOY4cYZ$_Zt4ctu1Puw4cwx1Px#O4c#O#P1i#P#Q.m#Q;'S4c;'S;=`5^<%lO4cV5aP;=`<%l4cV5gP;=`<%l.mT5qUhSrPOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V6[WaRhSOt$_uw$_x!_$_!_!`6t!`#O$_#P;'S$_;'S;=`$v<%lO$_V6{UbRhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V7fU_RhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V8PWcRhSOt$_uw$_x!_$_!_!`8i!`#O$_#P;'S$_;'S;=`$v<%lO$_V8pUdRhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_~9XO!i~V9`UhStROt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V9w[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#b;W#b#c;{#c#o;W#o;'S$_;'S;=`$v<%lO$_U:tUvQhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_U;]YhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_V<Q[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#W;W#W#X<v#X#o;W#o;'S$_;'S;=`$v<%lO$_V<}YeRhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_V=r^hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#`;W#`#a>n#a#b;W#b#cCR#c#o;W#o;'S$_;'S;=`$v<%lO$_V>s[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#g;W#g#h?i#h#o;W#o;'S$_;'S;=`$v<%lO$_V?n^hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#X;W#X#Y@j#Y#];W#]#^Aa#^#o;W#o;'S$_;'S;=`$v<%lO$_V@qY!QPhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_VAf[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#Y;W#Y#ZB[#Z#o;W#o;'S$_;'S;=`$v<%lO$_VBcY!OPhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_VCW[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#W;W#W#XC|#X#o;W#o;'S$_;'S;=`$v<%lO$_VDTYhSsROt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_VDx]hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#UEq#U#b;W#b#cIX#c#o;W#o;'S$_;'S;=`$v<%lO$_VEv[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#`;W#`#aFl#a#o;W#o;'S$_;'S;=`$v<%lO$_VFq[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#g;W#g#hGg#h#o;W#o;'S$_;'S;=`$v<%lO$_VGl[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#X;W#X#YHb#Y#o;W#o;'S$_;'S;=`$v<%lO$_VHiYlRhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_VI`YpRhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_VJT[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#Y;W#Y#ZJy#Z#o;W#o;'S$_;'S;=`$v<%lO$_VKQYzPhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$__Kw[!jWhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#i;W#i#jLm#j#o;W#o;'S$_;'S;=`$v<%lO$_VLr[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#`;W#`#aMh#a#o;W#o;'S$_;'S;=`$v<%lO$_VMm[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#`;W#`#aNc#a#o;W#o;'S$_;'S;=`$v<%lO$_VNjYnRhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_V! _[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#f;W#f#g!!T#g#o;W#o;'S$_;'S;=`$v<%lO$_V!![YfRhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_^!#RY!lWhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$__!#x[!kWhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#f;W#f#g!$n#g#o;W#o;'S$_;'S;=`$v<%lO$_V!$s[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#i;W#i#jGg#j#o;W#o;'S$_;'S;=`$v<%lO$_V!%pUwRhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_~!&XO!t~", tokenData: "!&X~R!SOX$_XY$|YZ%gZp$_pq$|qr&Qrt$_tu'Yuw$_wx'_xy'dyz'}z{(h{|)R|}$_}!O)l!O!P,b!P!Q,{!Q![*]![!]5j!]!^%g!^!_6T!_!`7_!`!a7x!a#O$_#O#P9S#P#R$_#R#S9X#S#T$_#T#U9r#U#X;W#X#Y=m#Y#ZDs#Z#];W#]#^JO#^#b;W#b#cKp#c#d! Y#d#f;W#f#g!!z#g#h;W#h#i!#q#i#o;W#o#p$_#p#q!%i#q;'S$_;'S;=`$v<%l~$_~O$_~~!&SS$dUhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_S$yP;=`<%l$__%TUhS!]ZOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V%nUhS!pROt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V&VWhSOt$_uw$_x!_$_!_!`&o!`#O$_#P;'S$_;'S;=`$v<%lO$_V&vU`RhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_~'_O!h~~'dO!f~V'kUhS!dROt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V(UUhS!eROt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V(oUYRhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V)YU[RhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V)sWhS]ROt$_uw$_x!Q$_!Q![*]![#O$_#P;'S$_;'S;=`$v<%lO$_V*dYhSkROt$_uw$_x!O$_!O!P+S!P!Q$_!Q![*]![#O$_#P;'S$_;'S;=`$v<%lO$_V+XWhSOt$_uw$_x!Q$_!Q![+q![#O$_#P;'S$_;'S;=`$v<%lO$_V+xWhSkROt$_uw$_x!Q$_!Q![+q![#O$_#P;'S$_;'S;=`$v<%lO$_V,iU!mRhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V-SWhSZROt$_uw$_x!P$_!P!Q-l!Q#O$_#P;'S$_;'S;=`$v<%lO$_V-q^hSOY.mYZ$_Zt.mtu/puw.mwx/px!P.m!P!Q$_!Q!}.m!}#O4c#O#P2O#P;'S.m;'S;=`5d<%lO.mV.t^hSmROY.mYZ$_Zt.mtu/puw.mwx/px!P.m!P!Q2e!Q!}.m!}#O4c#O#P2O#P;'S.m;'S;=`5d<%lO.mR/uXmROY/pZ!P/p!P!Q0b!Q!}/p!}#O1P#O#P2O#P;'S/p;'S;=`2_<%lO/pR0eP!P!Q0hR0mUmR#Z#[0h#]#^0h#a#b0h#g#h0h#i#j0h#m#n0hR1SVOY1PZ#O1P#O#P1i#P#Q/p#Q;'S1P;'S;=`1x<%lO1PR1lSOY1PZ;'S1P;'S;=`1x<%lO1PR1{P;=`<%l1PR2RSOY/pZ;'S/p;'S;=`2_<%lO/pR2bP;=`<%l/pV2jWhSOt$_uw$_x!P$_!P!Q3S!Q#O$_#P;'S$_;'S;=`$v<%lO$_V3ZbhSmROt$_uw$_x#O$_#P#Z$_#Z#[3S#[#]$_#]#^3S#^#a$_#a#b3S#b#g$_#g#h3S#h#i$_#i#j3S#j#m$_#m#n3S#n;'S$_;'S;=`$v<%lO$_V4h[hSOY4cYZ$_Zt4ctu1Puw4cwx1Px#O4c#O#P1i#P#Q.m#Q;'S4c;'S;=`5^<%lO4cV5aP;=`<%l4cV5gP;=`<%l.mT5qUhSsPOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V6[WaRhSOt$_uw$_x!_$_!_!`6t!`#O$_#P;'S$_;'S;=`$v<%lO$_V6{UbRhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V7fU_RhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V8PWcRhSOt$_uw$_x!_$_!_!`8i!`#O$_#P;'S$_;'S;=`$v<%lO$_V8pUdRhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_~9XO!i~V9`UhSuROt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V9w[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#b;W#b#c;{#c#o;W#o;'S$_;'S;=`$v<%lO$_U:tUwQhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_U;]YhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_V<Q[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#W;W#W#X<v#X#o;W#o;'S$_;'S;=`$v<%lO$_V<}YeRhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_V=r^hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#`;W#`#a>n#a#b;W#b#cCR#c#o;W#o;'S$_;'S;=`$v<%lO$_V>s[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#g;W#g#h?i#h#o;W#o;'S$_;'S;=`$v<%lO$_V?n^hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#X;W#X#Y@j#Y#];W#]#^Aa#^#o;W#o;'S$_;'S;=`$v<%lO$_V@qY!QPhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_VAf[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#Y;W#Y#ZB[#Z#o;W#o;'S$_;'S;=`$v<%lO$_VBcY!OPhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_VCW[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#W;W#W#XC|#X#o;W#o;'S$_;'S;=`$v<%lO$_VDTYhStROt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_VDx]hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#UEq#U#b;W#b#cIX#c#o;W#o;'S$_;'S;=`$v<%lO$_VEv[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#`;W#`#aFl#a#o;W#o;'S$_;'S;=`$v<%lO$_VFq[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#g;W#g#hGg#h#o;W#o;'S$_;'S;=`$v<%lO$_VGl[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#X;W#X#YHb#Y#o;W#o;'S$_;'S;=`$v<%lO$_VHiYlRhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_VI`YqRhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_VJT[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#Y;W#Y#ZJy#Z#o;W#o;'S$_;'S;=`$v<%lO$_VKQYzPhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$__Kw[!jWhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#i;W#i#jLm#j#o;W#o;'S$_;'S;=`$v<%lO$_VLr[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#`;W#`#aMh#a#o;W#o;'S$_;'S;=`$v<%lO$_VMm[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#`;W#`#aNc#a#o;W#o;'S$_;'S;=`$v<%lO$_VNjYnRhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_V! _[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#f;W#f#g!!T#g#o;W#o;'S$_;'S;=`$v<%lO$_V!![YfRhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_^!#RY!lWhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$__!#x[!kWhSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#f;W#f#g!$n#g#o;W#o;'S$_;'S;=`$v<%lO$_V!$s[hSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#i;W#i#jGg#j#o;W#o;'S$_;'S;=`$v<%lO$_V!%pUxRhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_~!&XO!t~",
tokenizers: [0, 1, 2, 3, tokenizer], tokenizers: [0, 1, 2, 3, tokenizer],
topRules: {"Program":[0,3]}, topRules: {"Program":[0,3]},
tokenPrec: 775 tokenPrec: 858
}) })

View File

@ -0,0 +1,130 @@
import { describe, test, expect } from 'bun:test'
import '../../testSetup'
describe('DotGet', () => {
test('readme.txt is Word when readme not in scope', () => {
expect('readme.txt').toMatchTree(`Word readme.txt`)
})
test('obj.prop is DotGet when obj is assigned', () => {
expect('obj = 5; obj.prop').toMatchTree(`
Assign
Identifier obj
operator =
Number 5
DotGet
Identifier obj
Identifier prop
`)
})
test('function parameters are in scope within function body', () => {
expect('fn config: config.path end').toMatchTree(`
FunctionDef
keyword fn
Params
Identifier config
colon :
DotGet
Identifier config
Identifier path
end end
`)
})
test('parameters out of scope outside function', () => {
expect('fn x: x.prop end; x.prop').toMatchTree(`
FunctionDef
keyword fn
Params
Identifier x
colon :
DotGet
Identifier x
Identifier prop
end end
Word x.prop
`)
})
test('multiple parameters work correctly', () => {
expect(`fn x y:
x.foo
y.bar
end`).toMatchTree(`
FunctionDef
keyword fn
Params
Identifier x
Identifier y
colon :
DotGet
Identifier x
Identifier foo
DotGet
Identifier y
Identifier bar
end end
`)
})
test('nested functions with scope isolation', () => {
expect(`fn x:
x.outer
fn y: y.inner end
end`).toMatchTree(`
FunctionDef
keyword fn
Params
Identifier x
colon :
DotGet
Identifier x
Identifier outer
FunctionDef
keyword fn
Params
Identifier y
colon :
DotGet
Identifier y
Identifier inner
end end
end end
`)
})
test('dot get works as function argument', () => {
expect('config = 42; echo config.path').toMatchTree(`
Assign
Identifier config
operator =
Number 42
FunctionCall
Identifier echo
PositionalArg
DotGet
Identifier config
Identifier path
`)
})
test('mixed file paths and dot get', () => {
expect('config = 42; cat readme.txt; echo config.path').toMatchTree(`
Assign
Identifier config
operator =
Number 42
FunctionCall
Identifier cat
PositionalArg
Word readme.txt
FunctionCall
Identifier echo
PositionalArg
DotGet
Identifier config
Identifier path
`)
})
})

View File

@ -17,7 +17,7 @@ export const tokenizer = new ExternalTokenizer((input: InputStream, stack: Stack
// Check for dot and scope - property access detection // Check for dot and scope - property access detection
if (ch === 46 /* . */ && isValidIdentifier) { if (ch === 46 /* . */ && isValidIdentifier) {
const identifierText = input.read(0, pos) const identifierText = input.read(input.pos, input.pos + pos)
const scope = stack.context as Scope | undefined const scope = stack.context as Scope | undefined
if (scope?.has(identifierText)) { if (scope?.has(identifierText)) {