Prettier printing.

This commit is contained in:
Daniel Jones 2013-03-03 18:57:14 -08:00
parent 8a218f35c0
commit 467d1d3d44
5 changed files with 73 additions and 26 deletions

View file

@ -196,14 +196,14 @@ func expandSigils(input string, vars map[string][]string) []string {
func expandRecipeSigils(input string, vars map[string][]string) string {
expanded := ""
for i := 0; i < len(input); {
j := strings.IndexAny(input[i:], "$\\")
if j < 0 {
off := strings.IndexAny(input[i:], "$\\")
if off < 0 {
expanded += input[i:]
break
}
expanded += input[i : i+off]
i += off
expanded += input[i:j]
i = j
c, w := utf8.DecodeRuneInString(input[i:])
if c == '$' {
i += w

8
lex.go
View file

@ -49,6 +49,7 @@ type token struct {
typ tokenType // token type
val string // token string
line int // line where it was found
col int // column on which the token began
}
func (t *token) String() string {
@ -65,6 +66,7 @@ type lexer struct {
input string // input string to be lexed
output chan token // channel on which tokens are sent
start int // token beginning
startcol int // column on which the token begins
pos int // position within input
line int // line within input
col int // column within input
@ -129,11 +131,13 @@ func (l *lexer) next() rune {
func (l *lexer) skip() {
l.next()
l.start = l.pos
l.startcol = l.col
}
func (l *lexer) emit(typ tokenType) {
l.output <- token{typ, l.input[l.start:l.pos], l.line}
l.output <- token{typ, l.input[l.start:l.pos], l.line, l.startcol}
l.start = l.pos
l.startcol = 0
}
// Consume the next run if it is in the given string.
@ -189,7 +193,7 @@ func (l *lexer) skipUntil(invalid string) {
// Start a new lexer to lex the given input.
func lex(input string) (*lexer, chan token) {
l := &lexer{input: input, output: make(chan token), line: 1, indented: true}
l := &lexer{input: input, output: make(chan token), line: 1, col: 0, indented: true}
go l.run()
return l, l.output
}

30
mk.go
View file

@ -5,6 +5,7 @@ import (
"fmt"
"io/ioutil"
"os"
"sync"
)
// True if messages should be printed without fancy colors.
@ -13,6 +14,9 @@ var nocolor bool = false
// True if we are no actualyl executing any recipes or updating any timestamps.
var dryrun bool = false
// Lock on standard out, messages don't get interleaved too much.
var mkMsgMutex sync.Mutex
// The maximum number of times an rule may be applied.
const max_rule_cnt = 3
@ -29,7 +33,9 @@ const (
func mk(rs *ruleSet, target string, dryrun bool) {
g := buildgraph(rs, target)
//g.visualize(os.Stdout)
if g.root.exists {
return
}
mkNode(g, g.root)
}
@ -129,13 +135,13 @@ func mkNode(g *graph, u *node) {
// execute the recipe, unless the prereqs failed
if finalstatus != nodeStatusFailed {
mkPrintMessage("mking " + u.name)
//mkPrintMessage("mking " + u.name)
if !dorecipe(u.name, u, e) {
finalstatus = nodeStatusFailed
}
}
mkPrintSuccess("finished mking " + u.name)
//mkPrintSuccess("finished mking " + u.name)
}
func mkError(msg string) {
@ -158,26 +164,34 @@ func mkPrintSuccess(msg string) {
}
func mkPrintMessage(msg string) {
mkMsgMutex.Lock()
if nocolor {
fmt.Println(msg)
} else {
fmt.Printf("%s%s%s\n", colorBlue, msg, colorDefault)
}
mkMsgMutex.Unlock()
}
func mkPrintRecipe(msg string) {
if !nocolor {
os.Stdout.WriteString(colorYellow)
func mkPrintRecipe(target string, recipe string) {
mkMsgMutex.Lock()
if nocolor {
fmt.Printf("%s: ", target)
} else {
fmt.Printf("%s%s%s => %s", colorBlue, target, colorDefault, colorMagenta)
}
printIndented(os.Stdout, recipe, len(target)+4)
if len(recipe) == 0 {
os.Stdout.WriteString("\n")
}
printIndented(os.Stdout, msg)
if !nocolor {
os.Stdout.WriteString(colorDefault)
}
mkMsgMutex.Unlock()
}
func main() {
var mkfilepath string
var dryrun bool
flag.StringVar(&mkfilepath, "f", "mkfile", "use the given file as mkfile")
flag.BoolVar(&dryrun, "n", false, "print commands without actually executing")
flag.Parse()

View file

@ -76,7 +76,7 @@ func parseInto(input string, name string, rules *ruleSet) {
// insert a dummy newline to allow parsing of any assignments or recipeless
// rules to finish.
state = state(p, token{tokenNewline, "\n", l.line})
state = state(p, token{tokenNewline, "\n", l.line, l.col})
// TODO: Error when state != parseTopLevel
}
@ -339,7 +339,7 @@ func parseRecipe(p *parser, t token) parserStateFun {
}
if t.typ == tokenRecipe {
r.recipe = t.val
r.recipe = stripIndentation(t.val, t.col)
}
p.rules.add(r)

View file

@ -10,28 +10,57 @@ import (
"os"
"os/exec"
"strings"
"unicode/utf8"
)
// Try to unindent a recipe, so that it begins an column 0. (This is mainly for
// recipes in python, or other indentation-significant languages.)
//func stripIndentation(s string) string {
//}
// Indent each line of a recipe.
func printIndented(out io.Writer, s string) {
func stripIndentation(s string, mincol int) string {
// trim leading whitespace
reader := bufio.NewReader(strings.NewReader(s))
output := ""
for {
line, err := reader.ReadString('\n')
if len(line) > 0 {
io.WriteString(out, " ")
io.WriteString(out, line)
col := 0
i := 0
for i < len(line) && col < mincol {
c, w := utf8.DecodeRuneInString(line[i:])
if strings.IndexRune(" \t\n", c) >= 0 {
col += 1
i += w
} else {
break
}
}
output += line[i:]
if err != nil {
break
}
}
return output
}
// Indent each line of a recipe.
func printIndented(out io.Writer, s string, ind int) {
indentation := strings.Repeat(" ", ind)
reader := bufio.NewReader(strings.NewReader(s))
firstline := true
for {
line, err := reader.ReadString('\n')
if len(line) > 0 {
if !firstline {
io.WriteString(out, indentation)
}
io.WriteString(out, line)
}
if err != nil {
break
}
firstline = false
}
}
// Execute a recipe.
@ -70,7 +99,7 @@ func dorecipe(target string, u *node, e *edge) bool {
}
if !e.r.attributes.quiet {
mkPrintRecipe(input)
mkPrintRecipe(target, input)
}
if dryrun {