Small code cleanup things, mainly backing up the changes.

This commit is contained in:
Maurice Makaay 2019-06-18 15:46:09 +00:00
parent 99654c2f9e
commit 2293627232
7 changed files with 87 additions and 51 deletions

View File

@ -71,7 +71,7 @@ type simpleCalculator struct {
} }
// A definition of an int64, which conveniently drops surrounding blanks. // A definition of an int64, which conveniently drops surrounding blanks.
var dropBlank = tokenize.M.Drop(tokenize.C.Opt(tokenize.A.Blanks)) var dropBlank = tokenize.M.Drop(tokenize.C.Optional(tokenize.A.Blanks))
var bareInteger = tokenize.C.Seq(dropBlank, tokenize.A.Integer, dropBlank) var bareInteger = tokenize.C.Seq(dropBlank, tokenize.A.Integer, dropBlank)
var int64Token = tokenize.T.Int64(nil, bareInteger) var int64Token = tokenize.T.Int64(nil, bareInteger)

View File

@ -54,7 +54,7 @@ func createHelloTokenizer() tokenize.Func {
// that does all the work. The 'greeting' Handler matches the whole input and // that does all the work. The 'greeting' Handler matches the whole input and
// drops all but the name from it. // drops all but the name from it.
hello := a.StrNoCase("hello") hello := a.StrNoCase("hello")
comma := c.Seq(c.Opt(a.Blanks), a.Comma, c.Opt(a.Blanks)) comma := c.Seq(c.Optional(a.Blanks), a.Comma, c.Optional(a.Blanks))
separator := c.Any(comma, a.Blanks) separator := c.Any(comma, a.Blanks)
name := c.OneOrMore(c.Not(a.Excl)) name := c.OneOrMore(c.Not(a.Excl))
greeting := m.Drop(hello). greeting := m.Drop(hello).

View File

@ -85,7 +85,7 @@ func (h *helloparser2) start(p *parse.API) {
p.Error("the greeting is not being friendly") p.Error("the greeting is not being friendly")
return return
} }
if !p.Accept(c.Seq(c.Opt(a.Blanks), a.Comma, c.Opt(a.Blanks))) { if !p.Accept(c.Seq(c.Optional(a.Blanks), a.Comma, c.Optional(a.Blanks))) {
p.Error("the greeting is not properly separated") p.Error("the greeting is not properly separated")
return return
} }

View File

@ -41,9 +41,9 @@ func (handler Handler) SeparatedBy(separatorHandler Handler) Handler {
} }
// Optional is syntactic sugar that allows you to write a construction like // Optional is syntactic sugar that allows you to write a construction like
// MatchOpt(handler) as handler.Optional(). // MatchOptional(handler) as handler.Optional().
func (handler Handler) Optional() Handler { func (handler Handler) Optional() Handler {
return MatchOpt(handler) return MatchOptional(handler)
} }
// Except is syntactic sugar that allows you to write a construction like // Except is syntactic sugar that allows you to write a construction like

View File

