Merge pull request #44 from colincross/local
Fix bugs related to local vs. inherited variables
diff --git a/context.go b/context.go
index 3892f4e..dd2dc1d 100644
--- a/context.go
+++ b/context.go
@@ -469,12 +469,12 @@
}
file.Name = relBlueprintsFile
- subdirs, subdirsPos, err := getStringListFromScope(scope, "subdirs")
+ subdirs, subdirsPos, err := getLocalStringListFromScope(scope, "subdirs")
if err != nil {
errs = append(errs, err)
}
- build, buildPos, err := getStringListFromScope(scope, "build")
+ build, buildPos, err := getLocalStringListFromScope(scope, "build")
if err != nil {
errs = append(errs, err)
}
@@ -799,8 +799,10 @@
return blueprints, deps, errs
}
-func getStringListFromScope(scope *parser.Scope, v string) ([]string, scanner.Position, error) {
- if assignment, err := scope.Get(v); err == nil {
+func getLocalStringListFromScope(scope *parser.Scope, v string) ([]string, scanner.Position, error) {
+ if assignment, local := scope.Get(v); assignment == nil || !local {
+ return nil, scanner.Position{}, nil
+ } else {
switch assignment.Value.Type {
case parser.List:
ret := make([]string, 0, len(assignment.Value.ListValue))
@@ -824,12 +826,12 @@
panic(fmt.Errorf("unknown value type: %d", assignment.Value.Type))
}
}
-
- return nil, scanner.Position{}, nil
}
func getStringFromScope(scope *parser.Scope, v string) (string, scanner.Position, error) {
- if assignment, err := scope.Get(v); err == nil {
+ if assignment, _ := scope.Get(v); assignment == nil {
+ return "", scanner.Position{}, nil
+ } else {
switch assignment.Value.Type {
case parser.String:
return assignment.Value.StringValue, assignment.Pos, nil
@@ -842,8 +844,6 @@
panic(fmt.Errorf("unknown value type: %d", assignment.Value.Type))
}
}
-
- return "", scanner.Position{}, nil
}
func (c *Context) createVariations(origModule *moduleInfo, mutatorName string,
diff --git a/parser/parser.go b/parser/parser.go
index 6027275..fb931af 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -104,13 +104,13 @@
return p
}
-func (p *parser) errorf(format string, args ...interface{}) {
+func (p *parser) error(err error) {
pos := p.scanner.Position
if !pos.IsValid() {
pos = p.scanner.Pos()
}
- err := &ParseError{
- Err: fmt.Errorf(format, args...),
+ err = &ParseError{
+ Err: err,
Pos: pos,
}
p.errors = append(p.errors, err)
@@ -119,6 +119,10 @@
}
}
+func (p *parser) errorf(format string, args ...interface{}) {
+ p.error(fmt.Errorf(format, args...))
+}
+
func (p *parser) accept(toks ...rune) bool {
for _, tok := range toks {
if p.tok != tok {
@@ -193,18 +197,26 @@
if p.scope != nil {
if assigner == "+=" {
- if old, err := p.scope.Get(assignment.Name.Name); err == nil {
- if old.Referenced {
- p.errorf("modified variable with += after referencing")
+ if old, local := p.scope.Get(assignment.Name.Name); old == nil {
+ p.errorf("modified non-existent variable %q with +=", assignment.Name.Name)
+ } else if !local {
+ p.errorf("modified non-local variable %q with +=", assignment.Name.Name)
+ } else if old.Referenced {
+ p.errorf("modified variable %q with += after referencing",
+ assignment.Name.Name)
+ } else {
+ val, err := p.evaluateOperator(old.Value, assignment.Value, '+', assignment.Pos)
+ if err != nil {
+ p.error(err)
+ } else {
+ old.Value = val
}
- old.Value, err = p.evaluateOperator(old.Value, assignment.Value, '+', assignment.Pos)
- return
}
- }
-
- err := p.scope.Add(assignment)
- if err != nil {
- p.errorf("%s", err.Error())
+ } else {
+ err := p.scope.Add(assignment)
+ if err != nil {
+ p.error(err)
+ }
}
}
@@ -392,7 +404,7 @@
value, err := p.evaluateOperator(value1, value2, operator, pos)
if err != nil {
- p.errorf(err.Error())
+ p.error(err)
return Value{}
}
@@ -427,12 +439,14 @@
default:
variable := p.scanner.TokenText()
if p.eval {
- assignment, err := p.scope.Get(variable)
- if err != nil {
- p.errorf(err.Error())
+ if assignment, local := p.scope.Get(variable); assignment == nil {
+ p.errorf("variable %q is not set", variable)
+ } else {
+ if local {
+ assignment.Referenced = true
+ }
+ value = assignment.Value
}
- assignment.Referenced = true
- value = assignment.Value
}
value.Variable = variable
}
@@ -684,17 +698,22 @@
}
type Scope struct {
- vars map[string]*Assignment
+ vars map[string]*Assignment
+ inheritedVars map[string]*Assignment
}
func NewScope(s *Scope) *Scope {
newScope := &Scope{
- vars: make(map[string]*Assignment),
+ vars: make(map[string]*Assignment),
+ inheritedVars: make(map[string]*Assignment),
}
if s != nil {
for k, v := range s.vars {
- newScope.vars[k] = v
+ newScope.inheritedVars[k] = v
+ }
+ for k, v := range s.inheritedVars {
+ newScope.inheritedVars[k] = v
}
}
@@ -706,6 +725,10 @@
return fmt.Errorf("variable already set, previous assignment: %s", old)
}
+ if old, ok := s.inheritedVars[assignment.Name.Name]; ok {
+ return fmt.Errorf("variable already set in inherited scope, previous assignment: %s", old)
+ }
+
s.vars[assignment.Name.Name] = assignment
return nil
@@ -713,14 +736,19 @@
func (s *Scope) Remove(name string) {
delete(s.vars, name)
+ delete(s.inheritedVars, name)
}
-func (s *Scope) Get(name string) (*Assignment, error) {
+func (s *Scope) Get(name string) (*Assignment, bool) {
if a, ok := s.vars[name]; ok {
- return a, nil
+ return a, true
}
- return nil, fmt.Errorf("variable %s not set", name)
+ if a, ok := s.inheritedVars[name]; ok {
+ return a, false
+ }
+
+ return nil, false
}
func (s *Scope) String() string {
@@ -729,12 +757,19 @@
for k := range s.vars {
vars = append(vars, k)
}
+ for k := range s.inheritedVars {
+ vars = append(vars, k)
+ }
sort.Strings(vars)
ret := []string{}
for _, v := range vars {
- ret = append(ret, s.vars[v].String())
+ if assignment, ok := s.vars[v]; ok {
+ ret = append(ret, assignment.String())
+ } else {
+ ret = append(ret, s.inheritedVars[v].String())
+ }
}
return strings.Join(ret, "\n")