Annotation of pkgsrc/pkgtools/pkglint/files/mkparser.go, Revision 1.18
1.1 rillig 1: package main
2:
3: import (
1.7 rillig 4: "netbsd.org/pkglint/regex"
5: "netbsd.org/pkglint/trace"
1.1 rillig 6: "strings"
7: )
8:
1.17 rillig 9: // MkParser wraps a Parser and provides methods for parsing
10: // things related to Makefiles.
1.1 rillig 11: type MkParser struct {
12: *Parser
13: }
14:
1.10 rillig 15: func NewMkParser(line Line, text string, emitWarnings bool) *MkParser {
1.1 rillig 16: return &MkParser{NewParser(line, text, emitWarnings)}
17: }
18:
19: func (p *MkParser) MkTokens() []*MkToken {
20: repl := p.repl
21:
22: var tokens []*MkToken
23: for !p.EOF() {
24: if repl.AdvanceStr("#") {
25: repl.AdvanceRest()
26: }
27:
28: mark := repl.Mark()
29: if varuse := p.VarUse(); varuse != nil {
30: tokens = append(tokens, &MkToken{Text: repl.Since(mark), Varuse: varuse})
31: continue
32: }
33:
34: again:
1.7 rillig 35: dollar := strings.IndexByte(repl.Rest(), '$')
1.1 rillig 36: if dollar == -1 {
1.7 rillig 37: dollar = len(repl.Rest())
1.1 rillig 38: }
39: repl.Skip(dollar)
40: if repl.AdvanceStr("$$") {
41: goto again
42: }
43: text := repl.Since(mark)
44: if text != "" {
45: tokens = append(tokens, &MkToken{Text: text})
46: continue
47: }
48:
49: break
50: }
51: return tokens
52: }
53:
54: func (p *MkParser) VarUse() *MkVarUse {
55: repl := p.repl
56:
57: mark := repl.Mark()
58: if repl.AdvanceStr("${") || repl.AdvanceStr("$(") {
59: usingRoundParen := repl.Since(mark) == "$("
60: closing := ifelseStr(usingRoundParen, ")", "}")
61:
62: varnameMark := repl.Mark()
63: varname := p.Varname()
64: if varname != "" {
65: modifiers := p.VarUseModifiers(varname, closing)
66: if repl.AdvanceStr(closing) {
1.6 rillig 67: if usingRoundParen && p.EmitWarnings {
68: parenVaruse := repl.Since(mark)
69: bracesVaruse := "${" + parenVaruse[2:len(parenVaruse)-1] + "}"
1.12 rillig 70: fix := p.Line.Autofix()
71: fix.Warnf("Please use curly braces {} instead of round parentheses () for %s.", varname)
72: fix.Replace(parenVaruse, bracesVaruse)
73: fix.Apply()
1.6 rillig 74: }
1.1 rillig 75: return &MkVarUse{varname, modifiers}
76: }
77: }
78:
1.9 rillig 79: for p.VarUse() != nil || repl.AdvanceRegexp(regex.Pattern(`^([^$:`+closing+`]|\$\$)+`)) {
1.1 rillig 80: }
81: rest := p.Rest()
82: if hasPrefix(rest, ":L") || hasPrefix(rest, ":?") {
83: varexpr := repl.Since(varnameMark)
84: modifiers := p.VarUseModifiers(varexpr, closing)
85: if repl.AdvanceStr(closing) {
86: return &MkVarUse{varexpr, modifiers}
87: }
88: }
89: repl.Reset(mark)
90: }
91:
92: if repl.AdvanceStr("$@") {
93: return &MkVarUse{"@", nil}
94: }
95: if repl.AdvanceStr("$<") {
96: return &MkVarUse{"<", nil}
97: }
1.2 rillig 98: if repl.PeekByte() == '$' && repl.AdvanceRegexp(`^\$(\w)`) {
1.7 rillig 99: varname := repl.Group(1)
1.1 rillig 100: if p.EmitWarnings {
1.5 rillig 101: p.Line.Warnf("$%[1]s is ambiguous. Use ${%[1]s} if you mean a Makefile variable or $$%[1]s if you mean a shell variable.", varname)
1.1 rillig 102: }
103: return &MkVarUse{varname, nil}
104: }
105: return nil
106: }
107:
108: func (p *MkParser) VarUseModifiers(varname, closing string) []string {
109: repl := p.repl
110:
111: var modifiers []string
112: mayOmitColon := false
1.15 rillig 113: loop:
1.1 rillig 114: for repl.AdvanceStr(":") || mayOmitColon {
115: mayOmitColon = false
116: modifierMark := repl.Mark()
117:
118: switch repl.PeekByte() {
119: case 'E', 'H', 'L', 'O', 'Q', 'R', 'T', 's', 't', 'u':
120: if repl.AdvanceRegexp(`^(E|H|L|Ox?|Q|R|T|sh|tA|tW|tl|tu|tw|u)`) {
121: modifiers = append(modifiers, repl.Since(modifierMark))
122: continue
123: }
124: if repl.AdvanceStr("ts") {
1.7 rillig 125: rest := repl.Rest()
1.1 rillig 126: if len(rest) >= 2 && (rest[1] == closing[0] || rest[1] == ':') {
127: repl.Skip(1)
128: } else if len(rest) >= 1 && (rest[0] == closing[0] || rest[0] == ':') {
129: } else if repl.AdvanceRegexp(`^\\\d+`) {
130: } else {
1.15 rillig 131: break loop
1.1 rillig 132: }
133: modifiers = append(modifiers, repl.Since(modifierMark))
134: continue
135: }
136:
137: case '=', 'D', 'M', 'N', 'U':
138: if repl.AdvanceRegexp(`^[=DMNU]`) {
1.16 rillig 139: for p.VarUse() != nil || repl.AdvanceRegexp(regex.Pattern(`^([^$:\\`+closing+`]|\$\$|\\.)+`)) {
1.1 rillig 140: }
1.16 rillig 141: arg := repl.Since(modifierMark)
142: modifiers = append(modifiers, strings.Replace(arg, "\\:", ":", -1))
1.1 rillig 143: continue
144: }
145:
146: case 'C', 'S':
147: if repl.AdvanceRegexp(`^[CS]([%,/:;@^|])`) {
1.7 rillig 148: separator := repl.Group(1)
1.1 rillig 149: repl.AdvanceStr("^")
1.9 rillig 150: re := regex.Pattern(`^([^\` + separator + `$` + closing + `\\]|\$\$|\\.)+`)
1.1 rillig 151: for p.VarUse() != nil || repl.AdvanceRegexp(re) {
152: }
153: repl.AdvanceStr("$")
154: if repl.AdvanceStr(separator) {
155: for p.VarUse() != nil || repl.AdvanceRegexp(re) {
156: }
157: if repl.AdvanceStr(separator) {
158: repl.AdvanceRegexp(`^[1gW]`)
159: modifiers = append(modifiers, repl.Since(modifierMark))
160: mayOmitColon = true
161: continue
162: }
163: }
164: }
165:
166: case '@':
167: if repl.AdvanceRegexp(`^@([\w.]+)@`) {
1.7 rillig 168: loopvar := repl.Group(1)
1.9 rillig 169: for p.VarUse() != nil || repl.AdvanceRegexp(regex.Pattern(`^([^$:@`+closing+`\\]|\$\$|\\.)+`)) {
1.1 rillig 170: }
171: if !repl.AdvanceStr("@") && p.EmitWarnings {
1.5 rillig 172: p.Line.Warnf("Modifier ${%s:@%s@...@} is missing the final \"@\".", varname, loopvar)
1.1 rillig 173: }
174: modifiers = append(modifiers, repl.Since(modifierMark))
175: continue
176: }
177:
178: case '[':
1.11 rillig 179: if repl.AdvanceRegexp(`^\[(?:[-.\d]+|#)\]`) {
1.1 rillig 180: modifiers = append(modifiers, repl.Since(modifierMark))
181: continue
182: }
183:
184: case '?':
185: repl.AdvanceStr("?")
1.9 rillig 186: re := regex.Pattern(`^([^$:` + closing + `]|\$\$)+`)
1.1 rillig 187: for p.VarUse() != nil || repl.AdvanceRegexp(re) {
188: }
189: if repl.AdvanceStr(":") {
190: for p.VarUse() != nil || repl.AdvanceRegexp(re) {
191: }
192: modifiers = append(modifiers, repl.Since(modifierMark))
193: continue
194: }
195: }
196:
197: repl.Reset(modifierMark)
1.17 rillig 198: // FIXME: Why AdvanceRegexp? This accepts :S,a,b,c,d,e,f but shouldn't.
1.9 rillig 199: for p.VarUse() != nil || repl.AdvanceRegexp(regex.Pattern(`^([^:$`+closing+`]|\$\$)+`)) {
1.1 rillig 200: }
201: if suffixSubst := repl.Since(modifierMark); contains(suffixSubst, "=") {
202: modifiers = append(modifiers, suffixSubst)
203: continue
204: }
205: }
206: return modifiers
207: }
208:
1.13 rillig 209: // MkCond parses a condition like ${OPSYS} == "NetBSD".
210: // See devel/bmake/files/cond.c.
1.14 rillig 211: func (p *MkParser) MkCond() MkCond {
1.1 rillig 212: and := p.mkCondAnd()
213: if and == nil {
214: return nil
215: }
216:
1.14 rillig 217: ands := []MkCond{and}
1.1 rillig 218: for {
219: mark := p.repl.Mark()
1.18 ! rillig 220: p.repl.SkipHspace()
! 221: if !p.repl.AdvanceStr("||") {
1.1 rillig 222: break
223: }
224: next := p.mkCondAnd()
225: if next == nil {
226: p.repl.Reset(mark)
227: break
228: }
229: ands = append(ands, next)
230: }
231: if len(ands) == 1 {
232: return and
233: }
1.14 rillig 234: return &mkCond{Or: ands}
1.1 rillig 235: }
236:
1.14 rillig 237: func (p *MkParser) mkCondAnd() MkCond {
1.1 rillig 238: atom := p.mkCondAtom()
239: if atom == nil {
240: return nil
241: }
242:
1.14 rillig 243: atoms := []MkCond{atom}
1.1 rillig 244: for {
245: mark := p.repl.Mark()
246: if !p.repl.AdvanceRegexp(`^\s*&&\s*`) {
247: break
248: }
249: next := p.mkCondAtom()
250: if next == nil {
251: p.repl.Reset(mark)
252: break
253: }
254: atoms = append(atoms, next)
255: }
256: if len(atoms) == 1 {
257: return atom
258: }
1.14 rillig 259: return &mkCond{And: atoms}
1.1 rillig 260: }
261:
1.14 rillig 262: func (p *MkParser) mkCondAtom() MkCond {
1.7 rillig 263: if trace.Tracing {
264: defer trace.Call1(p.Rest())()
1.1 rillig 265: }
266:
267: repl := p.repl
268: mark := repl.Mark()
1.18 ! rillig 269: repl.SkipHspace()
1.1 rillig 270: switch {
271: case repl.AdvanceStr("!"):
272: cond := p.mkCondAtom()
273: if cond != nil {
1.14 rillig 274: return &mkCond{Not: cond}
1.1 rillig 275: }
276: case repl.AdvanceStr("("):
277: cond := p.MkCond()
278: if cond != nil {
1.18 ! rillig 279: repl.SkipHspace()
1.1 rillig 280: if repl.AdvanceStr(")") {
281: return cond
282: }
283: }
1.18 ! rillig 284: case repl.HasPrefix("defined") && repl.AdvanceRegexp(`^defined\s*\(`):
1.1 rillig 285: if varname := p.Varname(); varname != "" {
286: if repl.AdvanceStr(")") {
1.14 rillig 287: return &mkCond{Defined: varname}
1.1 rillig 288: }
289: }
1.18 ! rillig 290: case repl.HasPrefix("empty") && repl.AdvanceRegexp(`^empty\s*\(`):
1.1 rillig 291: if varname := p.Varname(); varname != "" {
292: modifiers := p.VarUseModifiers(varname, ")")
293: if repl.AdvanceStr(")") {
1.14 rillig 294: return &mkCond{Empty: &MkVarUse{varname, modifiers}}
1.1 rillig 295: }
296: }
1.18 ! rillig 297: case uint(repl.PeekByte()-'a') <= 'z'-'a' && repl.AdvanceRegexp(`^(commands|exists|make|target)\s*\(`):
1.7 rillig 298: funcname := repl.Group(1)
1.1 rillig 299: argMark := repl.Mark()
300: for p.VarUse() != nil || repl.AdvanceRegexp(`^[^$)]+`) {
301: }
302: arg := repl.Since(argMark)
303: if repl.AdvanceStr(")") {
1.14 rillig 304: return &mkCond{Call: &MkCondCall{funcname, arg}}
1.1 rillig 305: }
306: default:
307: lhs := p.VarUse()
308: mark := repl.Mark()
309: if lhs == nil && repl.AdvanceStr("\"") {
310: if quotedLHS := p.VarUse(); quotedLHS != nil && repl.AdvanceStr("\"") {
311: lhs = quotedLHS
312: } else {
313: repl.Reset(mark)
314: }
315: }
316: if lhs != nil {
317: if repl.AdvanceRegexp(`^\s*(<|<=|==|!=|>=|>)\s*(\d+(?:\.\d+)?)`) {
1.14 rillig 318: return &mkCond{CompareVarNum: &MkCondCompareVarNum{lhs, repl.Group(1), repl.Group(2)}}
1.1 rillig 319: }
320: if repl.AdvanceRegexp(`^\s*(<|<=|==|!=|>=|>)\s*`) {
1.7 rillig 321: op := repl.Group(1)
1.1 rillig 322: if (op == "!=" || op == "==") && repl.AdvanceRegexp(`^"([^"\$\\]*)"`) {
1.14 rillig 323: return &mkCond{CompareVarStr: &MkCondCompareVarStr{lhs, op, repl.Group(1)}}
1.1 rillig 324: } else if repl.AdvanceRegexp(`^\w+`) {
1.14 rillig 325: return &mkCond{CompareVarStr: &MkCondCompareVarStr{lhs, op, repl.Group(0)}}
1.1 rillig 326: } else if rhs := p.VarUse(); rhs != nil {
1.14 rillig 327: return &mkCond{CompareVarVar: &MkCondCompareVarVar{lhs, op, rhs}}
1.4 rillig 328: } else if repl.PeekByte() == '"' {
329: mark := repl.Mark()
330: if repl.AdvanceStr("\"") {
331: if quotedRHS := p.VarUse(); quotedRHS != nil {
332: if repl.AdvanceStr("\"") {
1.14 rillig 333: return &mkCond{CompareVarVar: &MkCondCompareVarVar{lhs, op, quotedRHS}}
1.4 rillig 334: }
335: }
336: }
337: repl.Reset(mark)
1.1 rillig 338: }
339: } else {
1.14 rillig 340: return &mkCond{Not: &mkCond{Empty: lhs}} // See devel/bmake/files/cond.c:/\* For \.if \$/
1.1 rillig 341: }
342: }
343: if repl.AdvanceRegexp(`^\d+(?:\.\d+)?`) {
1.14 rillig 344: return &mkCond{Num: repl.Group(0)}
1.1 rillig 345: }
346: }
347: repl.Reset(mark)
348: return nil
349: }
350:
351: func (p *MkParser) Varname() string {
352: repl := p.repl
353:
354: mark := repl.Mark()
355: repl.AdvanceStr(".")
356: isVarnameChar := func(c byte) bool {
357: return 'A' <= c && c <= 'Z' || c == '_' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '+' || c == '-' || c == '.' || c == '*'
358: }
359: for p.VarUse() != nil || repl.AdvanceBytesFunc(isVarnameChar) {
360: }
361: return repl.Since(mark)
362: }
1.14 rillig 363:
364: type MkCond = *mkCond
365:
366: type mkCond struct {
367: Or []*mkCond
368: And []*mkCond
369: Not *mkCond
370:
371: Defined string
372: Empty *MkVarUse
373: CompareVarNum *MkCondCompareVarNum
374: CompareVarStr *MkCondCompareVarStr
375: CompareVarVar *MkCondCompareVarVar
376: Call *MkCondCall
377: Num string
378: }
379: type MkCondCompareVarNum struct {
380: Var *MkVarUse
381: Op string // One of <, <=, ==, !=, >=, >.
382: Num string
383: }
384: type MkCondCompareVarStr struct {
385: Var *MkVarUse
386: Op string // One of ==, !=.
387: Str string
388: }
389: type MkCondCompareVarVar struct {
390: Left *MkVarUse
391: Op string // One of <, <=, ==, !=, >=, >.
392: Right *MkVarUse
393: }
394: type MkCondCall struct {
395: Name string
396: Arg string
397: }
398:
399: type MkCondCallback struct {
400: Defined func(varname string)
401: Empty func(empty *MkVarUse)
402: CompareVarNum func(varuse *MkVarUse, op string, num string)
403: CompareVarStr func(varuse *MkVarUse, op string, str string)
404: CompareVarVar func(left *MkVarUse, op string, right *MkVarUse)
405: Call func(name string, arg string)
1.18 ! rillig 406: VarUse func(varuse *MkVarUse)
1.14 rillig 407: }
408:
409: type MkCondWalker struct{}
410:
411: func NewMkCondWalker() *MkCondWalker { return &MkCondWalker{} }
412:
413: func (w *MkCondWalker) Walk(cond MkCond, callback *MkCondCallback) {
414: switch {
415: case cond.Or != nil:
416: for _, or := range cond.Or {
417: w.Walk(or, callback)
418: }
419: case cond.And != nil:
420: for _, and := range cond.And {
421: w.Walk(and, callback)
422: }
423: case cond.Not != nil:
424: w.Walk(cond.Not, callback)
425:
426: case cond.Defined != "":
427: if callback.Defined != nil {
428: callback.Defined(cond.Defined)
429: }
1.18 ! rillig 430: if callback.VarUse != nil {
! 431: callback.VarUse(&MkVarUse{cond.Defined, nil})
! 432: }
1.14 rillig 433: case cond.Empty != nil:
434: if callback.Empty != nil {
435: callback.Empty(cond.Empty)
436: }
1.18 ! rillig 437: if callback.VarUse != nil {
! 438: callback.VarUse(cond.Empty)
! 439: }
1.14 rillig 440: case cond.CompareVarVar != nil:
441: if callback.CompareVarVar != nil {
442: cvv := cond.CompareVarVar
443: callback.CompareVarVar(cvv.Left, cvv.Op, cvv.Right)
444: }
1.18 ! rillig 445: if callback.VarUse != nil {
! 446: cvv := cond.CompareVarVar
! 447: callback.VarUse(cvv.Left)
! 448: callback.VarUse(cvv.Right)
! 449: }
1.14 rillig 450: case cond.CompareVarStr != nil:
451: if callback.CompareVarStr != nil {
452: cvs := cond.CompareVarStr
453: callback.CompareVarStr(cvs.Var, cvs.Op, cvs.Str)
454: }
1.18 ! rillig 455: if callback.VarUse != nil {
! 456: callback.VarUse(cond.CompareVarStr.Var)
! 457: }
1.14 rillig 458: case cond.CompareVarNum != nil:
459: if callback.CompareVarNum != nil {
460: cvn := cond.CompareVarNum
461: callback.CompareVarNum(cvn.Var, cvn.Op, cvn.Num)
462: }
1.18 ! rillig 463: if callback.VarUse != nil {
! 464: callback.VarUse(cond.CompareVarNum.Var)
! 465: }
1.14 rillig 466: case cond.Call != nil:
467: if callback.Call != nil {
468: call := cond.Call
469: callback.Call(call.Name, call.Arg)
470: }
471: }
472: }
CVSweb <webmaster@jp.NetBSD.org>