@ -31,7 +31,7 @@ func ExampleHandler_Times() {
func ExampleHandler_Then() { func ExampleHandler_Then() {
c, a := tokenize.C, tokenize.A c, a := tokenize.C, tokenize.A
phoneNumber := a.Rune('0').Then(c.Rep(9, a.Digit)) phoneNumber := a.Rune('0').Then(c.Repeated(9, a.Digit))
fmt.Println(phoneNumber.Match("0208888888")) fmt.Println(phoneNumber.Match("0208888888"))
// Output: // Output:
@ -40,7 +40,7 @@ func ExampleHandler_Then() {
func ExampleHandler_Or() { func ExampleHandler_Or() {
c, a := tokenize.C, tokenize.A c, a := tokenize.C, tokenize.A
phoneNumber := c.Seq(a.Str("00").Or(a.Plus), a.Str("31"), a.DigitNotZero, c.Rep(8, a.Digit)) phoneNumber := c.Seq(a.Str("00").Or(a.Plus), a.Str("31"), a.DigitNotZero, c.Repeated(8, a.Digit))
fmt.Println(phoneNumber.Match("+31209876543")) fmt.Println(phoneNumber.Match("+31209876543"))
fmt.Println(phoneNumber.Match("0031209876543")) fmt.Println(phoneNumber.Match("0031209876543"))

View File

@ -28,29 +28,33 @@ import (
var C = struct { var C = struct {
Any func(...Handler) Handler Any func(...Handler) Handler
Not func(Handler) Handler Not func(Handler) Handler
Opt func(Handler) Handler
Seq func(...Handler) Handler Seq func(...Handler) Handler
Rep func(times int, handler Handler) Handler
Min func(min int, handler Handler) Handler Min func(min int, handler Handler) Handler
Max func(max int, handler Handler) Handler Max func(max int, handler Handler) Handler
Repeated func(times int, handler Handler) Handler
Optional func(Handler) Handler
ZeroOrMore func(Handler) Handler ZeroOrMore func(Handler) Handler
OneOrMore func(Handler) Handler OneOrMore func(Handler) Handler
MinMax func(min int, max int, handler Handler) Handler MinMax func(min int, max int, handler Handler) Handler
Separated func(separated Handler, separator Handler) Handler Separated func(separated Handler, separator Handler) Handler
Except func(except Handler, handler Handler) Handler Except func(except Handler, handler Handler) Handler
FollowedBy func(lookAhead Handler, handler Handler) Handler
WhileFollowedBy func(lookahead Handler, handler Handler) Handler
}{ }{
Opt: MatchOpt,
Any: MatchAny, Any: MatchAny,
Not: MatchNot, Not: MatchNot,
Seq: MatchSeq, Seq: MatchSeq,
Rep: MatchRep,
Min: MatchMin, Min: MatchMin,
Max: MatchMax, Max: MatchMax,
Repeated: MatchRep,
Optional: MatchOptional,
ZeroOrMore: MatchZeroOrMore, ZeroOrMore: MatchZeroOrMore,
OneOrMore: MatchOneOrMore, OneOrMore: MatchOneOrMore,
MinMax: MatchMinMax, MinMax: MatchMinMax,
Separated: MatchSeparated, Separated: MatchSeparated,
Except: MatchExcept, Except: MatchExcept,
FollowedBy: MatchFollowedBy,
WhileFollowedBy: MatchWhileFollowedBy,
} }
// A provides convenient access to a range of atoms or functions to build atoms. // A provides convenient access to a range of atoms or functions to build atoms.
@ -199,6 +203,7 @@ var A = struct {
Pipe: MatchRune('|'), Pipe: MatchRune('|'),
CurlyClose: MatchRune('}'), CurlyClose: MatchRune('}'),
Tilde: MatchRune('~'), Tilde: MatchRune('~'),
Newline: MatchNewline(),
Blank: MatchBlank(), Blank: MatchBlank(),
Blanks: MatchBlanks(), Blanks: MatchBlanks(),
Whitespace: MatchWhitespace(), Whitespace: MatchWhitespace(),
@ -339,6 +344,12 @@ func MatchRuneRange(start rune, end rune) Handler {
return MatchRuneByCallback(func(r rune) bool { return r >= start && r <= end }) return MatchRuneByCallback(func(r rune) bool { return r >= start && r <= end })
} }
// MatchNewline creates a handler that matches a newline, which is either
// a DOS-style newline (CRLF, \r\n) or a UNIX-style newline (just a LF, \n).
func MatchNewline() Handler {
return MatchAny(MatchStr("\r\n"), MatchRune('\n'))
}
// MatchBlank creates a Handler that matches one rune from the input // MatchBlank creates a Handler that matches one rune from the input
// against blank characters, meaning tabs and spaces. // against blank characters, meaning tabs and spaces.
// //
@ -382,7 +393,7 @@ func MatchRuneByCallback(callback func(rune) bool) Handler {
// MatchEndOfLine creates a Handler that matches a newline ("\r\n" or "\n") or EOF. // MatchEndOfLine creates a Handler that matches a newline ("\r\n" or "\n") or EOF.
func MatchEndOfLine() Handler { func MatchEndOfLine() Handler {
return MatchAny(MatchStr("\r\n"), MatchRune('\n'), MatchEndOfFile()) return MatchAny(MatchNewline(), MatchEndOfFile())
} }
// MatchStr creates a Handler that matches the input against the provided string. // MatchStr creates a Handler that matches the input against the provided string.
@ -406,11 +417,11 @@ func MatchStrNoCase(expected string) Handler {
return MatchSeq(handlers...) return MatchSeq(handlers...)
} }
// MatchOpt creates a Handler that makes the provided Handler optional. // MatchOptional creates a Handler that makes the provided Handler optional.
// When the provided Handler applies, then its output is used, otherwise // When the provided Handler applies, then its output is used, otherwise
// no output is generated but still a successful match is reported (but the // no output is generated but still a successful match is reported (but the
// result will be empty). // result will be empty).
func MatchOpt(handler Handler) Handler { func MatchOptional(handler Handler) Handler {
return MatchMinMax(0, 1, handler) return MatchMinMax(0, 1, handler)
} }
@ -580,13 +591,38 @@ func MatchExcept(handler Handler, except Handler) Handler {
} }
} }
// TODO keep this?
func MatchFollowedBy(lookAhead Handler, handler Handler) Handler {
return func(t *API) bool {
child := t.Fork()
if handler(child) && lookAhead(child.Fork()) {
child.Merge()
return true
}
return false
}
}
// TODO keep this? Make some useful tests first.
func MatchWhileFollowedBy(lookAhead Handler, handler Handler) Handler {
followedBy := MatchFollowedBy(lookAhead, handler)
return func(t *API) bool {
matches := 0
for followedBy(t) {
fmt.Printf("Matches so far: %q\n", t.Result().String())
matches++
}
return matches > 0
}
}
// MatchSigned creates a Handler that checks if the provided Handler is // MatchSigned creates a Handler that checks if the provided Handler is
// prefixed by an optional '+' or '-' sign. This can be used to turn numeric // prefixed by an optional '+' or '-' sign. This can be used to turn numeric
// atoms into a signed version, e.g. // atoms into a signed version, e.g.
// //
// C.Signed(A.Integer) // C.Signed(A.Integer)
func MatchSigned(handler Handler) Handler { func MatchSigned(handler Handler) Handler {
sign := MatchOpt(MatchAny(MatchRune('+'), MatchRune('-'))) sign := MatchOptional(MatchAny(MatchRune('+'), MatchRune('-')))
return MatchSeq(sign, handler) return MatchSeq(sign, handler)
} }
@ -695,7 +731,7 @@ func MatchInteger() Handler {
// Handler will report a match, so both "123" and "123.123" will match. // Handler will report a match, so both "123" and "123.123" will match.
func MatchFloat() Handler { func MatchFloat() Handler {
digits := MatchDigits() digits := MatchDigits()
return MatchSeq(digits, MatchOpt(MatchSeq(MatchRune('.'), digits))) return MatchSeq(digits, MatchOptional(MatchSeq(MatchRune('.'), digits)))
} }
// MatchBoolean creates a Handler function that checks if a boolean // MatchBoolean creates a Handler function that checks if a boolean
@ -950,7 +986,7 @@ func MatchIPv6Net(normalize bool) Handler {
// even though we would have dropped the output anyway. So if you would like // even though we would have dropped the output anyway. So if you would like
// to drop optional blanks (spaces and tabs), then use something like: // to drop optional blanks (spaces and tabs), then use something like:
// //
// M.Drop(C.Opt(A.Blanks)) // M.Drop(C.Optional(A.Blanks))
// //
// instead of: // instead of:
// //

View File

@ -18,8 +18,8 @@ func TestCombinators(t *testing.T) {
{"abc", c.Any(a.Rune('a'), a.Rune('b')), true, "a"}, {"abc", c.Any(a.Rune('a'), a.Rune('b')), true, "a"},
{"bcd", c.Any(a.Rune('a'), a.Rune('b')), true, "b"}, {"bcd", c.Any(a.Rune('a'), a.Rune('b')), true, "b"},
{"cde", c.Any(a.Rune('a'), a.Rune('b')), false, ""}, {"cde", c.Any(a.Rune('a'), a.Rune('b')), false, ""},
{"ababc", c.Rep(4, a.Runes('a', 'b')), true, "abab"}, {"ababc", c.Repeated(4, a.Runes('a', 'b')), true, "abab"},
{"ababc", c.Rep(5, a.Runes('a', 'b')), false, ""}, {"ababc", c.Repeated(5, a.Runes('a', 'b')), false, ""},
{"", c.Min(0, a.Rune('a')), true, ""}, {"", c.Min(0, a.Rune('a')), true, ""},
{"a", c.Min(0, a.Rune('a')), true, "a"}, {"a", c.Min(0, a.Rune('a')), true, "a"},
{"aaaaa", c.Min(4, a.Rune('a')), true, "aaaaa"}, {"aaaaa", c.Min(4, a.Rune('a')), true, "aaaaa"},
@ -55,12 +55,12 @@ func TestCombinators(t *testing.T) {
{"eeeeeX", c.ZeroOrMore(a.Rune('e')), true, "eeeee"}, {"eeeeeX", c.ZeroOrMore(a.Rune('e')), true, "eeeee"},
{"Hello, world!X", c.Seq(a.Str("Hello"), a.Comma, a.Space, a.Str("world"), a.Excl), true, "Hello, world!"}, {"Hello, world!X", c.Seq(a.Str("Hello"), a.Comma, a.Space, a.Str("world"), a.Excl), true, "Hello, world!"},
{"101010123", c.OneOrMore(c.Seq(a.Rune('1'), a.Rune('0'))), true, "101010"}, {"101010123", c.OneOrMore(c.Seq(a.Rune('1'), a.Rune('0'))), true, "101010"},
{"", c.Opt(c.OneOrMore(a.Rune('f'))), true, ""}, {"", c.Optional(c.OneOrMore(a.Rune('f'))), true, ""},
{"ghijkl", c.Opt(a.Rune('h')), true, ""}, {"ghijkl", c.Optional(a.Rune('h')), true, ""},
{"ghijkl", c.Opt(a.Rune('g')), true, "g"}, {"ghijkl", c.Optional(a.Rune('g')), true, "g"},
{"fffffX", c.Opt(c.OneOrMore(a.Rune('f'))), true, "fffff"}, {"fffffX", c.Optional(c.OneOrMore(a.Rune('f'))), true, "fffff"},
{"1,2,3,b,c", c.Separated(a.Comma, a.Digit), true, "1,2,3"}, {"1,2,3,b,c", c.Separated(a.Comma, a.Digit), true, "1,2,3"},
{`\x9a\x01\xF0\xfCAndSomeMoreStuff`, c.OneOrMore(c.Seq(a.Backslash, a.Rune('x'), c.Rep(2, a.HexDigit))), true, `\x9a\x01\xF0\xfC`}, {`\x9a\x01\xF0\xfCAndSomeMoreStuff`, c.OneOrMore(c.Seq(a.Backslash, a.Rune('x'), c.Repeated(2, a.HexDigit))), true, `\x9a\x01\xF0\xfC`},
{" ", m.Trim(c.OneOrMore(a.AnyRune), " "), true, ""}, {" ", m.Trim(c.OneOrMore(a.AnyRune), " "), true, ""},
{" ", m.TrimLeft(c.OneOrMore(a.AnyRune), " "), true, ""}, {" ", m.TrimLeft(c.OneOrMore(a.AnyRune), " "), true, ""},
{" ", m.TrimRight(c.OneOrMore(a.AnyRune), " "), true, ""}, {" ", m.TrimRight(c.OneOrMore(a.AnyRune), " "), true, ""},
@ -382,23 +382,23 @@ func TestTokenMakers(t *testing.T) {
func TestCombination(t *testing.T) { func TestCombination(t *testing.T) {
var c, a, m = tokenize.C, tokenize.A, tokenize.M var c, a, m = tokenize.C, tokenize.A, tokenize.M
demonic := c.Seq( demonic := c.Seq(
c.Opt(a.SquareOpen), c.Optional(a.SquareOpen),
m.Trim( m.Trim(
c.Seq( c.Seq(
c.Opt(a.Blanks), c.Optional(a.Blanks),
c.Rep(3, a.AngleClose), c.Repeated(3, a.AngleClose),
m.ByCallback(c.OneOrMore(a.StrNoCase("hello")), func(s string) string { m.ByCallback(c.OneOrMore(a.StrNoCase("hello")), func(s string) string {
return fmt.Sprintf("%d", len(s)) return fmt.Sprintf("%d", len(s))
}), }),
m.Replace(c.Separated(a.Comma, c.Opt(a.Blanks)), ", "), m.Replace(c.Separated(a.Comma, c.Optional(a.Blanks)), ", "),
m.ToUpper(c.Min(1, a.ASCIILower)), m.ToUpper(c.Min(1, a.ASCIILower)),
m.Drop(a.Excl), m.Drop(a.Excl),
c.Rep(3, a.AngleOpen), c.Repeated(3, a.AngleOpen),
c.Opt(a.Blanks), c.Optional(a.Blanks),
), ),
" \t", " \t",
), ),
c.Opt(a.SquareClose), c.Optional(a.SquareClose),
) )
AssertHandlers(t, []HandlerT{ AssertHandlers(t, []HandlerT{