[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.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>