Merge remote-tracking branch 'aosp/upstream' into master
3a17686 Merge remote-tracking branch 'aosp/upstream' into master
d4f49b0 Merge pull request #103 from colincross/parser
e32cc80 Refactor blueprint parser nodes to an interface
aedd490 Merge pull request #101 from colincross/doc
7932496 Fix documentation property type for pointers
d9f6fd5 Remove naming stutter in bpdoc
017ed2e Fix govet issues
Change-Id: Ie0863b79ee0d22ae6dbc066ca06303f046d904b7
diff --git a/Blueprints b/Blueprints
index 74db5c1..f3eb843 100644
--- a/Blueprints
+++ b/Blueprints
@@ -32,6 +32,7 @@
name = "blueprint-parser",
pkgPath = "github.com/google/blueprint/parser",
srcs = [
+ "parser/ast.go",
"parser/modify.go",
"parser/parser.go",
"parser/printer.go",
diff --git a/bootstrap/bpdoc/bpdoc.go b/bootstrap/bpdoc/bpdoc.go
index f96d37e..dcb6f65 100644
--- a/bootstrap/bpdoc/bpdoc.go
+++ b/bootstrap/bpdoc/bpdoc.go
@@ -19,97 +19,97 @@
"github.com/google/blueprint/proptools"
)
-type DocCollector struct {
+type Context struct {
pkgFiles map[string][]string // Map of package name to source files, provided by constructor
- mutex sync.Mutex
- pkgDocs map[string]*doc.Package // Map of package name to parsed Go AST, protected by mutex
- docs map[string]*PropertyStructDocs // Map of type name to docs, protected by mutex
+ mutex sync.Mutex
+ pkgs map[string]*doc.Package // Map of package name to parsed Go AST, protected by mutex
+ ps map[string]*PropertyStruct // Map of type name to property struct, protected by mutex
}
-func NewDocCollector(pkgFiles map[string][]string) *DocCollector {
- return &DocCollector{
+func NewContext(pkgFiles map[string][]string) *Context {
+ return &Context{
pkgFiles: pkgFiles,
- pkgDocs: make(map[string]*doc.Package),
- docs: make(map[string]*PropertyStructDocs),
+ pkgs: make(map[string]*doc.Package),
+ ps: make(map[string]*PropertyStruct),
}
}
-// Return the PropertyStructDocs associated with a property struct type. The type should be in the
+// Return the PropertyStruct associated with a property struct type. The type should be in the
// format <package path>.<type name>
-func (dc *DocCollector) Docs(pkg, name string, defaults reflect.Value) (*PropertyStructDocs, error) {
- docs := dc.getDocs(pkg, name)
+func (c *Context) PropertyStruct(pkgPath, name string, defaults reflect.Value) (*PropertyStruct, error) {
+ ps := c.getPropertyStruct(pkgPath, name)
- if docs == nil {
- pkgDocs, err := dc.packageDocs(pkg)
+ if ps == nil {
+ pkg, err := c.pkg(pkgPath)
if err != nil {
return nil, err
}
- for _, t := range pkgDocs.Types {
+ for _, t := range pkg.Types {
if t.Name == name {
- docs, err = newDocs(t)
+ ps, err = newPropertyStruct(t)
if err != nil {
return nil, err
}
- docs = dc.putDocs(pkg, name, docs)
+ ps = c.putPropertyStruct(pkgPath, name, ps)
}
}
}
- if docs == nil {
- return nil, fmt.Errorf("package %q type %q not found", pkg, name)
+ if ps == nil {
+ return nil, fmt.Errorf("package %q type %q not found", pkgPath, name)
}
- docs = docs.Clone()
- docs.SetDefaults(defaults)
+ ps = ps.Clone()
+ ps.SetDefaults(defaults)
- return docs, nil
+ return ps, nil
}
-func (dc *DocCollector) getDocs(pkg, name string) *PropertyStructDocs {
- dc.mutex.Lock()
- defer dc.mutex.Unlock()
+func (c *Context) getPropertyStruct(pkgPath, name string) *PropertyStruct {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
- name = pkg + "." + name
+ name = pkgPath + "." + name
- return dc.docs[name]
+ return c.ps[name]
}
-func (dc *DocCollector) putDocs(pkg, name string, docs *PropertyStructDocs) *PropertyStructDocs {
- dc.mutex.Lock()
- defer dc.mutex.Unlock()
+func (c *Context) putPropertyStruct(pkgPath, name string, ps *PropertyStruct) *PropertyStruct {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
- name = pkg + "." + name
+ name = pkgPath + "." + name
- if dc.docs[name] != nil {
- return dc.docs[name]
+ if c.ps[name] != nil {
+ return c.ps[name]
} else {
- dc.docs[name] = docs
- return docs
+ c.ps[name] = ps
+ return ps
}
}
-type PropertyStructDocs struct {
+type PropertyStruct struct {
Name string
Text string
- Properties []PropertyDocs
+ Properties []Property
}
-type PropertyDocs struct {
+type Property struct {
Name string
OtherNames []string
Type string
Tag reflect.StructTag
Text string
OtherTexts []string
- Properties []PropertyDocs
+ Properties []Property
Default string
}
-func (docs *PropertyStructDocs) Clone() *PropertyStructDocs {
- ret := *docs
- ret.Properties = append([]PropertyDocs(nil), ret.Properties...)
+func (ps *PropertyStruct) Clone() *PropertyStruct {
+ ret := *ps
+ ret.Properties = append([]Property(nil), ret.Properties...)
for i, prop := range ret.Properties {
ret.Properties[i] = prop.Clone()
}
@@ -117,9 +117,9 @@
return &ret
}
-func (docs *PropertyDocs) Clone() PropertyDocs {
- ret := *docs
- ret.Properties = append([]PropertyDocs(nil), ret.Properties...)
+func (p *Property) Clone() Property {
+ ret := *p
+ ret.Properties = append([]Property(nil), ret.Properties...)
for i, prop := range ret.Properties {
ret.Properties[i] = prop.Clone()
}
@@ -127,19 +127,19 @@
return ret
}
-func (docs *PropertyDocs) Equal(other PropertyDocs) bool {
- return docs.Name == other.Name && docs.Type == other.Type && docs.Tag == other.Tag &&
- docs.Text == other.Text && docs.Default == other.Default &&
- stringArrayEqual(docs.OtherNames, other.OtherNames) &&
- stringArrayEqual(docs.OtherTexts, other.OtherTexts) &&
- docs.SameSubProperties(other)
+func (p *Property) Equal(other Property) bool {
+ return p.Name == other.Name && p.Type == other.Type && p.Tag == other.Tag &&
+ p.Text == other.Text && p.Default == other.Default &&
+ stringArrayEqual(p.OtherNames, other.OtherNames) &&
+ stringArrayEqual(p.OtherTexts, other.OtherTexts) &&
+ p.SameSubProperties(other)
}
-func (docs *PropertyStructDocs) SetDefaults(defaults reflect.Value) {
- setDefaults(docs.Properties, defaults)
+func (ps *PropertyStruct) SetDefaults(defaults reflect.Value) {
+ setDefaults(ps.Properties, defaults)
}
-func setDefaults(properties []PropertyDocs, defaults reflect.Value) {
+func setDefaults(properties []Property, defaults reflect.Value) {
for i := range properties {
prop := &properties[i]
fieldName := proptools.FieldNameForProperty(prop.Name)
@@ -182,13 +182,13 @@
return true
}
-func (docs *PropertyDocs) SameSubProperties(other PropertyDocs) bool {
- if len(docs.Properties) != len(other.Properties) {
+func (p *Property) SameSubProperties(other Property) bool {
+ if len(p.Properties) != len(other.Properties) {
return false
}
- for i := range docs.Properties {
- if !docs.Properties[i].Equal(other.Properties[i]) {
+ for i := range p.Properties {
+ if !p.Properties[i].Equal(other.Properties[i]) {
return false
}
}
@@ -196,11 +196,11 @@
return true
}
-func (docs *PropertyStructDocs) GetByName(name string) *PropertyDocs {
- return getByName(name, "", &docs.Properties)
+func (ps *PropertyStruct) GetByName(name string) *Property {
+ return getByName(name, "", &ps.Properties)
}
-func getByName(name string, prefix string, props *[]PropertyDocs) *PropertyDocs {
+func getByName(name string, prefix string, props *[]Property) *Property {
for i := range *props {
if prefix+(*props)[i].Name == name {
return &(*props)[i]
@@ -211,15 +211,15 @@
return nil
}
-func (prop *PropertyDocs) Nest(nested *PropertyStructDocs) {
- //prop.Name += "(" + nested.Name + ")"
- //prop.Text += "(" + nested.Text + ")"
- prop.Properties = append(prop.Properties, nested.Properties...)
+func (p *Property) Nest(nested *PropertyStruct) {
+ //p.Name += "(" + nested.Name + ")"
+ //p.Text += "(" + nested.Text + ")"
+ p.Properties = append(p.Properties, nested.Properties...)
}
-func newDocs(t *doc.Type) (*PropertyStructDocs, error) {
+func newPropertyStruct(t *doc.Type) (*PropertyStruct, error) {
typeSpec := t.Decl.Specs[0].(*ast.TypeSpec)
- docs := PropertyStructDocs{
+ ps := PropertyStruct{
Name: t.Name,
Text: t.Doc,
}
@@ -230,15 +230,15 @@
}
var err error
- docs.Properties, err = structProperties(structType)
+ ps.Properties, err = structProperties(structType)
if err != nil {
return nil, err
}
- return &docs, nil
+ return &ps, nil
}
-func structProperties(structType *ast.StructType) (props []PropertyDocs, err error) {
+func structProperties(structType *ast.StructType) (props []Property, err error) {
for _, f := range structType.Fields.List {
names := f.Names
if names == nil {
@@ -250,7 +250,7 @@
}
for _, n := range names {
var name, typ, tag, text string
- var innerProps []PropertyDocs
+ var innerProps []Property
if n != nil {
name = proptools.PropertyNameForField(n.Name)
}
@@ -263,7 +263,12 @@
return nil, err
}
}
- switch a := f.Type.(type) {
+
+ t := f.Type
+ if star, ok := t.(*ast.StarExpr); ok {
+ t = star.X
+ }
+ switch a := t.(type) {
case *ast.ArrayType:
typ = "list of strings"
case *ast.InterfaceType:
@@ -279,7 +284,7 @@
typ = fmt.Sprintf("%T", f.Type)
}
- props = append(props, PropertyDocs{
+ props = append(props, Property{
Name: name,
Type: typ,
Tag: reflect.StructTag(tag),
@@ -292,15 +297,15 @@
return props, nil
}
-func (docs *PropertyStructDocs) ExcludeByTag(key, value string) {
- filterPropsByTag(&docs.Properties, key, value, true)
+func (ps *PropertyStruct) ExcludeByTag(key, value string) {
+ filterPropsByTag(&ps.Properties, key, value, true)
}
-func (docs *PropertyStructDocs) IncludeByTag(key, value string) {
- filterPropsByTag(&docs.Properties, key, value, false)
+func (ps *PropertyStruct) IncludeByTag(key, value string) {
+ filterPropsByTag(&ps.Properties, key, value, false)
}
-func filterPropsByTag(props *[]PropertyDocs, key, value string, exclude bool) {
+func filterPropsByTag(props *[]Property, key, value string, exclude bool) {
// Create a slice that shares the storage of props but has 0 length. Appending up to
// len(props) times to this slice will overwrite the original slice contents
filtered := (*props)[:0]
@@ -317,40 +322,40 @@
}
// Package AST generation and storage
-func (dc *DocCollector) packageDocs(pkg string) (*doc.Package, error) {
- pkgDocs := dc.getPackageDocs(pkg)
- if pkgDocs == nil {
- if files, ok := dc.pkgFiles[pkg]; ok {
+func (c *Context) pkg(pkgPath string) (*doc.Package, error) {
+ pkg := c.getPackage(pkgPath)
+ if pkg == nil {
+ if files, ok := c.pkgFiles[pkgPath]; ok {
var err error
pkgAST, err := NewPackageAST(files)
if err != nil {
return nil, err
}
- pkgDocs = doc.New(pkgAST, pkg, doc.AllDecls)
- pkgDocs = dc.putPackageDocs(pkg, pkgDocs)
+ pkg = doc.New(pkgAST, pkgPath, doc.AllDecls)
+ pkg = c.putPackage(pkgPath, pkg)
} else {
- return nil, fmt.Errorf("unknown package %q", pkg)
+ return nil, fmt.Errorf("unknown package %q", pkgPath)
}
}
- return pkgDocs, nil
+ return pkg, nil
}
-func (dc *DocCollector) getPackageDocs(pkg string) *doc.Package {
- dc.mutex.Lock()
- defer dc.mutex.Unlock()
+func (c *Context) getPackage(pkgPath string) *doc.Package {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
- return dc.pkgDocs[pkg]
+ return c.pkgs[pkgPath]
}
-func (dc *DocCollector) putPackageDocs(pkg string, pkgDocs *doc.Package) *doc.Package {
- dc.mutex.Lock()
- defer dc.mutex.Unlock()
+func (c *Context) putPackage(pkgPath string, pkg *doc.Package) *doc.Package {
+ c.mutex.Lock()
+ defer c.mutex.Unlock()
- if dc.pkgDocs[pkg] != nil {
- return dc.pkgDocs[pkg]
+ if c.pkgs[pkgPath] != nil {
+ return c.pkgs[pkgPath]
} else {
- dc.pkgDocs[pkg] = pkgDocs
- return pkgDocs
+ c.pkgs[pkgPath] = pkg
+ return pkg
}
}
@@ -373,19 +378,19 @@
func Write(filename string, pkgFiles map[string][]string,
moduleTypePropertyStructs map[string][]interface{}) error {
- docSet := NewDocCollector(pkgFiles)
+ c := NewContext(pkgFiles)
- var moduleTypeList []*moduleTypeDoc
+ var moduleTypeList []*moduleType
for moduleType, propertyStructs := range moduleTypePropertyStructs {
- mtDoc, err := getModuleTypeDoc(docSet, moduleType, propertyStructs)
+ mt, err := getModuleType(c, moduleType, propertyStructs)
if err != nil {
return err
}
- removeEmptyPropertyStructs(mtDoc)
- collapseDuplicatePropertyStructs(mtDoc)
- collapseNestedPropertyStructs(mtDoc)
- combineDuplicateProperties(mtDoc)
- moduleTypeList = append(moduleTypeList, mtDoc)
+ removeEmptyPropertyStructs(mt)
+ collapseDuplicatePropertyStructs(mt)
+ collapseNestedPropertyStructs(mt)
+ combineDuplicateProperties(mt)
+ moduleTypeList = append(moduleTypeList, mt)
}
sort.Sort(moduleTypeByName(moduleTypeList))
@@ -416,11 +421,11 @@
return nil
}
-func getModuleTypeDoc(docSet *DocCollector, moduleType string,
- propertyStructs []interface{}) (*moduleTypeDoc, error) {
- mtDoc := &moduleTypeDoc{
- Name: moduleType,
- //Text: docSet.ModuleTypeDocs(moduleType),
+func getModuleType(c *Context, moduleTypeName string,
+ propertyStructs []interface{}) (*moduleType, error) {
+ mt := &moduleType{
+ Name: moduleTypeName,
+ //Text: c.ModuleTypeDocs(moduleType),
}
for _, s := range propertyStructs {
@@ -431,27 +436,27 @@
if t.PkgPath() == "" {
continue
}
- psDoc, err := docSet.Docs(t.PkgPath(), t.Name(), v)
+ ps, err := c.PropertyStruct(t.PkgPath(), t.Name(), v)
if err != nil {
return nil, err
}
- psDoc.ExcludeByTag("blueprint", "mutated")
+ ps.ExcludeByTag("blueprint", "mutated")
- for nested, nestedValue := range nestedPropertyStructs(v) {
+ for nestedName, nestedValue := range nestedPropertyStructs(v) {
nestedType := nestedValue.Type()
// Ignore property structs with unexported or unnamed types
if nestedType.PkgPath() == "" {
continue
}
- nestedDoc, err := docSet.Docs(nestedType.PkgPath(), nestedType.Name(), nestedValue)
+ nested, err := c.PropertyStruct(nestedType.PkgPath(), nestedType.Name(), nestedValue)
if err != nil {
return nil, err
}
- nestedDoc.ExcludeByTag("blueprint", "mutated")
- nestPoint := psDoc.GetByName(nested)
+ nested.ExcludeByTag("blueprint", "mutated")
+ nestPoint := ps.GetByName(nestedName)
if nestPoint == nil {
- return nil, fmt.Errorf("nesting point %q not found", nested)
+ return nil, fmt.Errorf("nesting point %q not found", nestedName)
}
key, value, err := blueprint.HasFilter(nestPoint.Tag)
@@ -459,15 +464,15 @@
return nil, err
}
if key != "" {
- nestedDoc.IncludeByTag(key, value)
+ nested.IncludeByTag(key, value)
}
- nestPoint.Nest(nestedDoc)
+ nestPoint.Nest(nested)
}
- mtDoc.PropertyStructs = append(mtDoc.PropertyStructs, psDoc)
+ mt.PropertyStructs = append(mt.PropertyStructs, ps)
}
- return mtDoc, nil
+ return mt, nil
}
func nestedPropertyStructs(s reflect.Value) map[string]reflect.Value {
@@ -520,33 +525,33 @@
}
// Remove any property structs that have no exported fields
-func removeEmptyPropertyStructs(mtDoc *moduleTypeDoc) {
- for i := 0; i < len(mtDoc.PropertyStructs); i++ {
- if len(mtDoc.PropertyStructs[i].Properties) == 0 {
- mtDoc.PropertyStructs = append(mtDoc.PropertyStructs[:i], mtDoc.PropertyStructs[i+1:]...)
+func removeEmptyPropertyStructs(mt *moduleType) {
+ for i := 0; i < len(mt.PropertyStructs); i++ {
+ if len(mt.PropertyStructs[i].Properties) == 0 {
+ mt.PropertyStructs = append(mt.PropertyStructs[:i], mt.PropertyStructs[i+1:]...)
i--
}
}
}
// Squashes duplicates of the same property struct into single entries
-func collapseDuplicatePropertyStructs(mtDoc *moduleTypeDoc) {
- var collapsedDocs []*PropertyStructDocs
+func collapseDuplicatePropertyStructs(mt *moduleType) {
+ var collapsed []*PropertyStruct
propertyStructLoop:
- for _, from := range mtDoc.PropertyStructs {
- for _, to := range collapsedDocs {
+ for _, from := range mt.PropertyStructs {
+ for _, to := range collapsed {
if from.Name == to.Name {
collapseDuplicateProperties(&to.Properties, &from.Properties)
continue propertyStructLoop
}
}
- collapsedDocs = append(collapsedDocs, from)
+ collapsed = append(collapsed, from)
}
- mtDoc.PropertyStructs = collapsedDocs
+ mt.PropertyStructs = collapsed
}
-func collapseDuplicateProperties(to, from *[]PropertyDocs) {
+func collapseDuplicateProperties(to, from *[]Property) {
propertyLoop:
for _, f := range *from {
for i := range *to {
@@ -562,14 +567,14 @@
// Find all property structs that only contain structs, and move their children up one with
// a prefixed name
-func collapseNestedPropertyStructs(mtDoc *moduleTypeDoc) {
- for _, ps := range mtDoc.PropertyStructs {
+func collapseNestedPropertyStructs(mt *moduleType) {
+ for _, ps := range mt.PropertyStructs {
collapseNestedProperties(&ps.Properties)
}
}
-func collapseNestedProperties(p *[]PropertyDocs) {
- var n []PropertyDocs
+func collapseNestedProperties(p *[]Property) {
+ var n []Property
for _, parent := range *p {
var containsProperty bool
@@ -594,14 +599,14 @@
*p = n
}
-func combineDuplicateProperties(mtDoc *moduleTypeDoc) {
- for _, ps := range mtDoc.PropertyStructs {
+func combineDuplicateProperties(mt *moduleType) {
+ for _, ps := range mt.PropertyStructs {
combineDuplicateSubProperties(&ps.Properties)
}
}
-func combineDuplicateSubProperties(p *[]PropertyDocs) {
- var n []PropertyDocs
+func combineDuplicateSubProperties(p *[]Property) {
+ var n []Property
propertyLoop:
for _, child := range *p {
if len(child.Properties) > 0 {
@@ -621,16 +626,16 @@
*p = n
}
-type moduleTypeByName []*moduleTypeDoc
+type moduleTypeByName []*moduleType
func (l moduleTypeByName) Len() int { return len(l) }
func (l moduleTypeByName) Less(i, j int) bool { return l[i].Name < l[j].Name }
func (l moduleTypeByName) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
-type moduleTypeDoc struct {
+type moduleType struct {
Name string
Text string
- PropertyStructs []*PropertyStructDocs
+ PropertyStructs []*PropertyStruct
}
var (
diff --git a/bpmodify/bpmodify.go b/bpmodify/bpmodify.go
index f4216dd..6f7a5bf 100644
--- a/bpmodify/bpmodify.go
+++ b/bpmodify/bpmodify.go
@@ -9,7 +9,6 @@
"bytes"
"flag"
"fmt"
- "github.com/google/blueprint/parser"
"io"
"io/ioutil"
"os"
@@ -17,6 +16,8 @@
"path/filepath"
"strings"
"unicode"
+
+ "github.com/google/blueprint/parser"
)
var (
@@ -123,8 +124,8 @@
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 @@
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 @@
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
diff --git a/build.ninja.in b/build.ninja.in
index 9ba88c8..b70ea22 100644
--- a/build.ninja.in
+++ b/build.ninja.in
@@ -81,7 +81,7 @@
# 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 @@
# 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 @@
# 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 @@
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 @@
# 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 @@
# 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 @@
# 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 @@
# 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 @@
# 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 @@
# 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 | $
diff --git a/context.go b/context.go
index dde4734..b644ac5 100644
--- a/context.go
+++ b/context.go
@@ -861,21 +861,22 @@
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 @@
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,
diff --git a/package_ctx.go b/package_ctx.go
index cedee04..8e27150 100644
--- a/package_ctx.go
+++ b/package_ctx.go
@@ -96,7 +96,7 @@
checkCalledFromInit()
if _, present := packageContexts[pkgPath]; present {
- panic(fmt.Errorf("package %q already has a package context"))
+ panic(fmt.Errorf("package %q already has a package context", pkgPath))
}
pkgName := pkgPathToName(pkgPath)
diff --git a/parser/ast.go b/parser/ast.go
new file mode 100644
index 0000000..387f6d5
--- /dev/null
+++ b/parser/ast.go
@@ -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)
+}
diff --git a/parser/modify.go b/parser/modify.go
index 1b11e2c..08a3f3f 100644
--- a/parser/modify.go
+++ b/parser/modify.go
@@ -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
diff --git a/parser/parser.go b/parser/parser.go
index fb931af..6909c50 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -41,6 +41,7 @@
Name string
Defs []Definition
Comments []Comment
+ Lines []scanner.Position
}
func parse(p *parser) (file *File, errs []error) {
@@ -178,8 +179,8 @@
}
}
-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 @@
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 @@
}
if !p.accept(p.tok) {
- return
+ return nil
}
properties := p.parsePropertyList(true, compat)
rbracePos := p.scanner.Position
@@ -244,11 +243,14 @@
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 @@
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) 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 @@
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 @@
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) 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 @@
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.StringValue = str
+
+ value := &String{
+ LiteralPos: p.scanner.Position,
+ Value: 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 @@
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 @@
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
-}
diff --git a/parser/parser_test.go b/parser/parser_test.go
index 8925684..e93bb09 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -39,9 +39,11 @@
`,
[]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 @@
`,
[]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 @@
`,
[]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 @@
`,
[]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 @@
`,
[]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 @@
{`
// 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 @@
[]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 @@
`,
[]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 @@
&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 @@
&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 @@
&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",
},
},
- Operator: '+',
- Pos: mkpos(41, 4, 13),
+ &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",
+ },
+ },
+ },
},
},
- 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",
},
},
- Operator: '+',
- Pos: mkpos(41, 4, 13),
+ &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",
+ },
+ },
+ },
},
},
Assigner: "=",
@@ -385,69 +407,90 @@
&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",
},
},
- Operator: '+',
- Pos: mkpos(41, 4, 13),
+ &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",
+ },
+ },
+ },
},
},
- {
- 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 @@
&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 @@
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])
diff --git a/parser/printer.go b/parser/printer.go
index b27f5e0..1e7bc2a 100644
--- a/parser/printer.go
+++ b/parser/printer.go
@@ -22,7 +22,7 @@
"unicode"
)
-var noPos = scanner.Position{}
+var noPos scanner.Position
type printer struct {
defs []Definition
@@ -91,96 +91,94 @@
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 @@
// 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 @@
// 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 @@
// 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 @@
// 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)
diff --git a/parser/printer_test.go b/parser/printer_test.go
index e5acf06..3e759cb 100644
--- a/parser/printer_test.go
+++ b/parser/printer_test.go
@@ -62,6 +62,49 @@
},
{
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"],
+}
+`,
+ },
+ {
+ input: `
foo {
stuff: {
isGood: true,
diff --git a/parser/sort.go b/parser/sort.go
index 381ef82..05ce5fd 100644
--- a/parser/sort.go
+++ b/parser/sort.go
@@ -32,40 +32,40 @@
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 @@
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 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) 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) {
diff --git a/unpack.go b/unpack.go
index b023956..11718b4 100644
--- a/unpack.go
+++ b/unpack.go
@@ -88,7 +88,6 @@
// 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 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 @@
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
}
diff --git a/unpack_test.go b/unpack_test.go
index b33ae79..7b314dd 100644
--- a/unpack_test.go
+++ b/unpack_test.go
@@ -27,7 +27,7 @@
var validUnpackTestCases = []struct {
input string
- output interface{}
+ output []interface{}
errs []error
}{
{`
@@ -36,14 +36,16 @@
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 @@
name: "abc",
}
`,
- struct {
- Name string
- }{
- Name: "abc",
+ []interface{}{
+ struct {
+ Name string
+ }{
+ Name: "abc",
+ },
},
nil,
},
@@ -66,10 +70,12 @@
isGood: true,
}
`,
- struct {
- IsGood bool
- }{
- IsGood: true,
+ []interface{}{
+ struct {
+ IsGood bool
+ }{
+ IsGood: true,
+ },
},
nil,
},
@@ -80,14 +86,16 @@
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 @@
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 @@
}
}
`,
- 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 @@
}
}
`,
- 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,23 +170,81 @@
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"},
},
- 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"},
+ },
},
[]error{
&Error{
Err: fmt.Errorf("filtered field nested.foo cannot be set in a Blueprint file"),
- Pos: scanner.Position{"", 27, 4, 8},
+ Pos: mkpos(27, 4, 8),
},
},
},
@@ -238,20 +258,22 @@
},
}
`,
- struct {
- EmbeddedStruct
- Nested struct {
+ []interface{}{
+ struct {
EmbeddedStruct
- }
- }{
- EmbeddedStruct: EmbeddedStruct{
- Name: "abc",
- },
- Nested: struct {
- EmbeddedStruct
+ Nested struct {
+ EmbeddedStruct
+ }
}{
EmbeddedStruct: EmbeddedStruct{
- Name: "def",
+ Name: "abc",
+ },
+ Nested: struct {
+ EmbeddedStruct
+ }{
+ EmbeddedStruct: EmbeddedStruct{
+ Name: "def",
+ },
},
},
},
@@ -267,20 +289,22 @@
},
}
`,
- struct {
- EmbeddedInterface
- Nested struct {
+ []interface{}{
+ struct {
EmbeddedInterface
- }
- }{
- EmbeddedInterface: &struct{ Name string }{
- Name: "abc",
- },
- Nested: 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 @@
},
}
`,
- struct {
- Name string
- EmbeddedStruct
- Nested struct {
+ []interface{}{
+ struct {
Name string
EmbeddedStruct
- }
- }{
- Name: "abc",
- EmbeddedStruct: EmbeddedStruct{
- Name: "abc",
- },
- Nested: 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 @@
},
}
`,
- struct {
- Name string
- EmbeddedInterface
- Nested struct {
+ []interface{}{
+ struct {
Name string
EmbeddedInterface
- }
- }{
- Name: "abc",
- EmbeddedInterface: &struct{ Name string }{
- Name: "abc",
- },
- Nested: 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 @@
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,53 @@
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)
+ }
+ }
}
}
}
+
+func mkpos(offset, line, column int) scanner.Position {
+ return scanner.Position{
+ Offset: offset,
+ Line: line,
+ Column: column,
+ }
+}