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",
|
name = "blueprint-parser",
|
||||||
pkgPath = "github.com/google/blueprint/parser",
|
pkgPath = "github.com/google/blueprint/parser",
|
||||||
srcs = [
|
srcs = [
|
||||||
|
"parser/ast.go",
|
||||||
"parser/modify.go",
|
"parser/modify.go",
|
||||||
"parser/parser.go",
|
"parser/parser.go",
|
||||||
"parser/printer.go",
|
"parser/printer.go",
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/google/blueprint/parser"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
@ -17,6 +16,8 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
"github.com/google/blueprint/parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -123,8 +124,8 @@ func findModules(file *parser.File) (modified bool, errs []error) {
|
||||||
for _, def := range file.Defs {
|
for _, def := range file.Defs {
|
||||||
if module, ok := def.(*parser.Module); ok {
|
if module, ok := def.(*parser.Module); ok {
|
||||||
for _, prop := range module.Properties {
|
for _, prop := range module.Properties {
|
||||||
if prop.Name.Name == "name" && prop.Value.Type == parser.String {
|
if prop.Name.Name == "name" && prop.Value.Type() == parser.StringType {
|
||||||
if targetedModule(prop.Value.StringValue) {
|
if targetedModule(prop.Value.Eval().(*parser.String).Value) {
|
||||||
m, newErrs := processModule(module, prop.Name.Name, file)
|
m, newErrs := processModule(module, prop.Name.Name, file)
|
||||||
errs = append(errs, newErrs...)
|
errs = append(errs, newErrs...)
|
||||||
modified = modified || m
|
modified = modified || m
|
||||||
|
@ -142,7 +143,7 @@ func processModule(module *parser.Module, moduleName string,
|
||||||
|
|
||||||
for _, prop := range module.Properties {
|
for _, prop := range module.Properties {
|
||||||
if prop.Name.Name == *parameter {
|
if prop.Name.Name == *parameter {
|
||||||
modified, errs = processParameter(&prop.Value, *parameter, moduleName, file)
|
modified, errs = processParameter(prop.Value, *parameter, moduleName, file)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,37 +151,38 @@ func processModule(module *parser.Module, moduleName string,
|
||||||
return false, nil
|
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) {
|
file *parser.File) (modified bool, errs []error) {
|
||||||
if value.Type != parser.List {
|
if _, ok := value.(*parser.Variable); ok {
|
||||||
return false, []error{fmt.Errorf("expected parameter %s in module %s to be list, found %s",
|
|
||||||
paramName, moduleName, value.Type.String())}
|
|
||||||
}
|
|
||||||
|
|
||||||
if value.Variable != "" {
|
|
||||||
return false, []error{fmt.Errorf("parameter %s in module %s is a variable, unsupported",
|
return false, []error{fmt.Errorf("parameter %s in module %s is a variable, unsupported",
|
||||||
paramName, moduleName)}
|
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",
|
return false, []error{fmt.Errorf("parameter %s in module %s is an expression, unsupported",
|
||||||
paramName, moduleName)}
|
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 {
|
for _, a := range addIdents.idents {
|
||||||
m := parser.AddStringToList(value, a)
|
m := parser.AddStringToList(list, a)
|
||||||
modified = modified || m
|
modified = modified || m
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, r := range removeIdents.idents {
|
for _, r := range removeIdents.idents {
|
||||||
m := parser.RemoveStringFromList(value, r)
|
m := parser.RemoveStringFromList(list, r)
|
||||||
modified = modified || m
|
modified = modified || m
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wasSorted || *sortLists) && modified {
|
if (wasSorted || *sortLists) && modified {
|
||||||
parser.SortList(file, *value)
|
parser.SortList(file, list)
|
||||||
}
|
}
|
||||||
|
|
||||||
return modified, nil
|
return modified, nil
|
||||||
|
|
|
@ -81,7 +81,7 @@ default $
|
||||||
# Variant:
|
# Variant:
|
||||||
# Type: bootstrap_go_package
|
# Type: bootstrap_go_package
|
||||||
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
||||||
# Defined: Blueprints:80:1
|
# Defined: Blueprints:81:1
|
||||||
|
|
||||||
build $
|
build $
|
||||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a $
|
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap/pkg/github.com/google/blueprint/bootstrap.a $
|
||||||
|
@ -108,7 +108,7 @@ default $
|
||||||
# Variant:
|
# Variant:
|
||||||
# Type: bootstrap_go_package
|
# Type: bootstrap_go_package
|
||||||
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
||||||
# Defined: Blueprints:99:1
|
# Defined: Blueprints:100:1
|
||||||
|
|
||||||
build $
|
build $
|
||||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a $
|
${g.bootstrap.buildDir}/.bootstrap/blueprint-bootstrap-bpdoc/pkg/github.com/google/blueprint/bootstrap/bpdoc.a $
|
||||||
|
@ -128,7 +128,7 @@ default $
|
||||||
# Variant:
|
# Variant:
|
||||||
# Type: bootstrap_go_package
|
# Type: bootstrap_go_package
|
||||||
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
||||||
# Defined: Blueprints:46:1
|
# Defined: Blueprints:47:1
|
||||||
|
|
||||||
build $
|
build $
|
||||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
|
${g.bootstrap.buildDir}/.bootstrap/blueprint-deptools/pkg/github.com/google/blueprint/deptools.a $
|
||||||
|
@ -147,7 +147,8 @@ default $
|
||||||
|
|
||||||
build $
|
build $
|
||||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-parser/pkg/github.com/google/blueprint/parser.a $
|
${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/parser.go $
|
||||||
${g.bootstrap.srcDir}/parser/printer.go $
|
${g.bootstrap.srcDir}/parser/printer.go $
|
||||||
${g.bootstrap.srcDir}/parser/sort.go | ${g.bootstrap.compileCmd}
|
${g.bootstrap.srcDir}/parser/sort.go | ${g.bootstrap.compileCmd}
|
||||||
|
@ -160,7 +161,7 @@ default $
|
||||||
# Variant:
|
# Variant:
|
||||||
# Type: bootstrap_go_package
|
# Type: bootstrap_go_package
|
||||||
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
||||||
# Defined: Blueprints:52:1
|
# Defined: Blueprints:53:1
|
||||||
|
|
||||||
build $
|
build $
|
||||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
|
${g.bootstrap.buildDir}/.bootstrap/blueprint-pathtools/pkg/github.com/google/blueprint/pathtools.a $
|
||||||
|
@ -175,7 +176,7 @@ default $
|
||||||
# Variant:
|
# Variant:
|
||||||
# Type: bootstrap_go_package
|
# Type: bootstrap_go_package
|
||||||
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
# Factory: github.com/google/blueprint/bootstrap.newGoPackageModuleFactory.func1
|
||||||
# Defined: Blueprints:64:1
|
# Defined: Blueprints:65:1
|
||||||
|
|
||||||
build $
|
build $
|
||||||
${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
|
${g.bootstrap.buildDir}/.bootstrap/blueprint-proptools/pkg/github.com/google/blueprint/proptools.a $
|
||||||
|
@ -193,7 +194,7 @@ default $
|
||||||
# Variant:
|
# Variant:
|
||||||
# Type: bootstrap_core_go_binary
|
# Type: bootstrap_core_go_binary
|
||||||
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
|
# 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: $
|
build ${g.bootstrap.buildDir}/.bootstrap/choosestage/obj/choosestage.a: $
|
||||||
g.bootstrap.compile ${g.bootstrap.srcDir}/choosestage/choosestage.go | $
|
g.bootstrap.compile ${g.bootstrap.srcDir}/choosestage/choosestage.go | $
|
||||||
|
@ -216,7 +217,7 @@ default ${g.bootstrap.BinDir}/choosestage
|
||||||
# Variant:
|
# Variant:
|
||||||
# Type: bootstrap_core_go_binary
|
# Type: bootstrap_core_go_binary
|
||||||
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
|
# 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: $
|
build ${g.bootstrap.buildDir}/.bootstrap/gotestmain/obj/gotestmain.a: $
|
||||||
g.bootstrap.compile ${g.bootstrap.srcDir}/gotestmain/gotestmain.go | $
|
g.bootstrap.compile ${g.bootstrap.srcDir}/gotestmain/gotestmain.go | $
|
||||||
|
@ -239,7 +240,7 @@ default ${g.bootstrap.BinDir}/gotestmain
|
||||||
# Variant:
|
# Variant:
|
||||||
# Type: bootstrap_core_go_binary
|
# Type: bootstrap_core_go_binary
|
||||||
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
|
# 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: $
|
build ${g.bootstrap.buildDir}/.bootstrap/gotestrunner/obj/gotestrunner.a: $
|
||||||
g.bootstrap.compile ${g.bootstrap.srcDir}/gotestrunner/gotestrunner.go $
|
g.bootstrap.compile ${g.bootstrap.srcDir}/gotestrunner/gotestrunner.go $
|
||||||
|
@ -262,7 +263,7 @@ default ${g.bootstrap.BinDir}/gotestrunner
|
||||||
# Variant:
|
# Variant:
|
||||||
# Type: bootstrap_core_go_binary
|
# Type: bootstrap_core_go_binary
|
||||||
# Factory: github.com/google/blueprint/bootstrap.newGoBinaryModuleFactory.func1
|
# 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: $
|
build ${g.bootstrap.buildDir}/.bootstrap/minibp/obj/minibp.a: $
|
||||||
g.bootstrap.compile ${g.bootstrap.srcDir}/bootstrap/minibp/main.go | $
|
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 {
|
if assignment, local := scope.Get(v); assignment == nil || !local {
|
||||||
return nil, scanner.Position{}, nil
|
return nil, scanner.Position{}, nil
|
||||||
} else {
|
} else {
|
||||||
switch assignment.Value.Type {
|
switch value := assignment.Value.Eval().(type) {
|
||||||
case parser.List:
|
case *parser.List:
|
||||||
ret := make([]string, 0, len(assignment.Value.ListValue))
|
ret := make([]string, 0, len(value.Values))
|
||||||
|
|
||||||
for _, value := range assignment.Value.ListValue {
|
for _, listValue := range value.Values {
|
||||||
if value.Type != parser.String {
|
s, ok := listValue.(*parser.String)
|
||||||
|
if !ok {
|
||||||
// The parser should not produce this.
|
// The parser should not produce this.
|
||||||
panic("non-string value found in list")
|
panic("non-string value found in list")
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = append(ret, value.StringValue)
|
ret = append(ret, s.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret, assignment.Pos, nil
|
return ret, assignment.Pos, nil
|
||||||
case parser.Bool, parser.String:
|
case *parser.Bool, *parser.String:
|
||||||
return nil, scanner.Position{}, &Error{
|
return nil, scanner.Position{}, &Error{
|
||||||
Err: fmt.Errorf("%q must be a list of strings", v),
|
Err: fmt.Errorf("%q must be a list of strings", v),
|
||||||
Pos: assignment.Pos,
|
Pos: assignment.Pos,
|
||||||
|
@ -890,10 +891,10 @@ func getStringFromScope(scope *parser.Scope, v string) (string, scanner.Position
|
||||||
if assignment, _ := scope.Get(v); assignment == nil {
|
if assignment, _ := scope.Get(v); assignment == nil {
|
||||||
return "", scanner.Position{}, nil
|
return "", scanner.Position{}, nil
|
||||||
} else {
|
} else {
|
||||||
switch assignment.Value.Type {
|
switch value := assignment.Value.Eval().(type) {
|
||||||
case parser.String:
|
case *parser.String:
|
||||||
return assignment.Value.StringValue, assignment.Pos, nil
|
return value.Value, assignment.Pos, nil
|
||||||
case parser.Bool, parser.List:
|
case *parser.Bool, *parser.List:
|
||||||
return "", scanner.Position{}, &Error{
|
return "", scanner.Position{}, &Error{
|
||||||
Err: fmt.Errorf("%q must be a string", v),
|
Err: fmt.Errorf("%q must be a string", v),
|
||||||
Pos: assignment.Pos,
|
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
|
package parser
|
||||||
|
|
||||||
func AddStringToList(value *Value, s string) (modified bool) {
|
import "fmt"
|
||||||
if value.Type != List {
|
|
||||||
panic("expected list value, 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()))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range value.ListValue {
|
if sv, ok := v.(*String); ok && sv.Value == s {
|
||||||
if v.Type != String {
|
|
||||||
panic("expected string in list, got " + value.Type.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.StringValue == s {
|
|
||||||
// string already exists
|
// string already exists
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
value.ListValue = append(value.ListValue, Value{
|
list.Values = append(list.Values, &String{
|
||||||
Type: String,
|
LiteralPos: list.RBracePos,
|
||||||
Pos: value.EndPos,
|
Value: s,
|
||||||
StringValue: s,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func RemoveStringFromList(value *Value, s string) (modified bool) {
|
func RemoveStringFromList(list *List, s string) (modified bool) {
|
||||||
if value.Type != List {
|
for i, v := range list.Values {
|
||||||
panic("expected list value, got " + value.Type.String())
|
if v.Type() != StringType {
|
||||||
|
panic(fmt.Errorf("expected string in list, got %s", v.Type()))
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, v := range value.ListValue {
|
if sv, ok := v.(*String); ok && sv.Value == s {
|
||||||
if v.Type != String {
|
list.Values = append(list.Values[:i], list.Values[i+1:]...)
|
||||||
panic("expected string in list, got " + value.Type.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
if v.StringValue == s {
|
|
||||||
value.ListValue = append(value.ListValue[:i], value.ListValue[i+1:]...)
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
|
400
parser/parser.go
400
parser/parser.go
|
@ -41,6 +41,7 @@ type File struct {
|
||||||
Name string
|
Name string
|
||||||
Defs []Definition
|
Defs []Definition
|
||||||
Comments []Comment
|
Comments []Comment
|
||||||
|
Lines []scanner.Position
|
||||||
}
|
}
|
||||||
|
|
||||||
func parse(p *parser) (file *File, errs []error) {
|
func parse(p *parser) (file *File, errs []error) {
|
||||||
|
@ -178,8 +179,8 @@ func (p *parser) parseDefinitions() (defs []Definition) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseAssignment(name string,
|
func (p *parser) parseAssignment(name string, namePos scanner.Position,
|
||||||
namePos scanner.Position, assigner string) (assignment *Assignment) {
|
assigner string) (assignment *Assignment) {
|
||||||
|
|
||||||
assignment = new(Assignment)
|
assignment = new(Assignment)
|
||||||
|
|
||||||
|
@ -223,10 +224,8 @@ func (p *parser) parseAssignment(name string,
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseModule(typ string,
|
func (p *parser) parseModule(typ string, typPos scanner.Position) *Module {
|
||||||
typPos scanner.Position) (module *Module) {
|
|
||||||
|
|
||||||
module = new(Module)
|
|
||||||
compat := false
|
compat := false
|
||||||
lbracePos := p.scanner.Position
|
lbracePos := p.scanner.Position
|
||||||
if p.tok == '{' {
|
if p.tok == '{' {
|
||||||
|
@ -234,7 +233,7 @@ func (p *parser) parseModule(typ string,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !p.accept(p.tok) {
|
if !p.accept(p.tok) {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
properties := p.parsePropertyList(true, compat)
|
properties := p.parsePropertyList(true, compat)
|
||||||
rbracePos := p.scanner.Position
|
rbracePos := p.scanner.Position
|
||||||
|
@ -244,11 +243,14 @@ func (p *parser) parseModule(typ string,
|
||||||
p.accept('}')
|
p.accept('}')
|
||||||
}
|
}
|
||||||
|
|
||||||
module.Type = Ident{typ, typPos}
|
return &Module{
|
||||||
module.Properties = properties
|
Type: Ident{typ, typPos},
|
||||||
module.LbracePos = lbracePos
|
Map: Map{
|
||||||
module.RbracePos = rbracePos
|
Properties: properties,
|
||||||
return
|
LBracePos: lbracePos,
|
||||||
|
RBracePos: rbracePos,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parsePropertyList(isModule, compat bool) (properties []*Property) {
|
func (p *parser) parsePropertyList(isModule, compat bool) (properties []*Property) {
|
||||||
|
@ -298,7 +300,7 @@ func (p *parser) parseProperty(isModule, compat bool) (property *Property) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseExpression() (value Value) {
|
func (p *parser) parseExpression() (value Expression) {
|
||||||
value = p.parseValue()
|
value = p.parseValue()
|
||||||
switch p.tok {
|
switch p.tok {
|
||||||
case '+':
|
case '+':
|
||||||
|
@ -308,50 +310,48 @@ func (p *parser) parseExpression() (value Value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) evaluateOperator(value1, value2 Value, operator rune,
|
func (p *parser) evaluateOperator(value1, value2 Expression, operator rune,
|
||||||
pos scanner.Position) (Value, error) {
|
pos scanner.Position) (*Operator, error) {
|
||||||
|
|
||||||
value := Value{}
|
value := value1
|
||||||
|
|
||||||
if p.eval {
|
if p.eval {
|
||||||
if value1.Type != value2.Type {
|
e1 := value1.Eval()
|
||||||
return Value{}, fmt.Errorf("mismatched type in operator %c: %s != %s", operator,
|
e2 := value2.Eval()
|
||||||
value1.Type, value2.Type)
|
if e1.Type() != e2.Type() {
|
||||||
|
return nil, fmt.Errorf("mismatched type in operator %c: %s != %s", operator,
|
||||||
|
e1.Type(), e2.Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
value = value1
|
value = e1.Copy()
|
||||||
value.Variable = ""
|
|
||||||
|
|
||||||
switch operator {
|
switch operator {
|
||||||
case '+':
|
case '+':
|
||||||
switch value1.Type {
|
switch v := value.(type) {
|
||||||
case String:
|
case *String:
|
||||||
value.StringValue = value1.StringValue + value2.StringValue
|
v.Value += e2.(*String).Value
|
||||||
case List:
|
case *List:
|
||||||
value.ListValue = append([]Value{}, value1.ListValue...)
|
v.Values = append(v.Values, e2.(*List).Values...)
|
||||||
value.ListValue = append(value.ListValue, value2.ListValue...)
|
case *Map:
|
||||||
case Map:
|
|
||||||
var err error
|
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 {
|
if err != nil {
|
||||||
return Value{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return Value{}, fmt.Errorf("operator %c not supported on type %s", operator,
|
return nil, fmt.Errorf("operator %c not supported on type %s", operator, v.Type())
|
||||||
value1.Type)
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic("unknown operator " + string(operator))
|
panic("unknown operator " + string(operator))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
value.Expression = &Expression{
|
return &Operator{
|
||||||
Args: [2]Value{value1, value2},
|
Args: [2]Expression{value1, value2},
|
||||||
Operator: operator,
|
Operator: operator,
|
||||||
Pos: pos,
|
OperatorPos: pos,
|
||||||
}
|
Value: value,
|
||||||
|
}, nil
|
||||||
return value, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) addMaps(map1, map2 []*Property, pos scanner.Position) ([]*Property, error) {
|
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
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseOperator(value1 Value) Value {
|
func (p *parser) parseOperator(value1 Expression) *Operator {
|
||||||
operator := p.tok
|
operator := p.tok
|
||||||
pos := p.scanner.Position
|
pos := p.scanner.Position
|
||||||
p.accept(operator)
|
p.accept(operator)
|
||||||
|
@ -405,13 +405,14 @@ func (p *parser) parseOperator(value1 Value) Value {
|
||||||
value, err := p.evaluateOperator(value1, value2, operator, pos)
|
value, err := p.evaluateOperator(value1, value2, operator, pos)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.error(err)
|
p.error(err)
|
||||||
return Value{}
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseValue() (value Value) {
|
func (p *parser) parseValue() (value Expression) {
|
||||||
switch p.tok {
|
switch p.tok {
|
||||||
case scanner.Ident:
|
case scanner.Ident:
|
||||||
return p.parseVariable()
|
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 {
|
switch text := p.scanner.TokenText(); text {
|
||||||
case "true":
|
case "true", "false":
|
||||||
value.Type = Bool
|
value = &Bool{
|
||||||
value.BoolValue = true
|
LiteralPos: p.scanner.Position,
|
||||||
case "false":
|
Value: text == "true",
|
||||||
value.Type = Bool
|
}
|
||||||
value.BoolValue = false
|
|
||||||
default:
|
default:
|
||||||
variable := p.scanner.TokenText()
|
|
||||||
if p.eval {
|
if p.eval {
|
||||||
if assignment, local := p.scope.Get(variable); assignment == nil {
|
if assignment, local := p.scope.Get(text); assignment == nil {
|
||||||
p.errorf("variable %q is not set", variable)
|
p.errorf("variable %q is not set", text)
|
||||||
} else {
|
} else {
|
||||||
if local {
|
if local {
|
||||||
assignment.Referenced = true
|
assignment.Referenced = true
|
||||||
|
@ -448,40 +449,44 @@ func (p *parser) parseVariable() (value Value) {
|
||||||
value = assignment.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)
|
p.accept(scanner.Ident)
|
||||||
return
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseStringValue() (value Value) {
|
func (p *parser) parseStringValue() *String {
|
||||||
value.Type = String
|
|
||||||
value.Pos = p.scanner.Position
|
|
||||||
str, err := strconv.Unquote(p.scanner.TokenText())
|
str, err := strconv.Unquote(p.scanner.TokenText())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.errorf("couldn't parse string: %s", err)
|
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)
|
p.accept(scanner.String)
|
||||||
return
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseListValue() (value Value) {
|
func (p *parser) parseListValue() *List {
|
||||||
value.Type = List
|
lBracePos := p.scanner.Position
|
||||||
value.Pos = p.scanner.Position
|
|
||||||
if !p.accept('[') {
|
if !p.accept('[') {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var elements []Value
|
var elements []Expression
|
||||||
for p.tok != ']' {
|
for p.tok != ']' {
|
||||||
element := p.parseExpression()
|
element := p.parseExpression()
|
||||||
if p.eval && element.Type != String {
|
if p.eval && element.Type() != StringType {
|
||||||
p.errorf("Expected string in list, found %s", element.String())
|
p.errorf("Expected string in list, found %s", element.Type().String())
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
elements = append(elements, element)
|
elements = append(elements, element)
|
||||||
|
|
||||||
|
@ -493,208 +498,32 @@ func (p *parser) parseListValue() (value Value) {
|
||||||
p.accept(',')
|
p.accept(',')
|
||||||
}
|
}
|
||||||
|
|
||||||
value.ListValue = elements
|
rBracePos := p.scanner.Position
|
||||||
value.EndPos = p.scanner.Position
|
|
||||||
|
|
||||||
p.accept(']')
|
p.accept(']')
|
||||||
return
|
|
||||||
|
return &List{
|
||||||
|
LBracePos: lBracePos,
|
||||||
|
RBracePos: rBracePos,
|
||||||
|
Values: elements,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parser) parseMapValue() (value Value) {
|
func (p *parser) parseMapValue() *Map {
|
||||||
value.Type = Map
|
lBracePos := p.scanner.Position
|
||||||
value.Pos = p.scanner.Position
|
|
||||||
if !p.accept('{') {
|
if !p.accept('{') {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
properties := p.parsePropertyList(false, false)
|
properties := p.parsePropertyList(false, false)
|
||||||
value.MapValue = properties
|
|
||||||
|
|
||||||
value.EndPos = p.scanner.Position
|
rBracePos := p.scanner.Position
|
||||||
p.accept('}')
|
p.accept('}')
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type Expression struct {
|
return &Map{
|
||||||
Args [2]Value
|
LBracePos: lBracePos,
|
||||||
Operator rune
|
RBracePos: rBracePos,
|
||||||
Pos scanner.Position
|
Properties: properties,
|
||||||
}
|
}
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
type Scope struct {
|
||||||
|
@ -774,58 +603,3 @@ func (s *Scope) String() string {
|
||||||
|
|
||||||
return strings.Join(ret, "\n")
|
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -40,8 +40,10 @@ var validParseTestCases = []struct {
|
||||||
[]Definition{
|
[]Definition{
|
||||||
&Module{
|
&Module{
|
||||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||||
LbracePos: mkpos(7, 2, 7),
|
Map: Map{
|
||||||
RbracePos: mkpos(8, 2, 8),
|
LBracePos: mkpos(7, 2, 7),
|
||||||
|
RBracePos: mkpos(8, 2, 8),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
@ -55,16 +57,17 @@ var validParseTestCases = []struct {
|
||||||
[]Definition{
|
[]Definition{
|
||||||
&Module{
|
&Module{
|
||||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||||
LbracePos: mkpos(7, 2, 7),
|
Map: Map{
|
||||||
RbracePos: mkpos(27, 4, 3),
|
LBracePos: mkpos(7, 2, 7),
|
||||||
|
RBracePos: mkpos(27, 4, 3),
|
||||||
Properties: []*Property{
|
Properties: []*Property{
|
||||||
{
|
{
|
||||||
Name: Ident{"name", mkpos(12, 3, 4)},
|
Name: Ident{"name", mkpos(12, 3, 4)},
|
||||||
Pos: mkpos(16, 3, 8),
|
Pos: mkpos(16, 3, 8),
|
||||||
Value: Value{
|
Value: &String{
|
||||||
Type: String,
|
LiteralPos: mkpos(18, 3, 10),
|
||||||
Pos: mkpos(18, 3, 10),
|
Value: "abc",
|
||||||
StringValue: "abc",
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -81,16 +84,17 @@ var validParseTestCases = []struct {
|
||||||
[]Definition{
|
[]Definition{
|
||||||
&Module{
|
&Module{
|
||||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||||
LbracePos: mkpos(7, 2, 7),
|
Map: Map{
|
||||||
RbracePos: mkpos(28, 4, 3),
|
LBracePos: mkpos(7, 2, 7),
|
||||||
|
RBracePos: mkpos(28, 4, 3),
|
||||||
Properties: []*Property{
|
Properties: []*Property{
|
||||||
{
|
{
|
||||||
Name: Ident{"isGood", mkpos(12, 3, 4)},
|
Name: Ident{"isGood", mkpos(12, 3, 4)},
|
||||||
Pos: mkpos(18, 3, 10),
|
Pos: mkpos(18, 3, 10),
|
||||||
Value: Value{
|
Value: &Bool{
|
||||||
Type: Bool,
|
LiteralPos: mkpos(20, 3, 12),
|
||||||
Pos: mkpos(20, 3, 12),
|
Value: true,
|
||||||
BoolValue: true,
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -108,41 +112,37 @@ var validParseTestCases = []struct {
|
||||||
[]Definition{
|
[]Definition{
|
||||||
&Module{
|
&Module{
|
||||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||||
LbracePos: mkpos(7, 2, 7),
|
Map: Map{
|
||||||
RbracePos: mkpos(67, 5, 3),
|
LBracePos: mkpos(7, 2, 7),
|
||||||
|
RBracePos: mkpos(67, 5, 3),
|
||||||
Properties: []*Property{
|
Properties: []*Property{
|
||||||
{
|
{
|
||||||
Name: Ident{"stuff", mkpos(12, 3, 4)},
|
Name: Ident{"stuff", mkpos(12, 3, 4)},
|
||||||
Pos: mkpos(17, 3, 9),
|
Pos: mkpos(17, 3, 9),
|
||||||
Value: Value{
|
Value: &List{
|
||||||
Type: List,
|
LBracePos: mkpos(19, 3, 11),
|
||||||
Pos: mkpos(19, 3, 11),
|
RBracePos: mkpos(63, 4, 19),
|
||||||
EndPos: mkpos(63, 4, 19),
|
Values: []Expression{
|
||||||
ListValue: []Value{
|
&String{
|
||||||
Value{
|
LiteralPos: mkpos(20, 3, 12),
|
||||||
Type: String,
|
Value: "asdf",
|
||||||
Pos: mkpos(20, 3, 12),
|
|
||||||
StringValue: "asdf",
|
|
||||||
},
|
},
|
||||||
Value{
|
&String{
|
||||||
Type: String,
|
LiteralPos: mkpos(28, 3, 20),
|
||||||
Pos: mkpos(28, 3, 20),
|
Value: "jkl;",
|
||||||
StringValue: "jkl;",
|
|
||||||
},
|
},
|
||||||
Value{
|
&String{
|
||||||
Type: String,
|
LiteralPos: mkpos(36, 3, 28),
|
||||||
Pos: mkpos(36, 3, 28),
|
Value: "qwert",
|
||||||
StringValue: "qwert",
|
|
||||||
},
|
},
|
||||||
Value{
|
&String{
|
||||||
Type: String,
|
LiteralPos: mkpos(49, 4, 5),
|
||||||
Pos: mkpos(49, 4, 5),
|
Value: "uiop",
|
||||||
StringValue: "uiop",
|
},
|
||||||
|
&String{
|
||||||
|
LiteralPos: mkpos(57, 4, 13),
|
||||||
|
Value: "bnm,",
|
||||||
},
|
},
|
||||||
Value{
|
|
||||||
Type: String,
|
|
||||||
Pos: mkpos(57, 4, 13),
|
|
||||||
StringValue: "bnm,",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -164,33 +164,32 @@ var validParseTestCases = []struct {
|
||||||
[]Definition{
|
[]Definition{
|
||||||
&Module{
|
&Module{
|
||||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||||
LbracePos: mkpos(7, 2, 7),
|
Map: Map{
|
||||||
RbracePos: mkpos(62, 7, 3),
|
LBracePos: mkpos(7, 2, 7),
|
||||||
|
RBracePos: mkpos(62, 7, 3),
|
||||||
Properties: []*Property{
|
Properties: []*Property{
|
||||||
{
|
{
|
||||||
Name: Ident{"stuff", mkpos(12, 3, 4)},
|
Name: Ident{"stuff", mkpos(12, 3, 4)},
|
||||||
Pos: mkpos(17, 3, 9),
|
Pos: mkpos(17, 3, 9),
|
||||||
Value: Value{
|
Value: &Map{
|
||||||
Type: Map,
|
LBracePos: mkpos(19, 3, 11),
|
||||||
Pos: mkpos(19, 3, 11),
|
RBracePos: mkpos(58, 6, 4),
|
||||||
EndPos: mkpos(58, 6, 4),
|
Properties: []*Property{
|
||||||
MapValue: []*Property{
|
|
||||||
{
|
{
|
||||||
Name: Ident{"isGood", mkpos(25, 4, 5)},
|
Name: Ident{"isGood", mkpos(25, 4, 5)},
|
||||||
Pos: mkpos(31, 4, 11),
|
Pos: mkpos(31, 4, 11),
|
||||||
Value: Value{
|
Value: &Bool{
|
||||||
Type: Bool,
|
LiteralPos: mkpos(33, 4, 13),
|
||||||
Pos: mkpos(33, 4, 13),
|
Value: true,
|
||||||
BoolValue: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: Ident{"name", mkpos(43, 5, 5)},
|
Name: Ident{"name", mkpos(43, 5, 5)},
|
||||||
Pos: mkpos(47, 5, 9),
|
Pos: mkpos(47, 5, 9),
|
||||||
Value: Value{
|
Value: &String{
|
||||||
Type: String,
|
LiteralPos: mkpos(49, 5, 11),
|
||||||
Pos: mkpos(49, 5, 11),
|
Value: "bar",
|
||||||
StringValue: "bar",
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -204,7 +203,7 @@ var validParseTestCases = []struct {
|
||||||
|
|
||||||
{`
|
{`
|
||||||
// comment1
|
// comment1
|
||||||
foo {
|
foo /* test */ {
|
||||||
// comment2
|
// comment2
|
||||||
isGood: true, // comment3
|
isGood: true, // comment3
|
||||||
}
|
}
|
||||||
|
@ -212,16 +211,17 @@ var validParseTestCases = []struct {
|
||||||
[]Definition{
|
[]Definition{
|
||||||
&Module{
|
&Module{
|
||||||
Type: Ident{"foo", mkpos(17, 3, 3)},
|
Type: Ident{"foo", mkpos(17, 3, 3)},
|
||||||
LbracePos: mkpos(21, 3, 7),
|
Map: Map{
|
||||||
RbracePos: mkpos(70, 6, 3),
|
LBracePos: mkpos(32, 3, 18),
|
||||||
|
RBracePos: mkpos(81, 6, 3),
|
||||||
Properties: []*Property{
|
Properties: []*Property{
|
||||||
{
|
{
|
||||||
Name: Ident{"isGood", mkpos(41, 5, 4)},
|
Name: Ident{"isGood", mkpos(52, 5, 4)},
|
||||||
Pos: mkpos(47, 5, 10),
|
Pos: mkpos(58, 5, 10),
|
||||||
Value: Value{
|
Value: &Bool{
|
||||||
Type: Bool,
|
LiteralPos: mkpos(60, 5, 12),
|
||||||
Pos: mkpos(49, 5, 12),
|
Value: true,
|
||||||
BoolValue: true,
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -230,15 +230,19 @@ var validParseTestCases = []struct {
|
||||||
[]Comment{
|
[]Comment{
|
||||||
Comment{
|
Comment{
|
||||||
Comment: []string{"// comment1"},
|
Comment: []string{"// comment1"},
|
||||||
Pos: mkpos(3, 2, 3),
|
Slash: mkpos(3, 2, 3),
|
||||||
|
},
|
||||||
|
Comment{
|
||||||
|
Comment: []string{"/* test */"},
|
||||||
|
Slash: mkpos(21, 3, 7),
|
||||||
},
|
},
|
||||||
Comment{
|
Comment{
|
||||||
Comment: []string{"// comment2"},
|
Comment: []string{"// comment2"},
|
||||||
Pos: mkpos(26, 4, 4),
|
Slash: mkpos(37, 4, 4),
|
||||||
},
|
},
|
||||||
Comment{
|
Comment{
|
||||||
Comment: []string{"// comment3"},
|
Comment: []string{"// comment3"},
|
||||||
Pos: mkpos(56, 5, 19),
|
Slash: mkpos(67, 5, 19),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -255,32 +259,34 @@ var validParseTestCases = []struct {
|
||||||
[]Definition{
|
[]Definition{
|
||||||
&Module{
|
&Module{
|
||||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||||
LbracePos: mkpos(7, 2, 7),
|
Map: Map{
|
||||||
RbracePos: mkpos(27, 4, 3),
|
LBracePos: mkpos(7, 2, 7),
|
||||||
|
RBracePos: mkpos(27, 4, 3),
|
||||||
Properties: []*Property{
|
Properties: []*Property{
|
||||||
{
|
{
|
||||||
Name: Ident{"name", mkpos(12, 3, 4)},
|
Name: Ident{"name", mkpos(12, 3, 4)},
|
||||||
Pos: mkpos(16, 3, 8),
|
Pos: mkpos(16, 3, 8),
|
||||||
Value: Value{
|
Value: &String{
|
||||||
Type: String,
|
LiteralPos: mkpos(18, 3, 10),
|
||||||
Pos: mkpos(18, 3, 10),
|
Value: "abc",
|
||||||
StringValue: "abc",
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&Module{
|
&Module{
|
||||||
Type: Ident{"bar", mkpos(32, 6, 3)},
|
Type: Ident{"bar", mkpos(32, 6, 3)},
|
||||||
LbracePos: mkpos(36, 6, 7),
|
Map: Map{
|
||||||
RbracePos: mkpos(56, 8, 3),
|
LBracePos: mkpos(36, 6, 7),
|
||||||
|
RBracePos: mkpos(56, 8, 3),
|
||||||
Properties: []*Property{
|
Properties: []*Property{
|
||||||
{
|
{
|
||||||
Name: Ident{"name", mkpos(41, 7, 4)},
|
Name: Ident{"name", mkpos(41, 7, 4)},
|
||||||
Pos: mkpos(45, 7, 8),
|
Pos: mkpos(45, 7, 8),
|
||||||
Value: Value{
|
Value: &String{
|
||||||
Type: String,
|
LiteralPos: mkpos(47, 7, 10),
|
||||||
Pos: mkpos(47, 7, 10),
|
Value: "def",
|
||||||
StringValue: "def",
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -299,15 +305,13 @@ var validParseTestCases = []struct {
|
||||||
&Assignment{
|
&Assignment{
|
||||||
Name: Ident{"foo", mkpos(3, 2, 3)},
|
Name: Ident{"foo", mkpos(3, 2, 3)},
|
||||||
Pos: mkpos(7, 2, 7),
|
Pos: mkpos(7, 2, 7),
|
||||||
Value: Value{
|
Value: &String{
|
||||||
Type: String,
|
LiteralPos: mkpos(9, 2, 9),
|
||||||
Pos: mkpos(9, 2, 9),
|
Value: "stuff",
|
||||||
StringValue: "stuff",
|
|
||||||
},
|
},
|
||||||
OrigValue: Value{
|
OrigValue: &String{
|
||||||
Type: String,
|
LiteralPos: mkpos(9, 2, 9),
|
||||||
Pos: mkpos(9, 2, 9),
|
Value: "stuff",
|
||||||
StringValue: "stuff",
|
|
||||||
},
|
},
|
||||||
Assigner: "=",
|
Assigner: "=",
|
||||||
Referenced: true,
|
Referenced: true,
|
||||||
|
@ -315,17 +319,21 @@ var validParseTestCases = []struct {
|
||||||
&Assignment{
|
&Assignment{
|
||||||
Name: Ident{"bar", mkpos(19, 3, 3)},
|
Name: Ident{"bar", mkpos(19, 3, 3)},
|
||||||
Pos: mkpos(23, 3, 7),
|
Pos: mkpos(23, 3, 7),
|
||||||
Value: Value{
|
Value: &Variable{
|
||||||
Type: String,
|
Name: "foo",
|
||||||
Pos: mkpos(25, 3, 9),
|
NamePos: mkpos(25, 3, 9),
|
||||||
StringValue: "stuff",
|
Value: &String{
|
||||||
Variable: "foo",
|
LiteralPos: mkpos(9, 2, 9),
|
||||||
|
Value: "stuff",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OrigValue: &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",
|
|
||||||
},
|
},
|
||||||
Assigner: "=",
|
Assigner: "=",
|
||||||
Referenced: true,
|
Referenced: true,
|
||||||
|
@ -333,50 +341,64 @@ var validParseTestCases = []struct {
|
||||||
&Assignment{
|
&Assignment{
|
||||||
Name: Ident{"baz", mkpos(31, 4, 3)},
|
Name: Ident{"baz", mkpos(31, 4, 3)},
|
||||||
Pos: mkpos(35, 4, 7),
|
Pos: mkpos(35, 4, 7),
|
||||||
Value: Value{
|
Value: &Operator{
|
||||||
Type: String,
|
OperatorPos: mkpos(41, 4, 13),
|
||||||
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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Operator: '+',
|
Operator: '+',
|
||||||
Pos: mkpos(41, 4, 13),
|
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",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
OrigValue: Value{
|
&Variable{
|
||||||
Type: String,
|
Name: "bar",
|
||||||
Pos: mkpos(37, 4, 9),
|
NamePos: mkpos(43, 4, 15),
|
||||||
StringValue: "stuffstuff",
|
Value: &Variable{
|
||||||
Expression: &Expression{
|
Name: "foo",
|
||||||
Args: [2]Value{
|
NamePos: mkpos(25, 3, 9),
|
||||||
{
|
Value: &String{
|
||||||
Type: String,
|
LiteralPos: mkpos(9, 2, 9),
|
||||||
Pos: mkpos(37, 4, 9),
|
Value: "stuff",
|
||||||
StringValue: "stuff",
|
|
||||||
Variable: "foo",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Type: String,
|
|
||||||
Pos: mkpos(43, 4, 15),
|
|
||||||
StringValue: "stuff",
|
|
||||||
Variable: "bar",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OrigValue: &Operator{
|
||||||
|
OperatorPos: mkpos(41, 4, 13),
|
||||||
Operator: '+',
|
Operator: '+',
|
||||||
Pos: mkpos(41, 4, 13),
|
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",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Assigner: "=",
|
Assigner: "=",
|
||||||
|
@ -385,69 +407,90 @@ var validParseTestCases = []struct {
|
||||||
&Assignment{
|
&Assignment{
|
||||||
Name: Ident{"boo", mkpos(49, 5, 3)},
|
Name: Ident{"boo", mkpos(49, 5, 3)},
|
||||||
Pos: mkpos(53, 5, 7),
|
Pos: mkpos(53, 5, 7),
|
||||||
Value: Value{
|
Value: &Operator{
|
||||||
Type: String,
|
Args: [2]Expression{
|
||||||
Pos: mkpos(55, 5, 9),
|
&Variable{
|
||||||
StringValue: "stuffstuffstuff",
|
Name: "baz",
|
||||||
Expression: &Expression{
|
NamePos: mkpos(55, 5, 9),
|
||||||
Args: [2]Value{
|
Value: &Operator{
|
||||||
{
|
OperatorPos: mkpos(41, 4, 13),
|
||||||
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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Operator: '+',
|
Operator: '+',
|
||||||
Pos: mkpos(41, 4, 13),
|
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{
|
||||||
Variable: "foo",
|
Name: "bar",
|
||||||
Type: String,
|
NamePos: mkpos(43, 4, 15),
|
||||||
Pos: mkpos(68, 6, 10),
|
Value: &Variable{
|
||||||
StringValue: "stuff",
|
Name: "foo",
|
||||||
|
NamePos: mkpos(25, 3, 9),
|
||||||
|
Value: &String{
|
||||||
|
LiteralPos: mkpos(9, 2, 9),
|
||||||
|
Value: "stuff",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Pos: mkpos(66, 6, 8),
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&Variable{
|
||||||
|
Name: "foo",
|
||||||
|
NamePos: mkpos(68, 6, 10),
|
||||||
|
Value: &String{
|
||||||
|
LiteralPos: mkpos(9, 2, 9),
|
||||||
|
Value: "stuff",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OperatorPos: mkpos(66, 6, 8),
|
||||||
Operator: '+',
|
Operator: '+',
|
||||||
|
Value: &String{
|
||||||
|
LiteralPos: mkpos(9, 2, 9),
|
||||||
|
Value: "stuffstuffstuff",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
OrigValue: Value{
|
OrigValue: &Variable{
|
||||||
Type: String,
|
Name: "baz",
|
||||||
Pos: mkpos(55, 5, 9),
|
NamePos: mkpos(55, 5, 9),
|
||||||
StringValue: "stuffstuff",
|
Value: &Operator{
|
||||||
Variable: "baz",
|
OperatorPos: mkpos(41, 4, 13),
|
||||||
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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Operator: '+',
|
Operator: '+',
|
||||||
Pos: mkpos(41, 4, 13),
|
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",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Assigner: "=",
|
Assigner: "=",
|
||||||
|
@ -455,17 +498,21 @@ var validParseTestCases = []struct {
|
||||||
&Assignment{
|
&Assignment{
|
||||||
Name: Ident{"boo", mkpos(61, 6, 3)},
|
Name: Ident{"boo", mkpos(61, 6, 3)},
|
||||||
Pos: mkpos(66, 6, 8),
|
Pos: mkpos(66, 6, 8),
|
||||||
Value: Value{
|
Value: &Variable{
|
||||||
Type: String,
|
Name: "foo",
|
||||||
Pos: mkpos(68, 6, 10),
|
NamePos: mkpos(68, 6, 10),
|
||||||
StringValue: "stuff",
|
Value: &String{
|
||||||
Variable: "foo",
|
LiteralPos: mkpos(9, 2, 9),
|
||||||
|
Value: "stuff",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
OrigValue: &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",
|
|
||||||
},
|
},
|
||||||
Assigner: "+=",
|
Assigner: "+=",
|
||||||
},
|
},
|
||||||
|
@ -504,7 +551,7 @@ func TestParseValidInput(t *testing.T) {
|
||||||
|
|
||||||
if len(file.Comments) == len(testCase.comments) {
|
if len(file.Comments) == len(testCase.comments) {
|
||||||
for i := range file.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("test case: %s", testCase.input)
|
||||||
t.Errorf("incorrect comment %d:", i)
|
t.Errorf("incorrect comment %d:", i)
|
||||||
t.Errorf(" expected: %s", testCase.comments[i])
|
t.Errorf(" expected: %s", testCase.comments[i])
|
||||||
|
|
|
@ -22,7 +22,7 @@ import (
|
||||||
"unicode"
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
var noPos = scanner.Position{}
|
var noPos scanner.Position
|
||||||
|
|
||||||
type printer struct {
|
type printer struct {
|
||||||
defs []Definition
|
defs []Definition
|
||||||
|
@ -91,96 +91,94 @@ func (p *printer) printAssignment(assignment *Assignment) {
|
||||||
p.requestSpace()
|
p.requestSpace()
|
||||||
p.printToken(assignment.Assigner, assignment.Pos)
|
p.printToken(assignment.Assigner, assignment.Pos)
|
||||||
p.requestSpace()
|
p.requestSpace()
|
||||||
p.printValue(assignment.OrigValue)
|
p.printExpression(assignment.OrigValue)
|
||||||
p.requestNewline()
|
p.requestNewline()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *printer) printModule(module *Module) {
|
func (p *printer) printModule(module *Module) {
|
||||||
p.printToken(module.Type.Name, module.Type.Pos)
|
p.printToken(module.Type.Name, module.Type.Pos)
|
||||||
p.printMap(module.Properties, module.LbracePos, module.RbracePos)
|
p.printMap(&module.Map)
|
||||||
p.requestDoubleNewline()
|
p.requestDoubleNewline()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *printer) printValue(value Value) {
|
func (p *printer) printExpression(value Expression) {
|
||||||
if value.Variable != "" {
|
switch v := value.(type) {
|
||||||
p.printToken(value.Variable, value.Pos)
|
case *Variable:
|
||||||
} else if value.Expression != nil {
|
p.printToken(v.Name, v.NamePos)
|
||||||
p.printExpression(*value.Expression)
|
case *Operator:
|
||||||
} else {
|
p.printOperator(v)
|
||||||
switch value.Type {
|
case *Bool:
|
||||||
case Bool:
|
|
||||||
var s string
|
var s string
|
||||||
if value.BoolValue {
|
if v.Value {
|
||||||
s = "true"
|
s = "true"
|
||||||
} else {
|
} else {
|
||||||
s = "false"
|
s = "false"
|
||||||
}
|
}
|
||||||
p.printToken(s, value.Pos)
|
p.printToken(s, v.LiteralPos)
|
||||||
case String:
|
case *String:
|
||||||
p.printToken(strconv.Quote(value.StringValue), value.Pos)
|
p.printToken(strconv.Quote(v.Value), v.LiteralPos)
|
||||||
case List:
|
case *List:
|
||||||
p.printList(value.ListValue, value.Pos, value.EndPos)
|
p.printList(v.Values, v.LBracePos, v.RBracePos)
|
||||||
case Map:
|
case *Map:
|
||||||
p.printMap(value.MapValue, value.Pos, value.EndPos)
|
p.printMap(v)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Errorf("bad property type: %d", value.Type))
|
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.requestSpace()
|
||||||
p.printToken("[", pos)
|
p.printToken("[", pos)
|
||||||
if len(list) > 1 || pos.Line != endPos.Line {
|
if len(list) > 1 || pos.Line != endPos.Line {
|
||||||
p.requestNewline()
|
p.requestNewline()
|
||||||
p.indent(p.curIndent() + 4)
|
p.indent(p.curIndent() + 4)
|
||||||
for _, value := range list {
|
for _, value := range list {
|
||||||
p.printValue(value)
|
p.printExpression(value)
|
||||||
p.printToken(",", noPos)
|
p.printToken(",", noPos)
|
||||||
p.requestNewline()
|
p.requestNewline()
|
||||||
}
|
}
|
||||||
p.unindent(endPos)
|
p.unindent(endPos)
|
||||||
} else {
|
} else {
|
||||||
for _, value := range list {
|
for _, value := range list {
|
||||||
p.printValue(value)
|
p.printExpression(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.printToken("]", endPos)
|
p.printToken("]", endPos)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *printer) printMap(list []*Property, pos, endPos scanner.Position) {
|
func (p *printer) printMap(m *Map) {
|
||||||
p.requestSpace()
|
p.requestSpace()
|
||||||
p.printToken("{", pos)
|
p.printToken("{", m.LBracePos)
|
||||||
if len(list) > 0 || pos.Line != endPos.Line {
|
if len(m.Properties) > 0 || m.LBracePos.Line != m.RBracePos.Line {
|
||||||
p.requestNewline()
|
p.requestNewline()
|
||||||
p.indent(p.curIndent() + 4)
|
p.indent(p.curIndent() + 4)
|
||||||
for _, prop := range list {
|
for _, prop := range m.Properties {
|
||||||
p.printProperty(prop)
|
p.printProperty(prop)
|
||||||
p.printToken(",", noPos)
|
p.printToken(",", noPos)
|
||||||
p.requestNewline()
|
p.requestNewline()
|
||||||
}
|
}
|
||||||
p.unindent(endPos)
|
p.unindent(m.RBracePos)
|
||||||
}
|
}
|
||||||
p.printToken("}", endPos)
|
p.printToken("}", m.RBracePos)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *printer) printExpression(expression Expression) {
|
func (p *printer) printOperator(operator *Operator) {
|
||||||
p.printValue(expression.Args[0])
|
p.printExpression(operator.Args[0])
|
||||||
p.requestSpace()
|
p.requestSpace()
|
||||||
p.printToken(string(expression.Operator), expression.Pos)
|
p.printToken(string(operator.Operator), operator.OperatorPos)
|
||||||
if expression.Args[0].Pos.Line == expression.Args[1].Pos.Line {
|
if operator.Args[0].End().Line == operator.Args[1].Pos().Line {
|
||||||
p.requestSpace()
|
p.requestSpace()
|
||||||
} else {
|
} else {
|
||||||
p.requestNewline()
|
p.requestNewline()
|
||||||
}
|
}
|
||||||
p.printValue(expression.Args[1])
|
p.printExpression(operator.Args[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *printer) printProperty(property *Property) {
|
func (p *printer) printProperty(property *Property) {
|
||||||
p.printToken(property.Name.Name, property.Name.Pos)
|
p.printToken(property.Name.Name, property.Name.Pos)
|
||||||
p.printToken(":", property.Pos)
|
p.printToken(":", property.Pos)
|
||||||
p.requestSpace()
|
p.requestSpace()
|
||||||
p.printValue(property.Value)
|
p.printExpression(property.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print a single token, including any necessary comments or whitespace between
|
// 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
|
// Print any in-line (single line /* */) comments that appear _before_ pos
|
||||||
func (p *printer) printInLineCommentsBefore(pos scanner.Position) {
|
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]
|
c := p.comments[p.curComment]
|
||||||
if c.Comment[0][0:2] == "//" || len(c.Comment) > 1 {
|
if c.Comment[0][0:2] == "//" || len(c.Comment) > 1 {
|
||||||
p.skippedComments = append(p.skippedComments, c)
|
p.skippedComments = append(p.skippedComments, c)
|
||||||
|
@ -225,16 +223,16 @@ func (p *printer) printInLineCommentsBefore(pos scanner.Position) {
|
||||||
// by pos
|
// by pos
|
||||||
func (p *printer) printEndOfLineCommentsBefore(pos scanner.Position) {
|
func (p *printer) printEndOfLineCommentsBefore(pos scanner.Position) {
|
||||||
for _, c := range p.skippedComments {
|
for _, c := range p.skippedComments {
|
||||||
if !p.requestNewlinesForPos(c.Pos) {
|
if !p.requestNewlinesForPos(c.Slash) {
|
||||||
p.requestSpace()
|
p.requestSpace()
|
||||||
}
|
}
|
||||||
p.printComment(c)
|
p.printComment(c)
|
||||||
p._requestNewline()
|
p._requestNewline()
|
||||||
}
|
}
|
||||||
p.skippedComments = []Comment{}
|
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]
|
c := p.comments[p.curComment]
|
||||||
if !p.requestNewlinesForPos(c.Pos) {
|
if !p.requestNewlinesForPos(c.Slash) {
|
||||||
p.requestSpace()
|
p.requestSpace()
|
||||||
}
|
}
|
||||||
p.printComment(c)
|
p.printComment(c)
|
||||||
|
@ -303,7 +301,7 @@ func (p *printer) flushSpace() {
|
||||||
|
|
||||||
// Print a single comment, which may be a multi-line comment
|
// Print a single comment, which may be a multi-line comment
|
||||||
func (p *printer) printComment(comment Comment) {
|
func (p *printer) printComment(comment Comment) {
|
||||||
pos := comment.Pos
|
pos := comment.Slash
|
||||||
for i, line := range comment.Comment {
|
for i, line := range comment.Comment {
|
||||||
line = strings.TrimRightFunc(line, unicode.IsSpace)
|
line = strings.TrimRightFunc(line, unicode.IsSpace)
|
||||||
p.flushSpace()
|
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
|
// Print any comments that occur after the last token, and a trailing newline
|
||||||
func (p *printer) flush() {
|
func (p *printer) flush() {
|
||||||
for _, c := range p.skippedComments {
|
for _, c := range p.skippedComments {
|
||||||
if !p.requestNewlinesForPos(c.Pos) {
|
if !p.requestNewlinesForPos(c.Slash) {
|
||||||
p.requestSpace()
|
p.requestSpace()
|
||||||
}
|
}
|
||||||
p.printComment(c)
|
p.printComment(c)
|
||||||
}
|
}
|
||||||
for p.curComment < len(p.comments) {
|
for p.curComment < len(p.comments) {
|
||||||
c := p.comments[p.curComment]
|
c := p.comments[p.curComment]
|
||||||
if !p.requestNewlinesForPos(c.Pos) {
|
if !p.requestNewlinesForPos(c.Slash) {
|
||||||
p.requestSpace()
|
p.requestSpace()
|
||||||
}
|
}
|
||||||
p.printComment(c)
|
p.printComment(c)
|
||||||
|
|
|
@ -58,6 +58,49 @@ foo {
|
||||||
"uiop",
|
"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))
|
sort.Sort(commentsByOffset(file.Comments))
|
||||||
}
|
}
|
||||||
|
|
||||||
func SortList(file *File, value Value) {
|
func SortList(file *File, list *List) {
|
||||||
for i := 0; i < len(value.ListValue); i++ {
|
for i := 0; i < len(list.Values); i++ {
|
||||||
// Find a set of values on contiguous lines
|
// Find a set of values on contiguous lines
|
||||||
line := value.ListValue[i].Pos.Line
|
line := list.Values[i].Pos().Line
|
||||||
var j int
|
var j int
|
||||||
for j = i + 1; j < len(value.ListValue); j++ {
|
for j = i + 1; j < len(list.Values); j++ {
|
||||||
if value.ListValue[j].Pos.Line > line+1 {
|
if list.Values[j].Pos().Line > line+1 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
line = value.ListValue[j].Pos.Line
|
line = list.Values[j].Pos().Line
|
||||||
}
|
}
|
||||||
|
|
||||||
nextPos := value.EndPos
|
nextPos := list.End()
|
||||||
if j < len(value.ListValue) {
|
if j < len(list.Values) {
|
||||||
nextPos = value.ListValue[j].Pos
|
nextPos = list.Values[j].Pos()
|
||||||
}
|
}
|
||||||
sortSubList(value.ListValue[i:j], nextPos, file)
|
sortSubList(list.Values[i:j], nextPos, file)
|
||||||
i = j - 1
|
i = j - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListIsSorted(value Value) bool {
|
func ListIsSorted(list *List) bool {
|
||||||
for i := 0; i < len(value.ListValue); i++ {
|
for i := 0; i < len(list.Values); i++ {
|
||||||
// Find a set of values on contiguous lines
|
// Find a set of values on contiguous lines
|
||||||
line := value.ListValue[i].Pos.Line
|
line := list.Values[i].Pos().Line
|
||||||
var j int
|
var j int
|
||||||
for j = i + 1; j < len(value.ListValue); j++ {
|
for j = i + 1; j < len(list.Values); j++ {
|
||||||
if value.ListValue[j].Pos.Line > line+1 {
|
if list.Values[j].Pos().Line > line+1 {
|
||||||
break
|
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
|
return false
|
||||||
}
|
}
|
||||||
i = j - 1
|
i = j - 1
|
||||||
|
@ -74,55 +74,49 @@ func ListIsSorted(value Value) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func sortListsInValue(value Value, file *File) {
|
func sortListsInValue(value Expression, file *File) {
|
||||||
if value.Variable != "" {
|
switch v := value.(type) {
|
||||||
return
|
case *Variable:
|
||||||
}
|
// Nothing
|
||||||
|
case *Operator:
|
||||||
if value.Expression != nil {
|
sortListsInValue(v.Args[0], file)
|
||||||
sortListsInValue(value.Expression.Args[0], file)
|
sortListsInValue(v.Args[1], file)
|
||||||
sortListsInValue(value.Expression.Args[1], file)
|
case *Map:
|
||||||
return
|
for _, p := range v.Properties {
|
||||||
}
|
|
||||||
|
|
||||||
if value.Type == Map {
|
|
||||||
for _, p := range value.MapValue {
|
|
||||||
sortListsInValue(p.Value, file)
|
sortListsInValue(p.Value, file)
|
||||||
}
|
}
|
||||||
return
|
case *List:
|
||||||
} else if value.Type != List {
|
SortList(file, v)
|
||||||
return
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SortList(file, value)
|
func sortSubList(values []Expression, nextPos scanner.Position, file *File) {
|
||||||
}
|
|
||||||
|
|
||||||
func sortSubList(values []Value, nextPos scanner.Position, file *File) {
|
|
||||||
l := make(elemList, len(values))
|
l := make(elemList, len(values))
|
||||||
for i, v := range values {
|
for i, v := range values {
|
||||||
if v.Type != String {
|
s, ok := v.(*String)
|
||||||
|
if !ok {
|
||||||
panic("list contains non-string element")
|
panic("list contains non-string element")
|
||||||
}
|
}
|
||||||
n := nextPos
|
n := nextPos
|
||||||
if i < len(values)-1 {
|
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)
|
sort.Sort(l)
|
||||||
|
|
||||||
copyValues := append([]Value{}, values...)
|
copyValues := append([]Expression{}, values...)
|
||||||
copyComments := append([]Comment{}, file.Comments...)
|
copyComments := append([]Comment{}, file.Comments...)
|
||||||
|
|
||||||
curPos := values[0].Pos
|
curPos := values[0].Pos()
|
||||||
for i, e := range l {
|
for i, e := range l {
|
||||||
values[i] = copyValues[e.i]
|
values[i] = copyValues[e.i]
|
||||||
values[i].Pos = curPos
|
values[i].(*String).LiteralPos = curPos
|
||||||
for j, c := range copyComments {
|
for j, c := range copyComments {
|
||||||
if c.Pos.Offset > e.pos.Offset && c.Pos.Offset < e.nextPos.Offset {
|
if c.Pos().Offset > e.pos.Offset && c.Pos().Offset < e.nextPos.Offset {
|
||||||
file.Comments[j].Pos.Line = curPos.Line
|
file.Comments[j].Slash.Line = curPos.Line
|
||||||
file.Comments[j].Pos.Offset += values[i].Pos.Offset - e.pos.Offset
|
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 := ""
|
prev := ""
|
||||||
for _, v := range values {
|
for _, v := range values {
|
||||||
if v.Type != String {
|
s, ok := v.(*String)
|
||||||
|
if !ok {
|
||||||
panic("list contains non-string element")
|
panic("list contains non-string element")
|
||||||
}
|
}
|
||||||
if prev > v.StringValue {
|
if prev > s.Value {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
prev = v.StringValue
|
prev = s.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
@ -174,7 +169,7 @@ func (l commentsByOffset) Len() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l commentsByOffset) Less(i, j int) bool {
|
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) {
|
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.
|
// We've already added this property.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
errs = append(errs, &Error{
|
errs = append(errs, &Error{
|
||||||
Err: fmt.Errorf("property %q already defined", name),
|
Err: fmt.Errorf("property %q already defined", name),
|
||||||
Pos: propertyDef.Pos,
|
Pos: propertyDef.Pos,
|
||||||
|
@ -276,47 +275,49 @@ func unpackStructValue(namePrefix string, structValue reflect.Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
func unpackBool(boolValue reflect.Value, property *parser.Property) []error {
|
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{
|
return []error{
|
||||||
fmt.Errorf("%s: can't assign %s value to %s property %q",
|
fmt.Errorf("%s: can't assign %s value to bool property %q",
|
||||||
property.Value.Pos, property.Value.Type, parser.Bool,
|
property.Value.Pos, property.Value.Type, property.Name),
|
||||||
property.Name),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
boolValue.SetBool(property.Value.BoolValue)
|
boolValue.SetBool(b.Value)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func unpackString(stringValue reflect.Value,
|
func unpackString(stringValue reflect.Value,
|
||||||
property *parser.Property) []error {
|
property *parser.Property) []error {
|
||||||
|
|
||||||
if property.Value.Type != parser.String {
|
s, ok := property.Value.Eval().(*parser.String)
|
||||||
|
if !ok {
|
||||||
return []error{
|
return []error{
|
||||||
fmt.Errorf("%s: can't assign %s value to %s property %q",
|
fmt.Errorf("%s: can't assign %s value to string property %q",
|
||||||
property.Value.Pos, property.Value.Type, parser.String,
|
property.Value.Pos, property.Value.Type, property.Name),
|
||||||
property.Name),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stringValue.SetString(property.Value.StringValue)
|
stringValue.SetString(s.Value)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func unpackSlice(sliceValue reflect.Value, property *parser.Property) []error {
|
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{
|
return []error{
|
||||||
fmt.Errorf("%s: can't assign %s value to %s property %q",
|
fmt.Errorf("%s: can't assign %s value to list property %q",
|
||||||
property.Value.Pos, property.Value.Type, parser.List,
|
property.Value.Pos, property.Value.Type, property.Name),
|
||||||
property.Name),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
list := []string{}
|
list := make([]string, len(l.Values))
|
||||||
for _, value := range property.Value.ListValue {
|
for i, value := range l.Values {
|
||||||
if value.Type != parser.String {
|
s, ok := value.Eval().(*parser.String)
|
||||||
|
if !ok {
|
||||||
// The parser should not produce this.
|
// 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))
|
sliceValue.Set(reflect.ValueOf(list))
|
||||||
|
@ -327,15 +328,15 @@ func unpackStruct(namePrefix string, structValue reflect.Value,
|
||||||
property *parser.Property, propertyMap map[string]*packedProperty,
|
property *parser.Property, propertyMap map[string]*packedProperty,
|
||||||
filterKey, filterValue string) []error {
|
filterKey, filterValue string) []error {
|
||||||
|
|
||||||
if property.Value.Type != parser.Map {
|
m, ok := property.Value.Eval().(*parser.Map)
|
||||||
|
if !ok {
|
||||||
return []error{
|
return []error{
|
||||||
fmt.Errorf("%s: can't assign %s value to %s property %q",
|
fmt.Errorf("%s: can't assign %s value to map property %q",
|
||||||
property.Value.Pos, property.Value.Type, parser.Map,
|
property.Value.Pos, property.Value.Type, property.Name),
|
||||||
property.Name),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
errs := buildPropertyMap(namePrefix, property.Value.MapValue, propertyMap)
|
errs := buildPropertyMap(namePrefix, m.Properties, propertyMap)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
|
121
unpack_test.go
121
unpack_test.go
|
@ -27,7 +27,7 @@ import (
|
||||||
|
|
||||||
var validUnpackTestCases = []struct {
|
var validUnpackTestCases = []struct {
|
||||||
input string
|
input string
|
||||||
output interface{}
|
output []interface{}
|
||||||
errs []error
|
errs []error
|
||||||
}{
|
}{
|
||||||
{`
|
{`
|
||||||
|
@ -36,6 +36,7 @@ var validUnpackTestCases = []struct {
|
||||||
blank: "",
|
blank: "",
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
[]interface{}{
|
||||||
struct {
|
struct {
|
||||||
Name *string
|
Name *string
|
||||||
Blank *string
|
Blank *string
|
||||||
|
@ -45,6 +46,7 @@ var validUnpackTestCases = []struct {
|
||||||
Blank: proptools.StringPtr(""),
|
Blank: proptools.StringPtr(""),
|
||||||
Unset: nil,
|
Unset: nil,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -53,11 +55,13 @@ var validUnpackTestCases = []struct {
|
||||||
name: "abc",
|
name: "abc",
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
[]interface{}{
|
||||||
struct {
|
struct {
|
||||||
Name string
|
Name string
|
||||||
}{
|
}{
|
||||||
Name: "abc",
|
Name: "abc",
|
||||||
},
|
},
|
||||||
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -66,11 +70,13 @@ var validUnpackTestCases = []struct {
|
||||||
isGood: true,
|
isGood: true,
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
[]interface{}{
|
||||||
struct {
|
struct {
|
||||||
IsGood bool
|
IsGood bool
|
||||||
}{
|
}{
|
||||||
IsGood: true,
|
IsGood: true,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -80,6 +86,7 @@ var validUnpackTestCases = []struct {
|
||||||
isBad: false,
|
isBad: false,
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
[]interface{}{
|
||||||
struct {
|
struct {
|
||||||
IsGood *bool
|
IsGood *bool
|
||||||
IsBad *bool
|
IsBad *bool
|
||||||
|
@ -89,6 +96,7 @@ var validUnpackTestCases = []struct {
|
||||||
IsBad: proptools.BoolPtr(false),
|
IsBad: proptools.BoolPtr(false),
|
||||||
IsUgly: nil,
|
IsUgly: nil,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -99,6 +107,7 @@ var validUnpackTestCases = []struct {
|
||||||
empty: []
|
empty: []
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
[]interface{}{
|
||||||
struct {
|
struct {
|
||||||
Stuff []string
|
Stuff []string
|
||||||
Empty []string
|
Empty []string
|
||||||
|
@ -108,6 +117,7 @@ var validUnpackTestCases = []struct {
|
||||||
Empty: []string{},
|
Empty: []string{},
|
||||||
Nil: nil,
|
Nil: nil,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -118,6 +128,7 @@ var validUnpackTestCases = []struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
[]interface{}{
|
||||||
struct {
|
struct {
|
||||||
Nested struct {
|
Nested struct {
|
||||||
Name string
|
Name string
|
||||||
|
@ -127,6 +138,7 @@ var validUnpackTestCases = []struct {
|
||||||
Name: "abc",
|
Name: "abc",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -137,6 +149,7 @@ var validUnpackTestCases = []struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
[]interface{}{
|
||||||
struct {
|
struct {
|
||||||
Nested interface{}
|
Nested interface{}
|
||||||
}{
|
}{
|
||||||
|
@ -144,6 +157,7 @@ var validUnpackTestCases = []struct {
|
||||||
Name: "def",
|
Name: "def",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -156,6 +170,7 @@ var validUnpackTestCases = []struct {
|
||||||
baz: ["def", "ghi"],
|
baz: ["def", "ghi"],
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
[]interface{}{
|
||||||
struct {
|
struct {
|
||||||
Nested struct {
|
Nested struct {
|
||||||
Foo string
|
Foo string
|
||||||
|
@ -169,6 +184,7 @@ var validUnpackTestCases = []struct {
|
||||||
Bar: false,
|
Bar: false,
|
||||||
Baz: []string{"def", "ghi"},
|
Baz: []string{"def", "ghi"},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -181,6 +197,7 @@ var validUnpackTestCases = []struct {
|
||||||
baz: ["def", "ghi"],
|
baz: ["def", "ghi"],
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
[]interface{}{
|
||||||
struct {
|
struct {
|
||||||
Nested struct {
|
Nested struct {
|
||||||
Foo string `allowNested:"true"`
|
Foo string `allowNested:"true"`
|
||||||
|
@ -196,6 +213,7 @@ var validUnpackTestCases = []struct {
|
||||||
Bar: false,
|
Bar: false,
|
||||||
Baz: []string{"def", "ghi"},
|
Baz: []string{"def", "ghi"},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -208,6 +226,7 @@ var validUnpackTestCases = []struct {
|
||||||
baz: ["def", "ghi"],
|
baz: ["def", "ghi"],
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
[]interface{}{
|
||||||
struct {
|
struct {
|
||||||
Nested struct {
|
Nested struct {
|
||||||
Foo string
|
Foo string
|
||||||
|
@ -221,6 +240,7 @@ var validUnpackTestCases = []struct {
|
||||||
Bar: false,
|
Bar: false,
|
||||||
Baz: []string{"def", "ghi"},
|
Baz: []string{"def", "ghi"},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
[]error{
|
[]error{
|
||||||
&Error{
|
&Error{
|
||||||
Err: fmt.Errorf("filtered field nested.foo cannot be set in a Blueprint file"),
|
Err: fmt.Errorf("filtered field nested.foo cannot be set in a Blueprint file"),
|
||||||
|
@ -238,6 +258,7 @@ var validUnpackTestCases = []struct {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
[]interface{}{
|
||||||
struct {
|
struct {
|
||||||
EmbeddedStruct
|
EmbeddedStruct
|
||||||
Nested struct {
|
Nested struct {
|
||||||
|
@ -255,6 +276,7 @@ var validUnpackTestCases = []struct {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -267,6 +289,7 @@ var validUnpackTestCases = []struct {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
[]interface{}{
|
||||||
struct {
|
struct {
|
||||||
EmbeddedInterface
|
EmbeddedInterface
|
||||||
Nested struct {
|
Nested struct {
|
||||||
|
@ -284,6 +307,7 @@ var validUnpackTestCases = []struct {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -296,6 +320,7 @@ var validUnpackTestCases = []struct {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
[]interface{}{
|
||||||
struct {
|
struct {
|
||||||
Name string
|
Name string
|
||||||
EmbeddedStruct
|
EmbeddedStruct
|
||||||
|
@ -318,6 +343,7 @@ var validUnpackTestCases = []struct {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -330,6 +356,7 @@ var validUnpackTestCases = []struct {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
[]interface{}{
|
||||||
struct {
|
struct {
|
||||||
Name string
|
Name string
|
||||||
EmbeddedInterface
|
EmbeddedInterface
|
||||||
|
@ -352,6 +379,65 @@ var validUnpackTestCases = []struct {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
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,
|
nil,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -362,7 +448,7 @@ type EmbeddedInterface interface{}
|
||||||
func TestUnpackProperties(t *testing.T) {
|
func TestUnpackProperties(t *testing.T) {
|
||||||
for _, testCase := range validUnpackTestCases {
|
for _, testCase := range validUnpackTestCases {
|
||||||
r := bytes.NewBufferString(testCase.input)
|
r := bytes.NewBufferString(testCase.input)
|
||||||
file, errs := parser.Parse("", r, nil)
|
file, errs := parser.ParseAndEval("", r, parser.NewScope(nil))
|
||||||
if len(errs) != 0 {
|
if len(errs) != 0 {
|
||||||
t.Errorf("test case: %s", testCase.input)
|
t.Errorf("test case: %s", testCase.input)
|
||||||
t.Errorf("unexpected parse errors:")
|
t.Errorf("unexpected parse errors:")
|
||||||
|
@ -372,10 +458,17 @@ func TestUnpackProperties(t *testing.T) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
|
|
||||||
module := file.Defs[0].(*parser.Module)
|
for _, def := range file.Defs {
|
||||||
properties := proptools.CloneProperties(reflect.ValueOf(testCase.output))
|
module, ok := def.(*parser.Module)
|
||||||
proptools.ZeroProperties(properties.Elem())
|
if !ok {
|
||||||
_, errs = unpackProperties(module.Properties, properties.Interface())
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
if len(errs) != 0 && len(testCase.errs) == 0 {
|
||||||
t.Errorf("test case: %s", testCase.input)
|
t.Errorf("test case: %s", testCase.input)
|
||||||
t.Errorf("unexpected unpack errors:")
|
t.Errorf("unexpected unpack errors:")
|
||||||
|
@ -390,12 +483,20 @@ func TestUnpackProperties(t *testing.T) {
|
||||||
t.Errorf(" got: %+v", errs)
|
t.Errorf(" got: %+v", errs)
|
||||||
}
|
}
|
||||||
|
|
||||||
output := properties.Elem().Interface()
|
if len(output) != len(testCase.output) {
|
||||||
if !reflect.DeepEqual(output, 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("test case: %s", testCase.input)
|
||||||
t.Errorf("incorrect output:")
|
t.Errorf("incorrect output:")
|
||||||
t.Errorf(" expected: %+v", testCase.output)
|
t.Errorf(" expected: %+v", testCase.output[i])
|
||||||
t.Errorf(" got: %+v", output)
|
t.Errorf(" got: %+v", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue