Add a regex parser

This commit is contained in:
Adam Scarr 2017-08-09 21:41:57 +10:00
parent d4b58316bc
commit dc3c5a8325
5 changed files with 51 additions and 8 deletions

View File

@ -4,7 +4,7 @@ import (
"bytes" "bytes"
) )
// Seq matches all of the given parsers in order and returns their nodes as .Child[n] // Seq matches all of the given parsers in order and returns their result as .Child[n]
func Seq(parsers ...Parserish) Parser { func Seq(parsers ...Parserish) Parser {
parserfied := ParsifyAll(parsers...) parserfied := ParsifyAll(parsers...)
@ -35,7 +35,7 @@ func NoAutoWS(parser Parserish) Parser {
} }
} }
// Any matches the first successful parser and returns its node // Any matches the first successful parser and returns its result
func Any(parsers ...Parserish) Parser { func Any(parsers ...Parserish) Parser {
parserfied := ParsifyAll(parsers...) parserfied := ParsifyAll(parsers...)

View File

@ -17,7 +17,7 @@ type Tag struct {
var ( var (
tag Parser tag Parser
identifier = NoAutoWS(Merge(Seq(WS(), Chars("a-zA-Z", 1), Chars("a-zA-Z0-9", 0)))) identifier = Regex("[a-zA-Z][a-zA-Z0-9]*")
text = Map(NotChars("<>"), func(n Result) Result { text = Map(NotChars("<>"), func(n Result) Result {
return Result{Result: n.Token} return Result{Result: n.Token}
}) })

View File

@ -3,6 +3,7 @@ package goparsify
import ( import (
"errors" "errors"
"fmt" "fmt"
"regexp"
"strings" "strings"
"unicode/utf8" "unicode/utf8"
) )
@ -102,6 +103,20 @@ func Run(parser Parserish, input string) (result interface{}, err error) {
return ret.Result, nil return ret.Result, nil
} }
// Regex returns a match if the regex successfully matches
func Regex(pattern string) Parser {
re := regexp.MustCompile("^" + pattern)
return NewParser(pattern, func(ps *State) Result {
ps.AutoWS()
if match := re.FindString(ps.Get()); match != "" {
ps.Advance(len(match))
return Result{Token: match}
}
ps.ErrorHere(pattern)
return Result{}
})
}
// Exact will fully match the exact string supplied, or error. The match will be stored in .Token // Exact will fully match the exact string supplied, or error. The match will be stored in .Token
func Exact(match string) Parser { func Exact(match string) Parser {
if len(match) == 1 { if len(match) == 1 {

View File

@ -133,6 +133,34 @@ func TestChars(t *testing.T) {
}) })
} }
func TestRegex(t *testing.T) {
t.Run("full match", func(t *testing.T) {
node, ps := runParser("hello", Regex("[a-z]*"))
require.Equal(t, "hello", node.Token)
require.Equal(t, "", ps.Get())
require.False(t, ps.Errored())
})
t.Run("limited match", func(t *testing.T) {
node, ps := runParser("hello world", Regex("[a-z]*"))
require.Equal(t, "hello", node.Token)
require.Equal(t, " world", ps.Get())
require.False(t, ps.Errored())
})
t.Run("no match", func(t *testing.T) {
_, ps := runParser("1234", Regex("[a-z]*"))
require.Equal(t, "offset 0: expected [a-z]*", ps.Error.Error())
require.Equal(t, 0, ps.Pos)
})
t.Run("eof", func(t *testing.T) {
_, ps := runParser("", Regex("[a-z]*"))
require.Equal(t, "offset 0: expected [a-z]*", ps.Error.Error())
require.Equal(t, 0, ps.Pos)
})
}
func TestParseString(t *testing.T) { func TestParseString(t *testing.T) {
Y := Map("hello", func(n Result) Result { return Result{Result: n.Token} }) Y := Map("hello", func(n Result) Result { return Result{Result: n.Token} })

View File

@ -63,12 +63,12 @@ func TestNumbers(t *testing.T) {
Then define a parser for numbers Then define a parser for numbers
```go ```go
var number = Map(NumberLit(), func(n Node) Node { var number = Map(NumberLit(), func(n Result) Result {
switch i := n.Result.(type) { switch i := n.Result.(type) {
case int64: case int64:
return Node{Result: float64(i)} return Result{Result: float64(i)}
case float64: case float64:
return Node{Result: i} return Result{Result: i}
default: default:
panic(fmt.Errorf("unknown value %#v", i)) panic(fmt.Errorf("unknown value %#v", i))
} }
@ -101,7 +101,7 @@ func TestAddition(t *testing.T) {
var sumOp = Chars("+-", 1, 1) var sumOp = Chars("+-", 1, 1)
sum = Map(Seq(number, Some(And(sumOp, number))), func(n Node) Node { sum = Map(Seq(number, Some(And(sumOp, number))), func(n Result) Result {
i := n.Child[0].Result.(float64) i := n.Child[0].Result.(float64)
for _, op := range n.Child[1].Child { for _, op := range n.Child[1].Child {
@ -113,7 +113,7 @@ sum = Map(Seq(number, Some(And(sumOp, number))), func(n Node) Node {
} }
} }
return Node{Result: i} return Result{Result: i}
}) })
// and update Calc to point to the new root parser -> `result, err := ParseString(sum, input)` // and update Calc to point to the new root parser -> `result, err := ParseString(sum, input)`