mirror of
https://github.com/Stichting-MINIX-Research-Foundation/pkgsrc-ng.git
synced 2025-09-22 02:57:40 -04:00
222 lines
4.3 KiB
Go
222 lines
4.3 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
)
|
|
|
|
func parseShellProgram(line *Line, program string) (list *MkShList, err error) {
|
|
if G.opts.Debug {
|
|
defer tracecall(program)()
|
|
}
|
|
|
|
tokens, rest := splitIntoShellTokens(line, program)
|
|
lexer := NewShellLexer(tokens, rest)
|
|
parser := ­yParserImpl{}
|
|
|
|
succeeded := parser.Parse(lexer)
|
|
|
|
if succeeded == 0 && lexer.error == "" {
|
|
return lexer.result, nil
|
|
}
|
|
return nil, &ParseError{append([]string{lexer.current}, lexer.remaining...)}
|
|
}
|
|
|
|
type ParseError struct {
|
|
RemainingTokens []string
|
|
}
|
|
|
|
func (e *ParseError) Error() string {
|
|
return fmt.Sprintf("parse error at %v", e.RemainingTokens)
|
|
}
|
|
|
|
type ShellLexer struct {
|
|
current string
|
|
ioredirect string
|
|
remaining []string
|
|
atCommandStart bool
|
|
sinceFor int
|
|
sinceCase int
|
|
error string
|
|
result *MkShList
|
|
}
|
|
|
|
func NewShellLexer(tokens []string, rest string) *ShellLexer {
|
|
return &ShellLexer{
|
|
current: "",
|
|
ioredirect: "",
|
|
remaining: tokens,
|
|
atCommandStart: true,
|
|
error: rest}
|
|
}
|
|
func (lex *ShellLexer) Lex(lval *shyySymType) (ttype int) {
|
|
if len(lex.remaining) == 0 {
|
|
return 0
|
|
}
|
|
|
|
if G.opts.Debug {
|
|
defer func() {
|
|
tname := shyyTokname(shyyTok2[ttype-shyyPrivate])
|
|
switch ttype {
|
|
case tkWORD, tkASSIGNMENT_WORD:
|
|
traceStep("lex %v %q", tname, lval.Word.MkText)
|
|
case tkIO_NUMBER:
|
|
traceStep("lex %v %v", tname, lval.IONum)
|
|
default:
|
|
traceStep("lex %v", tname)
|
|
}
|
|
}()
|
|
}
|
|
|
|
token := lex.ioredirect
|
|
lex.ioredirect = ""
|
|
if token == "" {
|
|
token = lex.remaining[0]
|
|
lex.current = token
|
|
lex.remaining = lex.remaining[1:]
|
|
}
|
|
|
|
switch token {
|
|
case ";":
|
|
lex.atCommandStart = true
|
|
return tkSEMI
|
|
case ";;":
|
|
lex.atCommandStart = true
|
|
return tkSEMISEMI
|
|
case "\n":
|
|
lex.atCommandStart = true
|
|
return tkNEWLINE
|
|
case "&":
|
|
lex.atCommandStart = true
|
|
return tkBACKGROUND
|
|
case "|":
|
|
lex.atCommandStart = true
|
|
return tkPIPE
|
|
case "(":
|
|
lex.atCommandStart = true
|
|
return tkLPAREN
|
|
case ")":
|
|
lex.atCommandStart = true
|
|
return tkRPAREN
|
|
case "&&":
|
|
lex.atCommandStart = true
|
|
return tkAND
|
|
case "||":
|
|
lex.atCommandStart = true
|
|
return tkOR
|
|
case ">":
|
|
lex.atCommandStart = false
|
|
return tkGT
|
|
case ">&":
|
|
lex.atCommandStart = false
|
|
return tkGTAND
|
|
case "<":
|
|
lex.atCommandStart = false
|
|
return tkLT
|
|
case "<&":
|
|
lex.atCommandStart = false
|
|
return tkLTAND
|
|
case "<>":
|
|
lex.atCommandStart = false
|
|
return tkLTGT
|
|
case ">>":
|
|
lex.atCommandStart = false
|
|
return tkGTGT
|
|
case "<<":
|
|
lex.atCommandStart = false
|
|
return tkLTLT
|
|
case "<<-":
|
|
lex.atCommandStart = false
|
|
return tkLTLTDASH
|
|
case ">|":
|
|
lex.atCommandStart = false
|
|
return tkGTPIPE
|
|
}
|
|
|
|
if m, fdstr, op := match2(token, `^(\d+)(<<-|<<|<>|<&|>>|>&|>\||<|>)$`); m {
|
|
fd, _ := strconv.Atoi(fdstr)
|
|
lval.IONum = fd
|
|
lex.ioredirect = op
|
|
return tkIO_NUMBER
|
|
}
|
|
|
|
if lex.atCommandStart {
|
|
lex.sinceCase = -1
|
|
lex.sinceFor = -1
|
|
switch token {
|
|
case "if":
|
|
return tkIF
|
|
case "then":
|
|
return tkTHEN
|
|
case "elif":
|
|
return tkELIF
|
|
case "else":
|
|
return tkELSE
|
|
case "fi":
|
|
return tkFI
|
|
case "for":
|
|
lex.atCommandStart = false
|
|
lex.sinceFor = 0
|
|
return tkFOR
|
|
case "while":
|
|
return tkWHILE
|
|
case "until":
|
|
return tkUNTIL
|
|
case "do":
|
|
return tkDO
|
|
case "done":
|
|
return tkDONE
|
|
case "in":
|
|
lex.atCommandStart = false
|
|
return tkIN
|
|
case "case":
|
|
lex.atCommandStart = false
|
|
lex.sinceCase = 0
|
|
return tkCASE
|
|
case "{":
|
|
return tkLBRACE
|
|
case "}":
|
|
return tkRBRACE
|
|
case "!":
|
|
return tkEXCLAM
|
|
}
|
|
}
|
|
|
|
if lex.sinceFor >= 0 {
|
|
lex.sinceFor++
|
|
}
|
|
if lex.sinceCase >= 0 {
|
|
lex.sinceCase++
|
|
}
|
|
|
|
switch {
|
|
case lex.sinceFor == 2 && token == "in":
|
|
ttype = tkIN
|
|
lex.atCommandStart = false
|
|
case lex.sinceFor == 2 && token == "do":
|
|
ttype = tkDO
|
|
lex.atCommandStart = true
|
|
case lex.sinceCase == 2 && token == "in":
|
|
ttype = tkIN
|
|
lex.atCommandStart = false
|
|
case (lex.atCommandStart || lex.sinceCase == 3) && token == "esac":
|
|
ttype = tkESAC
|
|
lex.atCommandStart = false
|
|
case lex.atCommandStart && matches(token, `^[A-Za-z_]\w*=`):
|
|
ttype = tkASSIGNMENT_WORD
|
|
p := NewShTokenizer(dummyLine, token, false)
|
|
lval.Word = p.ShToken()
|
|
default:
|
|
ttype = tkWORD
|
|
p := NewShTokenizer(dummyLine, token, false)
|
|
lval.Word = p.ShToken()
|
|
lex.atCommandStart = false
|
|
}
|
|
|
|
return ttype
|
|
}
|
|
|
|
func (lex *ShellLexer) Error(s string) {
|
|
lex.error = s
|
|
}
|