Add a regex parser
This commit is contained in:
parent
d4b58316bc
commit
dc3c5a8325
@ -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...)
|
||||||
|
|
||||||
|
@ -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}
|
||||||
})
|
})
|
||||||
|
15
parser.go
15
parser.go
@ -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 {
|
||||||
|
@ -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} })
|
||||||
|
|
||||||
|
10
readme.md
10
readme.md
@ -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)`
|
||||||
|
Loading…
Reference in New Issue
Block a user