package pkglint //go:generate goyacc -o shellyacc.go -v shellyacc.log -p shyy shell.y type ShAtomType uint8 const ( shtSpace ShAtomType = iota shtVaruse // ${PREFIX} shtShVarUse // $${var:-pol} shtText // while, cat, ENV=value shtOperator // (, ;, | shtComment // # ... shtSubshell // $$( ) func (t ShAtomType) String() string { return [...]string{ "space", "varuse", "shvaruse", "text", "operator", "comment", "subshell", }[t] } // IsWord checks whether the atom counts as text. // Makefile variables, shell variables and other text counts, // but keywords, operators and separators don't. func (t ShAtomType) IsWord() bool { switch t { case shtVaruse, shtShVarUse, shtText: return true } return false } type ShAtom struct { Type ShAtomType MkText string Quoting ShQuoting // The quoting state at the end of the token // * usually nil // * for shtVaruse a *MkVarUse // * for shtShVarUse a string data interface{} } func (atom *ShAtom) String() string { if atom.Type == shtText && atom.Quoting == shqPlain { return sprintf("%q", atom.MkText) } if atom.Type == shtVaruse { varuse := atom.VarUse() return sprintf("varuse(%q)", varuse.varname+varuse.Mod()) } return sprintf("ShAtom(%v, %q, %s)", atom.Type, atom.MkText, atom.Quoting) } // VarUse returns a read access to a Makefile variable, or nil for plain shell tokens. func (atom *ShAtom) VarUse() *MkVarUse { if atom.Type == shtVaruse { return atom.data.(*MkVarUse) } return nil } // ShVarname applies to shell variable atoms like $$varname or $${varname:-modifier} // and returns the name of the shell variable. func (atom *ShAtom) ShVarname() string { return atom.data.(string) } // ShQuoting describes the context in which a string appears // and how it must be unescaped to get its literal value. type ShQuoting uint8 const ( shqPlain ShQuoting = iota // e.g. word shqDquot // e.g. "word" shqSquot // e.g. 'word' shqBackt // e.g. `word` shqSubsh // e.g. $(word) shqDquotBackt // e.g. "`word`" shqBacktDquot // e.g. `"word"` shqBacktSquot // e.g. `'word'` shqSubshDquot // e.g. $("word") shqSubshSquot // e.g. $('word') shqSubshBackt // e.g. $(`word`) shqDquotBacktDquot // e.g. "`"word"`" shqDquotBacktSquot // e.g. "`'word'`" ) // String returns a very short identifier for the quoting state. // In this, d means double quotes, s means single quotes, // b means backticks and S means subshell. func (q ShQuoting) String() string { return [...]string{ "plain", "d", "s", "b", "S", "db", "bd", "bs", "Sd", "Ss", "Sb", "dbd", "dbs", }[q] } func (q ShQuoting) ToVarUseContext() VucQuoting { switch q { case shqPlain: return VucQuotPlain case shqDquot: return VucQuotDquot case shqSquot: return VucQuotSquot case shqBackt: return VucQuotBackt } return VucQuotUnknown } // ShToken is an operator or a keyword or some text intermingled with variables. // // Examples: // ; // then // "The number of pkgsrc packages in ${PREFIX} is $$packages." // // See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_10_02 type ShToken struct { MkText string // The text as it appeared in the Makefile, after replacing `\#` with `#` Atoms []*ShAtom } func NewShToken(mkText string, atoms ...*ShAtom) *ShToken { assert(mkText != "") assert(len(atoms) > 0) return &ShToken{mkText, atoms} } func (token *ShToken) String() string { return sprintf("ShToken(%v)", token.Atoms) }