commit
2e32b2facc
9 changed files with 303 additions and 170 deletions
|
@ -124,9 +124,9 @@ func findModules(file *parser.File) (modified bool, errs []error) {
|
|||
for _, def := range file.Defs {
|
||||
if module, ok := def.(*parser.Module); ok {
|
||||
for _, prop := range module.Properties {
|
||||
if prop.Name.Name == "name" && prop.Value.Type() == parser.StringType {
|
||||
if prop.Name == "name" && prop.Value.Type() == parser.StringType {
|
||||
if targetedModule(prop.Value.Eval().(*parser.String).Value) {
|
||||
m, newErrs := processModule(module, prop.Name.Name, file)
|
||||
m, newErrs := processModule(module, prop.Name, file)
|
||||
errs = append(errs, newErrs...)
|
||||
modified = modified || m
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ func processModule(module *parser.Module, moduleName string,
|
|||
file *parser.File) (modified bool, errs []error) {
|
||||
|
||||
for _, prop := range module.Properties {
|
||||
if prop.Name.Name == *parameter {
|
||||
if prop.Name == *parameter {
|
||||
modified, errs = processParameter(prop.Value, *parameter, moduleName, file)
|
||||
return
|
||||
}
|
||||
|
|
21
context.go
21
context.go
|
@ -875,11 +875,11 @@ func getLocalStringListFromScope(scope *parser.Scope, v string) ([]string, scann
|
|||
ret = append(ret, s.Value)
|
||||
}
|
||||
|
||||
return ret, assignment.Pos, nil
|
||||
return ret, assignment.EqualsPos, nil
|
||||
case *parser.Bool, *parser.String:
|
||||
return nil, scanner.Position{}, &Error{
|
||||
Err: fmt.Errorf("%q must be a list of strings", v),
|
||||
Pos: assignment.Pos,
|
||||
Pos: assignment.EqualsPos,
|
||||
}
|
||||
default:
|
||||
panic(fmt.Errorf("unknown value type: %d", assignment.Value.Type))
|
||||
|
@ -893,11 +893,11 @@ func getStringFromScope(scope *parser.Scope, v string) (string, scanner.Position
|
|||
} else {
|
||||
switch value := assignment.Value.Eval().(type) {
|
||||
case *parser.String:
|
||||
return value.Value, assignment.Pos, nil
|
||||
return value.Value, assignment.EqualsPos, nil
|
||||
case *parser.Bool, *parser.List:
|
||||
return "", scanner.Position{}, &Error{
|
||||
Err: fmt.Errorf("%q must be a string", v),
|
||||
Pos: assignment.Pos,
|
||||
Pos: assignment.EqualsPos,
|
||||
}
|
||||
default:
|
||||
panic(fmt.Errorf("unknown value type: %d", assignment.Value.Type))
|
||||
|
@ -1040,8 +1040,7 @@ func (c *Context) prettyPrintVariant(variant variationMap) string {
|
|||
func (c *Context) processModuleDef(moduleDef *parser.Module,
|
||||
relBlueprintsFile string) (*moduleInfo, []error) {
|
||||
|
||||
typeName := moduleDef.Type.Name
|
||||
factory, ok := c.moduleFactories[typeName]
|
||||
factory, ok := c.moduleFactories[moduleDef.Type]
|
||||
if !ok {
|
||||
if c.ignoreUnknownModuleTypes {
|
||||
return nil, nil
|
||||
|
@ -1049,8 +1048,8 @@ func (c *Context) processModuleDef(moduleDef *parser.Module,
|
|||
|
||||
return nil, []error{
|
||||
&Error{
|
||||
Err: fmt.Errorf("unrecognized module type %q", typeName),
|
||||
Pos: moduleDef.Type.Pos,
|
||||
Err: fmt.Errorf("unrecognized module type %q", moduleDef.Type),
|
||||
Pos: moduleDef.TypePos,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -1059,7 +1058,7 @@ func (c *Context) processModuleDef(moduleDef *parser.Module,
|
|||
|
||||
module := &moduleInfo{
|
||||
logicModule: logicModule,
|
||||
typeName: typeName,
|
||||
typeName: moduleDef.Type,
|
||||
relBlueprintsFile: relBlueprintsFile,
|
||||
}
|
||||
|
||||
|
@ -1074,10 +1073,10 @@ func (c *Context) processModuleDef(moduleDef *parser.Module,
|
|||
return nil, errs
|
||||
}
|
||||
|
||||
module.pos = moduleDef.Type.Pos
|
||||
module.pos = moduleDef.TypePos
|
||||
module.propertyPos = make(map[string]scanner.Position)
|
||||
for name, propertyDef := range propertyMap {
|
||||
module.propertyPos[name] = propertyDef.Pos
|
||||
module.propertyPos[name] = propertyDef.ColonPos
|
||||
}
|
||||
|
||||
return module, nil
|
||||
|
|
|
@ -20,8 +20,16 @@ import (
|
|||
"text/scanner"
|
||||
)
|
||||
|
||||
type Node interface {
|
||||
// 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
|
||||
}
|
||||
|
||||
// Definition is an Assignment or a Module at the top level of a Blueprints file
|
||||
type Definition interface {
|
||||
Node
|
||||
String() string
|
||||
definitionTag()
|
||||
}
|
||||
|
@ -29,23 +37,28 @@ type Definition interface {
|
|||
// 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
|
||||
Name string
|
||||
NamePos scanner.Position
|
||||
Value Expression
|
||||
OrigValue Expression
|
||||
Pos scanner.Position
|
||||
EqualsPos 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)
|
||||
return fmt.Sprintf("%s@%s %s %s (%s) %t", a.Name, a.EqualsPos, a.Assigner, a.Value, a.OrigValue, a.Referenced)
|
||||
}
|
||||
|
||||
func (a *Assignment) Pos() scanner.Position { return a.NamePos }
|
||||
func (a *Assignment) End() scanner.Position { return a.Value.End() }
|
||||
|
||||
func (a *Assignment) definitionTag() {}
|
||||
|
||||
// A Module is a module definition at the top level of a Blueprints file
|
||||
type Module struct {
|
||||
Type Ident
|
||||
Type string
|
||||
TypePos scanner.Position
|
||||
Map
|
||||
}
|
||||
|
||||
|
@ -70,11 +83,15 @@ func (m *Module) String() string {
|
|||
|
||||
func (m *Module) definitionTag() {}
|
||||
|
||||
func (m *Module) Pos() scanner.Position { return m.TypePos }
|
||||
func (m *Module) End() scanner.Position { return m.Map.End() }
|
||||
|
||||
// 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
|
||||
Name string
|
||||
NamePos scanner.Position
|
||||
ColonPos scanner.Position
|
||||
Value Expression
|
||||
}
|
||||
|
||||
func (p *Property) Copy() *Property {
|
||||
|
@ -84,33 +101,22 @@ func (p *Property) Copy() *Property {
|
|||
}
|
||||
|
||||
func (p *Property) String() string {
|
||||
return fmt.Sprintf("%s@%s: %s", p.Name, p.Pos, p.Value)
|
||||
return fmt.Sprintf("%s@%s: %s", p.Name, p.ColonPos, 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)
|
||||
}
|
||||
func (p *Property) Pos() scanner.Position { return p.NamePos }
|
||||
func (p *Property) End() scanner.Position { return p.Value.End() }
|
||||
|
||||
// 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 {
|
||||
Node
|
||||
// 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
|
||||
|
@ -172,8 +178,8 @@ func (x *Operator) String() string {
|
|||
|
||||
type Variable struct {
|
||||
Name string
|
||||
Value Expression
|
||||
NamePos scanner.Position
|
||||
Value Expression
|
||||
}
|
||||
|
||||
func (x *Variable) Pos() scanner.Position { return x.NamePos }
|
||||
|
@ -310,6 +316,13 @@ func (x *Bool) Type() Type {
|
|||
return BoolType
|
||||
}
|
||||
|
||||
type CommentGroup struct {
|
||||
Comments []*Comment
|
||||
}
|
||||
|
||||
func (x *CommentGroup) Pos() scanner.Position { return x.Comments[0].Pos() }
|
||||
func (x *CommentGroup) End() scanner.Position { return x.Comments[len(x.Comments)-1].End() }
|
||||
|
||||
type Comment struct {
|
||||
Comment []string
|
||||
Slash scanner.Position
|
||||
|
|
|
@ -40,8 +40,23 @@ func (e *ParseError) Error() string {
|
|||
type File struct {
|
||||
Name string
|
||||
Defs []Definition
|
||||
Comments []Comment
|
||||
Lines []scanner.Position
|
||||
Comments []*CommentGroup
|
||||
}
|
||||
|
||||
func (f *File) Pos() scanner.Position {
|
||||
return scanner.Position{
|
||||
Filename: f.Name,
|
||||
Line: 1,
|
||||
Column: 1,
|
||||
Offset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *File) End() scanner.Position {
|
||||
if len(f.Defs) > 0 {
|
||||
return f.Defs[len(f.Defs)-1].End()
|
||||
}
|
||||
return noPos
|
||||
}
|
||||
|
||||
func parse(p *parser) (file *File, errs []error) {
|
||||
|
@ -88,7 +103,7 @@ type parser struct {
|
|||
tok rune
|
||||
errors []error
|
||||
scope *Scope
|
||||
comments []Comment
|
||||
comments []*CommentGroup
|
||||
eval bool
|
||||
}
|
||||
|
||||
|
@ -139,10 +154,18 @@ func (p *parser) accept(toks ...rune) bool {
|
|||
func (p *parser) next() {
|
||||
if p.tok != scanner.EOF {
|
||||
p.tok = p.scanner.Scan()
|
||||
for p.tok == scanner.Comment {
|
||||
lines := strings.Split(p.scanner.TokenText(), "\n")
|
||||
p.comments = append(p.comments, Comment{lines, p.scanner.Position})
|
||||
p.tok = p.scanner.Scan()
|
||||
if p.tok == scanner.Comment {
|
||||
var comments []*Comment
|
||||
for p.tok == scanner.Comment {
|
||||
lines := strings.Split(p.scanner.TokenText(), "\n")
|
||||
if len(comments) > 0 && p.scanner.Position.Line > comments[len(comments)-1].End().Line+1 {
|
||||
p.comments = append(p.comments, &CommentGroup{Comments: comments})
|
||||
comments = nil
|
||||
}
|
||||
comments = append(comments, &Comment{lines, p.scanner.Position})
|
||||
p.tok = p.scanner.Scan()
|
||||
}
|
||||
p.comments = append(p.comments, &CommentGroup{Comments: comments})
|
||||
}
|
||||
}
|
||||
return
|
||||
|
@ -190,23 +213,23 @@ func (p *parser) parseAssignment(name string, namePos scanner.Position,
|
|||
}
|
||||
value := p.parseExpression()
|
||||
|
||||
assignment.Name = Ident{name, namePos}
|
||||
assignment.Name = name
|
||||
assignment.NamePos = namePos
|
||||
assignment.Value = value
|
||||
assignment.OrigValue = value
|
||||
assignment.Pos = pos
|
||||
assignment.EqualsPos = pos
|
||||
assignment.Assigner = assigner
|
||||
|
||||
if p.scope != nil {
|
||||
if assigner == "+=" {
|
||||
if old, local := p.scope.Get(assignment.Name.Name); old == nil {
|
||||
p.errorf("modified non-existent variable %q with +=", assignment.Name.Name)
|
||||
if old, local := p.scope.Get(assignment.Name); old == nil {
|
||||
p.errorf("modified non-existent variable %q with +=", assignment.Name)
|
||||
} else if !local {
|
||||
p.errorf("modified non-local variable %q with +=", assignment.Name.Name)
|
||||
p.errorf("modified non-local variable %q with +=", assignment.Name)
|
||||
} else if old.Referenced {
|
||||
p.errorf("modified variable %q with += after referencing",
|
||||
assignment.Name.Name)
|
||||
p.errorf("modified variable %q with += after referencing", assignment.Name)
|
||||
} else {
|
||||
val, err := p.evaluateOperator(old.Value, assignment.Value, '+', assignment.Pos)
|
||||
val, err := p.evaluateOperator(old.Value, assignment.Value, '+', assignment.EqualsPos)
|
||||
if err != nil {
|
||||
p.error(err)
|
||||
} else {
|
||||
|
@ -244,7 +267,8 @@ func (p *parser) parseModule(typ string, typPos scanner.Position) *Module {
|
|||
}
|
||||
|
||||
return &Module{
|
||||
Type: Ident{typ, typPos},
|
||||
Type: typ,
|
||||
TypePos: typPos,
|
||||
Map: Map{
|
||||
Properties: properties,
|
||||
LBracePos: lbracePos,
|
||||
|
@ -293,9 +317,10 @@ func (p *parser) parseProperty(isModule, compat bool) (property *Property) {
|
|||
|
||||
value := p.parseExpression()
|
||||
|
||||
property.Name = Ident{name, namePos}
|
||||
property.Name = name
|
||||
property.NamePos = namePos
|
||||
property.Value = value
|
||||
property.Pos = pos
|
||||
property.ColonPos = pos
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -362,18 +387,18 @@ func (p *parser) addMaps(map1, map2 []*Property, pos scanner.Position) ([]*Prope
|
|||
inBoth := make(map[string]*Property)
|
||||
|
||||
for _, prop1 := range map1 {
|
||||
inMap1[prop1.Name.Name] = prop1
|
||||
inMap1[prop1.Name] = prop1
|
||||
}
|
||||
|
||||
for _, prop2 := range map2 {
|
||||
inMap2[prop2.Name.Name] = prop2
|
||||
if _, ok := inMap1[prop2.Name.Name]; ok {
|
||||
inBoth[prop2.Name.Name] = prop2
|
||||
inMap2[prop2.Name] = prop2
|
||||
if _, ok := inMap1[prop2.Name]; ok {
|
||||
inBoth[prop2.Name] = prop2
|
||||
}
|
||||
}
|
||||
|
||||
for _, prop1 := range map1 {
|
||||
if prop2, ok := inBoth[prop1.Name.Name]; ok {
|
||||
if prop2, ok := inBoth[prop1.Name]; ok {
|
||||
var err error
|
||||
newProp := *prop1
|
||||
newProp.Value, err = p.evaluateOperator(prop1.Value, prop2.Value, '+', pos)
|
||||
|
@ -387,7 +412,7 @@ func (p *parser) addMaps(map1, map2 []*Property, pos scanner.Position) ([]*Prope
|
|||
}
|
||||
|
||||
for _, prop2 := range map2 {
|
||||
if _, ok := inBoth[prop2.Name.Name]; !ok {
|
||||
if _, ok := inBoth[prop2.Name]; !ok {
|
||||
ret = append(ret, prop2)
|
||||
}
|
||||
}
|
||||
|
@ -550,15 +575,15 @@ func NewScope(s *Scope) *Scope {
|
|||
}
|
||||
|
||||
func (s *Scope) Add(assignment *Assignment) error {
|
||||
if old, ok := s.vars[assignment.Name.Name]; ok {
|
||||
if old, ok := s.vars[assignment.Name]; ok {
|
||||
return fmt.Errorf("variable already set, previous assignment: %s", old)
|
||||
}
|
||||
|
||||
if old, ok := s.inheritedVars[assignment.Name.Name]; ok {
|
||||
if old, ok := s.inheritedVars[assignment.Name]; ok {
|
||||
return fmt.Errorf("variable already set in inherited scope, previous assignment: %s", old)
|
||||
}
|
||||
|
||||
s.vars[assignment.Name.Name] = assignment
|
||||
s.vars[assignment.Name] = assignment
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -32,14 +32,15 @@ func mkpos(offset, line, column int) scanner.Position {
|
|||
var validParseTestCases = []struct {
|
||||
input string
|
||||
defs []Definition
|
||||
comments []Comment
|
||||
comments []*CommentGroup
|
||||
}{
|
||||
{`
|
||||
foo {}
|
||||
`,
|
||||
[]Definition{
|
||||
&Module{
|
||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||
Type: "foo",
|
||||
TypePos: mkpos(3, 2, 3),
|
||||
Map: Map{
|
||||
LBracePos: mkpos(7, 2, 7),
|
||||
RBracePos: mkpos(8, 2, 8),
|
||||
|
@ -56,14 +57,16 @@ var validParseTestCases = []struct {
|
|||
`,
|
||||
[]Definition{
|
||||
&Module{
|
||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||
Type: "foo",
|
||||
TypePos: mkpos(3, 2, 3),
|
||||
Map: Map{
|
||||
LBracePos: mkpos(7, 2, 7),
|
||||
RBracePos: mkpos(27, 4, 3),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"name", mkpos(12, 3, 4)},
|
||||
Pos: mkpos(16, 3, 8),
|
||||
Name: "name",
|
||||
NamePos: mkpos(12, 3, 4),
|
||||
ColonPos: mkpos(16, 3, 8),
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(18, 3, 10),
|
||||
Value: "abc",
|
||||
|
@ -83,14 +86,16 @@ var validParseTestCases = []struct {
|
|||
`,
|
||||
[]Definition{
|
||||
&Module{
|
||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||
Type: "foo",
|
||||
TypePos: mkpos(3, 2, 3),
|
||||
Map: Map{
|
||||
LBracePos: mkpos(7, 2, 7),
|
||||
RBracePos: mkpos(28, 4, 3),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"isGood", mkpos(12, 3, 4)},
|
||||
Pos: mkpos(18, 3, 10),
|
||||
Name: "isGood",
|
||||
NamePos: mkpos(12, 3, 4),
|
||||
ColonPos: mkpos(18, 3, 10),
|
||||
Value: &Bool{
|
||||
LiteralPos: mkpos(20, 3, 12),
|
||||
Value: true,
|
||||
|
@ -111,14 +116,16 @@ var validParseTestCases = []struct {
|
|||
`,
|
||||
[]Definition{
|
||||
&Module{
|
||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||
Type: "foo",
|
||||
TypePos: mkpos(3, 2, 3),
|
||||
Map: Map{
|
||||
LBracePos: mkpos(7, 2, 7),
|
||||
RBracePos: mkpos(67, 5, 3),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"stuff", mkpos(12, 3, 4)},
|
||||
Pos: mkpos(17, 3, 9),
|
||||
Name: "stuff",
|
||||
NamePos: mkpos(12, 3, 4),
|
||||
ColonPos: mkpos(17, 3, 9),
|
||||
Value: &List{
|
||||
LBracePos: mkpos(19, 3, 11),
|
||||
RBracePos: mkpos(63, 4, 19),
|
||||
|
@ -163,29 +170,33 @@ var validParseTestCases = []struct {
|
|||
`,
|
||||
[]Definition{
|
||||
&Module{
|
||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||
Type: "foo",
|
||||
TypePos: mkpos(3, 2, 3),
|
||||
Map: Map{
|
||||
LBracePos: mkpos(7, 2, 7),
|
||||
RBracePos: mkpos(62, 7, 3),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"stuff", mkpos(12, 3, 4)},
|
||||
Pos: mkpos(17, 3, 9),
|
||||
Name: "stuff",
|
||||
NamePos: mkpos(12, 3, 4),
|
||||
ColonPos: mkpos(17, 3, 9),
|
||||
Value: &Map{
|
||||
LBracePos: mkpos(19, 3, 11),
|
||||
RBracePos: mkpos(58, 6, 4),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"isGood", mkpos(25, 4, 5)},
|
||||
Pos: mkpos(31, 4, 11),
|
||||
Name: "isGood",
|
||||
NamePos: mkpos(25, 4, 5),
|
||||
ColonPos: mkpos(31, 4, 11),
|
||||
Value: &Bool{
|
||||
LiteralPos: mkpos(33, 4, 13),
|
||||
Value: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: Ident{"name", mkpos(43, 5, 5)},
|
||||
Pos: mkpos(47, 5, 9),
|
||||
Name: "name",
|
||||
NamePos: mkpos(43, 5, 5),
|
||||
ColonPos: mkpos(47, 5, 9),
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(49, 5, 11),
|
||||
Value: "bar",
|
||||
|
@ -210,14 +221,16 @@ var validParseTestCases = []struct {
|
|||
`,
|
||||
[]Definition{
|
||||
&Module{
|
||||
Type: Ident{"foo", mkpos(17, 3, 3)},
|
||||
Type: "foo",
|
||||
TypePos: mkpos(17, 3, 3),
|
||||
Map: Map{
|
||||
LBracePos: mkpos(32, 3, 18),
|
||||
RBracePos: mkpos(81, 6, 3),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"isGood", mkpos(52, 5, 4)},
|
||||
Pos: mkpos(58, 5, 10),
|
||||
Name: "isGood",
|
||||
NamePos: mkpos(52, 5, 4),
|
||||
ColonPos: mkpos(58, 5, 10),
|
||||
Value: &Bool{
|
||||
LiteralPos: mkpos(60, 5, 12),
|
||||
Value: true,
|
||||
|
@ -227,22 +240,38 @@ var validParseTestCases = []struct {
|
|||
},
|
||||
},
|
||||
},
|
||||
[]Comment{
|
||||
Comment{
|
||||
Comment: []string{"// comment1"},
|
||||
Slash: mkpos(3, 2, 3),
|
||||
[]*CommentGroup{
|
||||
{
|
||||
Comments: []*Comment{
|
||||
&Comment{
|
||||
Comment: []string{"// comment1"},
|
||||
Slash: mkpos(3, 2, 3),
|
||||
},
|
||||
},
|
||||
},
|
||||
Comment{
|
||||
Comment: []string{"/* test */"},
|
||||
Slash: mkpos(21, 3, 7),
|
||||
{
|
||||
Comments: []*Comment{
|
||||
&Comment{
|
||||
Comment: []string{"/* test */"},
|
||||
Slash: mkpos(21, 3, 7),
|
||||
},
|
||||
},
|
||||
},
|
||||
Comment{
|
||||
Comment: []string{"// comment2"},
|
||||
Slash: mkpos(37, 4, 4),
|
||||
{
|
||||
Comments: []*Comment{
|
||||
&Comment{
|
||||
Comment: []string{"// comment2"},
|
||||
Slash: mkpos(37, 4, 4),
|
||||
},
|
||||
},
|
||||
},
|
||||
Comment{
|
||||
Comment: []string{"// comment3"},
|
||||
Slash: mkpos(67, 5, 19),
|
||||
{
|
||||
Comments: []*Comment{
|
||||
&Comment{
|
||||
Comment: []string{"// comment3"},
|
||||
Slash: mkpos(67, 5, 19),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -258,14 +287,16 @@ var validParseTestCases = []struct {
|
|||
`,
|
||||
[]Definition{
|
||||
&Module{
|
||||
Type: Ident{"foo", mkpos(3, 2, 3)},
|
||||
Type: "foo",
|
||||
TypePos: mkpos(3, 2, 3),
|
||||
Map: Map{
|
||||
LBracePos: mkpos(7, 2, 7),
|
||||
RBracePos: mkpos(27, 4, 3),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"name", mkpos(12, 3, 4)},
|
||||
Pos: mkpos(16, 3, 8),
|
||||
Name: "name",
|
||||
NamePos: mkpos(12, 3, 4),
|
||||
ColonPos: mkpos(16, 3, 8),
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(18, 3, 10),
|
||||
Value: "abc",
|
||||
|
@ -275,14 +306,16 @@ var validParseTestCases = []struct {
|
|||
},
|
||||
},
|
||||
&Module{
|
||||
Type: Ident{"bar", mkpos(32, 6, 3)},
|
||||
Type: "bar",
|
||||
TypePos: mkpos(32, 6, 3),
|
||||
Map: Map{
|
||||
LBracePos: mkpos(36, 6, 7),
|
||||
RBracePos: mkpos(56, 8, 3),
|
||||
Properties: []*Property{
|
||||
{
|
||||
Name: Ident{"name", mkpos(41, 7, 4)},
|
||||
Pos: mkpos(45, 7, 8),
|
||||
Name: "name",
|
||||
NamePos: mkpos(41, 7, 4),
|
||||
ColonPos: mkpos(45, 7, 8),
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(47, 7, 10),
|
||||
Value: "def",
|
||||
|
@ -303,8 +336,9 @@ var validParseTestCases = []struct {
|
|||
`,
|
||||
[]Definition{
|
||||
&Assignment{
|
||||
Name: Ident{"foo", mkpos(3, 2, 3)},
|
||||
Pos: mkpos(7, 2, 7),
|
||||
Name: "foo",
|
||||
NamePos: mkpos(3, 2, 3),
|
||||
EqualsPos: mkpos(7, 2, 7),
|
||||
Value: &String{
|
||||
LiteralPos: mkpos(9, 2, 9),
|
||||
Value: "stuff",
|
||||
|
@ -317,8 +351,9 @@ var validParseTestCases = []struct {
|
|||
Referenced: true,
|
||||
},
|
||||
&Assignment{
|
||||
Name: Ident{"bar", mkpos(19, 3, 3)},
|
||||
Pos: mkpos(23, 3, 7),
|
||||
Name: "bar",
|
||||
NamePos: mkpos(19, 3, 3),
|
||||
EqualsPos: mkpos(23, 3, 7),
|
||||
Value: &Variable{
|
||||
Name: "foo",
|
||||
NamePos: mkpos(25, 3, 9),
|
||||
|
@ -339,8 +374,9 @@ var validParseTestCases = []struct {
|
|||
Referenced: true,
|
||||
},
|
||||
&Assignment{
|
||||
Name: Ident{"baz", mkpos(31, 4, 3)},
|
||||
Pos: mkpos(35, 4, 7),
|
||||
Name: "baz",
|
||||
NamePos: mkpos(31, 4, 3),
|
||||
EqualsPos: mkpos(35, 4, 7),
|
||||
Value: &Operator{
|
||||
OperatorPos: mkpos(41, 4, 13),
|
||||
Operator: '+',
|
||||
|
@ -405,8 +441,9 @@ var validParseTestCases = []struct {
|
|||
Referenced: true,
|
||||
},
|
||||
&Assignment{
|
||||
Name: Ident{"boo", mkpos(49, 5, 3)},
|
||||
Pos: mkpos(53, 5, 7),
|
||||
Name: "boo",
|
||||
NamePos: mkpos(49, 5, 3),
|
||||
EqualsPos: mkpos(53, 5, 7),
|
||||
Value: &Operator{
|
||||
Args: [2]Expression{
|
||||
&Variable{
|
||||
|
@ -496,8 +533,9 @@ var validParseTestCases = []struct {
|
|||
Assigner: "=",
|
||||
},
|
||||
&Assignment{
|
||||
Name: Ident{"boo", mkpos(61, 6, 3)},
|
||||
Pos: mkpos(66, 6, 8),
|
||||
Name: "boo",
|
||||
NamePos: mkpos(61, 6, 3),
|
||||
EqualsPos: mkpos(66, 6, 8),
|
||||
Value: &Variable{
|
||||
Name: "foo",
|
||||
NamePos: mkpos(68, 6, 10),
|
||||
|
@ -519,6 +557,60 @@ var validParseTestCases = []struct {
|
|||
},
|
||||
nil,
|
||||
},
|
||||
{`
|
||||
// comment1
|
||||
// comment2
|
||||
|
||||
/* comment3
|
||||
comment4 */
|
||||
// comment5
|
||||
|
||||
/* comment6 */ /* comment7 */ // comment8
|
||||
`,
|
||||
nil,
|
||||
[]*CommentGroup{
|
||||
{
|
||||
Comments: []*Comment{
|
||||
&Comment{
|
||||
Comment: []string{"// comment1"},
|
||||
Slash: mkpos(3, 2, 3),
|
||||
},
|
||||
&Comment{
|
||||
Comment: []string{"// comment2"},
|
||||
Slash: mkpos(17, 3, 3),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Comments: []*Comment{
|
||||
&Comment{
|
||||
Comment: []string{"/* comment3", " comment4 */"},
|
||||
Slash: mkpos(32, 5, 3),
|
||||
},
|
||||
&Comment{
|
||||
Comment: []string{"// comment5"},
|
||||
Slash: mkpos(63, 7, 3),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Comments: []*Comment{
|
||||
&Comment{
|
||||
Comment: []string{"/* comment6 */"},
|
||||
Slash: mkpos(78, 9, 3),
|
||||
},
|
||||
&Comment{
|
||||
Comment: []string{"/* comment7 */"},
|
||||
Slash: mkpos(93, 9, 18),
|
||||
},
|
||||
&Comment{
|
||||
Comment: []string{"// comment8"},
|
||||
Slash: mkpos(108, 9, 33),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestParseValidInput(t *testing.T) {
|
||||
|
|
|
@ -26,7 +26,7 @@ var noPos scanner.Position
|
|||
|
||||
type printer struct {
|
||||
defs []Definition
|
||||
comments []Comment
|
||||
comments []*CommentGroup
|
||||
|
||||
curComment int
|
||||
|
||||
|
@ -40,7 +40,7 @@ type printer struct {
|
|||
indentList []int
|
||||
wsBuf []byte
|
||||
|
||||
skippedComments []Comment
|
||||
skippedComments *CommentGroup
|
||||
}
|
||||
|
||||
func newPrinter(file *File) *printer {
|
||||
|
@ -87,16 +87,16 @@ func (p *printer) printDef(def Definition) {
|
|||
}
|
||||
|
||||
func (p *printer) printAssignment(assignment *Assignment) {
|
||||
p.printToken(assignment.Name.Name, assignment.Name.Pos)
|
||||
p.printToken(assignment.Name, assignment.NamePos)
|
||||
p.requestSpace()
|
||||
p.printToken(assignment.Assigner, assignment.Pos)
|
||||
p.printToken(assignment.Assigner, assignment.EqualsPos)
|
||||
p.requestSpace()
|
||||
p.printExpression(assignment.OrigValue)
|
||||
p.requestNewline()
|
||||
}
|
||||
|
||||
func (p *printer) printModule(module *Module) {
|
||||
p.printToken(module.Type.Name, module.Type.Pos)
|
||||
p.printToken(module.Type, module.TypePos)
|
||||
p.printMap(&module.Map)
|
||||
p.requestDoubleNewline()
|
||||
}
|
||||
|
@ -175,8 +175,8 @@ func (p *printer) printOperator(operator *Operator) {
|
|||
}
|
||||
|
||||
func (p *printer) printProperty(property *Property) {
|
||||
p.printToken(property.Name.Name, property.Name.Pos)
|
||||
p.printToken(":", property.Pos)
|
||||
p.printToken(property.Name, property.NamePos)
|
||||
p.printToken(":", property.ColonPos)
|
||||
p.requestSpace()
|
||||
p.printExpression(property.Value)
|
||||
}
|
||||
|
@ -206,12 +206,14 @@ func (p *printer) printToken(s string, pos scanner.Position) {
|
|||
|
||||
// Print any in-line (single line /* */) comments that appear _before_ pos
|
||||
func (p *printer) printInLineCommentsBefore(pos scanner.Position) {
|
||||
for p.curComment < len(p.comments) && p.comments[p.curComment].Slash.Offset < pos.Offset {
|
||||
for p.curComment < len(p.comments) && p.comments[p.curComment].Pos().Offset < pos.Offset {
|
||||
c := p.comments[p.curComment]
|
||||
if c.Comment[0][0:2] == "//" || len(c.Comment) > 1 {
|
||||
p.skippedComments = append(p.skippedComments, c)
|
||||
if c.Comments[0].Comment[0][0:2] == "//" || len(c.Comments[0].Comment) > 1 {
|
||||
if p.skippedComments != nil {
|
||||
panic("multiple skipped comments")
|
||||
}
|
||||
p.skippedComments = c
|
||||
} else {
|
||||
p.flushSpace()
|
||||
p.printComment(c)
|
||||
p.requestSpace()
|
||||
}
|
||||
|
@ -222,19 +224,13 @@ func (p *printer) printInLineCommentsBefore(pos scanner.Position) {
|
|||
// Print any comments, including end of line comments, that appear _before_ the line specified
|
||||
// by pos
|
||||
func (p *printer) printEndOfLineCommentsBefore(pos scanner.Position) {
|
||||
for _, c := range p.skippedComments {
|
||||
if !p.requestNewlinesForPos(c.Slash) {
|
||||
p.requestSpace()
|
||||
}
|
||||
p.printComment(c)
|
||||
if p.skippedComments != nil {
|
||||
p.printComment(p.skippedComments)
|
||||
p._requestNewline()
|
||||
p.skippedComments = nil
|
||||
}
|
||||
p.skippedComments = []Comment{}
|
||||
for p.curComment < len(p.comments) && p.comments[p.curComment].Slash.Line < pos.Line {
|
||||
for p.curComment < len(p.comments) && p.comments[p.curComment].Pos().Line < pos.Line {
|
||||
c := p.comments[p.curComment]
|
||||
if !p.requestNewlinesForPos(c.Slash) {
|
||||
p.requestSpace()
|
||||
}
|
||||
p.printComment(c)
|
||||
p._requestNewline()
|
||||
p.curComment++
|
||||
|
@ -300,39 +296,38 @@ func (p *printer) flushSpace() {
|
|||
}
|
||||
|
||||
// Print a single comment, which may be a multi-line comment
|
||||
func (p *printer) printComment(comment Comment) {
|
||||
pos := comment.Slash
|
||||
for i, line := range comment.Comment {
|
||||
line = strings.TrimRightFunc(line, unicode.IsSpace)
|
||||
p.flushSpace()
|
||||
if i != 0 {
|
||||
lineIndent := strings.IndexFunc(line, func(r rune) bool { return !unicode.IsSpace(r) })
|
||||
lineIndent = max(lineIndent, p.curIndent())
|
||||
p.pad(lineIndent - p.curIndent())
|
||||
pos.Line++
|
||||
func (p *printer) printComment(cg *CommentGroup) {
|
||||
for _, comment := range cg.Comments {
|
||||
if !p.requestNewlinesForPos(comment.Pos()) {
|
||||
p.requestSpace()
|
||||
}
|
||||
p.output = append(p.output, strings.TrimSpace(line)...)
|
||||
if i < len(comment.Comment)-1 {
|
||||
p._requestNewline()
|
||||
for i, line := range comment.Comment {
|
||||
line = strings.TrimRightFunc(line, unicode.IsSpace)
|
||||
p.flushSpace()
|
||||
if i != 0 {
|
||||
lineIndent := strings.IndexFunc(line, func(r rune) bool { return !unicode.IsSpace(r) })
|
||||
lineIndent = max(lineIndent, p.curIndent())
|
||||
p.pad(lineIndent - p.curIndent())
|
||||
}
|
||||
p.output = append(p.output, strings.TrimSpace(line)...)
|
||||
if i < len(comment.Comment)-1 {
|
||||
p._requestNewline()
|
||||
}
|
||||
}
|
||||
p.pos = comment.End()
|
||||
}
|
||||
p.pos = pos
|
||||
}
|
||||
|
||||
// Print any comments that occur after the last token, and a trailing newline
|
||||
func (p *printer) flush() {
|
||||
for _, c := range p.skippedComments {
|
||||
if !p.requestNewlinesForPos(c.Slash) {
|
||||
if p.skippedComments != nil {
|
||||
if !p.requestNewlinesForPos(p.skippedComments.Pos()) {
|
||||
p.requestSpace()
|
||||
}
|
||||
p.printComment(c)
|
||||
p.printComment(p.skippedComments)
|
||||
}
|
||||
for p.curComment < len(p.comments) {
|
||||
c := p.comments[p.curComment]
|
||||
if !p.requestNewlinesForPos(c.Slash) {
|
||||
p.requestSpace()
|
||||
}
|
||||
p.printComment(c)
|
||||
p.printComment(p.comments[p.curComment])
|
||||
p.curComment++
|
||||
}
|
||||
p.output = append(p.output, '\n')
|
||||
|
|
|
@ -205,8 +205,8 @@ test /* test */ {
|
|||
deps: ["libabc"],
|
||||
incs: [],
|
||||
} //test
|
||||
|
||||
//test
|
||||
|
||||
test2 {
|
||||
}
|
||||
|
||||
|
@ -253,7 +253,7 @@ test {
|
|||
}
|
||||
|
||||
// This
|
||||
/* Is */
|
||||
/* Is *//* A */ // A
|
||||
// A
|
||||
|
||||
// Multiline
|
||||
|
@ -279,7 +279,7 @@ test {
|
|||
}
|
||||
|
||||
// This
|
||||
/* Is */
|
||||
/* Is */ /* A */ // A
|
||||
// A
|
||||
|
||||
// Multiline
|
||||
|
|
|
@ -107,7 +107,16 @@ func sortSubList(values []Expression, nextPos scanner.Position, file *File) {
|
|||
sort.Sort(l)
|
||||
|
||||
copyValues := append([]Expression{}, values...)
|
||||
copyComments := append([]Comment{}, file.Comments...)
|
||||
copyComments := make([]*CommentGroup, len(file.Comments))
|
||||
for i := range file.Comments {
|
||||
cg := *file.Comments[i]
|
||||
cg.Comments = make([]*Comment, len(cg.Comments))
|
||||
for j := range file.Comments[i].Comments {
|
||||
c := *file.Comments[i].Comments[j]
|
||||
cg.Comments[j] = &c
|
||||
}
|
||||
copyComments[i] = &cg
|
||||
}
|
||||
|
||||
curPos := values[0].Pos()
|
||||
for i, e := range l {
|
||||
|
@ -115,8 +124,8 @@ func sortSubList(values []Expression, nextPos scanner.Position, file *File) {
|
|||
values[i].(*String).LiteralPos = curPos
|
||||
for j, c := range copyComments {
|
||||
if c.Pos().Offset > e.pos.Offset && c.Pos().Offset < e.nextPos.Offset {
|
||||
file.Comments[j].Slash.Line = curPos.Line
|
||||
file.Comments[j].Slash.Offset += values[i].Pos().Offset - e.pos.Offset
|
||||
file.Comments[j].Comments[0].Slash.Line = curPos.Line
|
||||
file.Comments[j].Comments[0].Slash.Offset += values[i].Pos().Offset - e.pos.Offset
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,7 +171,7 @@ func (l elemList) Less(i, j int) bool {
|
|||
return l[i].s < l[j].s
|
||||
}
|
||||
|
||||
type commentsByOffset []Comment
|
||||
type commentsByOffset []*CommentGroup
|
||||
|
||||
func (l commentsByOffset) Len() int {
|
||||
return len(l)
|
||||
|
|
12
unpack.go
12
unpack.go
|
@ -65,7 +65,7 @@ func unpackProperties(propertyDefs []*parser.Property,
|
|||
if !packedProperty.unpacked {
|
||||
err := &Error{
|
||||
Err: fmt.Errorf("unrecognized property %q", name),
|
||||
Pos: packedProperty.property.Pos,
|
||||
Pos: packedProperty.property.ColonPos,
|
||||
}
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ func buildPropertyMap(namePrefix string, propertyDefs []*parser.Property,
|
|||
propertyMap map[string]*packedProperty) (errs []error) {
|
||||
|
||||
for _, propertyDef := range propertyDefs {
|
||||
name := namePrefix + propertyDef.Name.Name
|
||||
name := namePrefix + propertyDef.Name
|
||||
if first, present := propertyMap[name]; present {
|
||||
if first.property == propertyDef {
|
||||
// We've already added this property.
|
||||
|
@ -90,11 +90,11 @@ func buildPropertyMap(namePrefix string, propertyDefs []*parser.Property,
|
|||
}
|
||||
errs = append(errs, &Error{
|
||||
Err: fmt.Errorf("property %q already defined", name),
|
||||
Pos: propertyDef.Pos,
|
||||
Pos: propertyDef.ColonPos,
|
||||
})
|
||||
errs = append(errs, &Error{
|
||||
Err: fmt.Errorf("<-- previous definition here"),
|
||||
Pos: first.property.Pos,
|
||||
Pos: first.property.ColonPos,
|
||||
})
|
||||
if len(errs) >= maxErrors {
|
||||
return errs
|
||||
|
@ -200,7 +200,7 @@ func unpackStructValue(namePrefix string, structValue reflect.Value,
|
|||
errs = append(errs,
|
||||
&Error{
|
||||
Err: fmt.Errorf("mutated field %s cannot be set in a Blueprint file", propertyName),
|
||||
Pos: packedProperty.property.Pos,
|
||||
Pos: packedProperty.property.ColonPos,
|
||||
})
|
||||
if len(errs) >= maxErrors {
|
||||
return errs
|
||||
|
@ -212,7 +212,7 @@ func unpackStructValue(namePrefix string, structValue reflect.Value,
|
|||
errs = append(errs,
|
||||
&Error{
|
||||
Err: fmt.Errorf("filtered field %s cannot be set in a Blueprint file", propertyName),
|
||||
Pos: packedProperty.property.Pos,
|
||||
Pos: packedProperty.property.ColonPos,
|
||||
})
|
||||
if len(errs) >= maxErrors {
|
||||
return errs
|
||||
|
|
Loading…
Reference in a new issue