mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-04-20 19:38:39 +00:00
201 lines
4.3 KiB
Go
201 lines
4.3 KiB
Go
//Copyright 2013 GoGraphviz Authors
|
|
//
|
|
//Licensed under the Apache License, Version 2.0 (the "License");
|
|
//you may not use this file except in compliance with the License.
|
|
//You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
//Unless required by applicable law or agreed to in writing, software
|
|
//distributed under the License is distributed on an "AS IS" BASIS,
|
|
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
//See the License for the specific language governing permissions and
|
|
//limitations under the License.
|
|
|
|
package parser
|
|
|
|
import (
|
|
"errors"
|
|
"strconv"
|
|
)
|
|
|
|
import "github.com/awalterschulze/gographviz/token"
|
|
|
|
type (
|
|
ActionTab []ActionRow
|
|
ActionRow map[token.Type]Action
|
|
)
|
|
|
|
func (R ActionRow) String() string {
|
|
s := ""
|
|
for t, a := range R {
|
|
s += strconv.Itoa(int(t)) + " : " + a.String() + "\n"
|
|
}
|
|
return s
|
|
}
|
|
|
|
type (
|
|
Accept int
|
|
Shift State
|
|
Reduce int
|
|
|
|
Action interface {
|
|
Act()
|
|
String() string
|
|
}
|
|
)
|
|
|
|
func (this Accept) Act() {}
|
|
func (this Shift) Act() {}
|
|
func (this Reduce) Act() {}
|
|
|
|
func (this Accept) String() string { return "Accept(0)" }
|
|
func (this Shift) String() string { return "Shift(" + strconv.Itoa(int(this)) + ")" }
|
|
func (this Reduce) String() string { return "Reduce(" + strconv.Itoa(int(this)) + ")" }
|
|
|
|
type (
|
|
GotoTab []GotoRow
|
|
GotoRow map[NT]State
|
|
State int
|
|
NT string
|
|
)
|
|
|
|
type (
|
|
ProdTab []ProdTabEntry
|
|
ProdTabEntry struct {
|
|
String string
|
|
Head NT
|
|
NumSymbols int
|
|
ReduceFunc func([]Attrib) (Attrib, error)
|
|
}
|
|
Attrib interface {
|
|
String() string
|
|
}
|
|
)
|
|
|
|
/*** Stack ***/
|
|
|
|
type stack struct {
|
|
state []State
|
|
attrib []Attrib
|
|
}
|
|
|
|
const INITIAL_STACK_SIZE = 100
|
|
|
|
func NewStack() *stack {
|
|
return &stack{state: make([]State, 0, INITIAL_STACK_SIZE),
|
|
attrib: make([]Attrib, 0, INITIAL_STACK_SIZE),
|
|
}
|
|
}
|
|
|
|
func (this *stack) Push(s State, a Attrib) {
|
|
this.state = append(this.state, s)
|
|
this.attrib = append(this.attrib, a)
|
|
}
|
|
|
|
func (this *stack) Top() State {
|
|
return this.state[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 {
|
|
res := "stack:\n"
|
|
for i, st := range S.state {
|
|
res += "\t" + strconv.Itoa(i) + ": " + strconv.Itoa(int(st))
|
|
res += " , "
|
|
if S.attrib[i] == nil {
|
|
res += "nil"
|
|
} else {
|
|
res += S.attrib[i].String()
|
|
}
|
|
res += "\n"
|
|
}
|
|
return res
|
|
}
|
|
|
|
/*** Parser ***/
|
|
|
|
type Parser struct {
|
|
actTab ActionTab
|
|
gotoTab GotoTab
|
|
prodTab ProdTab
|
|
stack *stack
|
|
nextToken *token.Token
|
|
pos token.Position
|
|
tokenMap *token.TokenMap
|
|
}
|
|
|
|
type Scanner interface {
|
|
Scan() (*token.Token, token.Position)
|
|
}
|
|
|
|
func NewParser(act ActionTab, gto GotoTab, prod ProdTab, tm *token.TokenMap) *Parser {
|
|
p := &Parser{actTab: act, gotoTab: gto, prodTab: prod, stack: NewStack(), tokenMap: tm}
|
|
p.stack.Push(0, nil) //TODO: which attribute should be pushed here?
|
|
return p
|
|
}
|
|
|
|
func (P *Parser) Error(err error) error {
|
|
errmsg := "Error: " + P.TokString(P.nextToken) + " @ " + P.pos.String()
|
|
if err != nil {
|
|
errmsg += " " + err.Error()
|
|
} else {
|
|
errmsg += ", expected one of: "
|
|
actRow := P.actTab[P.stack.Top()]
|
|
i := 0
|
|
for t := range actRow {
|
|
errmsg += P.tokenMap.TokenString(t)
|
|
if i < len(actRow)-1 {
|
|
errmsg += " "
|
|
}
|
|
i++
|
|
}
|
|
}
|
|
return errors.New(errmsg)
|
|
}
|
|
|
|
func (P *Parser) TokString(tok *token.Token) string {
|
|
msg := P.tokenMap.TokenString(tok.Type) + "(" + strconv.Itoa(int(tok.Type)) + ")"
|
|
msg += " " + string(tok.Lit)
|
|
return msg
|
|
}
|
|
|
|
func (this *Parser) Parse(scanner Scanner) (res interface{}, err error) {
|
|
this.nextToken, this.pos = scanner.Scan()
|
|
for acc := false; !acc; {
|
|
action, ok := this.actTab[this.stack.Top()][this.nextToken.Type]
|
|
if !ok {
|
|
return nil, this.Error(nil)
|
|
}
|
|
switch act := action.(type) {
|
|
case Accept:
|
|
res = this.stack.PopN(1)[0]
|
|
acc = true
|
|
case Shift:
|
|
this.stack.Push(State(act), this.nextToken)
|
|
this.nextToken, this.pos = scanner.Scan()
|
|
case Reduce:
|
|
prod := this.prodTab[int(act)]
|
|
attrib, err := prod.ReduceFunc(this.stack.PopN(prod.NumSymbols))
|
|
if err != nil {
|
|
return nil, this.Error(err)
|
|
} else {
|
|
this.stack.Push(this.gotoTab[this.stack.Top()][prod.Head], attrib)
|
|
}
|
|
default:
|
|
panic("unknown action")
|
|
}
|
|
}
|
|
return res, nil
|
|
}
|