// GetInputAndPosition gets the input stream and current position from a parser state. func GetInputAndPosition(s PState) PState { return s.clone(func(e *PState) { e.value = InputAndPosition{inp: u8.Desur(s.input), pos: s.pos} e.ok = true e.empty = true e.error = "" }) }
// GetInput gets the input stream from a parser state. func GetInput(s PState) PState { return s.clone(func(e *PState) { e.value = u8.Desur(s.input) e.ok = true e.empty = true e.error = "" }) }
// Tokens is like Token but accepts more than one Text. It will try each such choice in turn. func Tokens(ts ...u8.Text) Parser { switch len(ts) { case 0: return Fail(u8.Desur(errNoAlts)) case 1: return Token(ts[0]) default: return Alt(Try(Token(ts[0])), Tokens(ts[1:]...)) } }
// Flatten applies one or more parsers; flattens the result and // converts it to text. Same functionality as <+> in Clojure's kern. func Flatten(ps ...Parser) Parser { var f func(c interface{}) interface{} f = func(c interface{}) interface{} { s := "" for _, n := range c.([]interface{}) { if _, isText := n.(u8.Text); !isText { n = f(n) } s = s + u8.Surr(n.(u8.Text)) } return u8.Desur(s) } switch len(ps) { case 0: return Fail(u8.Desur(errNoParser)) case 1: return Apply(f, ps[0]) default: return Apply(f, Collect(ps...)) } }
// Skip applies one or more parsers and skips the result. That is, it returns // a parser state record with a value of nil. func Skip(ps ...Parser) Parser { switch len(ps) { case 0: return Fail(u8.Desur(errNoParser)) case 1: return SeqRight(ps[0], Return(nil)) case 2: return Bind(ps[0], func(_ interface{}) Parser { return Skip(ps[1]) }) default: return SeqRight(ps[0], Skip(ps[1:]...)) } }
// SeqLeft parses each parser of ps in sequence; it keeps the first result and // skips the rest. // Same functionality as << in Clojure's kern. func SeqLeft(ps ...Parser) Parser { switch len(ps) { case 0: return Fail(u8.Desur(errNoParser)) case 1: return ps[0] case 2: return Bind(ps[0], func(x interface{}) Parser { return SeqRight(ps[1], Return(x)) }) default: return SeqLeft(ps[0], SeqLeft(ps[1:]...)) } }
// Regexp succeeds if the next Character/s mathes the given regex string, in which case it // advances the position of the input stream. It may fail on an unexpected end of input. func Regexp(tok string) Parser { return func(st PState) (so PState) { if log["Regexp"] || (len(st.log) > 0 && st.log[len(st.log)-1] && loggingEnabled) { defer logFnBA("Regexp", &st, &so) } if len(tok) == 0 { so = st.clone(func(e *PState) { e.value = nil e.ok = false e.empty = true e.error = errEmptyStr }) return } else if st.input == "" { so = st.clone(func(e *PState) { e.value = nil e.ok = false e.empty = true e.error = errUnexpEof }) return } else { r := regexp.MustCompile("^(?:" + tok + ")") //QUERY: change to \A ? loc := r.FindStringIndex(st.input) if loc != nil && loc[0] == 0 { so = st.clone(func(e *PState) { e.input = st.input[loc[1]:] e.pos = st.pos + uint64(loc[1]) e.value = u8.Desur(st.input[0:loc[1]]) e.ok = true e.empty = false e.error = "" }) return } else { so = st.clone(func(e *PState) { e.value = nil e.ok = false e.empty = true e.error = makeUnexpInp(st.input[:1]) }) return } } } }
//================================================================================ func TestInputAndPosition(t *testing.T) { ts.LogAsserts("InputAndPosition", t, func(tt *ts.T) { letter := Regexp(`\pL`) p1 := Flatten(letter, letter, letter) tt.AssertEqual(parseStr(SeqRight(SetInput(u8.Desur("Hi!")), GetInput), "abcdefg"), PState{input: "Hi!", pos: 0, value: u8.Desur("Hi!"), empty: true, ok: true}) tt.AssertEqual(parseStr(SeqRight(letter, GetInput), "abcdefg"), PState{input: "bcdefg", pos: 1, value: u8.Desur("bcdefg"), ok: true}) tt.AssertEqual(parseStr(SeqRight(SetPosition(100), GetPosition), "abcdefg"), PState{input: "abcdefg", pos: 100, value: uint64(100), empty: true, ok: true}) tt.AssertEqual(parseStr(SeqRight(SetInputAndPosition(InputAndPosition{u8.Desur("Hi there!"), 5}), GetInputAndPosition), "abcdefg"), PState{input: "Hi there!", pos: 5, value: InputAndPosition{u8.Desur("Hi there!"), 5}, empty: true, ok: true}) tt.AssertEqual(parseStr(Collect(p1, SeqRight(SetInput(u8.Desur("XYZ")), p1)), "ABCDEFG"), PState{input: "", pos: 6, value: arrText("ABC", "XYZ"), ok: true}) //TODO: what should pos be? tt.AssertEqual(parseStr(Collect(p1, SeqRight(SetInput(u8.Desur("XYZ")), p1)), "ABC"), PState{input: "", pos: 6, value: arrText("ABC", "XYZ"), ok: true}) //TODO: ditto tt.AssertEqual(parseStr(Collect(p1), "ABC"), PState{input: "", pos: 3, value: arrText("ABC"), ok: true}) p2 := SeqRight(Skip(SetInput(u8.Text("XY0")), SetPosition(0)), p1) tt.AssertEqual(parseStr(Collect(p1, p2), "ABC"), PState{input: "0", pos: 2, value: nil, error: makeUnexpInp("0")}) p3 := SeqRight(Skip(SetInput(u8.Text("WXYZ")), SetPosition(0)), GetPosition) tt.AssertEqual(parseStr(SeqRight(p1, p3), "ABC"), PState{input: "WXYZ", pos: 0, value: uint64(0), ok: true}) p4 := SeqRight(SetInput(u8.Text("XYZ")), GetInput) tt.AssertEqual(parseStr(SeqRight(p1, p4), "ABC"), PState{input: "XYZ", pos: 3, value: u8.Text("XYZ"), ok: true}) tt.AssertEqual(parseStr(Collect(p1, SeqRight(SetInputAndPosition(InputAndPosition{u8.Desur("XYZ"), 2}), p1)), "ABCDEFG"), //posn changed from 3 to 2... PState{input: "", pos: 5, value: arrText("ABC", "XYZ"), ok: true}) //...so posn is 5 instead of 6 }) }
// Alt tries each parser in ps; if any fails without consuming any input, it tries the // next one. It will stop and succeed if a parser succeeds; it will stop and fail // if a parser fails consuming input; or it will try the next one if a parser fails // without consuming input. // Same functionality as <|> in Clojure's kern. func Alt(ps ...Parser) Parser { switch len(ps) { case 0: return Fail(u8.Desur(errNoParser)) case 1: return ps[0] case 2: p := ps[0] q := ps[1] return func(st PState) (so PState) { if log["Alt"] || (len(st.log) > 0 && st.log[len(st.log)-1] && loggingEnabled) { logFnIn("Alt", &st) defer logFnDe("Alt", &so) } s2 := p(st) if !s2.ok && s2.empty { s3 := q(st) if s3.ok { so = s3 return } else { so = s3.clone(func(e *PState) { //QUERY: what about e.ok , etc? e.error = errNoAlts }) return } } else { so = s2 return } } default: return Alt(ps[0], Alt(ps[1:]...)) } }
// NotFollowedBy succeeds only if p fails; consumes no input. func NotFollowedBy(p Parser) Parser { return Try(Alt(Bind(Try(p), func(x interface{}) Parser { return Fail(u8.Desur(errParsFail)) }), Return(nil))) }