package parsekit_test import ( "testing" p "github.com/mmakaay/go-parsekit" ) var c = p.C const TestItem p.ItemType = 1 func newParser(input string, Matcher p.Matcher) *p.P { stateFn := func(p *p.P) { p.Expects("MATCH") if p.On(Matcher).Accept().End() { p.EmitLiteral(TestItem) p.RouteRepeat() } } return p.New(input, stateFn) } func TestMatchAnyRune(t *testing.T) { p := newParser("o", c.AnyRune()) r, err, ok := p.Next() if !ok { t.Fatalf("Parsing failed: %s", err) } if r.Type != TestItem { t.Error("Parser item type not expected TestTitem") } if r.Value != "o" { t.Errorf("Parser item value is %q instead of expected \"o\"", r.Value) } } func TestMatchAnyRune_AtEndOfFile(t *testing.T) { p := newParser("", c.AnyRune()) _, err, ok := p.Next() if ok { t.Fatalf("Parsing unexpectedly succeeded") } expected := "unexpected end of file (expected MATCH)" if err.Error() != expected { t.Fatalf("Unexpected error from parser:\nexpectd: %s\nactual: %s\n", expected, err.Error()) } } func TestMatchAnyRune_AtInvalidUtf8Rune(t *testing.T) { p := newParser("\xcd", c.AnyRune()) _, err, ok := p.Next() if ok { t.Fatalf("Parsing unexpectedly succeeded") } expected := "invalid UTF8 character in input (expected MATCH)" if err.Error() != expected { t.Fatalf("Unexpected error from parser:\nexpectd: %s\nactual: %s\n", expected, err.Error()) } } func TestMatchRune(t *testing.T) { p := newParser("xxx", c.Rune('x')) r, err, ok := p.Next() if !ok { t.Fatalf("Parsing failed: %s", err) } if r.Type != TestItem { t.Error("Parser item type not expected TestTitem") } if r.Value != "x" { t.Errorf("Parser item value is %q instead of expected \"x\"", r.Value) } } func TestMatchRune_OnMismatch(t *testing.T) { p := newParser("x ", c.Rune(' ')) _, err, ok := p.Next() if ok { t.Fatalf("Parsing did not fail unexpectedly") } expected := "unexpected character 'x' (expected MATCH)" if err.Error() != expected { t.Fatalf("Unexpected error from parser:\nexpectd: %s\nactual: %s\n", expected, err.Error()) } } func TestMatchRuneRange(t *testing.T) { m := c.RuneRange('b', 'y') s := "mnopqrstuvwxybcdefghijkl" p := newParser(s, m) for i := 0; i < len(s); i++ { r, err, ok := p.Next() if !ok { t.Fatalf("Parsing failed: %s", err) } if s[i] != r.Value[0] { t.Fatalf("Unexpected parse output on cycle %d:\nexpected: %q\nactual: %q\n", i+1, s[i], r.Value[0]) } } if _, _, ok := newParser("a", m).Next(); ok { t.Fatalf("Unexpected parse success for input 'a'") } if _, _, ok := newParser("z", m).Next(); ok { t.Fatalf("Unexpected parse success for input 'z'") } } func TestMatchString(t *testing.T) { p := newParser("Hello, world!", c.String("Hello")) r, err, ok := p.Next() if !ok { t.Fatalf("Parsing failed: %s", err) } if r.Type != TestItem { t.Error("Parser item type not expected TestTitem") } if r.Value != "Hello" { t.Errorf("Parser item value is %q instead of expected \"Hello\"", r.Value) } } func TestMatchStringNoCase(t *testing.T) { p := newParser("HellÖ, world!", c.StringNoCase("hellö")) r, err, ok := p.Next() if !ok { t.Fatalf("Parsing failed: %s", err) } if r.Type != TestItem { t.Error("Parser item type not expected TestTitem") } if r.Value != "HellÖ" { t.Errorf("Parser item value is %q instead of expected \"HellÖ\"", r.Value) } } func TestMatchRunes(t *testing.T) { m := c.Runes('+', '-', '*', '/') s := "-+/*+++" p := newParser(s, m) for i := 0; i < len(s); i++ { r, err, ok := p.Next() if !ok { t.Fatalf("Parsing failed: %s", err) } if s[i] != r.Value[0] { t.Fatalf("Unexpected parse output on cycle %d:\nexpected: %q\nactual: %q\n", i+1, s[i], r.Value[0]) } } if _, _, ok := newParser("^", m).Next(); ok { t.Fatalf("Unexpected parse success for input '^'") } if _, _, ok := newParser("x", m).Next(); ok { t.Fatalf("Unexpected parse success for input 'x'") } } func TestMatchNot(t *testing.T) { p := newParser("aabc", c.Not(c.Rune('b'))) r, err, ok := p.Next() if !ok { t.Fatalf("Parsing failed: %s", err) } if r.Value != "a" { t.Errorf("Parser item value is %q instead of expected \"a\"", r.Value) } } func TestMatchNot_Mismatch(t *testing.T) { p := newParser("aabc", c.Not(c.Rune('a'))) _, err, ok := p.Next() if ok { t.Fatalf("Parsing unexpectedly succeeded") } expected := "unexpected character 'a' (expected MATCH)" if err.Error() != expected { t.Fatalf("Unexpected error from parser:\nexpectd: %s\nactual: %s\n", expected, err.Error()) } } func TestMatchAnyOf(t *testing.T) { p := newParser("abc", c.AnyOf(c.Rune('a'), c.Rune('b'))) r, err, ok := p.Next() if !ok { t.Fatalf("Parsing failed: %s", err) } if r.Type != TestItem { t.Error("Parser item type not expected TestTitem") } if r.Value != "a" { t.Errorf("Parser item value is %q instead of expected \"a\"", r.Value) } r, err, ok = p.Next() if !ok { t.Fatalf("Parsing failed: %s", err) } if r.Type != TestItem { t.Error("Parser item type not expected TestTitem") } if r.Value != "b" { t.Errorf("Parser item value is %q instead of expected \"a\"", r.Value) } } func TestMatchRepeat(t *testing.T) { p := newParser("xxxxyyyy", c.Repeat(4, c.Rune('x'))) r, err, ok := p.Next() if !ok { t.Fatalf("Parsing failed: %s at row: %d, column %d\n", err, err.Line, err.Column) } if r.Value != "xxxx" { t.Errorf("Parser item value is %q instead of expected \"xxxx\"", r.Value) } } func TestMatchRepeat_Min(t *testing.T) { p := newParser("1111112345", c.Min(4, c.Rune('1'))) r, _, _ := p.Next() if r.Value != "111111" { t.Errorf("Parser item value is %q instead of expected \"111111\"", r.Value) } } func TestMatchRepeat_Max(t *testing.T) { p := newParser("1111112345", c.Max(4, c.Rune('1'))) r, _, _ := p.Next() if r.Value != "1111" { t.Errorf("Parser item value is %q instead of expected \"1111\"", r.Value) } } func TestMatchRepeat_Bounded(t *testing.T) { p := newParser("1111112345", c.Bounded(3, 5, c.Rune('1'))) r, _, _ := p.Next() if r.Value != "11111" { t.Errorf("Parser item value is %q instead of expected \"11111\"", r.Value) } } func TestMatchRepeat_Mismatch(t *testing.T) { p := newParser("xxxyyyy", c.Repeat(4, c.Rune('x'))) _, err, ok := p.Next() if ok { t.Fatalf("Parsing did not fail unexpectedly") } expected := "unexpected character 'x' (expected MATCH)" if err.Error() != expected { t.Fatalf("Unexpected error from parser:\nexpectd: %s\nactual: %s\n", expected, err.Error()) } } func TestMatchOneOrMore(t *testing.T) { p := newParser("xxxxxxxxyyyy", c.OneOrMore(c.Rune('x'))) r, err, ok := p.Next() if !ok { t.Fatalf("Parsing failed: %s at row: %d, column %d\n", err, err.Line, err.Column) } if r.Value != "xxxxxxxx" { t.Errorf("Parser item value is %q instead of expected \"xxxxxxxx\"", r.Value) } } func TestMatchSequence(t *testing.T) { p := newParser("10101", c.Sequence(c.Rune('1'), c.Rune('0'))) r, err, ok := p.Next() if !ok { t.Fatalf("Parsing failed: %s at row: %d, column %d\n", err, err.Line, err.Column) } if r.Value != "10" { t.Errorf("Parser item value is %q instead of expected \"10\"", r.Value) } } func TestMatchSequence_CombinedWithOneOrMore(t *testing.T) { p := newParser("101010987", c.OneOrMore(c.Sequence(c.Rune('1'), c.Rune('0')))) r, err, ok := p.Next() if !ok { t.Fatalf("Parsing failed: %s at row: %d, column %d\n", err, err.Line, err.Column) } if r.Value != "101010" { t.Errorf("Parser item value is %q instead of expected \"101010\"", r.Value) } } func TestSequence_WithRepeatedRunes(t *testing.T) { whitespace := c.Optional(c.OneOrMore(c.Rune(' '))) equal := c.Rune('=') assignment := c.Sequence(whitespace, equal, whitespace) p := newParser(" == 10", assignment) r, err, ok := p.Next() if !ok { t.Fatalf("Parsing failed: %s at row: %d, column %d\n", err, err.Line, err.Column) } if r.Value != " =" { t.Errorf("Parser item value is %q instead of expected \" =\"", r.Value) } } func TestMatchOptional(t *testing.T) { p := newParser("xyz", c.Optional(c.Rune('x'))) r, err, ok := p.Next() if !ok { t.Fatalf("Parsing failed: %s at row: %d, column %d\n", err, err.Line, err.Column) } if r.Value != "x" { t.Errorf("Parser item value is %q instead of expected \"x\"", r.Value) } p = newParser("xyz", c.Optional(c.Rune('y'))) r, err, ok = p.Next() if !ok { t.Fatalf("Parsing failed: %s at row: %d, column %d\n", err, err.Line, err.Column) } if r.Value != "" { t.Errorf("Parser item value is %q instead of expected \"\"", r.Value) } } func TestMatchDrop(t *testing.T) { dashes := c.OneOrMore(c.Rune('-')) p := newParser("---X---", c.Sequence(c.Drop(dashes), c.AnyRune(), c.Drop(dashes))) r, err, ok := p.Next() if !ok { t.Fatalf("Parsing failed: %s at row: %d, column %d\n", err, err.Line, err.Column) } if r.Value != "X" { t.Errorf("Parser item value is %q instead of expected \"x\"", r.Value) } } func TestMatchSeparated(t *testing.T) { number := c.Bounded(1, 3, c.RuneRange('0', '9')) separators := c.Runes('|', ';', ',') separated_numbers := c.Separated(separators, number) p := newParser("1,2;3|44,55|66;777,abc", separated_numbers) r, err, ok := p.Next() if !ok { t.Fatalf("Parsing failed: %s at row: %d, column %d\n", err, err.Line, err.Column) } if r.Value != "1,2;3|44,55|66;777" { t.Errorf("Parser item value is %q instead of expected \"1,2;3|44,55|66;777\"", r.Value) } } func TestMixAndMatch(t *testing.T) { hex := c.AnyOf(c.RuneRange('0', '9'), c.RuneRange('a', 'f'), c.RuneRange('A', 'F')) backslash := c.Rune('\\') x := c.Rune('x') hexbyte := c.Sequence(backslash, x, c.Repeat(2, hex)) p := newParser(`\x9a\x01\xF0\xfCAndSomeMoreStuff`, c.Repeat(4, hexbyte)) r, err, ok := p.Next() if !ok { t.Fatalf("Parsing failed: %s at row: %d, column %d\n", err, err.Line, err.Column) } if r.Value != `\x9a\x01\xF0\xfC` { t.Errorf("Parser item value is %q instead of expected \"%q\"", r.Value, `\x9a\x01\xF0\xfC`) } }