In the spirit of Go, slimmed down the ParseAPI interface. I'm no longer using ParseAPI.On(..).<DoSomething>(), but now it's simply ParseAPI.<DoSomething>(). I also dropped the difference between a Stay() and an Accept(). All that is possible now is ParseAPI.Peek() and ParseAPI.Accept().
This commit is contained in:
parent
9f5caa2024
commit
add28feb33
|
@ -75,7 +75,7 @@ var bareInteger = parsekit.C.Seq(dropBlank, parsekit.A.Integer, dropBlank)
|
||||||
var int64Token = parsekit.T.Int64(nil, bareInteger)
|
var int64Token = parsekit.T.Int64(nil, bareInteger)
|
||||||
|
|
||||||
func (c *simpleCalculator) number(p *parsekit.ParseAPI) {
|
func (c *simpleCalculator) number(p *parsekit.ParseAPI) {
|
||||||
if p.On(int64Token).Accept() {
|
if p.Accept(int64Token) {
|
||||||
c.Result += c.op * p.Result().Value(0).(int64)
|
c.Result += c.op * p.Result().Value(0).(int64)
|
||||||
p.Handle(c.operatorOrEndOfFile)
|
p.Handle(c.operatorOrEndOfFile)
|
||||||
} else {
|
} else {
|
||||||
|
@ -86,13 +86,13 @@ func (c *simpleCalculator) number(p *parsekit.ParseAPI) {
|
||||||
func (c *simpleCalculator) operatorOrEndOfFile(p *parsekit.ParseAPI) {
|
func (c *simpleCalculator) operatorOrEndOfFile(p *parsekit.ParseAPI) {
|
||||||
var A = parsekit.A
|
var A = parsekit.A
|
||||||
switch {
|
switch {
|
||||||
case p.On(A.Add).Skip():
|
case p.Accept(A.Add):
|
||||||
c.op = +1
|
c.op = +1
|
||||||
p.Handle(c.number)
|
p.Handle(c.number)
|
||||||
case p.On(A.Subtract).Skip():
|
case p.Accept(A.Subtract):
|
||||||
c.op = -1
|
c.op = -1
|
||||||
p.Handle(c.number)
|
p.Handle(c.number)
|
||||||
case !p.On(A.EndOfFile).Stay():
|
case !p.Accept(A.EndOfFile):
|
||||||
p.Expected("operator, '+' or '-'")
|
p.Expected("operator, '+' or '-'")
|
||||||
default:
|
default:
|
||||||
p.ExpectEndOfFile()
|
p.ExpectEndOfFile()
|
||||||
|
|
|
@ -96,7 +96,7 @@ func (calc *calculator) expr(p *parsekit.ParseAPI) {
|
||||||
|
|
||||||
var C, A = parsekit.C, parsekit.A
|
var C, A = parsekit.C, parsekit.A
|
||||||
if p.Handle(calc.term) {
|
if p.Handle(calc.term) {
|
||||||
for p.On(C.Any(A.Add, A.Subtract)).Accept() {
|
for p.Accept(C.Any(A.Add, A.Subtract)) {
|
||||||
op := p.Result().Rune(0)
|
op := p.Result().Rune(0)
|
||||||
if !p.Handle(calc.term) {
|
if !p.Handle(calc.term) {
|
||||||
return
|
return
|
||||||
|
@ -114,7 +114,7 @@ func (calc *calculator) term(p *parsekit.ParseAPI) {
|
||||||
|
|
||||||
var C, A = parsekit.C, parsekit.A
|
var C, A = parsekit.C, parsekit.A
|
||||||
if p.Handle(calc.factor) {
|
if p.Handle(calc.factor) {
|
||||||
for p.On(C.Any(A.Multiply, A.Divide)).Accept() {
|
for p.Accept(C.Any(A.Multiply, A.Divide)) {
|
||||||
op := p.Result().Rune(0)
|
op := p.Result().Rune(0)
|
||||||
if !p.Handle(calc.factor) {
|
if !p.Handle(calc.factor) {
|
||||||
return
|
return
|
||||||
|
@ -130,16 +130,16 @@ func (calc *calculator) term(p *parsekit.ParseAPI) {
|
||||||
// <factor> = <space> (FLOAT | LPAREN <expr> RPAREN) <space>
|
// <factor> = <space> (FLOAT | LPAREN <expr> RPAREN) <space>
|
||||||
func (calc *calculator) factor(p *parsekit.ParseAPI) {
|
func (calc *calculator) factor(p *parsekit.ParseAPI) {
|
||||||
var A, T = parsekit.A, parsekit.T
|
var A, T = parsekit.A, parsekit.T
|
||||||
p.On(A.Blanks).Skip()
|
p.Accept(A.Blanks)
|
||||||
switch {
|
switch {
|
||||||
case p.On(T.Float64(nil, A.Signed(A.Float))).Accept():
|
case p.Accept(T.Float64(nil, A.Signed(A.Float))):
|
||||||
value := p.Result().Value(0).(float64)
|
value := p.Result().Value(0).(float64)
|
||||||
calc.interpreter.pushValue(value)
|
calc.interpreter.pushValue(value)
|
||||||
case p.On(A.LeftParen).Skip():
|
case p.Accept(A.LeftParen):
|
||||||
if !p.Handle(calc.expr) {
|
if !p.Handle(calc.expr) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !p.On(A.RightParen).Skip() {
|
if !p.Accept(A.RightParen) {
|
||||||
p.Expected("')'")
|
p.Expected("')'")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -147,7 +147,7 @@ func (calc *calculator) factor(p *parsekit.ParseAPI) {
|
||||||
p.Expected("factor or parenthesized expression")
|
p.Expected("factor or parenthesized expression")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.On(A.Blanks).Skip()
|
p.Accept(A.Blanks)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -82,7 +82,7 @@ func (h *helloparser1) Parse(input string) (string, *parsekit.Error) {
|
||||||
|
|
||||||
func (h *helloparser1) start(p *parsekit.ParseAPI) {
|
func (h *helloparser1) start(p *parsekit.ParseAPI) {
|
||||||
a := parsekit.A
|
a := parsekit.A
|
||||||
if p.On(a.StrNoCase("hello")).Skip() {
|
if p.Accept(a.StrNoCase("hello")) {
|
||||||
p.Handle(h.comma)
|
p.Handle(h.comma)
|
||||||
} else {
|
} else {
|
||||||
p.Expected("hello")
|
p.Expected("hello")
|
||||||
|
@ -92,9 +92,9 @@ func (h *helloparser1) start(p *parsekit.ParseAPI) {
|
||||||
func (h *helloparser1) comma(p *parsekit.ParseAPI) {
|
func (h *helloparser1) comma(p *parsekit.ParseAPI) {
|
||||||
a := parsekit.A
|
a := parsekit.A
|
||||||
switch {
|
switch {
|
||||||
case p.On(a.Blanks).Skip():
|
case p.Accept(a.Blanks):
|
||||||
p.Handle(h.comma)
|
p.Handle(h.comma)
|
||||||
case p.On(a.Comma).Skip():
|
case p.Accept(a.Comma):
|
||||||
p.Handle(h.startName)
|
p.Handle(h.startName)
|
||||||
default:
|
default:
|
||||||
p.Expected("comma")
|
p.Expected("comma")
|
||||||
|
@ -103,8 +103,8 @@ func (h *helloparser1) comma(p *parsekit.ParseAPI) {
|
||||||
|
|
||||||
func (h *helloparser1) startName(p *parsekit.ParseAPI) {
|
func (h *helloparser1) startName(p *parsekit.ParseAPI) {
|
||||||
a := parsekit.A
|
a := parsekit.A
|
||||||
p.On(a.Blanks).Skip()
|
p.Accept(a.Blanks)
|
||||||
if p.On(a.AnyRune).Stay() {
|
if p.Peek(a.AnyRune) {
|
||||||
p.Handle(h.name)
|
p.Handle(h.name)
|
||||||
} else {
|
} else {
|
||||||
p.Expected("name")
|
p.Expected("name")
|
||||||
|
@ -114,9 +114,9 @@ func (h *helloparser1) startName(p *parsekit.ParseAPI) {
|
||||||
func (h *helloparser1) name(p *parsekit.ParseAPI) {
|
func (h *helloparser1) name(p *parsekit.ParseAPI) {
|
||||||
a := parsekit.A
|
a := parsekit.A
|
||||||
switch {
|
switch {
|
||||||
case p.On(a.Excl).Stay():
|
case p.Peek(a.Excl):
|
||||||
p.Handle(h.exclamation)
|
p.Handle(h.exclamation)
|
||||||
case p.On(a.AnyRune).Accept():
|
case p.Accept(a.AnyRune):
|
||||||
h.greetee += p.Result().String()
|
h.greetee += p.Result().String()
|
||||||
p.Handle(h.name)
|
p.Handle(h.name)
|
||||||
default:
|
default:
|
||||||
|
@ -126,7 +126,7 @@ func (h *helloparser1) name(p *parsekit.ParseAPI) {
|
||||||
|
|
||||||
func (h *helloparser1) exclamation(p *parsekit.ParseAPI) {
|
func (h *helloparser1) exclamation(p *parsekit.ParseAPI) {
|
||||||
a := parsekit.A
|
a := parsekit.A
|
||||||
if p.On(a.Excl).Accept() {
|
if p.Accept(a.Excl) {
|
||||||
p.Handle(h.end)
|
p.Handle(h.end)
|
||||||
} else {
|
} else {
|
||||||
p.Expected("exclamation")
|
p.Expected("exclamation")
|
||||||
|
@ -138,7 +138,7 @@ func (h *helloparser1) exclamation(p *parsekit.ParseAPI) {
|
||||||
// error message.
|
// error message.
|
||||||
func (h *helloparser1) end(p *parsekit.ParseAPI) {
|
func (h *helloparser1) end(p *parsekit.ParseAPI) {
|
||||||
var a = parsekit.A
|
var a = parsekit.A
|
||||||
if !p.On(a.EndOfFile).Stay() {
|
if !p.Accept(a.EndOfFile) {
|
||||||
p.Expected("end of greeting")
|
p.Expected("end of greeting")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,15 +80,15 @@ func (h *helloparser2) Parse(input string) (string, *parsekit.Error) {
|
||||||
|
|
||||||
func (h *helloparser2) start(p *parsekit.ParseAPI) {
|
func (h *helloparser2) start(p *parsekit.ParseAPI) {
|
||||||
c, a, m := parsekit.C, parsekit.A, parsekit.M
|
c, a, m := parsekit.C, parsekit.A, parsekit.M
|
||||||
if !p.On(a.StrNoCase("hello")).Skip() {
|
if !p.Accept(a.StrNoCase("hello")) {
|
||||||
p.Error("the greeting is not being friendly")
|
p.Error("the greeting is not being friendly")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !p.On(c.Seq(c.Opt(a.Blank), a.Comma, c.Opt(a.Blank))).Skip() {
|
if !p.Accept(c.Seq(c.Opt(a.Blank), a.Comma, c.Opt(a.Blank))) {
|
||||||
p.Error("the greeting is not properly separated")
|
p.Error("the greeting is not properly separated")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if p.On(m.TrimSpace(c.OneOrMore(c.Except(a.Excl, a.AnyRune)))).Accept() {
|
if p.Accept(m.TrimSpace(c.OneOrMore(c.Except(a.Excl, a.AnyRune)))) {
|
||||||
h.greetee = p.Result().String()
|
h.greetee = p.Result().String()
|
||||||
if h.greetee == "" {
|
if h.greetee == "" {
|
||||||
p.Error("the name cannot be empty")
|
p.Error("the name cannot be empty")
|
||||||
|
@ -98,9 +98,9 @@ func (h *helloparser2) start(p *parsekit.ParseAPI) {
|
||||||
p.Error("the greeting is targeted at thin air")
|
p.Error("the greeting is targeted at thin air")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !p.On(a.Excl).Skip() {
|
if !p.Accept(a.Excl) {
|
||||||
p.Error("the greeting is not loud enough")
|
p.Error("the greeting is not loud enough")
|
||||||
} else if !p.On(a.EndOfFile).Stay() {
|
} else if !p.Accept(a.EndOfFile) {
|
||||||
p.Error("too much stuff going on after the closing '!'")
|
p.Error("too much stuff going on after the closing '!'")
|
||||||
} else {
|
} else {
|
||||||
p.Stop()
|
p.Stop()
|
||||||
|
|
|
@ -20,7 +20,7 @@ func (l *Chunks) AddChopped(s string, chunkSize int) *parsekit.Error {
|
||||||
chunkOfRunes := c.MinMax(1, chunkSize, a.AnyRune)
|
chunkOfRunes := c.MinMax(1, chunkSize, a.AnyRune)
|
||||||
|
|
||||||
parser := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
parser := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
||||||
for p.On(chunkOfRunes).Accept() {
|
for p.Accept(chunkOfRunes) {
|
||||||
*l = append(*l, p.Result().String())
|
*l = append(*l, p.Result().String())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
189
parseapi.go
189
parseapi.go
|
@ -15,6 +15,59 @@ type ParseAPI struct {
|
||||||
stopped bool // a boolean set to true by Stop(), further ParseAPI calls are ignored
|
stopped bool // a boolean set to true by Stop(), further ParseAPI calls are ignored
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Peek checks if the upcoming input data matches the provided TokenHandler.
|
||||||
|
// If it does, then true will be returned, false otherwise. The read cursor
|
||||||
|
// will be kept at the same position, so the next call to Peek() or Accept()
|
||||||
|
// will start from the same cursor position.
|
||||||
|
//
|
||||||
|
// After calling this method, you can retrieve the produced TokenHandlerResult
|
||||||
|
// using the ParseAPI.Result() method.
|
||||||
|
func (p *ParseAPI) Peek(tokenHandler TokenHandler) bool {
|
||||||
|
p.result = nil
|
||||||
|
forkedTokenAPI, ok := p.invokeTokenHandler("Peek", tokenHandler)
|
||||||
|
if ok {
|
||||||
|
p.result = forkedTokenAPI.Result()
|
||||||
|
p.tokenAPI.clearResults()
|
||||||
|
p.tokenAPI.detachChilds()
|
||||||
|
}
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept checks if the upcoming input data matches the provided TokenHandler.
|
||||||
|
// If it does, then true will be returned, false otherwise. The read cursor
|
||||||
|
// will be moved forward to beyond the match that was found.
|
||||||
|
//
|
||||||
|
// After calling this method, you can retrieve the produced TokenHandlerResult
|
||||||
|
// using the ParseAPI.Result() method.
|
||||||
|
func (p *ParseAPI) Accept(tokenHandler TokenHandler) bool {
|
||||||
|
p.result = nil
|
||||||
|
forkedTokenAPI, ok := p.invokeTokenHandler("Accept", tokenHandler)
|
||||||
|
if ok {
|
||||||
|
forkedTokenAPI.Merge()
|
||||||
|
p.result = p.tokenAPI.Result()
|
||||||
|
p.tokenAPI.detachChilds()
|
||||||
|
if p.tokenAPI.flushReader() {
|
||||||
|
p.initLoopCheck()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ParseAPI) invokeTokenHandler(name string, tokenHandler TokenHandler) (*TokenAPI, bool) {
|
||||||
|
p.panicWhenStoppedOrInError()
|
||||||
|
p.checkForLoops()
|
||||||
|
if tokenHandler == nil {
|
||||||
|
callerPanic(2, "parsekit.ParseAPI.%s(): %s() called with nil tokenHandler argument at {caller}", name, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
p.result = nil
|
||||||
|
p.tokenAPI.clearResults()
|
||||||
|
child := p.tokenAPI.Fork()
|
||||||
|
ok := tokenHandler(child)
|
||||||
|
|
||||||
|
return child, ok
|
||||||
|
}
|
||||||
|
|
||||||
// panicWhenStoppedOrInError will panic when the parser has produced an error
|
// panicWhenStoppedOrInError will panic when the parser has produced an error
|
||||||
// or when it has been stopped. It is used from the ParseAPI methods, to
|
// or when it has been stopped. It is used from the ParseAPI methods, to
|
||||||
// prevent further calls to the ParseAPI on these occasions.
|
// prevent further calls to the ParseAPI on these occasions.
|
||||||
|
@ -47,147 +100,23 @@ func (p *ParseAPI) initLoopCheck() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *ParseAPI) checkForLoops() {
|
func (p *ParseAPI) checkForLoops() {
|
||||||
filepos := callerFilepos(2)
|
filepos := callerFilepos(3)
|
||||||
if _, ok := p.loopCheck[filepos]; ok {
|
if _, ok := p.loopCheck[filepos]; ok {
|
||||||
callerPanic(2, "parsekit.ParseAPI: Loop detected in parser at {caller}")
|
callerPanic(3, "parsekit.ParseAPI: Loop detected in parser at {caller}")
|
||||||
}
|
}
|
||||||
p.loopCheck[filepos] = true
|
p.loopCheck[filepos] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// On checks if the input at the current cursor position matches the provided
|
|
||||||
// TokenHandler. On must be chained with another method that tells the parser
|
|
||||||
// what action to perform when a match was found:
|
|
||||||
//
|
|
||||||
// 1) On(...).Skip() - Move read cursor forward, ignoring the match results.
|
|
||||||
//
|
|
||||||
// 2) On(...).Accept() - Move cursor, making results available through Result()
|
|
||||||
//
|
|
||||||
// 3) On(...).Stay() - Do nothing, the cursor stays at the same position.
|
|
||||||
//
|
|
||||||
// So an example chain could look like this:
|
|
||||||
//
|
|
||||||
// p.On(parsekit.A.Blank).Skip()
|
|
||||||
//
|
|
||||||
// The chain as a whole returns a boolean that indicates whether or not at match
|
|
||||||
// was found. When no match was found, false is returned and Skip() and Accept()
|
|
||||||
// will have no effect. Because of this, typical use of an On() chain is as
|
|
||||||
// expression for a conditional statement (if, switch/case, for). E.g.:
|
|
||||||
//
|
|
||||||
// // Skip multiple exclamation marks.
|
|
||||||
// for p.On(parsekit.A.Excl).Skip() { }
|
|
||||||
//
|
|
||||||
// // Fork a route based on the input.
|
|
||||||
// switch {
|
|
||||||
// case p.On(parsekit.A.Excl).Stay()
|
|
||||||
// p.RouteTo(stateHandlerA)
|
|
||||||
// case p.On(parsekit.A.Colon).Stay():
|
|
||||||
// p.RouteTo(stateHandlerB)
|
|
||||||
// default:
|
|
||||||
// p.RouteTo(stateHandlerC)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Echo back a sequence of digits on the input.
|
|
||||||
// if p.On(parsekit.A.Digits).Accept() {
|
|
||||||
// fmt.Println(p.Result().String())
|
|
||||||
// }
|
|
||||||
func (p *ParseAPI) On(tokenHandler TokenHandler) *ParseAPIOnAction {
|
|
||||||
p.panicWhenStoppedOrInError()
|
|
||||||
p.checkForLoops()
|
|
||||||
if tokenHandler == nil {
|
|
||||||
callerPanic(1, "parsekit.ParseAPI.On(): On() called with nil tokenHandler argument at {caller}")
|
|
||||||
}
|
|
||||||
|
|
||||||
p.result = nil
|
|
||||||
p.tokenAPI.clearResults()
|
|
||||||
child := p.tokenAPI.Fork()
|
|
||||||
ok := tokenHandler(child)
|
|
||||||
|
|
||||||
return &ParseAPIOnAction{
|
|
||||||
parseAPI: p,
|
|
||||||
tokenAPI: p.tokenAPI,
|
|
||||||
forkedTokenAPI: child,
|
|
||||||
ok: ok,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseAPIOnAction is a struct that is used for building the On()-method chain.
|
|
||||||
// The On() method will return an initialized struct of this type.
|
|
||||||
type ParseAPIOnAction struct {
|
|
||||||
parseAPI *ParseAPI
|
|
||||||
tokenAPI *TokenAPI
|
|
||||||
forkedTokenAPI *TokenAPI
|
|
||||||
ok bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accept tells the parser to move the read cursor past a match that was
|
|
||||||
// found by a TokenHandler, and to make the TokenHandlerResult from the
|
|
||||||
// TokenAPI available in the ParseAPI through the ParseAPI.Result() method.
|
|
||||||
//
|
|
||||||
// Returns true in case a match was found by On().
|
|
||||||
// When no match was found, then no action is taken, no results are
|
|
||||||
// exposed and false is returned.
|
|
||||||
func (a *ParseAPIOnAction) Accept() bool {
|
|
||||||
if a.ok {
|
|
||||||
a.forkedTokenAPI.Merge()
|
|
||||||
a.parseAPI.result = a.tokenAPI.Result()
|
|
||||||
a.tokenAPI.detachChilds()
|
|
||||||
if a.tokenAPI.flushReader() {
|
|
||||||
a.parseAPI.initLoopCheck()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return a.ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip tells the parser to move the cursor past a match that was found,
|
|
||||||
// without making the results available through the ParseAPI.
|
|
||||||
//
|
|
||||||
// Note that functionally, you could call Accept() just as well, simply
|
|
||||||
// ignoring the results. However, the Skip() call is a bit more efficient
|
|
||||||
// than the Accept() call and (more important if you ask me) the code
|
|
||||||
// expresses more clearly that your intent is to skip the match.
|
|
||||||
//
|
|
||||||
// Returns true in case a match was found by On().
|
|
||||||
// When no match was found, then no action is taken and false is returned.
|
|
||||||
func (a *ParseAPIOnAction) Skip() bool {
|
|
||||||
if a.ok {
|
|
||||||
a.parseAPI.result = nil
|
|
||||||
a.forkedTokenAPI.clearResults()
|
|
||||||
a.tokenAPI.detachChilds()
|
|
||||||
a.forkedTokenAPI.syncCursorTo(a.tokenAPI)
|
|
||||||
if a.tokenAPI.flushReader() {
|
|
||||||
a.parseAPI.initLoopCheck()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return a.ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stay tells the parser to not move the cursor after finding a match.
|
|
||||||
//
|
|
||||||
// A typical use of Stay() is to let one ParseHandler detect the start
|
|
||||||
// of some kind of token, but without moving the read cursor forward.
|
|
||||||
// When a match is found, it hands off control to another ParseHandler
|
|
||||||
// to take care of the actual token parsing.
|
|
||||||
//
|
|
||||||
// Returns true in case a match was found by On(), false otherwise.
|
|
||||||
func (a *ParseAPIOnAction) Stay() bool {
|
|
||||||
if a.ok {
|
|
||||||
a.parseAPI.result = nil
|
|
||||||
a.tokenAPI.clearResults()
|
|
||||||
a.tokenAPI.detachChilds()
|
|
||||||
}
|
|
||||||
return a.ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Result returns a TokenHandlerResult struct, containing results as produced by the
|
// Result returns a TokenHandlerResult struct, containing results as produced by the
|
||||||
// last ParseAPI.On().Accept() call.
|
// last Peek() or Accept() call.
|
||||||
//
|
//
|
||||||
// When Result() is called without first doing a ParsAPI.On().Accept(), then no
|
// When Result() is called without first doing a Peek() or Accept(), then no
|
||||||
// result will be available and the method will panic.
|
// result will be available and the method will panic.
|
||||||
func (p *ParseAPI) Result() *TokenHandlerResult {
|
func (p *ParseAPI) Result() *TokenHandlerResult {
|
||||||
result := p.result
|
result := p.result
|
||||||
if p.result == nil {
|
if p.result == nil {
|
||||||
callerPanic(1, "parsekit.ParseAPI.TokenHandlerResult(): TokenHandlerResult() called "+
|
callerPanic(1, "parsekit.ParseAPI.TokenHandlerResult(): TokenHandlerResult() called "+
|
||||||
"at {caller} without calling ParseAPI.Accept() on beforehand")
|
"at {caller} without calling ParseAPI.Peek() or ParseAPI.Accept() on beforehand")
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
@ -246,7 +175,7 @@ func (p *ParseAPI) Error(format string, args ...interface{}) {
|
||||||
// as the expectation.
|
// as the expectation.
|
||||||
func (p *ParseAPI) ExpectEndOfFile() {
|
func (p *ParseAPI) ExpectEndOfFile() {
|
||||||
p.panicWhenStoppedOrInError()
|
p.panicWhenStoppedOrInError()
|
||||||
if p.On(A.EndOfFile).Stay() {
|
if p.Peek(A.EndOfFile) {
|
||||||
p.Stop()
|
p.Stop()
|
||||||
} else {
|
} else {
|
||||||
p.Expected("end of file")
|
p.Expected("end of file")
|
||||||
|
|
|
@ -14,7 +14,7 @@ func ExampleParser_usingAcceptedRunes() {
|
||||||
matches := []string{}
|
matches := []string{}
|
||||||
|
|
||||||
parser := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
parser := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
||||||
for p.On(a.AnyRune).Accept() {
|
for p.Accept(a.AnyRune) {
|
||||||
matches = append(matches, p.Result().String())
|
matches = append(matches, p.Result().String())
|
||||||
}
|
}
|
||||||
p.ExpectEndOfFile()
|
p.ExpectEndOfFile()
|
||||||
|
@ -31,7 +31,7 @@ func ExampleParser_usingTokens() {
|
||||||
c, a, tok := parsekit.C, parsekit.A, parsekit.T
|
c, a, tok := parsekit.C, parsekit.A, parsekit.T
|
||||||
|
|
||||||
parser := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
parser := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
||||||
if p.On(c.OneOrMore(tok.Rune("RUNE", a.AnyRune))).Accept() {
|
if p.Accept(c.OneOrMore(tok.Rune("RUNE", a.AnyRune))) {
|
||||||
fmt.Printf("Runes accepted: %q\n", p.Result().String())
|
fmt.Printf("Runes accepted: %q\n", p.Result().String())
|
||||||
fmt.Printf("Token values: %s\n", p.Result().Tokens())
|
fmt.Printf("Token values: %s\n", p.Result().Tokens())
|
||||||
}
|
}
|
||||||
|
@ -55,12 +55,12 @@ func ExampleParseAPI_Expected() {
|
||||||
// unexpected input (expected a thing) at start of file
|
// unexpected input (expected a thing) at start of file
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleParseAPIOnAction_Accept() {
|
func ExampleParseAPI_Accept_inIfStatement() {
|
||||||
parser := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
parser := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
||||||
// When a case-insensitive match on "Yowza!" is found by the
|
// When a case-insensitive match on "Yowza!" is found by the
|
||||||
// tokenizer, then Accept() will make the result available
|
// tokenizer, then Accept() will make the result available
|
||||||
// through ParseAPI.Result()
|
// through ParseAPI.Result()
|
||||||
if p.On(parsekit.A.StrNoCase("Yowza!")).Accept() {
|
if p.Accept(parsekit.A.StrNoCase("Yowza!")) {
|
||||||
// Result.String() returns a string containing all
|
// Result.String() returns a string containing all
|
||||||
// accepted runes that were matched against.
|
// accepted runes that were matched against.
|
||||||
fmt.Println(p.Result().String())
|
fmt.Println(p.Result().String())
|
||||||
|
@ -72,14 +72,14 @@ func ExampleParseAPIOnAction_Accept() {
|
||||||
// YOWZA!
|
// YOWZA!
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleParseAPIOnAction_Skip() {
|
func ExampleParseAPI_Accept_inSwitchStatement() {
|
||||||
var result string
|
var result string
|
||||||
parser := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
parser := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
||||||
for loop := true; loop; {
|
for loop := true; loop; {
|
||||||
switch {
|
switch {
|
||||||
case p.On(parsekit.A.Rune('X')).Skip():
|
case p.Accept(parsekit.A.Rune('X')):
|
||||||
// NOOP, skip this rune
|
// NOOP, skip this rune
|
||||||
case p.On(parsekit.A.AnyRune).Accept():
|
case p.Accept(parsekit.A.AnyRune):
|
||||||
result += p.Result().String()
|
result += p.Result().String()
|
||||||
default:
|
default:
|
||||||
loop = false
|
loop = false
|
||||||
|
@ -98,7 +98,7 @@ func ExampleParseAPI_Stop() {
|
||||||
|
|
||||||
parser := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
parser := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
||||||
fmt.Printf("First word: ")
|
fmt.Printf("First word: ")
|
||||||
for p.On(C.Not(A.Space)).Accept() {
|
for p.Accept(C.Not(A.Space)) {
|
||||||
fmt.Printf("%s", p.Result())
|
fmt.Printf("%s", p.Result())
|
||||||
}
|
}
|
||||||
p.Stop()
|
p.Stop()
|
||||||
|
@ -114,7 +114,7 @@ func ExampleParseAPI_Stop_notCalledAndNoInputPending() {
|
||||||
|
|
||||||
parser := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
parser := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
||||||
fmt.Printf("Word: ")
|
fmt.Printf("Word: ")
|
||||||
for p.On(C.Not(A.Space)).Accept() {
|
for p.Accept(C.Not(A.Space)) {
|
||||||
fmt.Printf("%s", p.Result())
|
fmt.Printf("%s", p.Result())
|
||||||
}
|
}
|
||||||
fmt.Printf("\n")
|
fmt.Printf("\n")
|
||||||
|
@ -132,7 +132,7 @@ func ExampleParseAPI_Stop_notCalledButInputPending() {
|
||||||
|
|
||||||
parser := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
parser := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
||||||
fmt.Printf("First word: ")
|
fmt.Printf("First word: ")
|
||||||
for p.On(C.Not(A.Space)).Accept() {
|
for p.Accept(C.Not(A.Space)) {
|
||||||
fmt.Printf("%s", p.Result())
|
fmt.Printf("%s", p.Result())
|
||||||
}
|
}
|
||||||
fmt.Printf("\n")
|
fmt.Printf("\n")
|
||||||
|
@ -145,14 +145,14 @@ func ExampleParseAPI_Stop_notCalledButInputPending() {
|
||||||
// Error: unexpected input (expected end of file) at line 1, column 6
|
// Error: unexpected input (expected end of file) at line 1, column 6
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleParseAPIOnAction_Stay() {
|
func ExampleParseAPI_Peek() {
|
||||||
// Definition of a fantasy serial number format.
|
// Definition of a fantasy serial number format.
|
||||||
C, A := parsekit.C, parsekit.A
|
C, A := parsekit.C, parsekit.A
|
||||||
serialnr := C.Seq(A.Asterisk, A.ASCIIUpper, A.ASCIIUpper, A.Digits)
|
serialnr := C.Seq(A.Asterisk, A.ASCIIUpper, A.ASCIIUpper, A.Digits)
|
||||||
|
|
||||||
// This handler is able to handle serial numbers.
|
// This handler is able to handle serial numbers.
|
||||||
serialnrHandler := func(p *parsekit.ParseAPI) {
|
serialnrHandler := func(p *parsekit.ParseAPI) {
|
||||||
if p.On(serialnr).Accept() {
|
if p.Accept(serialnr) {
|
||||||
fmt.Println(p.Result().String())
|
fmt.Println(p.Result().String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,7 +160,7 @@ func ExampleParseAPIOnAction_Stay() {
|
||||||
// Start could function as a sort of dispatcher, handing over
|
// Start could function as a sort of dispatcher, handing over
|
||||||
// control to the correct ParseHandler function, based on the input.
|
// control to the correct ParseHandler function, based on the input.
|
||||||
start := func(p *parsekit.ParseAPI) {
|
start := func(p *parsekit.ParseAPI) {
|
||||||
if p.On(parsekit.A.Asterisk).Stay() {
|
if p.Peek(parsekit.A.Asterisk) {
|
||||||
p.Handle(serialnrHandler)
|
p.Handle(serialnrHandler)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -195,14 +195,25 @@ func TestGivenNullHandler_HandlePanics(t *testing.T) {
|
||||||
Expect: `parsekit\.ParseAPI\.Handle\(\): Handle\(\) called with nil input ` +
|
Expect: `parsekit\.ParseAPI\.Handle\(\): Handle\(\) called with nil input ` +
|
||||||
`at /.*/parser_test\.go:\d+`})
|
`at /.*/parser_test\.go:\d+`})
|
||||||
}
|
}
|
||||||
func TestGivenNilTokenHandler_OnPanics(t *testing.T) {
|
func TestGivenNilTokenHandler_AcceptPanics(t *testing.T) {
|
||||||
p := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
p := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
||||||
p.On(nil)
|
p.Accept(nil)
|
||||||
})
|
})
|
||||||
parsekit.AssertPanic(t, parsekit.PanicT{
|
parsekit.AssertPanic(t, parsekit.PanicT{
|
||||||
Function: func() { p.Execute("") },
|
Function: func() { p.Execute("") },
|
||||||
Regexp: true,
|
Regexp: true,
|
||||||
Expect: `parsekit\.ParseAPI\.On\(\): On\(\) called with nil ` +
|
Expect: `parsekit\.ParseAPI\.Accept\(\): Accept\(\) called with nil ` +
|
||||||
|
`tokenHandler argument at /.*/parser_test\.go:\d+`})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGivenNilTokenHandler_PeekPanics(t *testing.T) {
|
||||||
|
p := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
||||||
|
p.Peek(nil)
|
||||||
|
})
|
||||||
|
parsekit.AssertPanic(t, parsekit.PanicT{
|
||||||
|
Function: func() { p.Execute("") },
|
||||||
|
Regexp: true,
|
||||||
|
Expect: `parsekit\.ParseAPI\.Peek\(\): Peek\(\) called with nil ` +
|
||||||
`tokenHandler argument at /.*/parser_test\.go:\d+`})
|
`tokenHandler argument at /.*/parser_test\.go:\d+`})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,7 +247,7 @@ func TestGivenParserWithErrorSet_HandlePanics(t *testing.T) {
|
||||||
`at /.*/parser_test\.go:\d+: no calls allowed after ParseAPI\.Error\(\)`})
|
`at /.*/parser_test\.go:\d+: no calls allowed after ParseAPI\.Error\(\)`})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGivenParserWithoutCallToAccept_ResultPanics(t *testing.T) {
|
func TestGivenParserWithoutCallToPeekOrAccept_ResultPanics(t *testing.T) {
|
||||||
p := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
p := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
||||||
p.Result()
|
p.Result()
|
||||||
})
|
})
|
||||||
|
@ -244,7 +255,7 @@ func TestGivenParserWithoutCallToAccept_ResultPanics(t *testing.T) {
|
||||||
Function: func() { p.Execute("") },
|
Function: func() { p.Execute("") },
|
||||||
Regexp: true,
|
Regexp: true,
|
||||||
Expect: `parsekit\.ParseAPI\.TokenHandlerResult\(\): TokenHandlerResult\(\) called at ` +
|
Expect: `parsekit\.ParseAPI\.TokenHandlerResult\(\): TokenHandlerResult\(\) called at ` +
|
||||||
`/.*/parser_test.go:\d+ without calling ParseAPI.Accept\(\) on beforehand`})
|
`/.*/parser_test.go:\d+ without calling ParseAPI.Peek\(\) or ParseAPI.Accept\(\) on beforehand`})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGivenParserWhichIsNotStopped_WithNoMoreInput_FallbackExpectEndOfFileKicksIn(t *testing.T) {
|
func TestGivenParserWhichIsNotStopped_WithNoMoreInput_FallbackExpectEndOfFileKicksIn(t *testing.T) {
|
||||||
|
@ -264,12 +275,12 @@ type parserWithLoop struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *parserWithLoop) first(p *parsekit.ParseAPI) {
|
func (l *parserWithLoop) first(p *parsekit.ParseAPI) {
|
||||||
p.On(parsekit.A.ASCII).Accept()
|
p.Accept(parsekit.A.ASCII)
|
||||||
p.Handle(l.second)
|
p.Handle(l.second)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *parserWithLoop) second(p *parsekit.ParseAPI) {
|
func (l *parserWithLoop) second(p *parsekit.ParseAPI) {
|
||||||
p.On(parsekit.A.ASCII).Accept()
|
p.Accept(parsekit.A.ASCII)
|
||||||
p.Handle(l.third)
|
p.Handle(l.third)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,7 +289,7 @@ func (l *parserWithLoop) third(p *parsekit.ParseAPI) {
|
||||||
p.Error("Loop not detected by parsekit")
|
p.Error("Loop not detected by parsekit")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.On(parsekit.A.ASCII).Accept()
|
p.Accept(parsekit.A.ASCII)
|
||||||
p.Handle(l.first)
|
p.Handle(l.first)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,7 +318,7 @@ func TestGivenLoopingParserDefinition_ParserPanics(t *testing.T) {
|
||||||
func TestGivenLoopingParserDefinition2_ParserPanics(t *testing.T) {
|
func TestGivenLoopingParserDefinition2_ParserPanics(t *testing.T) {
|
||||||
var c, a = parsekit.C, parsekit.A
|
var c, a = parsekit.C, parsekit.A
|
||||||
parser := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
parser := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
||||||
for p.On(c.Max(5, a.AnyRune)).Accept() {
|
for p.Accept(c.Max(5, a.AnyRune)) {
|
||||||
}
|
}
|
||||||
p.Stop()
|
p.Stop()
|
||||||
})
|
})
|
||||||
|
|
|
@ -390,7 +390,7 @@ func TestSequenceOfRunes(t *testing.T) {
|
||||||
input := "#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
|
input := "#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
|
||||||
output := ""
|
output := ""
|
||||||
parser := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
parser := parsekit.NewParser(func(p *parsekit.ParseAPI) {
|
||||||
if p.On(sequence).Accept() {
|
if p.Accept(sequence) {
|
||||||
output = p.Result().String()
|
output = p.Result().String()
|
||||||
p.Stop()
|
p.Stop()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -26,7 +26,7 @@ type TokenHandler func(t *TokenAPI) bool
|
||||||
func NewTokenizer(tokenHandler TokenHandler) *Tokenizer {
|
func NewTokenizer(tokenHandler TokenHandler) *Tokenizer {
|
||||||
tokenizer := &Tokenizer{}
|
tokenizer := &Tokenizer{}
|
||||||
tokenizer.parser = NewParser(func(p *ParseAPI) {
|
tokenizer.parser = NewParser(func(p *ParseAPI) {
|
||||||
if p.On(tokenHandler).Accept() {
|
if p.Accept(tokenHandler) {
|
||||||
tokenizer.result = p.Result()
|
tokenizer.result = p.Result()
|
||||||
p.Stop()
|
p.Stop()
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue