All pastes #1745512 Raw Edit

Stuff

public text v1 · immutable
#1745512 ·published 2010-01-10 14:24 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 = *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