All pastes #1590601 Raw Copy code Copy link Edit

symboltable.pb

public unlisted text v1 · immutable
#1590601 ·published 2009-10-03 14:07 UTC
rendered paste body
; 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 = LookupSymbolNoScopeResolve(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

Procedure AddVariable(Name.s, TypeS.s, RefKind.i, RefOffset.i)
  ; Check if it's already declared in this scope
  Protected *V.SVariable
  Protected TypeI
  *V = LookupSymbolCurrentScope(Name)
  If *V
    Error("Variable declared twice: " + Name)
  EndIf
  
  ; Convert the type name into a type number.
  ; For now we have only one type: #TyLong
  Select TypeS
    Case "long": TypeI = #TyLong
    Default
      Error("Invalid type name: " + TypeS)
  EndSelect
  
  ; Add the variable to the symbol table
  *V = AllocateMemory(SizeOf(SVariable))
  *V\Name = Name
  *V\Kind = #SKind_Variable
  *V\Type = TypeI
  *V\RefKind = RefKind
  *V\RefOffset = RefOffset
  AddSymbol(*V)
EndProcedure