Refactor blueprint parser nodes to an interface
Refactor the blueprint parser Value object, which contained a Type enum and members to hold every possible type, into an interface (now called Expression). Rename the existing Expression object that represented a binary operator Operator. Also adds and fixes some new printer test cases with mulitline expressions. Change-Id: Icf4a20f92c8c2a27f18df8ca515a9d7f282ff133
This commit is contained in:
parent
aedd4903a9
commit
e32cc80f20
13 changed files with 1251 additions and 922 deletions
|
@ -32,6 +32,7 @@ bootstrap_go_package(
|
|||
name = "blueprint-parser",
|
||||
pkgPath = "github.com/google/blueprint/parser",
|
||||
srcs = [
|
||||
"parser/ast.go",
|
||||
"parser/modify.go",
|
||||
"parser/parser.go",
|
||||
"parser/printer.go",
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/google/blueprint/parser"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
@ -17,6 +16,8 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/google/blueprint/parser"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -123,8 +124,8 @@ func findModules(file *parser.File) (modified bool, errs []error) {
|
|||
for _, def := range file.Defs {
|
||||
if module, ok := def.(*parser.Module); ok {
|
||||
for _, prop := range module.Properties {
|
||||
if prop.Name.Name == "name" && prop.Value.Type == parser.String {
|
||||
if targetedModule(prop.Value.StringValue) {
|
||||
if prop.Name.Name == "name" && prop.Value.Type() == parser.StringType {
|
||||
if targetedModule(prop.Value.Eval().(*parser.String).Value) {
|
||||
m, newErrs := processModule(module, prop.Name.Name, file)
|
||||
errs = append(errs, newErrs...)
|
||||
modified = modified || m
|
||||
|
@ -142,7 +143,7 @@ func processModule(module *parser.Module, moduleName string,
|
|||
|
||||
for _, prop := range module.Properties {
|
||||
if prop.Name.Name == *parameter {
|
||||
modified, errs = processParameter(&prop.Value, *parameter, moduleName, file)
|
||||
modified, errs = processParameter(prop.Value, *parameter, moduleName, file)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -150,37 +151,38 @@ func processModule(module *parser.Module, moduleName string,
|
|||
return false, nil
|
||||
}
|
||||
|
||||
func processParameter(value *parser.Value, paramName, moduleName string,
|
||||
func processParameter(value parser.Expression, paramName, moduleName string,
|
||||
file *parser.File) (modified bool, errs []error) {
|
||||
if value.Type != parser.List {
|
||||
return false, []error{fmt.Errorf("expected parameter %s in module %s to be list, found %s",
|
||||
paramName, moduleName, value.Type.String())}
|
||||
}
|
||||
|
||||
if value.Variable != "" {
|
||||
if _, ok := value.(*parser.Variable); ok {
|
||||
return false, []error{fmt.Errorf("parameter %s in module %s is a variable, unsupported",
|
||||
paramName, moduleName)}
|
||||
}
|
||||
|
||||
if value.Expression != nil {
|
||||
if _, ok := value.(*parser.Operator); ok {
|
||||
return false, []error{fmt.Errorf("parameter %s in module %s is an expression, unsupported",
|
||||
paramName, moduleName)}
|
||||
}
|
||||
|
||||
wasSorted := parser.ListIsSorted(*value)
|
||||
list, ok := value.(*parser.List)
|
||||
if !ok {
|
||||
return false, []error{fmt.Errorf("expected parameter %s in module %s to be list, found %s",
|
||||
paramName, moduleName, value.Type().String())}
|
||||
}
|
||||
|
||||
wasSorted := parser.ListIsSorted(list)
|
||||
|
||||
for _, a := range addIdents.idents {
|
||||
m := parser.AddStringToList(value, a)
|
||||
m := parser.AddStringToList(list, a)
|
||||
modified = modified || m
|
||||
}
|
||||
|
||||
for _, r := range removeIdents.idents {
|
||||
m := parser.RemoveStringFromList(value, r)
|
||||
m := parser.RemoveStringFromList(list, r)
|
||||
modified = modified || m
|
||||
}
|
||||
|
||||
if (wasSorted || *sortLists) && modified {
|
||||
parser.SortList(file, *value)
|
||||
parser.SortList(file, list)
|
||||
}
|
||||
|
||||
return modified, nil
|
||||
|
|
|
@ -81,7 +81,7 @@ default $
|
|||
# Variant:
|
||||
# Type: bootstrap_go_package
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
||||
# Defined: Blueprints:80:1
|
||||
# Defined: Blueprints:81:1
|
||||
|
||||
build $
|
||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a $
|
||||
|
@ -108,7 +108,7 @@ default $
|
|||
# Variant:
|
||||
# Type: bootstrap_go_package
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
||||
# Defined: Blueprints:99:1
|
||||
# Defined: Blueprints:100:1
|
||||
|
||||
build $
|
||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a $
|
||||
|
@ -128,7 +128,7 @@ default $
|
|||
# Variant:
|
||||
# Type: bootstrap_go_package
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
||||
# Defined: Blueprints:46:1
|
||||
# Defined: Blueprints:47:1
|
||||
|
||||
build $
|
||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
|
||||
|
@ -147,7 +147,8 @@ default $
|
|||
|
||||
build $
|
||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
|
||||
: g.bootstrap.compile ${g.bootstrap.srcDir}/parser/modify.go $
|
||||
: g.bootstrap.compile ${g.bootstrap.srcDir}/parser/ast.go $
|
||||
${g.bootstrap.srcDir}/parser/modify.go $
|
||||
${g.bootstrap.srcDir}/parser/parser.go $
|
||||
${g.bootstrap.srcDir}/parser/printer.go $
|
||||
${g.bootstrap.srcDir}/parser/sort.go | ${g.bootstrap.compileCmd}
|
||||
|
@ -160,7 +161,7 @@ default $
|
|||
# Variant:
|
||||
# Type: bootstrap_go_package
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
||||
# Defined: Blueprints:52:1
|
||||
# Defined: Blueprints:53:1
|
||||
|
||||
build $
|
||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
|
||||
|
@ -175,7 +176,7 @@ default $
|
|||
# Variant:
|
||||
# Type: bootstrap_go_package
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
||||
# Defined: Blueprints:64:1
|
||||
# Defined: Blueprints:65:1
|
||||
|
||||
build $
|
||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
|
||||
|
@ -193,7 +194,7 @@ default $
|
|||
# Variant:
|
||||
# Type: bootstrap_core_go_binary
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
|
||||
# Defined: Blueprints:142:1
|
||||
# Defined: Blueprints:143:1
|
||||
|
||||
build ${g.bootstrap.buildDir}/.bootstrap/choosestage/obj/choosestage.a: $
|
||||
g.bootstrap.compile ${g.bootstrap.srcDir}/choosestage/choosestage.go | $
|
||||
|
@ -216,7 +217,7 @@ default ${g.bootstrap.BinDir}/choosestage
|
|||
# Variant:
|
||||
# Type: bootstrap_core_go_binary
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
|
||||
# Defined: Blueprints:132:1
|
||||
# Defined: Blueprints:133:1
|
||||
|
||||
build ${g.bootstrap.buildDir}/.bootstrap/gotestmain/obj/gotestmain.a: $
|
||||
g.bootstrap.compile ${g.bootstrap.srcDir}/gotestmain/gotestmain.go | $
|
||||
|
@ -239,7 +240,7 @@ default ${g.bootstrap.BinDir}/gotestmain
|
|||
# Variant:
|
||||
# Type: bootstrap_core_go_binary
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
|
||||
# Defined: Blueprints:137:1
|
||||
# Defined: Blueprints:138:1
|
||||
|
||||
build ${g.bootstrap.buildDir}/.bootstrap/gotestrunner/obj/gotestrunner.a: $
|
||||
g.bootstrap.compile ${g.bootstrap.srcDir}/gotestrunner/gotestrunner.go $
|
||||
|
@ -262,7 +263,7 @@ default ${g.bootstrap.BinDir}/gotestrunner
|
|||
# Variant:
|
||||
# Type: bootstrap_core_go_binary
|
||||
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
|
||||
# Defined: Blueprints:111:1
|
||||
# Defined: Blueprints:112:1
|
||||
|
||||
build ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/minibp.a: $
|
||||
g.bootstrap.compile ${g.bootstrap.srcDir}/bootstrap/minibp/main.go | $
|
||||
|
|
23
context.go
23
context.go
|
@ -861,21 +861,22 @@ func getLocalStringListFromScope(scope *parser.Scope, v string) ([]string, scann
|
|||
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))
|
||||
switch value := assignment.Value.Eval().(type) {
|
||||
case *parser.List:
|
||||
ret := make([]string, 0, len(value.Values))
|
||||
|
||||
for _, value := range assignment.Value.ListValue {
|
||||
if value.Type != parser.String {
|
||||
for _, listValue := range value.Values {
|
||||
s, ok := listValue.(*parser.String)
|
||||
if !ok {
|
||||
// The parser should not produce this.
|
||||
panic("non-string value found in list")
|
||||
}
|
||||
|
||||
ret = append(ret, value.StringValue)
|
||||
ret = append(ret, s.Value)
|
||||
}
|
||||
|
||||
return ret, assignment.Pos, nil
|
||||
case parser.Bool, parser.String:
|
||||
case *parser.Bool, *parser.String:
|
||||
return nil, scanner.Position{}, &Error{
|
||||
Err: fmt.Errorf("%q must be a list of strings", v),
|
||||
Pos: assignment.Pos,
|
||||
|
@ -890,10 +891,10 @@ func getStringFromScope(scope *parser.Scope, v string) (string, scanner.Position
|
|||
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
|
||||
case parser.Bool, parser.List:
|
||||
switch value := assignment.Value.Eval().(type) {
|
||||
case *parser.String:
|
||||
return value.Value, assignment.Pos, nil
|
||||
case *parser.Bool, *parser.List:
|
||||
return "", scanner.Position{}, &Error{
|
||||
Err: fmt.Errorf("%q must be a string", v),
|
||||
Pos: assignment.Pos,
|
||||
|
|
374
parser/ast.go
Normal file
374
parser/ast.go
Normal file
|
@ -0,0 +1,374 @@
|
|||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"text/scanner"
|
||||
)
|
||||
|
||||
// Definition is an Assignment or a Module at the top level of a Blueprints file
|
||||
type Definition interface {
|
||||
String() string
|
||||
definitionTag()
|
||||
}
|
||||
|
||||
// An Assignment is a variable assignment at the top level of a Blueprints file, scoped to the
|
||||
// file and and subdirs.
|
||||
type Assignment struct {
|
||||
Name Ident
|
||||
Value Expression
|
||||
OrigValue Expression
|
||||
Pos scanner.Position
|
||||
Assigner string
|
||||
Referenced bool
|
||||
}
|
||||
|
||||
func (a *Assignment) String() string {
|
||||
return fmt.Sprintf("%s@%s %s %s (%s) %t", a.Name, a.Pos, a.Assigner, a.Value, a.OrigValue, a.Referenced)
|
||||
}
|
||||
|
||||
func (a *Assignment) definitionTag() {}
|
||||
|
||||
// A Module is a module definition at the top level of a Blueprints file
|
||||
type Module struct {
|
||||
Type Ident
|
||||
Map
|
||||
}
|
||||
|
||||
func (m *Module) Copy() *Module {
|
||||
ret := *m
|
||||
ret.Properties = make([]*Property, len(m.Properties))
|
||||
for i := range m.Properties {
|
||||
ret.Properties[i] = m.Properties[i].Copy()
|
||||
}
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (m *Module) String() string {
|
||||
propertyStrings := make([]string, len(m.Properties))
|
||||
for i, property := range m.Properties {
|
||||
propertyStrings[i] = property.String()
|
||||
}
|
||||
return fmt.Sprintf("%s@%s-%s{%s}", m.Type,
|
||||
m.LBracePos, m.RBracePos,
|
||||
strings.Join(propertyStrings, ", "))
|
||||
}
|
||||
|
||||
func (m *Module) definitionTag() {}
|
||||
|
||||
// A Property is a name: value pair within a Map, which may be a top level Module.
|
||||
type Property struct {
|
||||
Name Ident
|
||||
Value Expression
|
||||
Pos scanner.Position
|
||||
}
|
||||
|
||||
func (p *Property) Copy() *Property {
|
||||
ret := *p
|
||||
ret.Value = p.Value.Copy()
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (p *Property) String() string {
|
||||
return fmt.Sprintf("%s@%s: %s", p.Name, p.Pos, p.Value)
|
||||
}
|
||||
|
||||
// An Ident is a name identifier, the Type of a Module, the Name of a Property, or the Name of a
|
||||
// Variable.
|
||||
type Ident struct {
|
||||
Name string
|
||||
Pos scanner.Position
|
||||
}
|
||||
|
||||
func (i Ident) String() string {
|
||||
return fmt.Sprintf("%s@%s", i.Name, i.Pos)
|
||||
}
|
||||
|
||||
// An Expression is a Value in a Property or Assignment. It can be a literal (String or Bool), a
|
||||
// Map, a List, an Operator that combines two expressions of the same type, or a Variable that
|
||||
// references and Assignment.
|
||||
type Expression interface {
|
||||
// Copy returns a copy of the Expression that will not affect the original if mutated
|
||||
Copy() Expression
|
||||
String() string
|
||||
// Type returns the underlying Type enum of the Expression if it were to be evalutated
|
||||
Type() Type
|
||||
// Pos returns the position of the first token in the Expression
|
||||
Pos() scanner.Position
|
||||
// End returns the position of the beginning of the last token in the Expression
|
||||
End() scanner.Position
|
||||
// Eval returns an expression that is fully evaluated to a simple type (List, Map, String, or
|
||||
// Bool). It will return the same object for every call to Eval().
|
||||
Eval() Expression
|
||||
}
|
||||
|
||||
type Type int
|
||||
|
||||
const (
|
||||
BoolType Type = iota + 1
|
||||
StringType
|
||||
ListType
|
||||
MapType
|
||||
)
|
||||
|
||||
func (t Type) String() string {
|
||||
switch t {
|
||||
case BoolType:
|
||||
return "bool"
|
||||
case StringType:
|
||||
return "string"
|
||||
case ListType:
|
||||
return "list"
|
||||
case MapType:
|
||||
return "map"
|
||||
default:
|
||||
panic(fmt.Errorf("Unknown type %d", t))
|
||||
}
|
||||
}
|
||||
|
||||
type Operator struct {
|
||||
Args [2]Expression
|
||||
Operator rune
|
||||
OperatorPos scanner.Position
|
||||
Value Expression
|
||||
}
|
||||
|
||||
func (x *Operator) Copy() Expression {
|
||||
ret := *x
|
||||
ret.Args[0] = x.Args[0].Copy()
|
||||
ret.Args[1] = x.Args[1].Copy()
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (x *Operator) Eval() Expression {
|
||||
return x.Value.Eval()
|
||||
}
|
||||
|
||||
func (x *Operator) Type() Type {
|
||||
return x.Args[0].Type()
|
||||
}
|
||||
|
||||
func (x *Operator) Pos() scanner.Position { return x.Args[0].Pos() }
|
||||
func (x *Operator) End() scanner.Position { return x.Args[1].End() }
|
||||
|
||||
func (x *Operator) String() string {
|
||||
return fmt.Sprintf("(%s %c %s = %s)@%s", x.Args[0].String(), x.Operator, x.Args[1].String(),
|
||||
x.Value, x.OperatorPos)
|
||||
}
|
||||
|
||||
type Variable struct {
|
||||
Name string
|
||||
Value Expression
|
||||
NamePos scanner.Position
|
||||
}
|
||||
|
||||
func (x *Variable) Pos() scanner.Position { return x.NamePos }
|
||||
func (x *Variable) End() scanner.Position { return x.NamePos }
|
||||
|
||||
func (x *Variable) Copy() Expression {
|
||||
ret := *x
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (x *Variable) Eval() Expression {
|
||||
return x.Value.Eval()
|
||||
}
|
||||
|
||||
func (x *Variable) String() string {
|
||||
return x.Name + " = " + x.Value.String()
|
||||
}
|
||||
|
||||
func (x *Variable) Type() Type { return x.Value.Type() }
|
||||
|
||||
type Map struct {
|
||||
LBracePos scanner.Position
|
||||
RBracePos scanner.Position
|
||||
Properties []*Property
|
||||
}
|
||||
|
||||
func (x *Map) Pos() scanner.Position { return x.LBracePos }
|
||||
func (x *Map) End() scanner.Position { return x.RBracePos }
|
||||
|
||||
func (x *Map) Copy() Expression {
|
||||
ret := *x
|
||||
ret.Properties = make([]*Property, len(x.Properties))
|
||||
for i := range x.Properties {
|
||||
ret.Properties[i] = x.Properties[i].Copy()
|
||||
}
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (x *Map) Eval() Expression {
|
||||
return x
|
||||
}
|
||||
|
||||
func (x *Map) String() string {
|
||||
propertyStrings := make([]string, len(x.Properties))
|
||||
for i, property := range x.Properties {
|
||||
propertyStrings[i] = property.String()
|
||||
}
|
||||
return fmt.Sprintf("@%s-%s{%s}", x.LBracePos, x.RBracePos,
|
||||
strings.Join(propertyStrings, ", "))
|
||||
}
|
||||
|
||||
func (x *Map) Type() Type { return MapType }
|
||||
|
||||
type List struct {
|
||||
LBracePos scanner.Position
|
||||
RBracePos scanner.Position
|
||||
Values []Expression
|
||||
}
|
||||
|
||||
func (x *List) Pos() scanner.Position { return x.LBracePos }
|
||||
func (x *List) End() scanner.Position { return x.RBracePos }
|
||||
|
||||
func (x *List) Copy() Expression {
|
||||
ret := *x
|
||||
ret.Values = make([]Expression, len(x.Values))
|
||||
for i := range ret.Values {
|
||||
ret.Values[i] = x.Values[i].Copy()
|
||||
}
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (x *List) Eval() Expression {
|
||||
return x
|
||||
}
|
||||
|
||||
func (x *List) String() string {
|
||||
valueStrings := make([]string, len(x.Values))
|
||||
for i, value := range x.Values {
|
||||
valueStrings[i] = value.String()
|
||||
}
|
||||
return fmt.Sprintf("@%s-%s[%s]", x.LBracePos, x.RBracePos,
|
||||
strings.Join(valueStrings, ", "))
|
||||
}
|
||||
|
||||
func (x *List) Type() Type { return ListType }
|
||||
|
||||
type String struct {
|
||||
LiteralPos scanner.Position
|
||||
Value string
|
||||
}
|
||||
|
||||
func (x *String) Pos() scanner.Position { return x.LiteralPos }
|
||||
func (x *String) End() scanner.Position { return x.LiteralPos }
|
||||
|
||||
func (x *String) Copy() Expression {
|
||||
ret := *x
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (x *String) Eval() Expression {
|
||||
return x
|
||||
}
|
||||
|
||||
func (x *String) String() string {
|
||||
return fmt.Sprintf("%q@%s", x.Value, x.LiteralPos)
|
||||
}
|
||||
|
||||
func (x *String) Type() Type {
|
||||
return StringType
|
||||
}
|
||||
|
||||
type Bool struct {
|
||||
LiteralPos scanner.Position
|
||||
Value bool
|
||||
}
|
||||
|
||||
func (x *Bool) Pos() scanner.Position { return x.LiteralPos }
|
||||
func (x *Bool) End() scanner.Position { return x.LiteralPos }
|
||||
|
||||
func (x *Bool) Copy() Expression {
|
||||
ret := *x
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (x *Bool) Eval() Expression {
|
||||
return x
|
||||
}
|
||||
|
||||
func (x *Bool) String() string {
|
||||
return fmt.Sprintf("%t@%s", x.Value, x.LiteralPos)
|
||||
}
|
||||
|
||||
func (x *Bool) Type() Type {
|
||||
return BoolType
|
||||
}
|
||||
|
||||
type Comment struct {
|
||||
Comment []string
|
||||
Slash scanner.Position
|
||||
}
|
||||
|
||||
func (c Comment) Pos() scanner.Position {
|
||||
return c.Slash
|
||||
}
|
||||
|
||||
func (c Comment) End() scanner.Position {
|
||||
pos := c.Slash
|
||||
for _, comment := range c.Comment {
|
||||
pos.Offset += len(comment)
|
||||
}
|
||||
pos.Line += len(c.Comment) - 1
|
||||
return pos
|
||||
}
|
||||
|
||||
func (c Comment) String() string {
|
||||
l := 0
|
||||
for _, comment := range c.Comment {
|
||||
l += len(comment) + 1
|
||||
}
|
||||
buf := make([]byte, 0, l)
|
||||
for _, comment := range c.Comment {
|
||||
buf = append(buf, comment...)
|
||||
buf = append(buf, '\n')
|
||||
}
|
||||
|
||||
return string(buf) + "@" + c.Slash.String()
|
||||
}
|
||||
|
||||
// Return the text of the comment with // or /* and */ stripped
|
||||
func (c Comment) Text() string {
|
||||
l := 0
|
||||
for _, comment := range c.Comment {
|
||||
l += len(comment) + 1
|
||||
}
|
||||
buf := make([]byte, 0, l)
|
||||
|
||||
blockComment := false
|
||||
if strings.HasPrefix(c.Comment[0], "/*") {
|
||||
blockComment = true
|
||||
}
|
||||
|
||||
for i, comment := range c.Comment {
|
||||
if blockComment {
|
||||
if i == 0 {
|
||||
comment = strings.TrimPrefix(comment, "/*")
|
||||
}
|
||||
if i == len(c.Comment)-1 {
|
||||
comment = strings.TrimSuffix(comment, "*/")
|
||||
}
|
||||
} else {
|
||||
comment = strings.TrimPrefix(comment, "//")
|
||||
}
|
||||
buf = append(buf, comment...)
|
||||
buf = append(buf, '\n')
|
||||
}
|
||||
|
||||
return string(buf)
|
||||
}
|
|
@ -14,47 +14,38 @@
|
|||
|
||||
package parser
|
||||
|
||||
func AddStringToList(value *Value, s string) (modified bool) {
|
||||
if value.Type != List {
|
||||
panic("expected list value, got " + value.Type.String())
|
||||
}
|
||||
import "fmt"
|
||||
|
||||
for _, v := range value.ListValue {
|
||||
if v.Type != String {
|
||||
panic("expected string in list, got " + value.Type.String())
|
||||
func AddStringToList(list *List, s string) (modified bool) {
|
||||
for _, v := range list.Values {
|
||||
if v.Type() != StringType {
|
||||
panic(fmt.Errorf("expected string in list, got %s", v.Type()))
|
||||
}
|
||||
|
||||
if v.StringValue == s {
|
||||
if sv, ok := v.(*String); ok && sv.Value == s {
|
||||
// string already exists
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
value.ListValue = append(value.ListValue, Value{
|
||||
Type: String,
|
||||
Pos: value.EndPos,
|
||||
StringValue: s,
|
||||
list.Values = append(list.Values, &String{
|
||||
LiteralPos: list.RBracePos,
|
||||
Value: s,
|
||||
})
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func RemoveStringFromList(value *Value, s string) (modified bool) {
|
||||
if value.Type != List {
|
||||
panic("expected list value, got " + value.Type.String())
|
||||
}
|
||||
|
||||
for i, v := range value.ListValue {
|
||||
if v.Type != String {
|
||||
panic("expected string in list, got " + value.Type.String())
|
||||
func RemoveStringFromList(list *List, s string) (modified bool) {
|
||||
for i, v := range list.Values {
|
||||
if v.Type() != StringType {
|
||||
panic(fmt.Errorf("expected string in list, got %s", v.Type()))
|
||||
}
|
||||
|
||||
if v.StringValue == s {
|
||||
value.ListValue = append(value.ListValue[:i], value.ListValue[i+1:]...)
|
||||
if sv, ok := v.(*String); ok && sv.Value == s {
|
||||
list.Values = append(list.Values[:i], list.Values[i+1:]...)
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false
|
||||
|
|
402
parser/parser.go
402
parser/parser.go
|
@ -41,6 +41,7 @@ type File struct {
|
|||
Name string
|
||||
Defs []Definition
|
||||
Comments []Comment
|
||||
Lines []scanner.Position
|
||||
}
|
||||
|
||||
func parse(p *parser) (file *File, errs []error) {
|
||||
|
@ -178,8 +179,8 @@ func (p *parser) parseDefinitions() (defs []Definition) {
|
|||
}
|
||||
}
|
||||
|
||||
func (p *parser) parseAssignment(name string,
|
||||
namePos scanner.Position, assigner string) (assignment *Assignment) {
|
||||
func (p *parser) parseAssignment(name string, namePos scanner.Position,
|
||||
assigner string) (assignment *Assignment) {
|
||||
|
||||
assignment = new(Assignment)
|
||||
|
||||
|
@ -223,10 +224,8 @@ func (p *parser) parseAssignment(name string,
|
|||
return
|
||||
}
|
||||
|
||||
func (p *parser) parseModule(typ string,
|
||||
typPos scanner.Position) (module *Module) {
|
||||
func (p *parser) parseModule(typ string, typPos scanner.Position) *Module {
|
||||
|
||||
module = new(Module)
|
||||
compat := false
|
||||
lbracePos := p.scanner.Position
|
||||
if p.tok == '{' {
|
||||
|
@ -234,7 +233,7 @@ func (p *parser) parseModule(typ string,
|
|||
}
|
||||
|
||||
if !p.accept(p.tok) {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
properties := p.parsePropertyList(true, compat)
|
||||
rbracePos := p.scanner.Position
|
||||
|
@ -244,11 +243,14 @@ func (p *parser) parseModule(typ string,
|
|||
p.accept('}')
|
||||
}
|
||||
|
||||
module.Type = Ident{typ, typPos}
|
||||
module.Properties = properties
|
||||
module.LbracePos = lbracePos
|
||||
module.RbracePos = rbracePos
|
||||
return
|
||||
return &Module{
|
||||
Type: Ident{typ, typPos},
|
||||
Map: Map{
|
||||
Properties: properties,
|
||||
LBracePos: lbracePos,
|
||||
RBracePos: rbracePos,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) parsePropertyList(isModule, compat bool) (properties []*Property) {
|
||||
|
@ -298,7 +300,7 @@ func (p *parser) parseProperty(isModule, compat bool) (property *Property) {
|
|||
return
|
||||
}
|
||||
|
||||
func (p *parser) parseExpression() (value Value) {
|
||||
func (p *parser) parseExpression() (value Expression) {
|
||||
value = p.parseValue()
|
||||
switch p.tok {
|
||||
case '+':
|
||||
|
@ -308,50 +310,48 @@ func (p *parser) parseExpression() (value Value) {
|
|||
}
|
||||
}
|
||||
|
||||
func (p *parser) evaluateOperator(value1, value2 Value, operator rune,
|
||||
pos scanner.Position) (Value, error) {
|
||||
func (p *parser) evaluateOperator(value1, value2 Expression, operator rune,
|
||||
pos scanner.Position) (*Operator, error) {
|
||||
|
||||
value := Value{}
|
||||
value := value1
|
||||
|
||||
if p.eval {
|
||||
if value1.Type != value2.Type {
|
||||
return Value{}, fmt.Errorf("mismatched type in operator %c: %s != %s", operator,
|
||||
value1.Type, value2.Type)
|
||||
e1 := value1.Eval()
|
||||
e2 := value2.Eval()
|
||||
if e1.Type() != e2.Type() {
|
||||
return nil, fmt.Errorf("mismatched type in operator %c: %s != %s", operator,
|
||||
e1.Type(), e2.Type())
|
||||
}
|
||||
|
||||
value = value1
|
||||
value.Variable = ""
|
||||
value = e1.Copy()
|
||||
|
||||
switch operator {
|
||||
case '+':
|
||||
switch value1.Type {
|
||||
case String:
|
||||
value.StringValue = value1.StringValue + value2.StringValue
|
||||
case List:
|
||||
value.ListValue = append([]Value{}, value1.ListValue...)
|
||||
value.ListValue = append(value.ListValue, value2.ListValue...)
|
||||
case Map:
|
||||
switch v := value.(type) {
|
||||
case *String:
|
||||
v.Value += e2.(*String).Value
|
||||
case *List:
|
||||
v.Values = append(v.Values, e2.(*List).Values...)
|
||||
case *Map:
|
||||
var err error
|
||||
value.MapValue, err = p.addMaps(value.MapValue, value2.MapValue, pos)
|
||||
v.Properties, err = p.addMaps(v.Properties, e2.(*Map).Properties, pos)
|
||||
if err != nil {
|
||||
return Value{}, err
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return Value{}, fmt.Errorf("operator %c not supported on type %s", operator,
|
||||
value1.Type)
|
||||
return nil, fmt.Errorf("operator %c not supported on type %s", operator, v.Type())
|
||||
}
|
||||
default:
|
||||
panic("unknown operator " + string(operator))
|
||||
}
|
||||
}
|
||||
|
||||
value.Expression = &Expression{
|
||||
Args: [2]Value{value1, value2},
|
||||
Operator: operator,
|
||||
Pos: pos,
|
||||
}
|
||||
|
||||
return value, nil
|
||||
return &Operator{
|
||||
Args: [2]Expression{value1, value2},
|
||||
Operator: operator,
|
||||
OperatorPos: pos,
|
||||
Value: value,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *parser) addMaps(map1, map2 []*Property, pos scanner.Position) ([]*Property, error) {
|
||||
|
@ -395,7 +395,7 @@ func (p *parser) addMaps(map1, map2 []*Property, pos scanner.Position) ([]*Prope
|
|||
return ret, nil
|
||||
}
|
||||
|
||||
func (p *parser) parseOperator(value1 Value) Value {
|
||||
func (p *parser) parseOperator(value1 Expression) *Operator {
|
||||
operator := p.tok
|
||||
pos := p.scanner.Position
|
||||
p.accept(operator)
|
||||
|
@ -405,13 +405,14 @@ func (p *parser) parseOperator(value1 Value) Value {
|
|||
value, err := p.evaluateOperator(value1, value2, operator, pos)
|
||||
if err != nil {
|
||||
p.error(err)
|
||||
return Value{}
|
||||
return nil
|
||||
}
|
||||
|
||||
return value
|
||||
|
||||
}
|
||||
|
||||
func (p *parser) parseValue() (value Value) {
|
||||
func (p *parser) parseValue() (value Expression) {
|
||||
switch p.tok {
|
||||
case scanner.Ident:
|
||||
return p.parseVariable()
|
||||
|
@ -428,19 +429,19 @@ func (p *parser) parseValue() (value Value) {
|
|||
}
|
||||
}
|
||||
|
||||
func (p *parser) parseVariable() (value Value) {
|
||||
func (p *parser) parseVariable() Expression {
|
||||
var value Expression
|
||||
|
||||
switch text := p.scanner.TokenText(); text {
|
||||
case "true":
|
||||
value.Type = Bool
|
||||
value.BoolValue = true
|
||||
case "false":
|
||||
value.Type = Bool
|
||||
value.BoolValue = false
|
||||
case "true", "false":
|
||||
value = &Bool{
|
||||
LiteralPos: p.scanner.Position,
|
||||
Value: text == "true",
|
||||
}
|
||||
default:
|
||||
variable := p.scanner.TokenText()
|
||||
if p.eval {
|
||||
if assignment, local := p.scope.Get(variable); assignment == nil {
|
||||
p.errorf("variable %q is not set", variable)
|
||||
if assignment, local := p.scope.Get(text); assignment == nil {
|
||||
p.errorf("variable %q is not set", text)
|
||||
} else {
|
||||
if local {
|
||||
assignment.Referenced = true
|
||||
|
@ -448,40 +449,44 @@ func (p *parser) parseVariable() (value Value) {
|
|||
value = assignment.Value
|
||||
}
|
||||
}
|
||||
value.Variable = variable
|
||||
value = &Variable{
|
||||
Name: text,
|
||||
NamePos: p.scanner.Position,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
value.Pos = p.scanner.Position
|
||||
|
||||
p.accept(scanner.Ident)
|
||||
return
|
||||
return value
|
||||
}
|
||||
|
||||
func (p *parser) parseStringValue() (value Value) {
|
||||
value.Type = String
|
||||
value.Pos = p.scanner.Position
|
||||
func (p *parser) parseStringValue() *String {
|
||||
str, err := strconv.Unquote(p.scanner.TokenText())
|
||||
if err != nil {
|
||||
p.errorf("couldn't parse string: %s", err)
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
value := &String{
|
||||
LiteralPos: p.scanner.Position,
|
||||
Value: str,
|
||||
}
|
||||
value.StringValue = str
|
||||
p.accept(scanner.String)
|
||||
return
|
||||
return value
|
||||
}
|
||||
|
||||
func (p *parser) parseListValue() (value Value) {
|
||||
value.Type = List
|
||||
value.Pos = p.scanner.Position
|
||||
func (p *parser) parseListValue() *List {
|
||||
lBracePos := p.scanner.Position
|
||||
if !p.accept('[') {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
var elements []Value
|
||||
var elements []Expression
|
||||
for p.tok != ']' {
|
||||
element := p.parseExpression()
|
||||
if p.eval && element.Type != String {
|
||||
p.errorf("Expected string in list, found %s", element.String())
|
||||
return
|
||||
if p.eval && element.Type() != StringType {
|
||||
p.errorf("Expected string in list, found %s", element.Type().String())
|
||||
return nil
|
||||
}
|
||||
elements = append(elements, element)
|
||||
|
||||
|
@ -493,210 +498,34 @@ func (p *parser) parseListValue() (value Value) {
|
|||
p.accept(',')
|
||||
}
|
||||
|
||||
value.ListValue = elements
|
||||
value.EndPos = p.scanner.Position
|
||||
|
||||
rBracePos := p.scanner.Position
|
||||
p.accept(']')
|
||||
return
|
||||
|
||||
return &List{
|
||||
LBracePos: lBracePos,
|
||||
RBracePos: rBracePos,
|
||||
Values: elements,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parser) parseMapValue() (value Value) {
|
||||
value.Type = Map
|
||||
value.Pos = p.scanner.Position
|
||||
func (p *parser) parseMapValue() *Map {
|
||||
lBracePos := p.scanner.Position
|
||||
if !p.accept('{') {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
|
||||
properties := p.parsePropertyList(false, false)
|
||||
value.MapValue = properties
|
||||
|
||||
value.EndPos = p.scanner.Position
|
||||
rBracePos := p.scanner.Position
|
||||
p.accept('}')
|
||||
return
|
||||
}
|
||||
|
||||
type Expression struct {
|
||||
Args [2]Value
|
||||
Operator rune
|
||||
Pos scanner.Position
|
||||
}
|
||||
|
||||
func (e *Expression) Copy() *Expression {
|
||||
ret := *e
|
||||
ret.Args[0] = e.Args[0].Copy()
|
||||
ret.Args[1] = e.Args[1].Copy()
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (e *Expression) String() string {
|
||||
return fmt.Sprintf("(%s %c %s)@%d:%s", e.Args[0].String(), e.Operator, e.Args[1].String(),
|
||||
e.Pos.Offset, e.Pos)
|
||||
}
|
||||
|
||||
type ValueType int
|
||||
|
||||
const (
|
||||
Bool ValueType = iota
|
||||
String
|
||||
List
|
||||
Map
|
||||
)
|
||||
|
||||
func (p ValueType) String() string {
|
||||
switch p {
|
||||
case Bool:
|
||||
return "bool"
|
||||
case String:
|
||||
return "string"
|
||||
case List:
|
||||
return "list"
|
||||
case Map:
|
||||
return "map"
|
||||
default:
|
||||
panic(fmt.Errorf("unknown value type: %d", p))
|
||||
return &Map{
|
||||
LBracePos: lBracePos,
|
||||
RBracePos: rBracePos,
|
||||
Properties: properties,
|
||||
}
|
||||
}
|
||||
|
||||
type Definition interface {
|
||||
String() string
|
||||
definitionTag()
|
||||
}
|
||||
|
||||
type Assignment struct {
|
||||
Name Ident
|
||||
Value Value
|
||||
OrigValue Value
|
||||
Pos scanner.Position
|
||||
Assigner string
|
||||
Referenced bool
|
||||
}
|
||||
|
||||
func (a *Assignment) String() string {
|
||||
return fmt.Sprintf("%s@%d:%s %s %s", a.Name, a.Pos.Offset, a.Pos, a.Assigner, a.Value)
|
||||
}
|
||||
|
||||
func (a *Assignment) definitionTag() {}
|
||||
|
||||
type Module struct {
|
||||
Type Ident
|
||||
Properties []*Property
|
||||
LbracePos scanner.Position
|
||||
RbracePos scanner.Position
|
||||
}
|
||||
|
||||
func (m *Module) Copy() *Module {
|
||||
ret := *m
|
||||
ret.Properties = make([]*Property, len(m.Properties))
|
||||
for i := range m.Properties {
|
||||
ret.Properties[i] = m.Properties[i].Copy()
|
||||
}
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (m *Module) String() string {
|
||||
propertyStrings := make([]string, len(m.Properties))
|
||||
for i, property := range m.Properties {
|
||||
propertyStrings[i] = property.String()
|
||||
}
|
||||
return fmt.Sprintf("%s@%d:%s-%d:%s{%s}", m.Type,
|
||||
m.LbracePos.Offset, m.LbracePos,
|
||||
m.RbracePos.Offset, m.RbracePos,
|
||||
strings.Join(propertyStrings, ", "))
|
||||
}
|
||||
|
||||
func (m *Module) definitionTag() {}
|
||||
|
||||
type Property struct {
|
||||
Name Ident
|
||||
Value Value
|
||||
Pos scanner.Position
|
||||
}
|
||||
|
||||
func (p *Property) Copy() *Property {
|
||||
ret := *p
|
||||
ret.Value = p.Value.Copy()
|
||||
return &ret
|
||||
}
|
||||
|
||||
func (p *Property) String() string {
|
||||
return fmt.Sprintf("%s@%d:%s: %s", p.Name, p.Pos.Offset, p.Pos, p.Value)
|
||||
}
|
||||
|
||||
type Ident struct {
|
||||
Name string
|
||||
Pos scanner.Position
|
||||
}
|
||||
|
||||
func (i Ident) String() string {
|
||||
return fmt.Sprintf("%s@%d:%s", i.Name, i.Pos.Offset, i.Pos)
|
||||
}
|
||||
|
||||
type Value struct {
|
||||
Type ValueType
|
||||
BoolValue bool
|
||||
StringValue string
|
||||
ListValue []Value
|
||||
MapValue []*Property
|
||||
Expression *Expression
|
||||
Variable string
|
||||
Pos scanner.Position
|
||||
EndPos scanner.Position
|
||||
}
|
||||
|
||||
func (p Value) Copy() Value {
|
||||
ret := p
|
||||
if p.MapValue != nil {
|
||||
ret.MapValue = make([]*Property, len(p.MapValue))
|
||||
for i := range p.MapValue {
|
||||
ret.MapValue[i] = p.MapValue[i].Copy()
|
||||
}
|
||||
}
|
||||
if p.ListValue != nil {
|
||||
ret.ListValue = make([]Value, len(p.ListValue))
|
||||
for i := range p.ListValue {
|
||||
ret.ListValue[i] = p.ListValue[i].Copy()
|
||||
}
|
||||
}
|
||||
if p.Expression != nil {
|
||||
ret.Expression = p.Expression.Copy()
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (p Value) String() string {
|
||||
var s string
|
||||
if p.Variable != "" {
|
||||
s += p.Variable + " = "
|
||||
}
|
||||
if p.Expression != nil {
|
||||
s += p.Expression.String()
|
||||
}
|
||||
switch p.Type {
|
||||
case Bool:
|
||||
s += fmt.Sprintf("%t@%d:%s", p.BoolValue, p.Pos.Offset, p.Pos)
|
||||
case String:
|
||||
s += fmt.Sprintf("%q@%d:%s", p.StringValue, p.Pos.Offset, p.Pos)
|
||||
case List:
|
||||
valueStrings := make([]string, len(p.ListValue))
|
||||
for i, value := range p.ListValue {
|
||||
valueStrings[i] = value.String()
|
||||
}
|
||||
s += fmt.Sprintf("@%d:%s-%d:%s[%s]", p.Pos.Offset, p.Pos, p.EndPos.Offset, p.EndPos,
|
||||
strings.Join(valueStrings, ", "))
|
||||
case Map:
|
||||
propertyStrings := make([]string, len(p.MapValue))
|
||||
for i, property := range p.MapValue {
|
||||
propertyStrings[i] = property.String()
|
||||
}
|
||||
s += fmt.Sprintf("@%d:%s-%d:%s{%s}", p.Pos.Offset, p.Pos, p.EndPos.Offset, p.EndPos,
|
||||
strings.Join(propertyStrings, ", "))
|
||||
default:
|
||||
panic(fmt.Errorf("bad property type: %d", p.Type))
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
type Scope struct {
|
||||
vars map[string]*Assignment
|
||||
inheritedVars map[string]*Assignment
|
||||
|
@ -774,58 +603,3 @@ func (s *Scope) String() string {
|
|||
|
||||
return strings.Join(ret, "\n")
|
||||
}
|
||||
|
||||
type Comment struct {
|
||||
Comment []string
|
||||
Pos scanner.Position
|
||||
}
|
||||
|
||||
func (c Comment) String() string {
|
||||
l := 0
|
||||
for _, comment := range c.Comment {
|
||||
l += len(comment) + 1
|
||||
}
|
||||
buf := make([]byte, 0, l)
|
||||
for _, comment := range c.Comment {
|
||||
buf = append(buf, comment...)
|
||||
buf = append(buf, '\n')
|
||||
}
|
||||
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
// Return the text of the comment with // or /* and */ stripped
|
||||
func (c Comment) Text() string {
|
||||
l := 0
|
||||
for _, comment := range c.Comment {
|
||||
l += len(comment) + 1
|
||||
}
|
||||
buf := make([]byte, 0, l)
|
||||
|
||||
blockComment := false
|
||||
if strings.HasPrefix(c.Comment[0], "/*") {
|
||||
blockComment = true
|
||||
}
|
||||
|
||||
for i, comment := range c.Comment {
|
||||
if blockComment {
|
||||
if i == 0 {
|
||||
comment = strings.TrimPrefix(comment, "/*")
|
||||
}
|
||||
if i == len(c.Comment)-1 {
|
||||
comment = strings.TrimSuffix(comment, "*/")
|
||||
}
|
||||
} else {
|
||||
comment = strings.TrimPrefix(comment, "//")
|
||||
}
|
||||
buf = append(buf, comment...)
|
||||
buf = append(buf, '\n')
|
||||
}
|
||||
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
// Return the line number that the comment ends on
|
||||
func (c Comment) EndLine() int {
|
||||
return c.Pos.Line + len(c.Comment) - 1
|
||||
}
|
||||
|
|
|
@ -39,9 +39,11 @@ var validParseTestCases = []struct {
|
|||
`,
|
||||
[]Definition{
|
||||
&Module{
|
||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||
LbracePos: mkpos(7, 2, 7),
|
||||
RbracePos: mkpos(8, 2, 8),
|
||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||
Map: Map{
|
||||
LBracePos: mkpos(7, 2, 7),
|
||||
RBracePos: mkpos(8, 2, 8),
|
||||
},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
|
@ -54,17 +56,18 @@ var validParseTestCases = []struct {
|
|||
`,
|
||||
[]Definition{
|
||||
&Module{
|
||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||
LbracePos: mkpos(7, 2, 7),
|
||||
RbracePos: mkpos(27, 4, 3),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"name", mkpos(12, 3, 4)},
|
||||
Pos: mkpos(16, 3, 8),
|
||||
Value: Value{
|
||||
Type: String,
|
||||
Pos: mkpos(18, 3, 10),
|
||||
StringValue: "abc",
|
||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||
Map: Map{
|
||||
LBracePos: mkpos(7, 2, 7),
|
||||
RBracePos: mkpos(27, 4, 3),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"name", mkpos(12, 3, 4)},
|
||||
Pos: mkpos(16, 3, 8),
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(18, 3, 10),
|
||||
Value: "abc",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -80,17 +83,18 @@ var validParseTestCases = []struct {
|
|||
`,
|
||||
[]Definition{
|
||||
&Module{
|
||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||
LbracePos: mkpos(7, 2, 7),
|
||||
RbracePos: mkpos(28, 4, 3),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"isGood", mkpos(12, 3, 4)},
|
||||
Pos: mkpos(18, 3, 10),
|
||||
Value: Value{
|
||||
Type: Bool,
|
||||
Pos: mkpos(20, 3, 12),
|
||||
BoolValue: true,
|
||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||
Map: Map{
|
||||
LBracePos: mkpos(7, 2, 7),
|
||||
RBracePos: mkpos(28, 4, 3),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"isGood", mkpos(12, 3, 4)},
|
||||
Pos: mkpos(18, 3, 10),
|
||||
Value: &Bool{
|
||||
LiteralPos: mkpos(20, 3, 12),
|
||||
Value: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -107,42 +111,38 @@ var validParseTestCases = []struct {
|
|||
`,
|
||||
[]Definition{
|
||||
&Module{
|
||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||
LbracePos: mkpos(7, 2, 7),
|
||||
RbracePos: mkpos(67, 5, 3),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"stuff", mkpos(12, 3, 4)},
|
||||
Pos: mkpos(17, 3, 9),
|
||||
Value: Value{
|
||||
Type: List,
|
||||
Pos: mkpos(19, 3, 11),
|
||||
EndPos: mkpos(63, 4, 19),
|
||||
ListValue: []Value{
|
||||
Value{
|
||||
Type: String,
|
||||
Pos: mkpos(20, 3, 12),
|
||||
StringValue: "asdf",
|
||||
},
|
||||
Value{
|
||||
Type: String,
|
||||
Pos: mkpos(28, 3, 20),
|
||||
StringValue: "jkl;",
|
||||
},
|
||||
Value{
|
||||
Type: String,
|
||||
Pos: mkpos(36, 3, 28),
|
||||
StringValue: "qwert",
|
||||
},
|
||||
Value{
|
||||
Type: String,
|
||||
Pos: mkpos(49, 4, 5),
|
||||
StringValue: "uiop",
|
||||
},
|
||||
Value{
|
||||
Type: String,
|
||||
Pos: mkpos(57, 4, 13),
|
||||
StringValue: "bnm,",
|
||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||
Map: Map{
|
||||
LBracePos: mkpos(7, 2, 7),
|
||||
RBracePos: mkpos(67, 5, 3),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"stuff", mkpos(12, 3, 4)},
|
||||
Pos: mkpos(17, 3, 9),
|
||||
Value: &List{
|
||||
LBracePos: mkpos(19, 3, 11),
|
||||
RBracePos: mkpos(63, 4, 19),
|
||||
Values: []Expression{
|
||||
&String{
|
||||
LiteralPos: mkpos(20, 3, 12),
|
||||
Value: "asdf",
|
||||
},
|
||||
&String{
|
||||
LiteralPos: mkpos(28, 3, 20),
|
||||
Value: "jkl;",
|
||||
},
|
||||
&String{
|
||||
LiteralPos: mkpos(36, 3, 28),
|
||||
Value: "qwert",
|
||||
},
|
||||
&String{
|
||||
LiteralPos: mkpos(49, 4, 5),
|
||||
Value: "uiop",
|
||||
},
|
||||
&String{
|
||||
LiteralPos: mkpos(57, 4, 13),
|
||||
Value: "bnm,",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -163,34 +163,33 @@ var validParseTestCases = []struct {
|
|||
`,
|
||||
[]Definition{
|
||||
&Module{
|
||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||
LbracePos: mkpos(7, 2, 7),
|
||||
RbracePos: mkpos(62, 7, 3),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"stuff", mkpos(12, 3, 4)},
|
||||
Pos: mkpos(17, 3, 9),
|
||||
Value: Value{
|
||||
Type: Map,
|
||||
Pos: mkpos(19, 3, 11),
|
||||
EndPos: mkpos(58, 6, 4),
|
||||
MapValue: []*Property{
|
||||
{
|
||||
Name: Ident{"isGood", mkpos(25, 4, 5)},
|
||||
Pos: mkpos(31, 4, 11),
|
||||
Value: Value{
|
||||
Type: Bool,
|
||||
Pos: mkpos(33, 4, 13),
|
||||
BoolValue: true,
|
||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||
Map: Map{
|
||||
LBracePos: mkpos(7, 2, 7),
|
||||
RBracePos: mkpos(62, 7, 3),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"stuff", mkpos(12, 3, 4)},
|
||||
Pos: mkpos(17, 3, 9),
|
||||
Value: &Map{
|
||||
LBracePos: mkpos(19, 3, 11),
|
||||
RBracePos: mkpos(58, 6, 4),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"isGood", mkpos(25, 4, 5)},
|
||||
Pos: mkpos(31, 4, 11),
|
||||
Value: &Bool{
|
||||
LiteralPos: mkpos(33, 4, 13),
|
||||
Value: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: Ident{"name", mkpos(43, 5, 5)},
|
||||
Pos: mkpos(47, 5, 9),
|
||||
Value: Value{
|
||||
Type: String,
|
||||
Pos: mkpos(49, 5, 11),
|
||||
StringValue: "bar",
|
||||
{
|
||||
Name: Ident{"name", mkpos(43, 5, 5)},
|
||||
Pos: mkpos(47, 5, 9),
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(49, 5, 11),
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -204,24 +203,25 @@ var validParseTestCases = []struct {
|
|||
|
||||
{`
|
||||
// comment1
|
||||
foo {
|
||||
foo /* test */ {
|
||||
// comment2
|
||||
isGood: true, // comment3
|
||||
}
|
||||
`,
|
||||
[]Definition{
|
||||
&Module{
|
||||
Type: Ident{"foo", mkpos(17, 3, 3)},
|
||||
LbracePos: mkpos(21, 3, 7),
|
||||
RbracePos: mkpos(70, 6, 3),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"isGood", mkpos(41, 5, 4)},
|
||||
Pos: mkpos(47, 5, 10),
|
||||
Value: Value{
|
||||
Type: Bool,
|
||||
Pos: mkpos(49, 5, 12),
|
||||
BoolValue: true,
|
||||
Type: Ident{"foo", mkpos(17, 3, 3)},
|
||||
Map: Map{
|
||||
LBracePos: mkpos(32, 3, 18),
|
||||
RBracePos: mkpos(81, 6, 3),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"isGood", mkpos(52, 5, 4)},
|
||||
Pos: mkpos(58, 5, 10),
|
||||
Value: &Bool{
|
||||
LiteralPos: mkpos(60, 5, 12),
|
||||
Value: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -230,15 +230,19 @@ var validParseTestCases = []struct {
|
|||
[]Comment{
|
||||
Comment{
|
||||
Comment: []string{"// comment1"},
|
||||
Pos: mkpos(3, 2, 3),
|
||||
Slash: mkpos(3, 2, 3),
|
||||
},
|
||||
Comment{
|
||||
Comment: []string{"/* test */"},
|
||||
Slash: mkpos(21, 3, 7),
|
||||
},
|
||||
Comment{
|
||||
Comment: []string{"// comment2"},
|
||||
Pos: mkpos(26, 4, 4),
|
||||
Slash: mkpos(37, 4, 4),
|
||||
},
|
||||
Comment{
|
||||
Comment: []string{"// comment3"},
|
||||
Pos: mkpos(56, 5, 19),
|
||||
Slash: mkpos(67, 5, 19),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -254,33 +258,35 @@ var validParseTestCases = []struct {
|
|||
`,
|
||||
[]Definition{
|
||||
&Module{
|
||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||
LbracePos: mkpos(7, 2, 7),
|
||||
RbracePos: mkpos(27, 4, 3),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"name", mkpos(12, 3, 4)},
|
||||
Pos: mkpos(16, 3, 8),
|
||||
Value: Value{
|
||||
Type: String,
|
||||
Pos: mkpos(18, 3, 10),
|
||||
StringValue: "abc",
|
||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||
Map: Map{
|
||||
LBracePos: mkpos(7, 2, 7),
|
||||
RBracePos: mkpos(27, 4, 3),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"name", mkpos(12, 3, 4)},
|
||||
Pos: mkpos(16, 3, 8),
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(18, 3, 10),
|
||||
Value: "abc",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&Module{
|
||||
Type: Ident{"bar", mkpos(32, 6, 3)},
|
||||
LbracePos: mkpos(36, 6, 7),
|
||||
RbracePos: mkpos(56, 8, 3),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"name", mkpos(41, 7, 4)},
|
||||
Pos: mkpos(45, 7, 8),
|
||||
Value: Value{
|
||||
Type: String,
|
||||
Pos: mkpos(47, 7, 10),
|
||||
StringValue: "def",
|
||||
Type: Ident{"bar", mkpos(32, 6, 3)},
|
||||
Map: Map{
|
||||
LBracePos: mkpos(36, 6, 7),
|
||||
RBracePos: mkpos(56, 8, 3),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"name", mkpos(41, 7, 4)},
|
||||
Pos: mkpos(45, 7, 8),
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(47, 7, 10),
|
||||
Value: "def",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -299,15 +305,13 @@ var validParseTestCases = []struct {
|
|||
&Assignment{
|
||||
Name: Ident{"foo", mkpos(3, 2, 3)},
|
||||
Pos: mkpos(7, 2, 7),
|
||||
Value: Value{
|
||||
Type: String,
|
||||
Pos: mkpos(9, 2, 9),
|
||||
StringValue: "stuff",
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(9, 2, 9),
|
||||
Value: "stuff",
|
||||
},
|
||||
OrigValue: Value{
|
||||
Type: String,
|
||||
Pos: mkpos(9, 2, 9),
|
||||
StringValue: "stuff",
|
||||
OrigValue: &String{
|
||||
LiteralPos: mkpos(9, 2, 9),
|
||||
Value: "stuff",
|
||||
},
|
||||
Assigner: "=",
|
||||
Referenced: true,
|
||||
|
@ -315,17 +319,21 @@ var validParseTestCases = []struct {
|
|||
&Assignment{
|
||||
Name: Ident{"bar", mkpos(19, 3, 3)},
|
||||
Pos: mkpos(23, 3, 7),
|
||||
Value: Value{
|
||||
Type: String,
|
||||
Pos: mkpos(25, 3, 9),
|
||||
StringValue: "stuff",
|
||||
Variable: "foo",
|
||||
Value: &Variable{
|
||||
Name: "foo",
|
||||
NamePos: mkpos(25, 3, 9),
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(9, 2, 9),
|
||||
Value: "stuff",
|
||||
},
|
||||
},
|
||||
OrigValue: Value{
|
||||
Type: String,
|
||||
Pos: mkpos(25, 3, 9),
|
||||
StringValue: "stuff",
|
||||
Variable: "foo",
|
||||
OrigValue: &Variable{
|
||||
Name: "foo",
|
||||
NamePos: mkpos(25, 3, 9),
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(9, 2, 9),
|
||||
Value: "stuff",
|
||||
},
|
||||
},
|
||||
Assigner: "=",
|
||||
Referenced: true,
|
||||
|
@ -333,50 +341,64 @@ var validParseTestCases = []struct {
|
|||
&Assignment{
|
||||
Name: Ident{"baz", mkpos(31, 4, 3)},
|
||||
Pos: mkpos(35, 4, 7),
|
||||
Value: Value{
|
||||
Type: String,
|
||||
Pos: mkpos(37, 4, 9),
|
||||
StringValue: "stuffstuff",
|
||||
Expression: &Expression{
|
||||
Args: [2]Value{
|
||||
{
|
||||
Type: String,
|
||||
Pos: mkpos(37, 4, 9),
|
||||
StringValue: "stuff",
|
||||
Variable: "foo",
|
||||
},
|
||||
{
|
||||
Type: String,
|
||||
Pos: mkpos(43, 4, 15),
|
||||
StringValue: "stuff",
|
||||
Variable: "bar",
|
||||
Value: &Operator{
|
||||
OperatorPos: mkpos(41, 4, 13),
|
||||
Operator: '+',
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(9, 2, 9),
|
||||
Value: "stuffstuff",
|
||||
},
|
||||
Args: [2]Expression{
|
||||
&Variable{
|
||||
Name: "foo",
|
||||
NamePos: mkpos(37, 4, 9),
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(9, 2, 9),
|
||||
Value: "stuff",
|
||||
},
|
||||
},
|
||||
&Variable{
|
||||
Name: "bar",
|
||||
NamePos: mkpos(43, 4, 15),
|
||||
Value: &Variable{
|
||||
Name: "foo",
|
||||
NamePos: mkpos(25, 3, 9),
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(9, 2, 9),
|
||||
Value: "stuff",
|
||||
},
|
||||
},
|
||||
},
|
||||
Operator: '+',
|
||||
Pos: mkpos(41, 4, 13),
|
||||
},
|
||||
},
|
||||
OrigValue: Value{
|
||||
Type: String,
|
||||
Pos: mkpos(37, 4, 9),
|
||||
StringValue: "stuffstuff",
|
||||
Expression: &Expression{
|
||||
Args: [2]Value{
|
||||
{
|
||||
Type: String,
|
||||
Pos: mkpos(37, 4, 9),
|
||||
StringValue: "stuff",
|
||||
Variable: "foo",
|
||||
},
|
||||
{
|
||||
Type: String,
|
||||
Pos: mkpos(43, 4, 15),
|
||||
StringValue: "stuff",
|
||||
Variable: "bar",
|
||||
OrigValue: &Operator{
|
||||
OperatorPos: mkpos(41, 4, 13),
|
||||
Operator: '+',
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(9, 2, 9),
|
||||
Value: "stuffstuff",
|
||||
},
|
||||
Args: [2]Expression{
|
||||
&Variable{
|
||||
Name: "foo",
|
||||
NamePos: mkpos(37, 4, 9),
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(9, 2, 9),
|
||||
Value: "stuff",
|
||||
},
|
||||
},
|
||||
&Variable{
|
||||
Name: "bar",
|
||||
NamePos: mkpos(43, 4, 15),
|
||||
Value: &Variable{
|
||||
Name: "foo",
|
||||
NamePos: mkpos(25, 3, 9),
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(9, 2, 9),
|
||||
Value: "stuff",
|
||||
},
|
||||
},
|
||||
},
|
||||
Operator: '+',
|
||||
Pos: mkpos(41, 4, 13),
|
||||
},
|
||||
},
|
||||
Assigner: "=",
|
||||
|
@ -385,69 +407,90 @@ var validParseTestCases = []struct {
|
|||
&Assignment{
|
||||
Name: Ident{"boo", mkpos(49, 5, 3)},
|
||||
Pos: mkpos(53, 5, 7),
|
||||
Value: Value{
|
||||
Type: String,
|
||||
Pos: mkpos(55, 5, 9),
|
||||
StringValue: "stuffstuffstuff",
|
||||
Expression: &Expression{
|
||||
Args: [2]Value{
|
||||
{
|
||||
Type: String,
|
||||
Pos: mkpos(55, 5, 9),
|
||||
StringValue: "stuffstuff",
|
||||
Variable: "baz",
|
||||
Expression: &Expression{
|
||||
Args: [2]Value{
|
||||
{
|
||||
Type: String,
|
||||
Pos: mkpos(37, 4, 9),
|
||||
StringValue: "stuff",
|
||||
Variable: "foo",
|
||||
},
|
||||
{
|
||||
Type: String,
|
||||
Pos: mkpos(43, 4, 15),
|
||||
StringValue: "stuff",
|
||||
Variable: "bar",
|
||||
Value: &Operator{
|
||||
Args: [2]Expression{
|
||||
&Variable{
|
||||
Name: "baz",
|
||||
NamePos: mkpos(55, 5, 9),
|
||||
Value: &Operator{
|
||||
OperatorPos: mkpos(41, 4, 13),
|
||||
Operator: '+',
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(9, 2, 9),
|
||||
Value: "stuffstuff",
|
||||
},
|
||||
Args: [2]Expression{
|
||||
&Variable{
|
||||
Name: "foo",
|
||||
NamePos: mkpos(37, 4, 9),
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(9, 2, 9),
|
||||
Value: "stuff",
|
||||
},
|
||||
},
|
||||
&Variable{
|
||||
Name: "bar",
|
||||
NamePos: mkpos(43, 4, 15),
|
||||
Value: &Variable{
|
||||
Name: "foo",
|
||||
NamePos: mkpos(25, 3, 9),
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(9, 2, 9),
|
||||
Value: "stuff",
|
||||
},
|
||||
},
|
||||
},
|
||||
Operator: '+',
|
||||
Pos: mkpos(41, 4, 13),
|
||||
},
|
||||
},
|
||||
{
|
||||
Variable: "foo",
|
||||
Type: String,
|
||||
Pos: mkpos(68, 6, 10),
|
||||
StringValue: "stuff",
|
||||
},
|
||||
&Variable{
|
||||
Name: "foo",
|
||||
NamePos: mkpos(68, 6, 10),
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(9, 2, 9),
|
||||
Value: "stuff",
|
||||
},
|
||||
},
|
||||
Pos: mkpos(66, 6, 8),
|
||||
Operator: '+',
|
||||
},
|
||||
OperatorPos: mkpos(66, 6, 8),
|
||||
Operator: '+',
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(9, 2, 9),
|
||||
Value: "stuffstuffstuff",
|
||||
},
|
||||
},
|
||||
OrigValue: Value{
|
||||
Type: String,
|
||||
Pos: mkpos(55, 5, 9),
|
||||
StringValue: "stuffstuff",
|
||||
Variable: "baz",
|
||||
Expression: &Expression{
|
||||
Args: [2]Value{
|
||||
{
|
||||
Type: String,
|
||||
Pos: mkpos(37, 4, 9),
|
||||
StringValue: "stuff",
|
||||
Variable: "foo",
|
||||
OrigValue: &Variable{
|
||||
Name: "baz",
|
||||
NamePos: mkpos(55, 5, 9),
|
||||
Value: &Operator{
|
||||
OperatorPos: mkpos(41, 4, 13),
|
||||
Operator: '+',
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(9, 2, 9),
|
||||
Value: "stuffstuff",
|
||||
},
|
||||
Args: [2]Expression{
|
||||
&Variable{
|
||||
Name: "foo",
|
||||
NamePos: mkpos(37, 4, 9),
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(9, 2, 9),
|
||||
Value: "stuff",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: String,
|
||||
Pos: mkpos(43, 4, 15),
|
||||
StringValue: "stuff",
|
||||
Variable: "bar",
|
||||
&Variable{
|
||||
Name: "bar",
|
||||
NamePos: mkpos(43, 4, 15),
|
||||
Value: &Variable{
|
||||
Name: "foo",
|
||||
NamePos: mkpos(25, 3, 9),
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(9, 2, 9),
|
||||
Value: "stuff",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Operator: '+',
|
||||
Pos: mkpos(41, 4, 13),
|
||||
},
|
||||
},
|
||||
Assigner: "=",
|
||||
|
@ -455,17 +498,21 @@ var validParseTestCases = []struct {
|
|||
&Assignment{
|
||||
Name: Ident{"boo", mkpos(61, 6, 3)},
|
||||
Pos: mkpos(66, 6, 8),
|
||||
Value: Value{
|
||||
Type: String,
|
||||
Pos: mkpos(68, 6, 10),
|
||||
StringValue: "stuff",
|
||||
Variable: "foo",
|
||||
Value: &Variable{
|
||||
Name: "foo",
|
||||
NamePos: mkpos(68, 6, 10),
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(9, 2, 9),
|
||||
Value: "stuff",
|
||||
},
|
||||
},
|
||||
OrigValue: Value{
|
||||
Type: String,
|
||||
Pos: mkpos(68, 6, 10),
|
||||
StringValue: "stuff",
|
||||
Variable: "foo",
|
||||
OrigValue: &Variable{
|
||||
Name: "foo",
|
||||
NamePos: mkpos(68, 6, 10),
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(9, 2, 9),
|
||||
Value: "stuff",
|
||||
},
|
||||
},
|
||||
Assigner: "+=",
|
||||
},
|
||||
|
@ -504,7 +551,7 @@ func TestParseValidInput(t *testing.T) {
|
|||
|
||||
if len(file.Comments) == len(testCase.comments) {
|
||||
for i := range file.Comments {
|
||||
if !reflect.DeepEqual(file.Comments, testCase.comments) {
|
||||
if !reflect.DeepEqual(file.Comments[i], testCase.comments[i]) {
|
||||
t.Errorf("test case: %s", testCase.input)
|
||||
t.Errorf("incorrect comment %d:", i)
|
||||
t.Errorf(" expected: %s", testCase.comments[i])
|
||||
|
|
|
@ -22,7 +22,7 @@ import (
|
|||
"unicode"
|
||||
)
|
||||
|
||||
var noPos = scanner.Position{}
|
||||
var noPos scanner.Position
|
||||
|
||||
type printer struct {
|
||||
defs []Definition
|
||||
|
@ -91,96 +91,94 @@ func (p *printer) printAssignment(assignment *Assignment) {
|
|||
p.requestSpace()
|
||||
p.printToken(assignment.Assigner, assignment.Pos)
|
||||
p.requestSpace()
|
||||
p.printValue(assignment.OrigValue)
|
||||
p.printExpression(assignment.OrigValue)
|
||||
p.requestNewline()
|
||||
}
|
||||
|
||||
func (p *printer) printModule(module *Module) {
|
||||
p.printToken(module.Type.Name, module.Type.Pos)
|
||||
p.printMap(module.Properties, module.LbracePos, module.RbracePos)
|
||||
p.printMap(&module.Map)
|
||||
p.requestDoubleNewline()
|
||||
}
|
||||
|
||||
func (p *printer) printValue(value Value) {
|
||||
if value.Variable != "" {
|
||||
p.printToken(value.Variable, value.Pos)
|
||||
} else if value.Expression != nil {
|
||||
p.printExpression(*value.Expression)
|
||||
} else {
|
||||
switch value.Type {
|
||||
case Bool:
|
||||
var s string
|
||||
if value.BoolValue {
|
||||
s = "true"
|
||||
} else {
|
||||
s = "false"
|
||||
}
|
||||
p.printToken(s, value.Pos)
|
||||
case String:
|
||||
p.printToken(strconv.Quote(value.StringValue), value.Pos)
|
||||
case List:
|
||||
p.printList(value.ListValue, value.Pos, value.EndPos)
|
||||
case Map:
|
||||
p.printMap(value.MapValue, value.Pos, value.EndPos)
|
||||
default:
|
||||
panic(fmt.Errorf("bad property type: %d", value.Type))
|
||||
func (p *printer) printExpression(value Expression) {
|
||||
switch v := value.(type) {
|
||||
case *Variable:
|
||||
p.printToken(v.Name, v.NamePos)
|
||||
case *Operator:
|
||||
p.printOperator(v)
|
||||
case *Bool:
|
||||
var s string
|
||||
if v.Value {
|
||||
s = "true"
|
||||
} else {
|
||||
s = "false"
|
||||
}
|
||||
p.printToken(s, v.LiteralPos)
|
||||
case *String:
|
||||
p.printToken(strconv.Quote(v.Value), v.LiteralPos)
|
||||
case *List:
|
||||
p.printList(v.Values, v.LBracePos, v.RBracePos)
|
||||
case *Map:
|
||||
p.printMap(v)
|
||||
default:
|
||||
panic(fmt.Errorf("bad property type: %d", value.Type))
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) printList(list []Value, pos, endPos scanner.Position) {
|
||||
func (p *printer) printList(list []Expression, pos, endPos scanner.Position) {
|
||||
p.requestSpace()
|
||||
p.printToken("[", pos)
|
||||
if len(list) > 1 || pos.Line != endPos.Line {
|
||||
p.requestNewline()
|
||||
p.indent(p.curIndent() + 4)
|
||||
for _, value := range list {
|
||||
p.printValue(value)
|
||||
p.printExpression(value)
|
||||
p.printToken(",", noPos)
|
||||
p.requestNewline()
|
||||
}
|
||||
p.unindent(endPos)
|
||||
} else {
|
||||
for _, value := range list {
|
||||
p.printValue(value)
|
||||
p.printExpression(value)
|
||||
}
|
||||
}
|
||||
p.printToken("]", endPos)
|
||||
}
|
||||
|
||||
func (p *printer) printMap(list []*Property, pos, endPos scanner.Position) {
|
||||
func (p *printer) printMap(m *Map) {
|
||||
p.requestSpace()
|
||||
p.printToken("{", pos)
|
||||
if len(list) > 0 || pos.Line != endPos.Line {
|
||||
p.printToken("{", m.LBracePos)
|
||||
if len(m.Properties) > 0 || m.LBracePos.Line != m.RBracePos.Line {
|
||||
p.requestNewline()
|
||||
p.indent(p.curIndent() + 4)
|
||||
for _, prop := range list {
|
||||
for _, prop := range m.Properties {
|
||||
p.printProperty(prop)
|
||||
p.printToken(",", noPos)
|
||||
p.requestNewline()
|
||||
}
|
||||
p.unindent(endPos)
|
||||
p.unindent(m.RBracePos)
|
||||
}
|
||||
p.printToken("}", endPos)
|
||||
p.printToken("}", m.RBracePos)
|
||||
}
|
||||
|
||||
func (p *printer) printExpression(expression Expression) {
|
||||
p.printValue(expression.Args[0])
|
||||
func (p *printer) printOperator(operator *Operator) {
|
||||
p.printExpression(operator.Args[0])
|
||||
p.requestSpace()
|
||||
p.printToken(string(expression.Operator), expression.Pos)
|
||||
if expression.Args[0].Pos.Line == expression.Args[1].Pos.Line {
|
||||
p.printToken(string(operator.Operator), operator.OperatorPos)
|
||||
if operator.Args[0].End().Line == operator.Args[1].Pos().Line {
|
||||
p.requestSpace()
|
||||
} else {
|
||||
p.requestNewline()
|
||||
}
|
||||
p.printValue(expression.Args[1])
|
||||
p.printExpression(operator.Args[1])
|
||||
}
|
||||
|
||||
func (p *printer) printProperty(property *Property) {
|
||||
p.printToken(property.Name.Name, property.Name.Pos)
|
||||
p.printToken(":", property.Pos)
|
||||
p.requestSpace()
|
||||
p.printValue(property.Value)
|
||||
p.printExpression(property.Value)
|
||||
}
|
||||
|
||||
// Print a single token, including any necessary comments or whitespace between
|
||||
|
@ -208,7 +206,7 @@ func (p *printer) printToken(s string, pos scanner.Position) {
|
|||
|
||||
// Print any in-line (single line /* */) comments that appear _before_ pos
|
||||
func (p *printer) printInLineCommentsBefore(pos scanner.Position) {
|
||||
for p.curComment < len(p.comments) && p.comments[p.curComment].Pos.Offset < pos.Offset {
|
||||
for p.curComment < len(p.comments) && p.comments[p.curComment].Slash.Offset < pos.Offset {
|
||||
c := p.comments[p.curComment]
|
||||
if c.Comment[0][0:2] == "//" || len(c.Comment) > 1 {
|
||||
p.skippedComments = append(p.skippedComments, c)
|
||||
|
@ -225,16 +223,16 @@ func (p *printer) printInLineCommentsBefore(pos scanner.Position) {
|
|||
// by pos
|
||||
func (p *printer) printEndOfLineCommentsBefore(pos scanner.Position) {
|
||||
for _, c := range p.skippedComments {
|
||||
if !p.requestNewlinesForPos(c.Pos) {
|
||||
if !p.requestNewlinesForPos(c.Slash) {
|
||||
p.requestSpace()
|
||||
}
|
||||
p.printComment(c)
|
||||
p._requestNewline()
|
||||
}
|
||||
p.skippedComments = []Comment{}
|
||||
for p.curComment < len(p.comments) && p.comments[p.curComment].Pos.Line < pos.Line {
|
||||
for p.curComment < len(p.comments) && p.comments[p.curComment].Slash.Line < pos.Line {
|
||||
c := p.comments[p.curComment]
|
||||
if !p.requestNewlinesForPos(c.Pos) {
|
||||
if !p.requestNewlinesForPos(c.Slash) {
|
||||
p.requestSpace()
|
||||
}
|
||||
p.printComment(c)
|
||||
|
@ -303,7 +301,7 @@ func (p *printer) flushSpace() {
|
|||
|
||||
// Print a single comment, which may be a multi-line comment
|
||||
func (p *printer) printComment(comment Comment) {
|
||||
pos := comment.Pos
|
||||
pos := comment.Slash
|
||||
for i, line := range comment.Comment {
|
||||
line = strings.TrimRightFunc(line, unicode.IsSpace)
|
||||
p.flushSpace()
|
||||
|
@ -324,14 +322,14 @@ func (p *printer) printComment(comment Comment) {
|
|||
// Print any comments that occur after the last token, and a trailing newline
|
||||
func (p *printer) flush() {
|
||||
for _, c := range p.skippedComments {
|
||||
if !p.requestNewlinesForPos(c.Pos) {
|
||||
if !p.requestNewlinesForPos(c.Slash) {
|
||||
p.requestSpace()
|
||||
}
|
||||
p.printComment(c)
|
||||
}
|
||||
for p.curComment < len(p.comments) {
|
||||
c := p.comments[p.curComment]
|
||||
if !p.requestNewlinesForPos(c.Pos) {
|
||||
if !p.requestNewlinesForPos(c.Slash) {
|
||||
p.requestSpace()
|
||||
}
|
||||
p.printComment(c)
|
||||
|
|
|
@ -58,6 +58,49 @@ foo {
|
|||
"uiop",
|
||||
],
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
var = "asdf"
|
||||
foo {
|
||||
stuff: ["asdf"] + var,
|
||||
}`,
|
||||
output: `
|
||||
var = "asdf"
|
||||
foo {
|
||||
stuff: ["asdf"] + var,
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
var = "asdf"
|
||||
foo {
|
||||
stuff: [
|
||||
"asdf"
|
||||
] + var,
|
||||
}`,
|
||||
output: `
|
||||
var = "asdf"
|
||||
foo {
|
||||
stuff: [
|
||||
"asdf",
|
||||
] + var,
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
var = "asdf"
|
||||
foo {
|
||||
stuff: ["asdf"] + var + ["qwert"],
|
||||
}`,
|
||||
output: `
|
||||
var = "asdf"
|
||||
foo {
|
||||
stuff: ["asdf"] + var + ["qwert"],
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
|
|
|
@ -32,40 +32,40 @@ func SortLists(file *File) {
|
|||
sort.Sort(commentsByOffset(file.Comments))
|
||||
}
|
||||
|
||||
func SortList(file *File, value Value) {
|
||||
for i := 0; i < len(value.ListValue); i++ {
|
||||
func SortList(file *File, list *List) {
|
||||
for i := 0; i < len(list.Values); i++ {
|
||||
// Find a set of values on contiguous lines
|
||||
line := value.ListValue[i].Pos.Line
|
||||
line := list.Values[i].Pos().Line
|
||||
var j int
|
||||
for j = i + 1; j < len(value.ListValue); j++ {
|
||||
if value.ListValue[j].Pos.Line > line+1 {
|
||||
for j = i + 1; j < len(list.Values); j++ {
|
||||
if list.Values[j].Pos().Line > line+1 {
|
||||
break
|
||||
}
|
||||
line = value.ListValue[j].Pos.Line
|
||||
line = list.Values[j].Pos().Line
|
||||
}
|
||||
|
||||
nextPos := value.EndPos
|
||||
if j < len(value.ListValue) {
|
||||
nextPos = value.ListValue[j].Pos
|
||||
nextPos := list.End()
|
||||
if j < len(list.Values) {
|
||||
nextPos = list.Values[j].Pos()
|
||||
}
|
||||
sortSubList(value.ListValue[i:j], nextPos, file)
|
||||
sortSubList(list.Values[i:j], nextPos, file)
|
||||
i = j - 1
|
||||
}
|
||||
}
|
||||
|
||||
func ListIsSorted(value Value) bool {
|
||||
for i := 0; i < len(value.ListValue); i++ {
|
||||
func ListIsSorted(list *List) bool {
|
||||
for i := 0; i < len(list.Values); i++ {
|
||||
// Find a set of values on contiguous lines
|
||||
line := value.ListValue[i].Pos.Line
|
||||
line := list.Values[i].Pos().Line
|
||||
var j int
|
||||
for j = i + 1; j < len(value.ListValue); j++ {
|
||||
if value.ListValue[j].Pos.Line > line+1 {
|
||||
for j = i + 1; j < len(list.Values); j++ {
|
||||
if list.Values[j].Pos().Line > line+1 {
|
||||
break
|
||||
}
|
||||
line = value.ListValue[j].Pos.Line
|
||||
line = list.Values[j].Pos().Line
|
||||
}
|
||||
|
||||
if !subListIsSorted(value.ListValue[i:j]) {
|
||||
if !subListIsSorted(list.Values[i:j]) {
|
||||
return false
|
||||
}
|
||||
i = j - 1
|
||||
|
@ -74,55 +74,49 @@ func ListIsSorted(value Value) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func sortListsInValue(value Value, file *File) {
|
||||
if value.Variable != "" {
|
||||
return
|
||||
}
|
||||
|
||||
if value.Expression != nil {
|
||||
sortListsInValue(value.Expression.Args[0], file)
|
||||
sortListsInValue(value.Expression.Args[1], file)
|
||||
return
|
||||
}
|
||||
|
||||
if value.Type == Map {
|
||||
for _, p := range value.MapValue {
|
||||
func sortListsInValue(value Expression, file *File) {
|
||||
switch v := value.(type) {
|
||||
case *Variable:
|
||||
// Nothing
|
||||
case *Operator:
|
||||
sortListsInValue(v.Args[0], file)
|
||||
sortListsInValue(v.Args[1], file)
|
||||
case *Map:
|
||||
for _, p := range v.Properties {
|
||||
sortListsInValue(p.Value, file)
|
||||
}
|
||||
return
|
||||
} else if value.Type != List {
|
||||
return
|
||||
case *List:
|
||||
SortList(file, v)
|
||||
}
|
||||
|
||||
SortList(file, value)
|
||||
}
|
||||
|
||||
func sortSubList(values []Value, nextPos scanner.Position, file *File) {
|
||||
func sortSubList(values []Expression, nextPos scanner.Position, file *File) {
|
||||
l := make(elemList, len(values))
|
||||
for i, v := range values {
|
||||
if v.Type != String {
|
||||
s, ok := v.(*String)
|
||||
if !ok {
|
||||
panic("list contains non-string element")
|
||||
}
|
||||
n := nextPos
|
||||
if i < len(values)-1 {
|
||||
n = values[i+1].Pos
|
||||
n = values[i+1].Pos()
|
||||
}
|
||||
l[i] = elem{v.StringValue, i, v.Pos, n}
|
||||
l[i] = elem{s.Value, i, v.Pos(), n}
|
||||
}
|
||||
|
||||
sort.Sort(l)
|
||||
|
||||
copyValues := append([]Value{}, values...)
|
||||
copyValues := append([]Expression{}, values...)
|
||||
copyComments := append([]Comment{}, file.Comments...)
|
||||
|
||||
curPos := values[0].Pos
|
||||
curPos := values[0].Pos()
|
||||
for i, e := range l {
|
||||
values[i] = copyValues[e.i]
|
||||
values[i].Pos = curPos
|
||||
values[i].(*String).LiteralPos = curPos
|
||||
for j, c := range copyComments {
|
||||
if c.Pos.Offset > e.pos.Offset && c.Pos.Offset < e.nextPos.Offset {
|
||||
file.Comments[j].Pos.Line = curPos.Line
|
||||
file.Comments[j].Pos.Offset += values[i].Pos.Offset - e.pos.Offset
|
||||
if c.Pos().Offset > e.pos.Offset && c.Pos().Offset < e.nextPos.Offset {
|
||||
file.Comments[j].Slash.Line = curPos.Line
|
||||
file.Comments[j].Slash.Offset += values[i].Pos().Offset - e.pos.Offset
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,16 +125,17 @@ func sortSubList(values []Value, nextPos scanner.Position, file *File) {
|
|||
}
|
||||
}
|
||||
|
||||
func subListIsSorted(values []Value) bool {
|
||||
func subListIsSorted(values []Expression) bool {
|
||||
prev := ""
|
||||
for _, v := range values {
|
||||
if v.Type != String {
|
||||
s, ok := v.(*String)
|
||||
if !ok {
|
||||
panic("list contains non-string element")
|
||||
}
|
||||
if prev > v.StringValue {
|
||||
if prev > s.Value {
|
||||
return false
|
||||
}
|
||||
prev = v.StringValue
|
||||
prev = s.Value
|
||||
}
|
||||
|
||||
return true
|
||||
|
@ -174,7 +169,7 @@ func (l commentsByOffset) Len() int {
|
|||
}
|
||||
|
||||
func (l commentsByOffset) Less(i, j int) bool {
|
||||
return l[i].Pos.Offset < l[j].Pos.Offset
|
||||
return l[i].Pos().Offset < l[j].Pos().Offset
|
||||
}
|
||||
|
||||
func (l commentsByOffset) Swap(i, j int) {
|
||||
|
|
51
unpack.go
51
unpack.go
|
@ -88,7 +88,6 @@ func buildPropertyMap(namePrefix string, propertyDefs []*parser.Property,
|
|||
// We've already added this property.
|
||||
continue
|
||||
}
|
||||
|
||||
errs = append(errs, &Error{
|
||||
Err: fmt.Errorf("property %q already defined", name),
|
||||
Pos: propertyDef.Pos,
|
||||
|
@ -276,47 +275,49 @@ func unpackStructValue(namePrefix string, structValue reflect.Value,
|
|||
}
|
||||
|
||||
func unpackBool(boolValue reflect.Value, property *parser.Property) []error {
|
||||
if property.Value.Type != parser.Bool {
|
||||
b, ok := property.Value.Eval().(*parser.Bool)
|
||||
if !ok {
|
||||
return []error{
|
||||
fmt.Errorf("%s: can't assign %s value to %s property %q",
|
||||
property.Value.Pos, property.Value.Type, parser.Bool,
|
||||
property.Name),
|
||||
fmt.Errorf("%s: can't assign %s value to bool property %q",
|
||||
property.Value.Pos, property.Value.Type, property.Name),
|
||||
}
|
||||
}
|
||||
boolValue.SetBool(property.Value.BoolValue)
|
||||
boolValue.SetBool(b.Value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func unpackString(stringValue reflect.Value,
|
||||
property *parser.Property) []error {
|
||||
|
||||
if property.Value.Type != parser.String {
|
||||
s, ok := property.Value.Eval().(*parser.String)
|
||||
if !ok {
|
||||
return []error{
|
||||
fmt.Errorf("%s: can't assign %s value to %s property %q",
|
||||
property.Value.Pos, property.Value.Type, parser.String,
|
||||
property.Name),
|
||||
fmt.Errorf("%s: can't assign %s value to string property %q",
|
||||
property.Value.Pos, property.Value.Type, property.Name),
|
||||
}
|
||||
}
|
||||
stringValue.SetString(property.Value.StringValue)
|
||||
stringValue.SetString(s.Value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func unpackSlice(sliceValue reflect.Value, property *parser.Property) []error {
|
||||
if property.Value.Type != parser.List {
|
||||
|
||||
l, ok := property.Value.Eval().(*parser.List)
|
||||
if !ok {
|
||||
return []error{
|
||||
fmt.Errorf("%s: can't assign %s value to %s property %q",
|
||||
property.Value.Pos, property.Value.Type, parser.List,
|
||||
property.Name),
|
||||
fmt.Errorf("%s: can't assign %s value to list property %q",
|
||||
property.Value.Pos, property.Value.Type, property.Name),
|
||||
}
|
||||
}
|
||||
|
||||
list := []string{}
|
||||
for _, value := range property.Value.ListValue {
|
||||
if value.Type != parser.String {
|
||||
list := make([]string, len(l.Values))
|
||||
for i, value := range l.Values {
|
||||
s, ok := value.Eval().(*parser.String)
|
||||
if !ok {
|
||||
// The parser should not produce this.
|
||||
panic("non-string value found in list")
|
||||
panic(fmt.Errorf("non-string value %q found in list", value))
|
||||
}
|
||||
list = append(list, value.StringValue)
|
||||
list[i] = s.Value
|
||||
}
|
||||
|
||||
sliceValue.Set(reflect.ValueOf(list))
|
||||
|
@ -327,15 +328,15 @@ func unpackStruct(namePrefix string, structValue reflect.Value,
|
|||
property *parser.Property, propertyMap map[string]*packedProperty,
|
||||
filterKey, filterValue string) []error {
|
||||
|
||||
if property.Value.Type != parser.Map {
|
||||
m, ok := property.Value.Eval().(*parser.Map)
|
||||
if !ok {
|
||||
return []error{
|
||||
fmt.Errorf("%s: can't assign %s value to %s property %q",
|
||||
property.Value.Pos, property.Value.Type, parser.Map,
|
||||
property.Name),
|
||||
fmt.Errorf("%s: can't assign %s value to map property %q",
|
||||
property.Value.Pos, property.Value.Type, property.Name),
|
||||
}
|
||||
}
|
||||
|
||||
errs := buildPropertyMap(namePrefix, property.Value.MapValue, propertyMap)
|
||||
errs := buildPropertyMap(namePrefix, m.Properties, propertyMap)
|
||||
if len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
|
461
unpack_test.go
461
unpack_test.go
|
@ -27,7 +27,7 @@ import (
|
|||
|
||||
var validUnpackTestCases = []struct {
|
||||
input string
|
||||
output interface{}
|
||||
output []interface{}
|
||||
errs []error
|
||||
}{
|
||||
{`
|
||||
|
@ -36,14 +36,16 @@ var validUnpackTestCases = []struct {
|
|||
blank: "",
|
||||
}
|
||||
`,
|
||||
struct {
|
||||
Name *string
|
||||
Blank *string
|
||||
Unset *string
|
||||
}{
|
||||
Name: proptools.StringPtr("abc"),
|
||||
Blank: proptools.StringPtr(""),
|
||||
Unset: nil,
|
||||
[]interface{}{
|
||||
struct {
|
||||
Name *string
|
||||
Blank *string
|
||||
Unset *string
|
||||
}{
|
||||
Name: proptools.StringPtr("abc"),
|
||||
Blank: proptools.StringPtr(""),
|
||||
Unset: nil,
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
|
@ -53,10 +55,12 @@ var validUnpackTestCases = []struct {
|
|||
name: "abc",
|
||||
}
|
||||
`,
|
||||
struct {
|
||||
Name string
|
||||
}{
|
||||
Name: "abc",
|
||||
[]interface{}{
|
||||
struct {
|
||||
Name string
|
||||
}{
|
||||
Name: "abc",
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
|
@ -66,10 +70,12 @@ var validUnpackTestCases = []struct {
|
|||
isGood: true,
|
||||
}
|
||||
`,
|
||||
struct {
|
||||
IsGood bool
|
||||
}{
|
||||
IsGood: true,
|
||||
[]interface{}{
|
||||
struct {
|
||||
IsGood bool
|
||||
}{
|
||||
IsGood: true,
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
|
@ -80,14 +86,16 @@ var validUnpackTestCases = []struct {
|
|||
isBad: false,
|
||||
}
|
||||
`,
|
||||
struct {
|
||||
IsGood *bool
|
||||
IsBad *bool
|
||||
IsUgly *bool
|
||||
}{
|
||||
IsGood: proptools.BoolPtr(true),
|
||||
IsBad: proptools.BoolPtr(false),
|
||||
IsUgly: nil,
|
||||
[]interface{}{
|
||||
struct {
|
||||
IsGood *bool
|
||||
IsBad *bool
|
||||
IsUgly *bool
|
||||
}{
|
||||
IsGood: proptools.BoolPtr(true),
|
||||
IsBad: proptools.BoolPtr(false),
|
||||
IsUgly: nil,
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
|
@ -99,14 +107,16 @@ var validUnpackTestCases = []struct {
|
|||
empty: []
|
||||
}
|
||||
`,
|
||||
struct {
|
||||
Stuff []string
|
||||
Empty []string
|
||||
Nil []string
|
||||
}{
|
||||
Stuff: []string{"asdf", "jkl;", "qwert", "uiop", "bnm,"},
|
||||
Empty: []string{},
|
||||
Nil: nil,
|
||||
[]interface{}{
|
||||
struct {
|
||||
Stuff []string
|
||||
Empty []string
|
||||
Nil []string
|
||||
}{
|
||||
Stuff: []string{"asdf", "jkl;", "qwert", "uiop", "bnm,"},
|
||||
Empty: []string{},
|
||||
Nil: nil,
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
|
@ -118,13 +128,15 @@ var validUnpackTestCases = []struct {
|
|||
}
|
||||
}
|
||||
`,
|
||||
struct {
|
||||
Nested struct {
|
||||
Name string
|
||||
}
|
||||
}{
|
||||
Nested: struct{ Name string }{
|
||||
Name: "abc",
|
||||
[]interface{}{
|
||||
struct {
|
||||
Nested struct {
|
||||
Name string
|
||||
}
|
||||
}{
|
||||
Nested: struct{ Name string }{
|
||||
Name: "abc",
|
||||
},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
|
@ -137,64 +149,14 @@ var validUnpackTestCases = []struct {
|
|||
}
|
||||
}
|
||||
`,
|
||||
struct {
|
||||
Nested interface{}
|
||||
}{
|
||||
Nested: &struct{ Name string }{
|
||||
Name: "def",
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
|
||||
{`
|
||||
m {
|
||||
nested: {
|
||||
foo: "abc",
|
||||
},
|
||||
bar: false,
|
||||
baz: ["def", "ghi"],
|
||||
}
|
||||
`,
|
||||
struct {
|
||||
Nested struct {
|
||||
Foo string
|
||||
}
|
||||
Bar bool
|
||||
Baz []string
|
||||
}{
|
||||
Nested: struct{ Foo string }{
|
||||
Foo: "abc",
|
||||
},
|
||||
Bar: false,
|
||||
Baz: []string{"def", "ghi"},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
|
||||
{`
|
||||
m {
|
||||
nested: {
|
||||
foo: "abc",
|
||||
},
|
||||
bar: false,
|
||||
baz: ["def", "ghi"],
|
||||
}
|
||||
`,
|
||||
struct {
|
||||
Nested struct {
|
||||
Foo string `allowNested:"true"`
|
||||
} `blueprint:"filter(allowNested:\"true\")"`
|
||||
Bar bool
|
||||
Baz []string
|
||||
}{
|
||||
Nested: struct {
|
||||
Foo string `allowNested:"true"`
|
||||
[]interface{}{
|
||||
struct {
|
||||
Nested interface{}
|
||||
}{
|
||||
Foo: "abc",
|
||||
Nested: &struct{ Name string }{
|
||||
Name: "def",
|
||||
},
|
||||
},
|
||||
Bar: false,
|
||||
Baz: []string{"def", "ghi"},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
|
@ -208,18 +170,76 @@ var validUnpackTestCases = []struct {
|
|||
baz: ["def", "ghi"],
|
||||
}
|
||||
`,
|
||||
struct {
|
||||
Nested struct {
|
||||
Foo string
|
||||
} `blueprint:"filter(allowNested:\"true\")"`
|
||||
Bar bool
|
||||
Baz []string
|
||||
}{
|
||||
Nested: struct{ Foo string }{
|
||||
Foo: "",
|
||||
[]interface{}{
|
||||
struct {
|
||||
Nested struct {
|
||||
Foo string
|
||||
}
|
||||
Bar bool
|
||||
Baz []string
|
||||
}{
|
||||
Nested: struct{ Foo string }{
|
||||
Foo: "abc",
|
||||
},
|
||||
Bar: false,
|
||||
Baz: []string{"def", "ghi"},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
|
||||
{`
|
||||
m {
|
||||
nested: {
|
||||
foo: "abc",
|
||||
},
|
||||
bar: false,
|
||||
baz: ["def", "ghi"],
|
||||
}
|
||||
`,
|
||||
[]interface{}{
|
||||
struct {
|
||||
Nested struct {
|
||||
Foo string `allowNested:"true"`
|
||||
} `blueprint:"filter(allowNested:\"true\")"`
|
||||
Bar bool
|
||||
Baz []string
|
||||
}{
|
||||
Nested: struct {
|
||||
Foo string `allowNested:"true"`
|
||||
}{
|
||||
Foo: "abc",
|
||||
},
|
||||
Bar: false,
|
||||
Baz: []string{"def", "ghi"},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
|
||||
{`
|
||||
m {
|
||||
nested: {
|
||||
foo: "abc",
|
||||
},
|
||||
bar: false,
|
||||
baz: ["def", "ghi"],
|
||||
}
|
||||
`,
|
||||
[]interface{}{
|
||||
struct {
|
||||
Nested struct {
|
||||
Foo string
|
||||
} `blueprint:"filter(allowNested:\"true\")"`
|
||||
Bar bool
|
||||
Baz []string
|
||||
}{
|
||||
Nested: struct{ Foo string }{
|
||||
Foo: "",
|
||||
},
|
||||
Bar: false,
|
||||
Baz: []string{"def", "ghi"},
|
||||
},
|
||||
Bar: false,
|
||||
Baz: []string{"def", "ghi"},
|
||||
},
|
||||
[]error{
|
||||
&Error{
|
||||
|
@ -238,20 +258,22 @@ var validUnpackTestCases = []struct {
|
|||
},
|
||||
}
|
||||
`,
|
||||
struct {
|
||||
EmbeddedStruct
|
||||
Nested struct {
|
||||
EmbeddedStruct
|
||||
}
|
||||
}{
|
||||
EmbeddedStruct: EmbeddedStruct{
|
||||
Name: "abc",
|
||||
},
|
||||
Nested: struct {
|
||||
[]interface{}{
|
||||
struct {
|
||||
EmbeddedStruct
|
||||
Nested struct {
|
||||
EmbeddedStruct
|
||||
}
|
||||
}{
|
||||
EmbeddedStruct: EmbeddedStruct{
|
||||
Name: "def",
|
||||
Name: "abc",
|
||||
},
|
||||
Nested: struct {
|
||||
EmbeddedStruct
|
||||
}{
|
||||
EmbeddedStruct: EmbeddedStruct{
|
||||
Name: "def",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -267,20 +289,22 @@ var validUnpackTestCases = []struct {
|
|||
},
|
||||
}
|
||||
`,
|
||||
struct {
|
||||
EmbeddedInterface
|
||||
Nested struct {
|
||||
EmbeddedInterface
|
||||
}
|
||||
}{
|
||||
EmbeddedInterface: &struct{ Name string }{
|
||||
Name: "abc",
|
||||
},
|
||||
Nested: struct {
|
||||
[]interface{}{
|
||||
struct {
|
||||
EmbeddedInterface
|
||||
Nested struct {
|
||||
EmbeddedInterface
|
||||
}
|
||||
}{
|
||||
EmbeddedInterface: &struct{ Name string }{
|
||||
Name: "def",
|
||||
Name: "abc",
|
||||
},
|
||||
Nested: struct {
|
||||
EmbeddedInterface
|
||||
}{
|
||||
EmbeddedInterface: &struct{ Name string }{
|
||||
Name: "def",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -296,25 +320,27 @@ var validUnpackTestCases = []struct {
|
|||
},
|
||||
}
|
||||
`,
|
||||
struct {
|
||||
Name string
|
||||
EmbeddedStruct
|
||||
Nested struct {
|
||||
Name string
|
||||
EmbeddedStruct
|
||||
}
|
||||
}{
|
||||
Name: "abc",
|
||||
EmbeddedStruct: EmbeddedStruct{
|
||||
Name: "abc",
|
||||
},
|
||||
Nested: struct {
|
||||
[]interface{}{
|
||||
struct {
|
||||
Name string
|
||||
EmbeddedStruct
|
||||
Nested struct {
|
||||
Name string
|
||||
EmbeddedStruct
|
||||
}
|
||||
}{
|
||||
Name: "def",
|
||||
Name: "abc",
|
||||
EmbeddedStruct: EmbeddedStruct{
|
||||
Name: "abc",
|
||||
},
|
||||
Nested: struct {
|
||||
Name string
|
||||
EmbeddedStruct
|
||||
}{
|
||||
Name: "def",
|
||||
EmbeddedStruct: EmbeddedStruct{
|
||||
Name: "def",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -330,30 +356,90 @@ var validUnpackTestCases = []struct {
|
|||
},
|
||||
}
|
||||
`,
|
||||
struct {
|
||||
Name string
|
||||
EmbeddedInterface
|
||||
Nested struct {
|
||||
Name string
|
||||
EmbeddedInterface
|
||||
}
|
||||
}{
|
||||
Name: "abc",
|
||||
EmbeddedInterface: &struct{ Name string }{
|
||||
Name: "abc",
|
||||
},
|
||||
Nested: struct {
|
||||
[]interface{}{
|
||||
struct {
|
||||
Name string
|
||||
EmbeddedInterface
|
||||
Nested struct {
|
||||
Name string
|
||||
EmbeddedInterface
|
||||
}
|
||||
}{
|
||||
Name: "def",
|
||||
Name: "abc",
|
||||
EmbeddedInterface: &struct{ Name string }{
|
||||
Name: "abc",
|
||||
},
|
||||
Nested: struct {
|
||||
Name string
|
||||
EmbeddedInterface
|
||||
}{
|
||||
Name: "def",
|
||||
EmbeddedInterface: &struct{ Name string }{
|
||||
Name: "def",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
|
||||
// Variables
|
||||
{`
|
||||
list = ["abc"]
|
||||
string = "def"
|
||||
list_with_variable = [string]
|
||||
m {
|
||||
name: string,
|
||||
list: list,
|
||||
list2: list_with_variable,
|
||||
}
|
||||
`,
|
||||
[]interface{}{
|
||||
struct {
|
||||
Name string
|
||||
List []string
|
||||
List2 []string
|
||||
}{
|
||||
Name: "def",
|
||||
List: []string{"abc"},
|
||||
List2: []string{"def"},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
|
||||
// Multiple property structs
|
||||
{`
|
||||
m {
|
||||
nested: {
|
||||
name: "abc",
|
||||
}
|
||||
}
|
||||
`,
|
||||
[]interface{}{
|
||||
struct {
|
||||
Nested struct {
|
||||
Name string
|
||||
}
|
||||
}{
|
||||
Nested: struct{ Name string }{
|
||||
Name: "abc",
|
||||
},
|
||||
},
|
||||
struct {
|
||||
Nested struct {
|
||||
Name string
|
||||
}
|
||||
}{
|
||||
Nested: struct{ Name string }{
|
||||
Name: "abc",
|
||||
},
|
||||
},
|
||||
struct {
|
||||
}{},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
}
|
||||
|
||||
type EmbeddedStruct struct{ Name string }
|
||||
|
@ -362,7 +448,7 @@ type EmbeddedInterface interface{}
|
|||
func TestUnpackProperties(t *testing.T) {
|
||||
for _, testCase := range validUnpackTestCases {
|
||||
r := bytes.NewBufferString(testCase.input)
|
||||
file, errs := parser.Parse("", r, nil)
|
||||
file, errs := parser.ParseAndEval("", r, parser.NewScope(nil))
|
||||
if len(errs) != 0 {
|
||||
t.Errorf("test case: %s", testCase.input)
|
||||
t.Errorf("unexpected parse errors:")
|
||||
|
@ -372,30 +458,45 @@ func TestUnpackProperties(t *testing.T) {
|
|||
t.FailNow()
|
||||
}
|
||||
|
||||
module := file.Defs[0].(*parser.Module)
|
||||
properties := proptools.CloneProperties(reflect.ValueOf(testCase.output))
|
||||
proptools.ZeroProperties(properties.Elem())
|
||||
_, errs = unpackProperties(module.Properties, properties.Interface())
|
||||
if len(errs) != 0 && len(testCase.errs) == 0 {
|
||||
t.Errorf("test case: %s", testCase.input)
|
||||
t.Errorf("unexpected unpack errors:")
|
||||
for _, err := range errs {
|
||||
t.Errorf(" %s", err)
|
||||
for _, def := range file.Defs {
|
||||
module, ok := def.(*parser.Module)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
t.FailNow()
|
||||
} else if !reflect.DeepEqual(errs, testCase.errs) {
|
||||
t.Errorf("test case: %s", testCase.input)
|
||||
t.Errorf("incorrect errors:")
|
||||
t.Errorf(" expected: %+v", testCase.errs)
|
||||
t.Errorf(" got: %+v", errs)
|
||||
}
|
||||
|
||||
output := properties.Elem().Interface()
|
||||
if !reflect.DeepEqual(output, testCase.output) {
|
||||
t.Errorf("test case: %s", testCase.input)
|
||||
t.Errorf("incorrect output:")
|
||||
t.Errorf(" expected: %+v", testCase.output)
|
||||
t.Errorf(" got: %+v", output)
|
||||
output := []interface{}{}
|
||||
for _, p := range testCase.output {
|
||||
output = append(output, proptools.CloneEmptyProperties(reflect.ValueOf(p)).Interface())
|
||||
}
|
||||
_, errs = unpackProperties(module.Properties, output...)
|
||||
if len(errs) != 0 && len(testCase.errs) == 0 {
|
||||
t.Errorf("test case: %s", testCase.input)
|
||||
t.Errorf("unexpected unpack errors:")
|
||||
for _, err := range errs {
|
||||
t.Errorf(" %s", err)
|
||||
}
|
||||
t.FailNow()
|
||||
} else if !reflect.DeepEqual(errs, testCase.errs) {
|
||||
t.Errorf("test case: %s", testCase.input)
|
||||
t.Errorf("incorrect errors:")
|
||||
t.Errorf(" expected: %+v", testCase.errs)
|
||||
t.Errorf(" got: %+v", errs)
|
||||
}
|
||||
|
||||
if len(output) != len(testCase.output) {
|
||||
t.Fatalf("incorrect number of property structs, expected %d got %d",
|
||||
len(testCase.output), len(output))
|
||||
}
|
||||
|
||||
for i := range output {
|
||||
got := reflect.ValueOf(output[i]).Elem().Interface()
|
||||
if !reflect.DeepEqual(got, testCase.output[i]) {
|
||||
t.Errorf("test case: %s", testCase.input)
|
||||
t.Errorf("incorrect output:")
|
||||
t.Errorf(" expected: %+v", testCase.output[i])
|
||||
t.Errorf(" got: %+v", got)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue