; 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 ; IDE Options = PureBasic 5.31 (Windows - x64) ; CursorPosition = 203 ; FirstLine = 143 ; Folding = --- ; EnableXP ; EnableUser ; EnableCompileCount = 0 ; EnableBuildCount = 0