; symboltable.pb
; Simple symbol table
; Scope separator is ":"
; So a local variable X in the function Func would be Func:X.
; A global variable X would be X.
Structure SSymbol
Name.s
Kind.i
EndStructure
Enumeration ; Symbol kind
#SKind_Variable
#SKind_Function
EndEnumeration
Structure SVariable Extends SSymbol
Type.i
RefKind.i ; Global, local, parameter
RefOffset.i ; Currently not needed for globals
EndStructure
Enumeration ; Ref kind
#Ref_Global
#Ref_Local
#Ref_Parameter
EndEnumeration
Structure SFunction Extends SSymbol
Type.i ; Return type
Ref.s ; Entry point
ParamCount.i
ParamTypes.i[10] ; We allow at most 10 parameters
EndStructure
Enumeration ; Symbol type
#TyLong
EndEnumeration
; Prototype for callback that is called once for each
; symbol in the table (for listing all symbols)
Prototype ProtoSymbolListEnum(ScopedName.s, *Symbol.SSymbol)
; Map of pointers to SSymbol
Global NewMap *Symbols.SSymbol()
; Linked list of the scopes we're in (for use with local variables)
; When looking up symbols we check innermost scopes first
; Innermost scopes will be first in the list
Global NewList CurrentScopes.S()
AddElement(CurrentScopes()) ; Add the global scope
Procedure EnterScope(Scope.s)
FirstElement(CurrentScopes())
Scope = CurrentScopes() + ":" + Scope
ResetList(CurrentScopes())
AddElement(CurrentScopes())
CurrentScopes() = Scope
EndProcedure
Procedure LeaveScope()
FirstElement(CurrentScopes())
DeleteElement(CurrentScopes())
EndProcedure
; Lookup a symbol without scope resolution
; Valid parameters would be "Foo" (global), "Bar:Glass" (local)
Procedure LookupSymbolNoScopeResolve(Name.s)
ProcedureReturn *Symbols(":"+Name)
EndProcedure
; Lookup a symbol using the scope resolution rules of our language.
; For example, I have decided that global variables are accessible
; in functions, however, local variables have the priority in case
; of name clashes, so we test for local names first.
Procedure LookupSymbol(Name.s)
Protected *S
; Try all scopes in order
ForEach CurrentScopes()
*S = *Symbols(CurrentScopes() + ":" + Name)
If *S
ProcedureReturn *S
EndIf
Next
ProcedureReturn 0
EndProcedure
; Lookup a symbol, but look only in the current scope
Procedure LookupSymbolCurrentScope(Name.s)
FirstElement(CurrentScopes())
ProcedureReturn *Symbols(CurrentScopes() + ":" + Name)
EndProcedure
; Add a local or global symbol.
; Note: when in the global scope, added symbols will
; be global even when added with Glbal = 0
Procedure AddSymbol(*Sym.SSymbol, Glbal = 0)
Protected N.s = *Sym\Name
If Not Glbal
; Not explicitly global, add it in the current scope
FirstElement(CurrentScopes())
*Symbols(CurrentScopes()+":"+N) = *Sym
Else
; Global, add without extra scope
*Symbols(N) = *Sym
EndIf
EndProcedure
; List all symbols in a custom callback
Procedure EnumerateSymbols(Callback.ProtoSymbolListEnum)
ForEach *Symbols()
Callback(MapKey(*Symbols()), *Symbols())
Next
EndProcedure
; Convert the type name into a type number.
; For now we have only one type: #TyLong
Procedure TypeFromString(TypeS.s)
Select TypeS
Case "long": ProcedureReturn #TyLong
Default
Error("Invalid type name: " + TypeS)
EndSelect
EndProcedure
Procedure AddVariable(Name.s, TypeS.s, RefKind.i, RefOffset.i)
; Check if it's already declared in this scope
Protected *V.SVariable
*V = LookupSymbolCurrentScope(Name)
If *V
Error("Variable declared twice: " + Name)
EndIf
; Add the variable to the symbol table
*V = AllocateMemory(SizeOf(SVariable))
*V\Name = Name
*V\Kind = #SKind_Variable
*V\Type = TypeFromString(TypeS)
*V\RefKind = RefKind
*V\RefOffset = RefOffset
AddSymbol(*V)
ProcedureReturn *V
EndProcedure
Procedure AddFunction(Name.s, Ref.s = "")
; Check if it's already declared in the global scope
Protected *F.SFunction
*F = LookupSymbolNoScopeResolve(Name)
If *F
Error("Function declared twice: " + Name)
EndIf
; Add the function to the symbol table
*F = AllocateMemory(SizeOf(SFunction))
*F\Name = Name
*F\Kind = #SKind_Function
*F\Type = #TyLong
*F\ParamCount = 0
If Ref = ""
*F\Ref = "_" + Name
Else
*F\Ref = Ref
EndIf
AddSymbol(*F)
ProcedureReturn *F
EndProcedure
;----------------
CompilerIf 0
Procedure ListSymbols(ScopedName.s, *V.SSymbol)
Debug ScopedName + ", " + *V\Name
EndProcedure
N.SVariable
N\Name = "Apple"
N\Kind = #SKind_Variable
N\RefKind = #Ref_Global
N\RefOffset = 0
AddSymbol(@N)
EnterScope("Blah")
O.SVariable
O\Name = "Orange"
O\Kind = #SKind_Variable
O\RefKind = #Ref_Local
O\RefOffset = 0
AddSymbol(@O)
Debug LookupSymbol("Orange")
Debug LookupSymbol("Apple")
LeaveScope()
Debug "--"
Debug LookupSymbol("Orange")
Debug LookupSymbol("Apple")
Debug LookupSymbol("Blah:Orange")
; EnumerateSymbols(@ListSymbols())
CompilerEndIf
10 January 2010
Listing: symboltable.pb
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment