Merge pull request #104 from colincross/parser
More parser cleanups
diff --git a/bpmodify/bpmodify.go b/bpmodify/bpmodify.go
index 6f7a5bf..6a3c803 100644
--- a/bpmodify/bpmodify.go
+++ b/bpmodify/bpmodify.go
@@ -124,9 +124,9 @@
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 @@
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
}
diff --git a/context.go b/context.go
index b644ac5..4e05ac8 100644
--- a/context.go
+++ b/context.go
@@ -875,11 +875,11 @@
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 @@
} 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) 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 @@
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 @@
module := &moduleInfo{
logicModule: logicModule,
- typeName: typeName,
+ typeName: moduleDef.Type,
relBlueprintsFile: relBlueprintsFile,
}
@@ -1074,10 +1073,10 @@
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
diff --git a/parser/ast.go b/parser/ast.go
index 387f6d5..19a79d7 100644
--- a/parser/ast.go
+++ b/parser/ast.go
@@ -20,8 +20,16 @@
"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 @@
// 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) 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) 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 @@
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 @@
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
diff --git a/parser/parser.go b/parser/parser.go
index 6909c50..de4e8e2 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -40,8 +40,23 @@
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 @@
tok rune
errors []error
scope *Scope
- comments []Comment
+ comments []*CommentGroup
eval bool
}
@@ -139,10 +154,18 @@
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 @@
}
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 @@
}
return &Module{
- Type: Ident{typ, typPos},
+ Type: typ,
+ TypePos: typPos,
Map: Map{
Properties: properties,
LBracePos: lbracePos,
@@ -293,9 +317,10 @@
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 @@
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 @@
}
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 (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
}
diff --git a/parser/parser_test.go b/parser/parser_test.go
index e93bb09..bde67e5 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -32,14 +32,15 @@
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 @@
`,
[]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 @@
`,
[]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 @@
`,
[]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 @@
`,
[]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 @@
`,
[]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 @@
},
},
},
- []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 @@
`,
[]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 @@
},
},
&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 @@
`,
[]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 @@
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 @@
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 @@
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 @@
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 @@
},
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) {
diff --git a/parser/printer.go b/parser/printer.go
index 1e7bc2a..943f930 100644
--- a/parser/printer.go
+++ b/parser/printer.go
@@ -26,7 +26,7 @@
type printer struct {
defs []Definition
- comments []Comment
+ comments []*CommentGroup
curComment int
@@ -40,7 +40,7 @@
indentList []int
wsBuf []byte
- skippedComments []Comment
+ skippedComments *CommentGroup
}
func newPrinter(file *File) *printer {
@@ -87,16 +87,16 @@
}
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) 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 @@
// 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 @@
// 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 @@
}
// 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')
diff --git a/parser/printer_test.go b/parser/printer_test.go
index 3e759cb..6c3e49c 100644
--- a/parser/printer_test.go
+++ b/parser/printer_test.go
@@ -205,8 +205,8 @@
deps: ["libabc"],
incs: [],
} //test
-
//test
+
test2 {
}
@@ -253,7 +253,7 @@
}
// This
-/* Is */
+/* Is *//* A */ // A
// A
// Multiline
@@ -279,7 +279,7 @@
}
// This
-/* Is */
+/* Is */ /* A */ // A
// A
// Multiline
diff --git a/parser/sort.go b/parser/sort.go
index 05ce5fd..c8bf74f 100644
--- a/parser/sort.go
+++ b/parser/sort.go
@@ -107,7 +107,16 @@
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 @@
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 @@
return l[i].s < l[j].s
}
-type commentsByOffset []Comment
+type commentsByOffset []*CommentGroup
func (l commentsByOffset) Len() int {
return len(l)
diff --git a/unpack.go b/unpack.go
index 11718b4..f7e0a2d 100644
--- a/unpack.go
+++ b/unpack.go
@@ -65,7 +65,7 @@
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 @@
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 @@
}
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 @@
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 @@
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