Improve makefile parser am: 08693d2bf2

am: 21b30618ae

* commit '21b30618ae3633491deee09decbca92b05ac3993':
  Improve makefile parser

Change-Id: If0d95586af952280be210028a4a849fe7c4afc9a
This commit is contained in:
Colin Cross
2016-05-26 17:00:07 +00:00
committed by android-build-merger
7 changed files with 245 additions and 277 deletions

View File

@@ -216,8 +216,8 @@ bootstrap_go_package {
name: "androidmk-parser", name: "androidmk-parser",
pkgPath: "android/soong/androidmk/parser", pkgPath: "android/soong/androidmk/parser",
srcs: [ srcs: [
"androidmk/parser/ast.go",
"androidmk/parser/make_strings.go", "androidmk/parser/make_strings.go",
"androidmk/parser/makething.go",
"androidmk/parser/parser.go", "androidmk/parser/parser.go",
"androidmk/parser/scope.go", "androidmk/parser/scope.go",
], ],

View File

@@ -29,8 +29,8 @@ type bpFile struct {
inModule bool inModule bool
} }
func (f *bpFile) errorf(thing mkparser.MakeThing, s string, args ...interface{}) { func (f *bpFile) errorf(node mkparser.Node, s string, args ...interface{}) {
orig := thing.Dump() orig := node.Dump()
s = fmt.Sprintf(s, args...) s = fmt.Sprintf(s, args...)
c := bpparser.Comment{ c := bpparser.Comment{
Comment: []string{fmt.Sprintf("// ANDROIDMK TRANSLATION ERROR: %s", s)}, Comment: []string{fmt.Sprintf("// ANDROIDMK TRANSLATION ERROR: %s", s)},
@@ -73,7 +73,7 @@ func main() {
p := mkparser.NewParser(os.Args[1], bytes.NewBuffer(b)) p := mkparser.NewParser(os.Args[1], bytes.NewBuffer(b))
things, errs := p.Parse() nodes, errs := p.Parse()
if len(errs) > 0 { if len(errs) > 0 {
for _, err := range errs { for _, err := range errs {
fmt.Println("ERROR: ", err) fmt.Println("ERROR: ", err)
@@ -90,33 +90,34 @@ func main() {
var conds []*conditional var conds []*conditional
var assignmentCond *conditional var assignmentCond *conditional
for _, t := range things { for _, node := range nodes {
file.setMkPos(t.Pos(), t.EndPos()) file.setMkPos(p.Unpack(node.Pos()), p.Unpack(node.End()))
if comment, ok := t.AsComment(); ok { switch x := node.(type) {
case *mkparser.Comment:
file.comments = append(file.comments, bpparser.Comment{ file.comments = append(file.comments, bpparser.Comment{
Comment: []string{"//" + comment.Comment},
Pos: file.bpPos, Pos: file.bpPos,
Comment: []string{"//" + x.Comment},
}) })
} else if assignment, ok := t.AsAssignment(); ok { case *mkparser.Assignment:
handleAssignment(file, assignment, assignmentCond) handleAssignment(file, x, assignmentCond)
} else if directive, ok := t.AsDirective(); ok { case *mkparser.Directive:
switch directive.Name { switch x.Name {
case "include": case "include":
val := directive.Args.Value(file.scope) val := x.Args.Value(file.scope)
switch { switch {
case soongModuleTypes[val]: case soongModuleTypes[val]:
handleModuleConditionals(file, directive, conds) handleModuleConditionals(file, x, conds)
makeModule(file, val) makeModule(file, val)
case val == clear_vars: case val == clear_vars:
resetModule(file) resetModule(file)
default: default:
file.errorf(directive, "unsupported include") file.errorf(x, "unsupported include")
continue continue
} }
case "ifeq", "ifneq", "ifdef", "ifndef": case "ifeq", "ifneq", "ifdef", "ifndef":
args := directive.Args.Dump() args := x.Args.Dump()
eq := directive.Name == "ifeq" || directive.Name == "ifdef" eq := x.Name == "ifeq" || x.Name == "ifdef"
if _, ok := conditionalTranslations[args]; ok { if _, ok := conditionalTranslations[args]; ok {
newCond := conditional{args, eq} newCond := conditional{args, eq}
conds = append(conds, &newCond) conds = append(conds, &newCond)
@@ -124,29 +125,29 @@ func main() {
if assignmentCond == nil { if assignmentCond == nil {
assignmentCond = &newCond assignmentCond = &newCond
} else { } else {
file.errorf(directive, "unsupported nested conditional in module") file.errorf(x, "unsupported nested conditional in module")
} }
} }
} else { } else {
file.errorf(directive, "unsupported conditional") file.errorf(x, "unsupported conditional")
conds = append(conds, nil) conds = append(conds, nil)
continue continue
} }
case "else": case "else":
if len(conds) == 0 { if len(conds) == 0 {
file.errorf(directive, "missing if before else") file.errorf(x, "missing if before else")
continue continue
} else if conds[len(conds)-1] == nil { } else if conds[len(conds)-1] == nil {
file.errorf(directive, "else from unsupported contitional") file.errorf(x, "else from unsupported contitional")
continue continue
} }
conds[len(conds)-1].eq = !conds[len(conds)-1].eq conds[len(conds)-1].eq = !conds[len(conds)-1].eq
case "endif": case "endif":
if len(conds) == 0 { if len(conds) == 0 {
file.errorf(directive, "missing if before endif") file.errorf(x, "missing if before endif")
continue continue
} else if conds[len(conds)-1] == nil { } else if conds[len(conds)-1] == nil {
file.errorf(directive, "endif from unsupported contitional") file.errorf(x, "endif from unsupported contitional")
conds = conds[:len(conds)-1] conds = conds[:len(conds)-1]
} else { } else {
if assignmentCond == conds[len(conds)-1] { if assignmentCond == conds[len(conds)-1] {
@@ -155,11 +156,11 @@ func main() {
conds = conds[:len(conds)-1] conds = conds[:len(conds)-1]
} }
default: default:
file.errorf(directive, "unsupported directive") file.errorf(x, "unsupported directive")
continue continue
} }
} else { default:
file.errorf(t, "unsupported line") file.errorf(x, "unsupported line")
} }
} }
@@ -175,7 +176,7 @@ func main() {
fmt.Print(string(out)) fmt.Print(string(out))
} }
func handleAssignment(file *bpFile, assignment mkparser.Assignment, c *conditional) { func handleAssignment(file *bpFile, assignment *mkparser.Assignment, c *conditional) {
if !assignment.Name.Const() { if !assignment.Name.Const() {
file.errorf(assignment, "unsupported non-const variable name") file.errorf(assignment, "unsupported non-const variable name")
return return
@@ -239,7 +240,7 @@ func handleAssignment(file *bpFile, assignment mkparser.Assignment, c *condition
// This is a hack to get the LOCAL_ARM_MODE value inside // This is a hack to get the LOCAL_ARM_MODE value inside
// of an arch: { arm: {} } block. // of an arch: { arm: {} } block.
armModeAssign := assignment armModeAssign := assignment
armModeAssign.Name = mkparser.SimpleMakeString("LOCAL_ARM_MODE_HACK_arm", assignment.Name.Pos) armModeAssign.Name = mkparser.SimpleMakeString("LOCAL_ARM_MODE_HACK_arm", assignment.Name.Pos())
handleAssignment(file, armModeAssign, c) handleAssignment(file, armModeAssign, c)
case name == "LOCAL_ADDITIONAL_DEPENDENCIES": case name == "LOCAL_ADDITIONAL_DEPENDENCIES":
// TODO: check for only .mk files? // TODO: check for only .mk files?
@@ -257,7 +258,7 @@ func handleAssignment(file *bpFile, assignment mkparser.Assignment, c *condition
} }
} }
func handleModuleConditionals(file *bpFile, directive mkparser.Directive, conds []*conditional) { func handleModuleConditionals(file *bpFile, directive *mkparser.Directive, conds []*conditional) {
for _, c := range conds { for _, c := range conds {
if c == nil { if c == nil {
continue continue
@@ -270,7 +271,7 @@ func handleModuleConditionals(file *bpFile, directive mkparser.Directive, conds
disabledPrefix := conditionalTranslations[c.cond][!c.eq] disabledPrefix := conditionalTranslations[c.cond][!c.eq]
// Create a fake assignment with enabled = false // Create a fake assignment with enabled = false
val, err := makeVariableToBlueprint(file, mkparser.SimpleMakeString("false", file.bpPos), bpparser.Bool) val, err := makeVariableToBlueprint(file, mkparser.SimpleMakeString("false", mkparser.NoPos), bpparser.Bool)
if err == nil { if err == nil {
err = setVariable(file, false, disabledPrefix, "enabled", val, true) err = setVariable(file, false, disabledPrefix, "enabled", val, true)
} }

110
androidmk/parser/ast.go Normal file
View File

@@ -0,0 +1,110 @@
package parser
type Pos int
const NoPos Pos = 0
type Node interface {
Dump() string
Pos() Pos
End() Pos
}
type Assignment struct {
Target *MakeString
Name *MakeString
Value *MakeString
Type string
}
func (x *Assignment) Dump() string {
target := ""
if x.Target != nil {
target = x.Target.Dump() + ": "
}
return target + x.Name.Dump() + x.Type + x.Value.Dump()
}
func (x *Assignment) Pos() Pos {
if x.Target != nil {
return x.Target.Pos()
}
return x.Name.Pos()
}
func (x *Assignment) End() Pos { return x.Value.End() }
type Comment struct {
CommentPos Pos
Comment string
}
func (x *Comment) Dump() string {
return "#" + x.Comment
}
func (x *Comment) Pos() Pos { return x.CommentPos }
func (x *Comment) End() Pos { return Pos(int(x.CommentPos) + len(x.Comment)) }
type Directive struct {
NamePos Pos
Name string
Args *MakeString
EndPos Pos
}
func (x *Directive) Dump() string {
return x.Name + " " + x.Args.Dump()
}
func (x *Directive) Pos() Pos { return x.NamePos }
func (x *Directive) End() Pos {
if x.EndPos != NoPos {
return x.EndPos
}
return x.Args.End()
}
type Rule struct {
Target *MakeString
Prerequisites *MakeString
RecipePos Pos
Recipe string
}
func (x *Rule) Dump() string {
recipe := ""
if x.Recipe != "" {
recipe = "\n" + x.Recipe
}
return "rule: " + x.Target.Dump() + ": " + x.Prerequisites.Dump() + recipe
}
func (x *Rule) Pos() Pos { return x.Target.Pos() }
func (x *Rule) End() Pos { return Pos(int(x.RecipePos) + len(x.Recipe)) }
type Variable struct {
Name *MakeString
}
func (x *Variable) Pos() Pos { return x.Name.Pos() }
func (x *Variable) End() Pos { return x.Name.End() }
func (x *Variable) Dump() string {
return "$(" + x.Name.Dump() + ")"
}
// Sort interface for []Node by position
type byPosition []Node
func (s byPosition) Len() int {
return len(s)
}
func (s byPosition) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s byPosition) Less(i, j int) bool {
return s[i].Pos() < s[j].Pos()
}

View File

@@ -2,7 +2,6 @@ package parser
import ( import (
"strings" "strings"
"text/scanner"
"unicode" "unicode"
) )
@@ -18,18 +17,30 @@ import (
// of Variables. The raw string list is always one longer than the variable // of Variables. The raw string list is always one longer than the variable
// list. // list.
type MakeString struct { type MakeString struct {
Pos scanner.Position StringPos Pos
Strings []string Strings []string
Variables []Variable Variables []Variable
} }
func SimpleMakeString(s string, pos scanner.Position) *MakeString { func SimpleMakeString(s string, pos Pos) *MakeString {
return &MakeString{ return &MakeString{
Pos: pos, StringPos: pos,
Strings: []string{s}, Strings: []string{s},
} }
} }
func (ms *MakeString) Pos() Pos {
return ms.StringPos
}
func (ms *MakeString) End() Pos {
pos := ms.StringPos
if len(ms.Strings) > 1 {
pos = ms.Variables[len(ms.Variables)-1].End()
}
return Pos(int(pos) + len(ms.Strings[len(ms.Strings)-1]))
}
func (ms *MakeString) appendString(s string) { func (ms *MakeString) appendString(s string) {
if len(ms.Strings) == 0 { if len(ms.Strings) == 0 {
ms.Strings = []string{s} ms.Strings = []string{s}
@@ -97,7 +108,7 @@ func (ms *MakeString) Split(sep string) []*MakeString {
func (ms *MakeString) SplitN(sep string, n int) []*MakeString { func (ms *MakeString) SplitN(sep string, n int) []*MakeString {
ret := []*MakeString{} ret := []*MakeString{}
curMs := SimpleMakeString("", ms.Pos) curMs := SimpleMakeString("", ms.Pos())
var i int var i int
var s string var s string
@@ -115,7 +126,7 @@ func (ms *MakeString) SplitN(sep string, n int) []*MakeString {
for _, r := range split[1:] { for _, r := range split[1:] {
ret = append(ret, curMs) ret = append(ret, curMs)
curMs = SimpleMakeString(r, ms.Pos) curMs = SimpleMakeString(r, ms.Pos())
} }
} else { } else {
curMs.appendString(s) curMs.appendString(s)
@@ -131,7 +142,9 @@ func (ms *MakeString) SplitN(sep string, n int) []*MakeString {
} }
func (ms *MakeString) TrimLeftSpaces() { func (ms *MakeString) TrimLeftSpaces() {
l := len(ms.Strings[0])
ms.Strings[0] = strings.TrimLeftFunc(ms.Strings[0], unicode.IsSpace) ms.Strings[0] = strings.TrimLeftFunc(ms.Strings[0], unicode.IsSpace)
ms.StringPos += Pos(len(ms.Strings[0]) - l)
} }
func (ms *MakeString) TrimRightSpaces() { func (ms *MakeString) TrimRightSpaces() {

View File

@@ -3,7 +3,6 @@ package parser
import ( import (
"strings" "strings"
"testing" "testing"
"text/scanner"
) )
var splitNTestCases = []struct { var splitNTestCases = []struct {
@@ -20,31 +19,31 @@ var splitNTestCases = []struct {
" h i j", " h i j",
}, },
Variables: []Variable{ Variables: []Variable{
Variable{Name: SimpleMakeString("var1", scanner.Position{})}, Variable{Name: SimpleMakeString("var1", NoPos)},
Variable{Name: SimpleMakeString("var2", scanner.Position{})}, Variable{Name: SimpleMakeString("var2", NoPos)},
}, },
}, },
sep: " ", sep: " ",
n: -1, n: -1,
expected: []*MakeString{ expected: []*MakeString{
SimpleMakeString("a", scanner.Position{}), SimpleMakeString("a", NoPos),
SimpleMakeString("b", scanner.Position{}), SimpleMakeString("b", NoPos),
&MakeString{ &MakeString{
Strings: []string{"c", "d"}, Strings: []string{"c", "d"},
Variables: []Variable{ Variables: []Variable{
Variable{Name: SimpleMakeString("var1", scanner.Position{})}, Variable{Name: SimpleMakeString("var1", NoPos)},
}, },
}, },
SimpleMakeString("e", scanner.Position{}), SimpleMakeString("e", NoPos),
&MakeString{ &MakeString{
Strings: []string{"f", ""}, Strings: []string{"f", ""},
Variables: []Variable{ Variables: []Variable{
Variable{Name: SimpleMakeString("var2", scanner.Position{})}, Variable{Name: SimpleMakeString("var2", NoPos)},
}, },
}, },
SimpleMakeString("h", scanner.Position{}), SimpleMakeString("h", NoPos),
SimpleMakeString("i", scanner.Position{}), SimpleMakeString("i", NoPos),
SimpleMakeString("j", scanner.Position{}), SimpleMakeString("j", NoPos),
}, },
}, },
{ {
@@ -55,20 +54,20 @@ var splitNTestCases = []struct {
" h i j", " h i j",
}, },
Variables: []Variable{ Variables: []Variable{
Variable{Name: SimpleMakeString("var1", scanner.Position{})}, Variable{Name: SimpleMakeString("var1", NoPos)},
Variable{Name: SimpleMakeString("var2", scanner.Position{})}, Variable{Name: SimpleMakeString("var2", NoPos)},
}, },
}, },
sep: " ", sep: " ",
n: 3, n: 3,
expected: []*MakeString{ expected: []*MakeString{
SimpleMakeString("a", scanner.Position{}), SimpleMakeString("a", NoPos),
SimpleMakeString("b", scanner.Position{}), SimpleMakeString("b", NoPos),
&MakeString{ &MakeString{
Strings: []string{"c", "d e f", " h i j"}, Strings: []string{"c", "d e f", " h i j"},
Variables: []Variable{ Variables: []Variable{
Variable{Name: SimpleMakeString("var1", scanner.Position{})}, Variable{Name: SimpleMakeString("var1", NoPos)},
Variable{Name: SimpleMakeString("var2", scanner.Position{})}, Variable{Name: SimpleMakeString("var2", NoPos)},
}, },
}, },
}, },

View File

@@ -1,142 +0,0 @@
package parser
import (
"text/scanner"
)
type MakeThing interface {
AsAssignment() (Assignment, bool)
AsComment() (Comment, bool)
AsDirective() (Directive, bool)
AsRule() (Rule, bool)
AsVariable() (Variable, bool)
Dump() string
Pos() scanner.Position
EndPos() scanner.Position
}
type Assignment struct {
makeThing
Name *MakeString
Value *MakeString
Target *MakeString
Type string
}
type Comment struct {
makeThing
Comment string
}
type Directive struct {
makeThing
Name string
Args *MakeString
}
type Rule struct {
makeThing
Target *MakeString
Prerequisites *MakeString
Recipe string
}
type Variable struct {
makeThing
Name *MakeString
}
type makeThing struct {
pos scanner.Position
endPos scanner.Position
}
func (m makeThing) Pos() scanner.Position {
return m.pos
}
func (m makeThing) EndPos() scanner.Position {
return m.endPos
}
func (makeThing) AsAssignment() (a Assignment, ok bool) {
return
}
func (a Assignment) AsAssignment() (Assignment, bool) {
return a, true
}
func (a Assignment) Dump() string {
target := ""
if a.Target != nil {
target = a.Target.Dump() + ": "
}
return target + a.Name.Dump() + a.Type + a.Value.Dump()
}
func (makeThing) AsComment() (c Comment, ok bool) {
return
}
func (c Comment) AsComment() (Comment, bool) {
return c, true
}
func (c Comment) Dump() string {
return "#" + c.Comment
}
func (makeThing) AsDirective() (d Directive, ok bool) {
return
}
func (d Directive) AsDirective() (Directive, bool) {
return d, true
}
func (d Directive) Dump() string {
return d.Name + " " + d.Args.Dump()
}
func (makeThing) AsRule() (r Rule, ok bool) {
return
}
func (r Rule) AsRule() (Rule, bool) {
return r, true
}
func (r Rule) Dump() string {
recipe := ""
if r.Recipe != "" {
recipe = "\n" + r.Recipe
}
return "rule: " + r.Target.Dump() + ": " + r.Prerequisites.Dump() + recipe
}
func (makeThing) AsVariable() (v Variable, ok bool) {
return
}
func (v Variable) AsVariable() (Variable, bool) {
return v, true
}
func (v Variable) Dump() string {
return "$(" + v.Name.Dump() + ")"
}
type byPosition []MakeThing
func (s byPosition) Len() int {
return len(s)
}
func (s byPosition) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s byPosition) Less(i, j int) bool {
return s[i].Pos().Offset < s[j].Pos().Offset
}

View File

@@ -21,7 +21,7 @@ func (e *ParseError) Error() string {
return fmt.Sprintf("%s: %s", e.Pos, e.Err) return fmt.Sprintf("%s: %s", e.Pos, e.Err)
} }
func (p *parser) Parse() ([]MakeThing, []error) { func (p *parser) Parse() ([]Node, []error) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
if r == errTooManyErrors { if r == errTooManyErrors {
@@ -33,22 +33,24 @@ func (p *parser) Parse() ([]MakeThing, []error) {
p.parseLines() p.parseLines()
p.accept(scanner.EOF) p.accept(scanner.EOF)
p.things = append(p.things, p.comments...) p.nodes = append(p.nodes, p.comments...)
sort.Sort(byPosition(p.things)) sort.Sort(byPosition(p.nodes))
return p.things, p.errors return p.nodes, p.errors
} }
type parser struct { type parser struct {
scanner scanner.Scanner scanner scanner.Scanner
tok rune tok rune
errors []error errors []error
comments []MakeThing comments []Node
things []MakeThing nodes []Node
lines []int
} }
func NewParser(filename string, r io.Reader) *parser { func NewParser(filename string, r io.Reader) *parser {
p := &parser{} p := &parser{}
p.lines = []int{0}
p.scanner.Init(r) p.scanner.Init(r)
p.scanner.Error = func(sc *scanner.Scanner, msg string) { p.scanner.Error = func(sc *scanner.Scanner, msg string) {
p.errorf(msg) p.errorf(msg)
@@ -65,14 +67,29 @@ func NewParser(filename string, r io.Reader) *parser {
return p return p
} }
func (p *parser) errorf(format string, args ...interface{}) { func (p *parser) Unpack(pos Pos) scanner.Position {
offset := int(pos)
line := sort.Search(len(p.lines), func(i int) bool { return p.lines[i] > offset }) - 1
return scanner.Position{
Filename: p.scanner.Filename,
Line: line + 1,
Column: offset - p.lines[line] + 1,
Offset: offset,
}
}
func (p *parser) pos() Pos {
pos := p.scanner.Position pos := p.scanner.Position
if !pos.IsValid() { if !pos.IsValid() {
pos = p.scanner.Pos() pos = p.scanner.Pos()
} }
return Pos(pos.Offset)
}
func (p *parser) errorf(format string, args ...interface{}) {
err := &ParseError{ err := &ParseError{
Err: fmt.Errorf(format, args...), Err: fmt.Errorf(format, args...),
Pos: pos, Pos: p.scanner.Position,
} }
p.errors = append(p.errors, err) p.errors = append(p.errors, err)
if len(p.errors) >= maxErrors { if len(p.errors) >= maxErrors {
@@ -99,7 +116,9 @@ func (p *parser) next() {
p.tok = p.scanner.Scan() p.tok = p.scanner.Scan()
} }
} }
return if p.tok == '\n' {
p.lines = append(p.lines, p.scanner.Position.Offset+1)
}
} }
func (p *parser) parseLines() { func (p *parser) parseLines() {
@@ -110,7 +129,7 @@ func (p *parser) parseLines() {
continue continue
} }
ident, _ := p.parseExpression('=', '?', ':', '#', '\n') ident := p.parseExpression('=', '?', ':', '#', '\n')
p.ignoreSpaces() p.ignoreSpaces()
@@ -142,7 +161,7 @@ func (p *parser) parseLines() {
case '#', '\n', scanner.EOF: case '#', '\n', scanner.EOF:
ident.TrimRightSpaces() ident.TrimRightSpaces()
if v, ok := toVariable(ident); ok { if v, ok := toVariable(ident); ok {
p.things = append(p.things, v) p.nodes = append(p.nodes, &v)
} else if !ident.Empty() { } else if !ident.Empty() {
p.errorf("expected directive, rule, or assignment after ident " + ident.Dump()) p.errorf("expected directive, rule, or assignment after ident " + ident.Dump())
} }
@@ -168,9 +187,9 @@ func (p *parser) parseDirective() bool {
} }
d := p.scanner.TokenText() d := p.scanner.TokenText()
pos := p.scanner.Position pos := p.pos()
endPos := pos
p.accept(scanner.Ident) p.accept(scanner.Ident)
endPos := NoPos
expression := SimpleMakeString("", pos) expression := SimpleMakeString("", pos)
@@ -178,35 +197,33 @@ func (p *parser) parseDirective() bool {
case "endif", "endef", "else": case "endif", "endef", "else":
// Nothing // Nothing
case "define": case "define":
expression = p.parseDefine() expression, endPos = p.parseDefine()
default: default:
p.ignoreSpaces() p.ignoreSpaces()
expression, endPos = p.parseExpression() expression = p.parseExpression()
} }
p.things = append(p.things, Directive{ p.nodes = append(p.nodes, &Directive{
makeThing: makeThing{ NamePos: pos,
pos: pos,
endPos: endPos,
},
Name: d, Name: d,
Args: expression, Args: expression,
EndPos: endPos,
}) })
return true return true
} }
func (p *parser) parseDefine() *MakeString { func (p *parser) parseDefine() (*MakeString, Pos) {
value := SimpleMakeString("", p.scanner.Position) value := SimpleMakeString("", p.pos())
loop: loop:
for { for {
switch p.tok { switch p.tok {
case scanner.Ident: case scanner.Ident:
value.appendString(p.scanner.TokenText())
if p.scanner.TokenText() == "endef" { if p.scanner.TokenText() == "endef" {
p.accept(scanner.Ident) p.accept(scanner.Ident)
break loop break loop
} }
value.appendString(p.scanner.TokenText())
p.accept(scanner.Ident) p.accept(scanner.Ident)
case '\\': case '\\':
p.parseEscape() p.parseEscape()
@@ -235,7 +252,7 @@ loop:
} }
} }
return value return value, p.pos()
} }
func (p *parser) parseEscape() { func (p *parser) parseEscape() {
@@ -244,8 +261,8 @@ func (p *parser) parseEscape() {
p.scanner.Mode = scanner.ScanIdents p.scanner.Mode = scanner.ScanIdents
} }
func (p *parser) parseExpression(end ...rune) (*MakeString, scanner.Position) { func (p *parser) parseExpression(end ...rune) *MakeString {
value := SimpleMakeString("", p.scanner.Position) value := SimpleMakeString("", p.pos())
endParen := false endParen := false
for _, r := range end { for _, r := range end {
@@ -255,14 +272,11 @@ func (p *parser) parseExpression(end ...rune) (*MakeString, scanner.Position) {
} }
parens := 0 parens := 0
endPos := p.scanner.Position
loop: loop:
for { for {
if endParen && parens > 0 && p.tok == ')' { if endParen && parens > 0 && p.tok == ')' {
parens-- parens--
value.appendString(")") value.appendString(")")
endPos = p.scanner.Position
p.accept(')') p.accept(')')
continue continue
} }
@@ -278,7 +292,6 @@ loop:
break loop break loop
case scanner.Ident: case scanner.Ident:
value.appendString(p.scanner.TokenText()) value.appendString(p.scanner.TokenText())
endPos = p.scanner.Position
p.accept(scanner.Ident) p.accept(scanner.Ident)
case '\\': case '\\':
p.parseEscape() p.parseEscape()
@@ -288,18 +301,17 @@ loop:
case scanner.EOF: case scanner.EOF:
p.errorf("expected escaped character, found %s", p.errorf("expected escaped character, found %s",
scanner.TokenString(p.tok)) scanner.TokenString(p.tok))
return value, endPos return value
default: default:
value.appendString(`\` + string(p.tok)) value.appendString(`\` + string(p.tok))
} }
endPos = p.scanner.Position
p.accept(p.tok) p.accept(p.tok)
case '#': case '#':
p.parseComment() p.parseComment()
break loop break loop
case '$': case '$':
var variable Variable var variable Variable
variable, endPos = p.parseVariable() variable = p.parseVariable()
value.appendVariable(variable) value.appendVariable(variable)
case scanner.EOF: case scanner.EOF:
break loop break loop
@@ -308,11 +320,9 @@ loop:
parens++ parens++
} }
value.appendString("(") value.appendString("(")
endPos = p.scanner.Position
p.accept('(') p.accept('(')
default: default:
value.appendString(p.scanner.TokenText()) value.appendString(p.scanner.TokenText())
endPos = p.scanner.Position
p.accept(p.tok) p.accept(p.tok)
} }
} }
@@ -320,12 +330,11 @@ loop:
if parens > 0 { if parens > 0 {
p.errorf("expected closing paren %s", value.Dump()) p.errorf("expected closing paren %s", value.Dump())
} }
return value, endPos return value
} }
func (p *parser) parseVariable() (Variable, scanner.Position) { func (p *parser) parseVariable() Variable {
pos := p.scanner.Position pos := p.pos()
endPos := pos
p.accept('$') p.accept('$')
var name *MakeString var name *MakeString
switch p.tok { switch p.tok {
@@ -334,30 +343,26 @@ func (p *parser) parseVariable() (Variable, scanner.Position) {
case '{': case '{':
return p.parseBracketedVariable('{', '}', pos) return p.parseBracketedVariable('{', '}', pos)
case '$': case '$':
name = SimpleMakeString("__builtin_dollar", scanner.Position{}) name = SimpleMakeString("__builtin_dollar", NoPos)
case scanner.EOF: case scanner.EOF:
p.errorf("expected variable name, found %s", p.errorf("expected variable name, found %s",
scanner.TokenString(p.tok)) scanner.TokenString(p.tok))
default: default:
name, endPos = p.parseExpression(variableNameEndRunes...) name = p.parseExpression(variableNameEndRunes...)
} }
return p.nameToVariable(name, pos, endPos), endPos return p.nameToVariable(name)
} }
func (p *parser) parseBracketedVariable(start, end rune, pos scanner.Position) (Variable, scanner.Position) { func (p *parser) parseBracketedVariable(start, end rune, pos Pos) Variable {
p.accept(start) p.accept(start)
name, endPos := p.parseExpression(end) name := p.parseExpression(end)
p.accept(end) p.accept(end)
return p.nameToVariable(name, pos, endPos), endPos return p.nameToVariable(name)
} }
func (p *parser) nameToVariable(name *MakeString, pos, endPos scanner.Position) Variable { func (p *parser) nameToVariable(name *MakeString) Variable {
return Variable{ return Variable{
makeThing: makeThing{
pos: pos,
endPos: endPos,
},
Name: name, Name: name,
} }
} }
@@ -366,12 +371,11 @@ func (p *parser) parseRule(target *MakeString) {
prerequisites, newLine := p.parseRulePrerequisites(target) prerequisites, newLine := p.parseRulePrerequisites(target)
recipe := "" recipe := ""
endPos := p.scanner.Position recipePos := p.pos()
loop: loop:
for { for {
if newLine { if newLine {
if p.tok == '\t' { if p.tok == '\t' {
endPos = p.scanner.Position
p.accept('\t') p.accept('\t')
newLine = false newLine = false
continue loop continue loop
@@ -388,31 +392,25 @@ loop:
case '\\': case '\\':
p.parseEscape() p.parseEscape()
recipe += string(p.tok) recipe += string(p.tok)
endPos = p.scanner.Position
p.accept(p.tok) p.accept(p.tok)
case '\n': case '\n':
newLine = true newLine = true
recipe += "\n" recipe += "\n"
endPos = p.scanner.Position
p.accept('\n') p.accept('\n')
case scanner.EOF: case scanner.EOF:
break loop break loop
default: default:
recipe += p.scanner.TokenText() recipe += p.scanner.TokenText()
endPos = p.scanner.Position
p.accept(p.tok) p.accept(p.tok)
} }
} }
if prerequisites != nil { if prerequisites != nil {
p.things = append(p.things, Rule{ p.nodes = append(p.nodes, &Rule{
makeThing: makeThing{
pos: target.Pos,
endPos: endPos,
},
Target: target, Target: target,
Prerequisites: prerequisites, Prerequisites: prerequisites,
Recipe: recipe, Recipe: recipe,
RecipePos: recipePos,
}) })
} }
} }
@@ -422,7 +420,7 @@ func (p *parser) parseRulePrerequisites(target *MakeString) (*MakeString, bool)
p.ignoreSpaces() p.ignoreSpaces()
prerequisites, _ := p.parseExpression('#', '\n', ';', ':', '=') prerequisites := p.parseExpression('#', '\n', ';', ':', '=')
switch p.tok { switch p.tok {
case '\n': case '\n':
@@ -439,7 +437,7 @@ func (p *parser) parseRulePrerequisites(target *MakeString) (*MakeString, bool)
p.parseAssignment(":=", target, prerequisites) p.parseAssignment(":=", target, prerequisites)
return nil, true return nil, true
} else { } else {
more, _ := p.parseExpression('#', '\n', ';') more := p.parseExpression('#', '\n', ';')
prerequisites.appendMakeString(more) prerequisites.appendMakeString(more)
} }
case '=': case '=':
@@ -453,10 +451,9 @@ func (p *parser) parseRulePrerequisites(target *MakeString) (*MakeString, bool)
} }
func (p *parser) parseComment() { func (p *parser) parseComment() {
pos := p.scanner.Position pos := p.pos()
p.accept('#') p.accept('#')
comment := "" comment := ""
endPos := pos
loop: loop:
for { for {
switch p.tok { switch p.tok {
@@ -467,26 +464,20 @@ loop:
} else { } else {
comment += "\\" + p.scanner.TokenText() comment += "\\" + p.scanner.TokenText()
} }
endPos = p.scanner.Position
p.accept(p.tok) p.accept(p.tok)
case '\n': case '\n':
endPos = p.scanner.Position
p.accept('\n') p.accept('\n')
break loop break loop
case scanner.EOF: case scanner.EOF:
break loop break loop
default: default:
comment += p.scanner.TokenText() comment += p.scanner.TokenText()
endPos = p.scanner.Position
p.accept(p.tok) p.accept(p.tok)
} }
} }
p.comments = append(p.comments, Comment{ p.comments = append(p.comments, &Comment{
makeThing: makeThing{ CommentPos: pos,
pos: pos,
endPos: endPos,
},
Comment: comment, Comment: comment,
}) })
} }
@@ -496,7 +487,7 @@ func (p *parser) parseAssignment(t string, target *MakeString, ident *MakeString
// non-whitespace character after the = until the end of the logical line, // non-whitespace character after the = until the end of the logical line,
// which may included escaped newlines // which may included escaped newlines
p.accept('=') p.accept('=')
value, endPos := p.parseExpression() value := p.parseExpression()
value.TrimLeftSpaces() value.TrimLeftSpaces()
if ident.EndsWith('+') && t == "=" { if ident.EndsWith('+') && t == "=" {
ident.TrimRightOne() ident.TrimRightOne()
@@ -505,11 +496,7 @@ func (p *parser) parseAssignment(t string, target *MakeString, ident *MakeString
ident.TrimRightSpaces() ident.TrimRightSpaces()
p.things = append(p.things, Assignment{ p.nodes = append(p.nodes, &Assignment{
makeThing: makeThing{
pos: ident.Pos,
endPos: endPos,
},
Name: ident, Name: ident,
Value: value, Value: value,
Target: target, Target: target,