2020-04-05 04:35:44 +00:00
|
|
|
package main
|
|
|
|
|
2020-04-11 00:49:08 +00:00
|
|
|
var (
|
|
|
|
unknownVarType gotype = gotype{Plain: `unknown`} // placeholder
|
|
|
|
mixedVarType gotype = gotype{Plain: `mixed`} // when setting an incompatible type
|
2020-04-05 04:35:44 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type LocalVar struct {
|
|
|
|
Name string
|
2020-04-11 00:49:08 +00:00
|
|
|
Type gotype
|
2020-04-05 04:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Scope struct {
|
|
|
|
parent *Scope
|
|
|
|
locals []LocalVar
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewScope() *Scope {
|
|
|
|
return &Scope{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *Scope) NewScope() *Scope {
|
|
|
|
return &Scope{
|
|
|
|
parent: this,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *Scope) Has(varName string) *LocalVar {
|
|
|
|
for idx := range this.locals {
|
|
|
|
if this.locals[idx].Name == varName {
|
|
|
|
return &this.locals[idx] // Mutable
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.parent != nil {
|
|
|
|
return this.parent.Has(varName)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil // not found
|
|
|
|
}
|
|
|
|
|
2020-04-11 00:49:08 +00:00
|
|
|
func (this *Scope) Set(Name string, Type gotype) *LocalVar {
|
2020-04-05 04:35:44 +00:00
|
|
|
|
|
|
|
if lv := this.Has(Name); lv != nil {
|
|
|
|
// Update known type for existing variable
|
|
|
|
if lv.Type == unknownVarType {
|
|
|
|
lv.Type = Type
|
|
|
|
} else if lv.Type == Type {
|
|
|
|
// no-op, more evidence for the same type
|
|
|
|
} else if lv.Type != Type {
|
|
|
|
// conflicting type information
|
|
|
|
lv.Type = mixedVarType
|
|
|
|
}
|
|
|
|
return lv
|
|
|
|
}
|
|
|
|
|
|
|
|
// Insert new
|
|
|
|
this.locals = append(this.locals, LocalVar{Name: Name, Type: Type})
|
|
|
|
return &this.locals[len(this.locals)-1]
|
|
|
|
}
|