Initial commit
This commit is contained in:
Executable
+50
@@ -0,0 +1,50 @@
|
||||
|
||||
package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type action interface {
|
||||
act()
|
||||
String() string
|
||||
}
|
||||
|
||||
type (
|
||||
accept bool
|
||||
shift int // value is next state index
|
||||
reduce int // value is production index
|
||||
)
|
||||
|
||||
func (this accept) act() {}
|
||||
func (this shift) act() {}
|
||||
func (this reduce) act() {}
|
||||
|
||||
func (this accept) Equal(that action) bool {
|
||||
if _, ok := that.(accept); ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (this reduce) Equal(that action) bool {
|
||||
that1, ok := that.(reduce)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return this == that1
|
||||
}
|
||||
|
||||
func (this shift) Equal(that action) bool {
|
||||
that1, ok := that.(shift)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return this == that1
|
||||
}
|
||||
|
||||
func (this accept) String() string { return "accept(0)" }
|
||||
func (this shift) String() string { return fmt.Sprintf("shift:%d", this) }
|
||||
func (this reduce) String() string {
|
||||
return fmt.Sprintf("reduce:%d(%s)", this, productionsTable[this].String)
|
||||
}
|
||||
Executable
+159
@@ -0,0 +1,159 @@
|
||||
|
||||
package parser
|
||||
|
||||
type(
|
||||
actionTable [numStates]actionRow
|
||||
actionRow struct {
|
||||
canRecover bool
|
||||
actions [numSymbols]action
|
||||
}
|
||||
)
|
||||
|
||||
var actionTab = actionTable{
|
||||
actionRow{ // S0
|
||||
canRecover: false,
|
||||
actions: [numSymbols]action{
|
||||
nil, /* INVALID */
|
||||
nil, /* $ */
|
||||
shift(2), /* word */
|
||||
nil, /* number */
|
||||
nil, /* = */
|
||||
nil, /* && */
|
||||
nil, /* || */
|
||||
nil, /* ! */
|
||||
nil, /* | */
|
||||
nil, /* ( */
|
||||
nil, /* ) */
|
||||
nil, /* for */
|
||||
nil, /* in */
|
||||
nil, /* name */
|
||||
nil, /* case */
|
||||
nil, /* esac */
|
||||
nil, /* ;; */
|
||||
nil, /* if */
|
||||
nil, /* then */
|
||||
nil, /* fi */
|
||||
nil, /* elif */
|
||||
nil, /* else */
|
||||
nil, /* while */
|
||||
nil, /* until */
|
||||
nil, /* { */
|
||||
nil, /* } */
|
||||
nil, /* do */
|
||||
nil, /* done */
|
||||
nil, /* < */
|
||||
nil, /* <& */
|
||||
nil, /* > */
|
||||
nil, /* >& */
|
||||
nil, /* >> */
|
||||
nil, /* <> */
|
||||
nil, /* >| */
|
||||
nil, /* << */
|
||||
nil, /* <<- */
|
||||
nil, /* \n */
|
||||
nil, /* nothing */
|
||||
nil, /* & */
|
||||
nil, /* ; */
|
||||
|
||||
},
|
||||
|
||||
},
|
||||
actionRow{ // S1
|
||||
canRecover: false,
|
||||
actions: [numSymbols]action{
|
||||
nil, /* INVALID */
|
||||
accept(true), /* $ */
|
||||
nil, /* word */
|
||||
nil, /* number */
|
||||
nil, /* = */
|
||||
nil, /* && */
|
||||
nil, /* || */
|
||||
nil, /* ! */
|
||||
nil, /* | */
|
||||
nil, /* ( */
|
||||
nil, /* ) */
|
||||
nil, /* for */
|
||||
nil, /* in */
|
||||
nil, /* name */
|
||||
nil, /* case */
|
||||
nil, /* esac */
|
||||
nil, /* ;; */
|
||||
nil, /* if */
|
||||
nil, /* then */
|
||||
nil, /* fi */
|
||||
nil, /* elif */
|
||||
nil, /* else */
|
||||
nil, /* while */
|
||||
nil, /* until */
|
||||
nil, /* { */
|
||||
nil, /* } */
|
||||
nil, /* do */
|
||||
nil, /* done */
|
||||
nil, /* < */
|
||||
nil, /* <& */
|
||||
nil, /* > */
|
||||
nil, /* >& */
|
||||
nil, /* >> */
|
||||
nil, /* <> */
|
||||
nil, /* >| */
|
||||
nil, /* << */
|
||||
nil, /* <<- */
|
||||
nil, /* \n */
|
||||
nil, /* nothing */
|
||||
nil, /* & */
|
||||
nil, /* ; */
|
||||
|
||||
},
|
||||
|
||||
},
|
||||
actionRow{ // S2
|
||||
canRecover: false,
|
||||
actions: [numSymbols]action{
|
||||
nil, /* INVALID */
|
||||
reduce(1), /* $, reduce: Word */
|
||||
nil, /* word */
|
||||
nil, /* number */
|
||||
nil, /* = */
|
||||
nil, /* && */
|
||||
nil, /* || */
|
||||
nil, /* ! */
|
||||
nil, /* | */
|
||||
nil, /* ( */
|
||||
nil, /* ) */
|
||||
nil, /* for */
|
||||
nil, /* in */
|
||||
nil, /* name */
|
||||
nil, /* case */
|
||||
nil, /* esac */
|
||||
nil, /* ;; */
|
||||
nil, /* if */
|
||||
nil, /* then */
|
||||
nil, /* fi */
|
||||
nil, /* elif */
|
||||
nil, /* else */
|
||||
nil, /* while */
|
||||
nil, /* until */
|
||||
nil, /* { */
|
||||
nil, /* } */
|
||||
nil, /* do */
|
||||
nil, /* done */
|
||||
nil, /* < */
|
||||
nil, /* <& */
|
||||
nil, /* > */
|
||||
nil, /* >& */
|
||||
nil, /* >> */
|
||||
nil, /* <> */
|
||||
nil, /* >| */
|
||||
nil, /* << */
|
||||
nil, /* <<- */
|
||||
nil, /* \n */
|
||||
nil, /* nothing */
|
||||
nil, /* & */
|
||||
nil, /* ; */
|
||||
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
Executable
+170
@@ -0,0 +1,170 @@
|
||||
|
||||
/*
|
||||
*/
|
||||
package parser
|
||||
|
||||
const numNTSymbols = 47
|
||||
type(
|
||||
gotoTable [numStates]gotoRow
|
||||
gotoRow [numNTSymbols] int
|
||||
)
|
||||
|
||||
var gotoTab = gotoTable{
|
||||
gotoRow{ // S0
|
||||
|
||||
-1, // S'
|
||||
1, // Word
|
||||
-1, // IoNumber
|
||||
-1, // AssignmentWord
|
||||
-1, // List
|
||||
-1, // AndOr
|
||||
-1, // Pipeline
|
||||
-1, // PipeSequence
|
||||
-1, // Command
|
||||
-1, // CompoundCommand
|
||||
-1, // Subshell
|
||||
-1, // CompoundList
|
||||
-1, // Term
|
||||
-1, // ForClause
|
||||
-1, // Name
|
||||
-1, // Wordlist
|
||||
-1, // CaseClause
|
||||
-1, // CaseListNs
|
||||
-1, // CaseList
|
||||
-1, // CaseItemNs
|
||||
-1, // CaseItem
|
||||
-1, // Pattern
|
||||
-1, // IfClause
|
||||
-1, // ElsePart
|
||||
-1, // WhileClause
|
||||
-1, // UntilClause
|
||||
-1, // FunctionDefinition
|
||||
-1, // FunctionBody
|
||||
-1, // FunctionName
|
||||
-1, // BraceGroup
|
||||
-1, // DoGroup
|
||||
-1, // SimpleCommand
|
||||
-1, // CmdName
|
||||
-1, // CmdWord
|
||||
-1, // CmdPrefix
|
||||
-1, // CmdSuffix
|
||||
-1, // RedirectList
|
||||
-1, // IoRedirect
|
||||
-1, // IoFile
|
||||
-1, // Filename
|
||||
-1, // IoHere
|
||||
-1, // HereEnd
|
||||
-1, // NewlineList
|
||||
-1, // Linebreak
|
||||
-1, // SeparatorOp
|
||||
-1, // Separator
|
||||
-1, // SequentialSep
|
||||
|
||||
|
||||
},
|
||||
gotoRow{ // S1
|
||||
|
||||
-1, // S'
|
||||
-1, // Word
|
||||
-1, // IoNumber
|
||||
-1, // AssignmentWord
|
||||
-1, // List
|
||||
-1, // AndOr
|
||||
-1, // Pipeline
|
||||
-1, // PipeSequence
|
||||
-1, // Command
|
||||
-1, // CompoundCommand
|
||||
-1, // Subshell
|
||||
-1, // CompoundList
|
||||
-1, // Term
|
||||
-1, // ForClause
|
||||
-1, // Name
|
||||
-1, // Wordlist
|
||||
-1, // CaseClause
|
||||
-1, // CaseListNs
|
||||
-1, // CaseList
|
||||
-1, // CaseItemNs
|
||||
-1, // CaseItem
|
||||
-1, // Pattern
|
||||
-1, // IfClause
|
||||
-1, // ElsePart
|
||||
-1, // WhileClause
|
||||
-1, // UntilClause
|
||||
-1, // FunctionDefinition
|
||||
-1, // FunctionBody
|
||||
-1, // FunctionName
|
||||
-1, // BraceGroup
|
||||
-1, // DoGroup
|
||||
-1, // SimpleCommand
|
||||
-1, // CmdName
|
||||
-1, // CmdWord
|
||||
-1, // CmdPrefix
|
||||
-1, // CmdSuffix
|
||||
-1, // RedirectList
|
||||
-1, // IoRedirect
|
||||
-1, // IoFile
|
||||
-1, // Filename
|
||||
-1, // IoHere
|
||||
-1, // HereEnd
|
||||
-1, // NewlineList
|
||||
-1, // Linebreak
|
||||
-1, // SeparatorOp
|
||||
-1, // Separator
|
||||
-1, // SequentialSep
|
||||
|
||||
|
||||
},
|
||||
gotoRow{ // S2
|
||||
|
||||
-1, // S'
|
||||
-1, // Word
|
||||
-1, // IoNumber
|
||||
-1, // AssignmentWord
|
||||
-1, // List
|
||||
-1, // AndOr
|
||||
-1, // Pipeline
|
||||
-1, // PipeSequence
|
||||
-1, // Command
|
||||
-1, // CompoundCommand
|
||||
-1, // Subshell
|
||||
-1, // CompoundList
|
||||
-1, // Term
|
||||
-1, // ForClause
|
||||
-1, // Name
|
||||
-1, // Wordlist
|
||||
-1, // CaseClause
|
||||
-1, // CaseListNs
|
||||
-1, // CaseList
|
||||
-1, // CaseItemNs
|
||||
-1, // CaseItem
|
||||
-1, // Pattern
|
||||
-1, // IfClause
|
||||
-1, // ElsePart
|
||||
-1, // WhileClause
|
||||
-1, // UntilClause
|
||||
-1, // FunctionDefinition
|
||||
-1, // FunctionBody
|
||||
-1, // FunctionName
|
||||
-1, // BraceGroup
|
||||
-1, // DoGroup
|
||||
-1, // SimpleCommand
|
||||
-1, // CmdName
|
||||
-1, // CmdWord
|
||||
-1, // CmdPrefix
|
||||
-1, // CmdSuffix
|
||||
-1, // RedirectList
|
||||
-1, // IoRedirect
|
||||
-1, // IoFile
|
||||
-1, // Filename
|
||||
-1, // IoHere
|
||||
-1, // HereEnd
|
||||
-1, // NewlineList
|
||||
-1, // Linebreak
|
||||
-1, // SeparatorOp
|
||||
-1, // Separator
|
||||
-1, // SequentialSep
|
||||
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
Executable
+214
@@ -0,0 +1,214 @@
|
||||
|
||||
package parser
|
||||
|
||||
import(
|
||||
"bytes"
|
||||
"fmt"
|
||||
"errors"
|
||||
parseError "github.com/localhots/penny/errors"
|
||||
"github.com/localhots/penny/token"
|
||||
)
|
||||
|
||||
const (
|
||||
numProductions = 108
|
||||
numStates = 3
|
||||
numSymbols = 88
|
||||
)
|
||||
|
||||
// Stack
|
||||
|
||||
type stack struct {
|
||||
state []int
|
||||
attrib []Attrib
|
||||
}
|
||||
|
||||
const iNITIAL_STACK_SIZE = 100
|
||||
|
||||
func newStack() *stack {
|
||||
return &stack{ state: make([]int, 0, iNITIAL_STACK_SIZE),
|
||||
attrib: make([]Attrib, 0, iNITIAL_STACK_SIZE),
|
||||
}
|
||||
}
|
||||
|
||||
func (this *stack) reset() {
|
||||
this.state = this.state[0:0]
|
||||
this.attrib = this.attrib[0:0]
|
||||
}
|
||||
|
||||
func (this *stack) push(s int, a Attrib) {
|
||||
this.state = append(this.state, s)
|
||||
this.attrib = append(this.attrib, a)
|
||||
}
|
||||
|
||||
func(this *stack) top() int {
|
||||
return this.state[len(this.state) - 1]
|
||||
}
|
||||
|
||||
func (this *stack) peek(pos int) int {
|
||||
return this.state[pos]
|
||||
}
|
||||
|
||||
func (this *stack) topIndex() int {
|
||||
return len(this.state) - 1
|
||||
}
|
||||
|
||||
func (this *stack) popN(items int) []Attrib {
|
||||
lo, hi := len(this.state) - items, len(this.state)
|
||||
|
||||
attrib := this.attrib[lo: hi]
|
||||
|
||||
this.state = this.state[:lo]
|
||||
this.attrib = this.attrib[:lo]
|
||||
|
||||
return attrib
|
||||
}
|
||||
|
||||
func (S *stack) String() string {
|
||||
w := new(bytes.Buffer)
|
||||
fmt.Fprintf(w, "stack:\n")
|
||||
for i, st := range S.state {
|
||||
fmt.Fprintf(w, "\t%d:%d , ", i, st)
|
||||
if S.attrib[i] == nil {
|
||||
fmt.Fprintf(w, "nil")
|
||||
} else {
|
||||
fmt.Fprintf(w, "%v", S.attrib[i])
|
||||
}
|
||||
w.WriteString("\n")
|
||||
}
|
||||
return w.String()
|
||||
}
|
||||
|
||||
// Parser
|
||||
|
||||
type Parser struct {
|
||||
stack *stack
|
||||
nextToken *token.Token
|
||||
pos int
|
||||
}
|
||||
|
||||
type Scanner interface {
|
||||
Scan() (tok *token.Token)
|
||||
}
|
||||
|
||||
func NewParser() *Parser {
|
||||
p := &Parser{stack: newStack()}
|
||||
p.Reset()
|
||||
return p
|
||||
}
|
||||
|
||||
func (P *Parser) Reset() {
|
||||
P.stack.reset()
|
||||
P.stack.push(0, nil)
|
||||
}
|
||||
|
||||
func (P *Parser) Error(err error, scanner Scanner) (recovered bool, errorAttrib *parseError.Error) {
|
||||
errorAttrib = &parseError.Error{
|
||||
Err: err,
|
||||
ErrorToken: P.nextToken,
|
||||
ErrorSymbols: P.popNonRecoveryStates(),
|
||||
ExpectedTokens: make([]string, 0, 8),
|
||||
}
|
||||
for t, action := range actionTab[P.stack.top()].actions {
|
||||
if action != nil {
|
||||
errorAttrib.ExpectedTokens = append(errorAttrib.ExpectedTokens, token.TokMap.Id(token.Type(t)))
|
||||
}
|
||||
}
|
||||
|
||||
if action := actionTab[P.stack.top()].actions[token.TokMap.Type("error")]; action != nil {
|
||||
P.stack.push(int(action.(shift)), errorAttrib) // action can only be shift
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
if action := actionTab[P.stack.top()].actions[P.nextToken.Type]; action != nil {
|
||||
recovered = true
|
||||
}
|
||||
for !recovered && P.nextToken.Type != token.EOF {
|
||||
P.nextToken = scanner.Scan()
|
||||
if action := actionTab[P.stack.top()].actions[P.nextToken.Type]; action != nil {
|
||||
recovered = true
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (P *Parser) popNonRecoveryStates() (removedAttribs []parseError.ErrorSymbol) {
|
||||
if rs, ok := P.firstRecoveryState(); ok {
|
||||
errorSymbols := P.stack.popN(int(P.stack.topIndex() - rs))
|
||||
removedAttribs = make([]parseError.ErrorSymbol, len(errorSymbols))
|
||||
for i, e := range errorSymbols {
|
||||
removedAttribs[i] = e
|
||||
}
|
||||
} else {
|
||||
removedAttribs = []parseError.ErrorSymbol{}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// recoveryState points to the highest state on the stack, which can recover
|
||||
func (P *Parser) firstRecoveryState() (recoveryState int, canRecover bool) {
|
||||
recoveryState, canRecover = P.stack.topIndex(), actionTab[P.stack.top()].canRecover
|
||||
for recoveryState > 0 && !canRecover {
|
||||
recoveryState--
|
||||
canRecover = actionTab[P.stack.peek(recoveryState)].canRecover
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (P *Parser) newError(err error) error {
|
||||
w := new(bytes.Buffer)
|
||||
fmt.Fprintf(w, "Error in S%d: %s, %s", P.stack.top(), token.TokMap.TokenString(P.nextToken), P.nextToken.Pos.String())
|
||||
if err != nil {
|
||||
w.WriteString(err.Error())
|
||||
} else {
|
||||
w.WriteString(", expected one of: ")
|
||||
actRow := actionTab[P.stack.top()]
|
||||
for i, t := range actRow.actions {
|
||||
if t != nil {
|
||||
fmt.Fprintf(w, "%s ", token.TokMap.Id(token.Type(i)))
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors.New(w.String())
|
||||
}
|
||||
|
||||
func (this *Parser) Parse(scanner Scanner) (res interface{}, err error) {
|
||||
this.Reset()
|
||||
this.nextToken = scanner.Scan()
|
||||
for acc := false; !acc; {
|
||||
action := actionTab[this.stack.top()].actions[this.nextToken.Type]
|
||||
if action == nil {
|
||||
if recovered, errAttrib := this.Error(nil, scanner); !recovered {
|
||||
this.nextToken = errAttrib.ErrorToken
|
||||
return nil, this.newError(nil)
|
||||
}
|
||||
if action = actionTab[this.stack.top()].actions[this.nextToken.Type]; action == nil {
|
||||
panic("Error recovery led to invalid action")
|
||||
}
|
||||
}
|
||||
|
||||
// fmt.Printf("S%d %s %s\n", this.stack.top(), token.TokMap.TokenString(this.nextToken), action.String())
|
||||
|
||||
|
||||
switch act := action.(type) {
|
||||
case accept:
|
||||
res = this.stack.popN(1)[0]
|
||||
acc = true
|
||||
case shift:
|
||||
this.stack.push(int(act), this.nextToken)
|
||||
this.nextToken = scanner.Scan()
|
||||
case reduce:
|
||||
prod := productionsTable[int(act)]
|
||||
attrib, err := prod.ReduceFunc(this.stack.popN(prod.NumSymbols))
|
||||
if err != nil {
|
||||
return nil, this.newError(err)
|
||||
} else {
|
||||
this.stack.push(gotoTab[this.stack.top()][prod.NTType], attrib)
|
||||
}
|
||||
default:
|
||||
panic("unknown action: " + action.String())
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
Executable
+1103
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user