[BACK]Return to mkparser.go CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / pkgsrc / pkgtools / pkglint / files

Annotation of pkgsrc/pkgtools/pkglint/files/mkparser.go, Revision 1.17

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()
                    220:                if !p.repl.AdvanceRegexp(`^\s*\|\|\s*`) {
                    221:                        break
                    222:                }
                    223:                next := p.mkCondAnd()
                    224:                if next == nil {
                    225:                        p.repl.Reset(mark)
                    226:                        break
                    227:                }
                    228:                ands = append(ands, next)
                    229:        }
                    230:        if len(ands) == 1 {
                    231:                return and
                    232:        }
1.14      rillig    233:        return &mkCond{Or: ands}
1.1       rillig    234: }
                    235:
1.14      rillig    236: func (p *MkParser) mkCondAnd() MkCond {
1.1       rillig    237:        atom := p.mkCondAtom()
                    238:        if atom == nil {
                    239:                return nil
                    240:        }
                    241:
1.14      rillig    242:        atoms := []MkCond{atom}
1.1       rillig    243:        for {
                    244:                mark := p.repl.Mark()
                    245:                if !p.repl.AdvanceRegexp(`^\s*&&\s*`) {
                    246:                        break
                    247:                }
                    248:                next := p.mkCondAtom()
                    249:                if next == nil {
                    250:                        p.repl.Reset(mark)
                    251:                        break
                    252:                }
                    253:                atoms = append(atoms, next)
                    254:        }
                    255:        if len(atoms) == 1 {
                    256:                return atom
                    257:        }
1.14      rillig    258:        return &mkCond{And: atoms}
1.1       rillig    259: }
                    260:
1.14      rillig    261: func (p *MkParser) mkCondAtom() MkCond {
1.7       rillig    262:        if trace.Tracing {
                    263:                defer trace.Call1(p.Rest())()
1.1       rillig    264:        }
                    265:
                    266:        repl := p.repl
                    267:        mark := repl.Mark()
                    268:        repl.SkipSpace()
                    269:        switch {
                    270:        case repl.AdvanceStr("!"):
                    271:                cond := p.mkCondAtom()
                    272:                if cond != nil {
1.14      rillig    273:                        return &mkCond{Not: cond}
1.1       rillig    274:                }
                    275:        case repl.AdvanceStr("("):
                    276:                cond := p.MkCond()
                    277:                if cond != nil {
                    278:                        repl.SkipSpace()
                    279:                        if repl.AdvanceStr(")") {
                    280:                                return cond
                    281:                        }
                    282:                }
                    283:        case repl.AdvanceRegexp(`^defined\s*\(`):
                    284:                if varname := p.Varname(); varname != "" {
                    285:                        if repl.AdvanceStr(")") {
1.14      rillig    286:                                return &mkCond{Defined: varname}
1.1       rillig    287:                        }
                    288:                }
                    289:        case repl.AdvanceRegexp(`^empty\s*\(`):
                    290:                if varname := p.Varname(); varname != "" {
                    291:                        modifiers := p.VarUseModifiers(varname, ")")
                    292:                        if repl.AdvanceStr(")") {
1.14      rillig    293:                                return &mkCond{Empty: &MkVarUse{varname, modifiers}}
1.1       rillig    294:                        }
                    295:                }
                    296:        case repl.AdvanceRegexp(`^(commands|exists|make|target)\s*\(`):
1.7       rillig    297:                funcname := repl.Group(1)
1.1       rillig    298:                argMark := repl.Mark()
                    299:                for p.VarUse() != nil || repl.AdvanceRegexp(`^[^$)]+`) {
                    300:                }
                    301:                arg := repl.Since(argMark)
                    302:                if repl.AdvanceStr(")") {
1.14      rillig    303:                        return &mkCond{Call: &MkCondCall{funcname, arg}}
1.1       rillig    304:                }
                    305:        default:
                    306:                lhs := p.VarUse()
                    307:                mark := repl.Mark()
                    308:                if lhs == nil && repl.AdvanceStr("\"") {
                    309:                        if quotedLHS := p.VarUse(); quotedLHS != nil && repl.AdvanceStr("\"") {
                    310:                                lhs = quotedLHS
                    311:                        } else {
                    312:                                repl.Reset(mark)
                    313:                        }
                    314:                }
                    315:                if lhs != nil {
                    316:                        if repl.AdvanceRegexp(`^\s*(<|<=|==|!=|>=|>)\s*(\d+(?:\.\d+)?)`) {
1.14      rillig    317:                                return &mkCond{CompareVarNum: &MkCondCompareVarNum{lhs, repl.Group(1), repl.Group(2)}}
1.1       rillig    318:                        }
                    319:                        if repl.AdvanceRegexp(`^\s*(<|<=|==|!=|>=|>)\s*`) {
1.7       rillig    320:                                op := repl.Group(1)
1.1       rillig    321:                                if (op == "!=" || op == "==") && repl.AdvanceRegexp(`^"([^"\$\\]*)"`) {
1.14      rillig    322:                                        return &mkCond{CompareVarStr: &MkCondCompareVarStr{lhs, op, repl.Group(1)}}
1.1       rillig    323:                                } else if repl.AdvanceRegexp(`^\w+`) {
1.14      rillig    324:                                        return &mkCond{CompareVarStr: &MkCondCompareVarStr{lhs, op, repl.Group(0)}}
1.1       rillig    325:                                } else if rhs := p.VarUse(); rhs != nil {
1.14      rillig    326:                                        return &mkCond{CompareVarVar: &MkCondCompareVarVar{lhs, op, rhs}}
1.4       rillig    327:                                } else if repl.PeekByte() == '"' {
                    328:                                        mark := repl.Mark()
                    329:                                        if repl.AdvanceStr("\"") {
                    330:                                                if quotedRHS := p.VarUse(); quotedRHS != nil {
                    331:                                                        if repl.AdvanceStr("\"") {
1.14      rillig    332:                                                                return &mkCond{CompareVarVar: &MkCondCompareVarVar{lhs, op, quotedRHS}}
1.4       rillig    333:                                                        }
                    334:                                                }
                    335:                                        }
                    336:                                        repl.Reset(mark)
1.1       rillig    337:                                }
                    338:                        } else {
1.14      rillig    339:                                return &mkCond{Not: &mkCond{Empty: lhs}} // See devel/bmake/files/cond.c:/\* For \.if \$/
1.1       rillig    340:                        }
                    341:                }
                    342:                if repl.AdvanceRegexp(`^\d+(?:\.\d+)?`) {
1.14      rillig    343:                        return &mkCond{Num: repl.Group(0)}
1.1       rillig    344:                }
                    345:        }
                    346:        repl.Reset(mark)
                    347:        return nil
                    348: }
                    349:
                    350: func (p *MkParser) Varname() string {
                    351:        repl := p.repl
                    352:
                    353:        mark := repl.Mark()
                    354:        repl.AdvanceStr(".")
                    355:        isVarnameChar := func(c byte) bool {
                    356:                return 'A' <= c && c <= 'Z' || c == '_' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '+' || c == '-' || c == '.' || c == '*'
                    357:        }
                    358:        for p.VarUse() != nil || repl.AdvanceBytesFunc(isVarnameChar) {
                    359:        }
                    360:        return repl.Since(mark)
                    361: }
