Skip to content

Commit a26b737

Browse files
committed
Closures working v0.0.1
1 parent eaab44f commit a26b737

File tree

7 files changed

+862
-110
lines changed

7 files changed

+862
-110
lines changed

testing/ASF_3.xlsm

80.4 KB
Binary file not shown.

testing/Compiler.cls

Lines changed: 153 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -812,124 +812,175 @@ Private Function ParsePrimaryNode(types() As String, vals() As Variant, n As Lon
812812
Set ParsePrimaryNode = bf
813813
Exit Function
814814
End If
815-
' Handle collapsed array-index form IDENT[NUM] (already collapsed by tokenizer)
816-
If InStr(name, "[") > 0 Then
817-
' keep existing array-index behavior handled elsewhere (or convert into Index node)
818-
Dim nm As String: nm = left$(name, InStr(name, "[") - 1)
819-
Dim idxS As String: idxS = Mid$(name, InStr(name, "[") + 1, Len(name) - InStr(name, "[") - 1)
820-
' create base variable node
821-
Dim baseVar As New Map
822-
baseVar.Add "type", "Variable"
823-
baseVar.SetValue "name", nm
824-
' index node from simple expression string
825-
Set idxExpr = ParseExprFromStringToNode(idxS)
826-
Set idxNode = New Map
827-
idxNode.Add "type", "Index"
828-
idxNode.SetValue "base", baseVar
829-
idxNode.SetValue "index", idxExpr
830-
Set ParsePrimaryNode = idxNode
815+
816+
' Expression-level anonymous function literal: fun (p1, p2) { ... }
817+
' This reuses the same 'fun' token used for top-level function declarations but emits
818+
' a FuncLiteral node usable inside expressions.
819+
If LCase$(name) = "fun" Then
820+
' advance past 'fun'
821+
idx = idx + 1
822+
' expect parameter list
823+
Dim params As New Collection
824+
If idx < n And types(idx) = "PAREN" And vals(idx) = "(" Then
825+
idx = idx + 1
826+
Do While idx < n And Not (types(idx) = "PAREN" And vals(idx) = ")")
827+
If types(idx) = "IDENT" Then
828+
params.Add CStr(vals(idx))
829+
idx = idx + 1
830+
If idx < n And types(idx) = "SEP" And vals(idx) = "," Then idx = idx + 1
831+
ElseIf types(idx) = "SEP" And vals(idx) = "," Then
832+
idx = idx + 1
833+
Else
834+
' skip unexpected tokens until ')'
835+
idx = idx + 1
836+
End If
837+
Loop
838+
If idx < n And types(idx) = "PAREN" And vals(idx) = ")" Then idx = idx + 1
839+
End If
840+
841+
' parse body { ... }
842+
If idx < n And types(idx) = "PAREN" And vals(idx) = "{" Then
843+
idx = idx + 1
844+
Dim depthBody As Long: depthBody = 0
845+
Dim bodyTok As New Collection
846+
Do While idx < n
847+
If types(idx) = "PAREN" Then
848+
If vals(idx) = "{" Then
849+
depthBody = depthBody + 1
850+
ElseIf vals(idx) = "}" Then
851+
If depthBody = 0 Then
852+
Exit Do
853+
Else
854+
depthBody = depthBody - 1
855+
End If
856+
End If
857+
End If
858+
bodyTok.Add Array(types(idx), vals(idx))
859+
idx = idx + 1
860+
Loop
861+
' consume closing '}'
862+
If idx < n And types(idx) = "PAREN" And vals(idx) = "}" Then idx = idx + 1
863+
Dim bodyStmts As Collection: Set bodyStmts = ParseTokensToAST(bodyTok)
864+
Dim fnode As Map: Set fnode = MakeNode("FuncLiteral")
865+
fnode.SetValue "params", params
866+
fnode.SetValue "body", bodyStmts
867+
Set ParsePrimaryNode = fnode
868+
Exit Function
869+
Else
870+
' no body found -> emit empty function literal
871+
Dim fnode2 As Map: Set fnode2 = MakeNode("FuncLiteral")
872+
fnode2.SetValue "params", params
873+
Dim emptyCol As New Collection
874+
fnode2.SetValue "body", emptyCol
875+
Set ParsePrimaryNode = fnode2
876+
Exit Function
877+
End If
878+
End If
879+
880+
' .length special-case on collapsed IDENT token (preserve behavior, but emit Call with callee)
881+
If right$(name, 7) = ".length" Then
882+
Dim arrName As String: arrName = left$(name, Len(name) - 7)
883+
Dim varN As Map: Set varN = MakeNode("Variable")
884+
varN.SetValue "name", arrName
885+
Dim lenCall As Map: Set lenCall = MakeNode("Call")
886+
lenCall.SetValue "callee", varN
887+
Dim al As New Collection
888+
al.Add varN
889+
lenCall.SetValue "args", al
831890
idx = idx + 1
891+
Set ParsePrimaryNode = lenCall
832892
Exit Function
833893
End If
834-
' Dotted identifiers: convert into nested Member nodes:
835-
' e.g. "a.b.c" -> Member(Member(Variable("a"), "b"), "c")
836-
If InStr(name, ".") > 0 Then
837-
Dim parts() As String
838-
parts = Split(name, ".")
839-
' create root variable
840-
Dim curNode As Map
841-
Dim vNode As New Map
842-
vNode.Add "type", "Variable"
843-
vNode.SetValue "name", parts(0)
844-
Set curNode = vNode
845-
Dim pi As Long
846-
For pi = LBound(parts) + 1 To UBound(parts)
847-
Dim mnode As New Map
848-
mnode.Add "type", "Member"
849-
mnode.SetValue "base", curNode
850-
mnode.SetValue "prop", CStr(parts(pi))
851-
Set curNode = mnode
852-
Next pi
853-
Set ParsePrimaryNode = curNode
894+
895+
' collapsed index literal, e.g. name[3] in single IDENT token => produce Index node
896+
If InStr(name, "[") > 0 Then
897+
Dim base As String: base = left$(name, InStr(name, "[") - 1)
898+
Dim idxStr As String: idxStr = Mid$(name, InStr(name, "[") + 1, Len(name) - InStr(name, "[") - 1)
899+
Dim idxNodeFromStr As Map: Set idxNodeFromStr = ParseExprFromStringToNode(idxStr)
900+
Dim inNode As Map: Set inNode = MakeNode("Index")
901+
Dim vn As Map: Set vn = MakeNode("Variable")
902+
vn.SetValue "name", base
903+
inNode.SetValue "base", vn
904+
inNode.SetValue "index", idxNodeFromStr
854905
idx = idx + 1
906+
Set ParsePrimaryNode = inNode
855907
Exit Function
856908
End If
857-
' plain identifier -> Variable node
858-
Dim plainVar As Map
859-
Set plainVar = MakeNode("Variable")
860-
plainVar.SetValue "name", name
861-
Set ParsePrimaryNode = plainVar
909+
910+
' create initial variable node for an IDENT and then apply postfix operators:
911+
Dim currentNode As Map: Set currentNode = MakeNode("Variable")
912+
currentNode.SetValue "name", name
862913
idx = idx + 1
863-
Exit Function
864-
If idx + 1 < n Then
865-
If types(idx + 1) = "PAREN" And vals(idx + 1) = "[" Then
866-
Dim baseName As String: baseName = name
867-
idx = idx + 2 ' after '['
868-
Dim indexTokens As New Collection
869-
Dim depthParen As Long: depthParen = 0
914+
915+
' Postfix loop: handle .prop, [...], func calls (...) as postfixes chaining onto currentNode
916+
Do While idx < n
917+
' member access a.b
918+
If types(idx) = "OP" And vals(idx) = "." Then
919+
idx = idx + 1
920+
If idx < n And types(idx) = "IDENT" Then
921+
Dim mem As Map: Set mem = MakeNode("Member")
922+
mem.SetValue "base", currentNode
923+
mem.SetValue "prop", CStr(vals(idx))
924+
Set currentNode = mem
925+
idx = idx + 1
926+
' continue loop
927+
Else
928+
' invalid member access; stop postfixing
929+
Exit Do
930+
End If
931+
' index access a[expr]
932+
ElseIf types(idx) = "PAREN" And vals(idx) = "[" Then
933+
idx = idx + 1
934+
Dim idxTok As New Collection
935+
Dim depthIdx As Long: depthIdx = 0
870936
Do While idx < n
871937
If types(idx) = "PAREN" Then
872-
If vals(idx) = "(" Then
873-
depthParen = depthParen + 1
874-
End If
875-
If vals(idx) = ")" Then
876-
depthParen = depthParen - 1
877-
End If
878-
If vals(idx) = "]" And depthParen = 0 Then
879-
Exit Do
938+
If vals(idx) = "[" Then
939+
depthIdx = depthIdx + 1
940+
ElseIf vals(idx) = "]" Then
941+
If depthIdx = 0 Then
942+
Exit Do
943+
Else
944+
depthIdx = depthIdx - 1
945+
End If
880946
End If
881947
End If
882-
indexTokens.Add Array(types(idx), vals(idx))
948+
idxTok.Add Array(types(idx), vals(idx))
883949
idx = idx + 1
884950
Loop
885-
If idx >= n Or vals(idx) <> "]" Then
886-
err.Raise vbObjectError + 5003, "Compiler.ParsePrimaryNode", "Unclosed array index"
887-
End If
888-
idx = idx + 1 ' After ]
889-
Set idxExpr = ParseExprTokensToNode(indexTokens)
890-
Dim in2 As Map: Set in2 = MakeNode("Index")
891-
Dim vvar As Map: Set vvar = MakeNode("Variable")
892-
vvar.SetValue "name", baseName
893-
in2.SetValue "base", vvar
894-
in2.SetValue "index", idxExpr
895-
Set ParsePrimaryNode = in2
896-
Exit Function
897-
End If
898-
End If
899-
If idx + 1 < n Then
900-
If types(idx + 1) = "PAREN" And vals(idx + 1) = "(" Then
901-
idx = idx + 2 ' after '('
951+
Dim idxExprNode As Map: Set idxExprNode = ParseExprTokensToNode(idxTok)
952+
If idx < n And types(idx) = "PAREN" And vals(idx) = "]" Then idx = idx + 1
953+
Dim indexNode As Map: Set indexNode = MakeNode("Index")
954+
indexNode.SetValue "base", currentNode
955+
indexNode.SetValue "index", idxExprNode
956+
Set currentNode = indexNode
957+
' function / method call: ( arglist )
958+
ElseIf types(idx) = "PAREN" And vals(idx) = "(" Then
959+
' parse args with nested depth counters (borrowed pattern from previous call parsing logic)
960+
idx = idx + 1
902961
Dim argNodes As New Collection
903-
Dim argTokDepthParen As Long, argTokDepthBr As Long, argTokDepthB As Long
904-
Do While idx < n
962+
Do
905963
Dim argTok As New Collection
906-
argTokDepthParen = 0: argTokDepthBr = 0: argTokDepthB = 0
964+
Dim argTokDepthParen As Long: argTokDepthParen = 0
965+
Dim argTokDepthBr As Long: argTokDepthBr = 0
966+
Dim argTokDepthB As Long: argTokDepthB = 0
907967
Do While idx < n
908968
If types(idx) = "PAREN" Then
909-
Select Case vals(idx)
969+
Select Case CStr(vals(idx))
910970
Case "("
911971
argTokDepthParen = argTokDepthParen + 1
912972
Case ")"
913-
If argTokDepthParen > 0 Then
914-
argTokDepthParen = argTokDepthParen - 1
915-
Else
916-
Exit Do
917-
End If
973+
If argTokDepthParen = 0 Then Exit Do Else argTokDepthParen = argTokDepthParen - 1
918974
Case "["
919975
argTokDepthBr = argTokDepthBr + 1
920976
Case "]"
921-
If argTokDepthBr > 0 Then argTokDepthBr = argTokDepthBr - 1
977+
If argTokDepthBr = 0 Then Exit Do Else argTokDepthBr = argTokDepthBr - 1
922978
Case "{"
923979
argTokDepthB = argTokDepthB + 1
924980
Case "}"
925-
If argTokDepthB > 0 Then argTokDepthB = argTokDepthB - 1
981+
If argTokDepthB = 0 Then Exit Do Else argTokDepthB = argTokDepthB - 1
926982
End Select
927-
End If
928-
If argTokDepthParen = 0 And argTokDepthBr = 0 And argTokDepthB = 0 _
929-
And types(idx) = "SEP" And (vals(idx) = "," Or vals(idx) = ";") Then
930-
Exit Do
931-
End If
932-
If types(idx) = "PAREN" And vals(idx) = ")" And argTokDepthParen = 0 And argTokDepthBr = 0 And argTokDepthB = 0 Then
983+
ElseIf types(idx) = "SEP" And vals(idx) = "," And argTokDepthParen = 0 And argTokDepthBr = 0 And argTokDepthB = 0 Then
933984
Exit Do
934985
End If
935986
argTok.Add Array(types(idx), vals(idx))
@@ -943,29 +994,29 @@ Private Function ParsePrimaryNode(types() As String, vals() As Variant, n As Lon
943994
argNodes.Add litEmpty
944995
End If
945996
If idx < n Then
946-
If types(idx) = "SEP" And (vals(idx) = "," Or vals(idx) = ";") Then
997+
If types(idx) = "SEP" And vals(idx) = "," Then
947998
idx = idx + 1
999+
' continue parsing next arg
9481000
ElseIf types(idx) = "PAREN" And vals(idx) = ")" Then
9491001
idx = idx + 1
9501002
Exit Do
9511003
Else
1004+
' unexpected token -> stop args parsing
9521005
Exit Do
9531006
End If
9541007
Else
9551008
Exit Do
9561009
End If
9571010
Loop
9581011
Dim callNode As Map: Set callNode = MakeNode("Call")
959-
callNode.SetValue "name", name
1012+
callNode.SetValue "callee", currentNode
9601013
callNode.SetValue "args", argNodes
961-
Set ParsePrimaryNode = callNode
962-
Exit Function
1014+
Set currentNode = callNode
1015+
Else
1016+
Exit Do
9631017
End If
964-
End If
965-
Dim varNode As Map: Set varNode = MakeNode("Variable")
966-
varNode.SetValue "name", name
967-
idx = idx + 1
968-
Set ParsePrimaryNode = varNode
1018+
Loop
1019+
Set ParsePrimaryNode = currentNode
9691020
Exit Function
9701021
Case "PAREN"
9711022
If v = "(" Then
@@ -2062,3 +2113,5 @@ NormNext:
20622113
' End Compiler class
20632114
' ---------------------
20642115

2116+
2117+

0 commit comments

Comments
 (0)