1.14      rillig    362:
                    363: type MkCond = *mkCond
                    364:
                    365: type mkCond struct {
                    366:        Or  []*mkCond
                    367:        And []*mkCond
                    368:        Not *mkCond
                    369:
                    370:        Defined       string
                    371:        Empty         *MkVarUse
                    372:        CompareVarNum *MkCondCompareVarNum
                    373:        CompareVarStr *MkCondCompareVarStr
                    374:        CompareVarVar *MkCondCompareVarVar
                    375:        Call          *MkCondCall
                    376:        Num           string
                    377: }
                    378: type MkCondCompareVarNum struct {
                    379:        Var *MkVarUse
                    380:        Op  string // One of <, <=, ==, !=, >=, >.
                    381:        Num string
                    382: }
                    383: type MkCondCompareVarStr struct {
                    384:        Var *MkVarUse
                    385:        Op  string // One of ==, !=.
                    386:        Str string
                    387: }
                    388: type MkCondCompareVarVar struct {
                    389:        Left  *MkVarUse
                    390:        Op    string // One of <, <=, ==, !=, >=, >.
                    391:        Right *MkVarUse
                    392: }
                    393: type MkCondCall struct {
                    394:        Name string
                    395:        Arg  string
                    396: }
                    397:
                    398: type MkCondCallback struct {
                    399:        Defined       func(varname string)
                    400:        Empty         func(empty *MkVarUse)
                    401:        CompareVarNum func(varuse *MkVarUse, op string, num string)
                    402:        CompareVarStr func(varuse *MkVarUse, op string, str string)
                    403:        CompareVarVar func(left *MkVarUse, op string, right *MkVarUse)
                    404:        Call          func(name string, arg string)
                    405: }
                    406:
                    407: type MkCondWalker struct{}
                    408:
                    409: func NewMkCondWalker() *MkCondWalker { return &MkCondWalker{} }
                    410:
                    411: func (w *MkCondWalker) Walk(cond MkCond, callback *MkCondCallback) {
                    412:        switch {
                    413:        case cond.Or != nil:
                    414:                for _, or := range cond.Or {
                    415:                        w.Walk(or, callback)
                    416:                }
                    417:        case cond.And != nil:
                    418:                for _, and := range cond.And {
                    419:                        w.Walk(and, callback)
                    420:                }
                    421:        case cond.Not != nil:
                    422:                w.Walk(cond.Not, callback)
                    423:
                    424:        case cond.Defined != "":
                    425:                if callback.Defined != nil {
                    426:                        callback.Defined(cond.Defined)
                    427:                }
                    428:        case cond.Empty != nil:
                    429:                if callback.Empty != nil {
                    430:                        callback.Empty(cond.Empty)
                    431:                }
                    432:        case cond.CompareVarVar != nil:
                    433:                if callback.CompareVarVar != nil {
                    434:                        cvv := cond.CompareVarVar
                    435:                        callback.CompareVarVar(cvv.Left, cvv.Op, cvv.Right)
                    436:                }
                    437:        case cond.CompareVarStr != nil:
                    438:                if callback.CompareVarStr != nil {
                    439:                        cvs := cond.CompareVarStr
                    440:                        callback.CompareVarStr(cvs.Var, cvs.Op, cvs.Str)
                    441:                }
                    442:        case cond.CompareVarNum != nil:
                    443:                if callback.CompareVarNum != nil {
                    444:                        cvn := cond.CompareVarNum
                    445:                        callback.CompareVarNum(cvn.Var, cvn.Op, cvn.Num)
                    446:                }
                    447:        case cond.Call != nil:
                    448:                if callback.Call != nil {
                    449:                        call := cond.Call
                    450:                        callback.Call(call.Name, call.Arg)
                    451:                }
                    452:        }
                    453: }

CVSweb <webmaster@jp.NetBSD.org>