2017-08-07 12:07:29 +02:00
|
|
|
package goparsify
|
2017-08-06 06:31:35 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
2017-08-09 11:35:15 +02:00
|
|
|
func TestSeq(t *testing.T) {
|
|
|
|
parser := Seq("hello", "world")
|
2017-08-06 06:31:35 +02:00
|
|
|
|
|
|
|
t.Run("matches sequence", func(t *testing.T) {
|
2017-08-06 15:32:10 +02:00
|
|
|
node, p2 := runParser("hello world", parser)
|
2017-08-07 10:25:23 +02:00
|
|
|
assertSequence(t, node, "hello", "world")
|
2017-08-06 07:43:23 +02:00
|
|
|
require.Equal(t, "", p2.Get())
|
2017-08-06 06:31:35 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("returns errors", func(t *testing.T) {
|
2017-08-06 15:32:10 +02:00
|
|
|
_, p2 := runParser("hello there", parser)
|
2017-08-09 13:18:14 +02:00
|
|
|
require.Equal(t, "world", p2.Error.expected)
|
2017-08-06 15:32:10 +02:00
|
|
|
require.Equal(t, 6, p2.Error.pos)
|
|
|
|
require.Equal(t, 0, p2.Pos)
|
2017-08-06 06:31:35 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-08-06 09:02:39 +02:00
|
|
|
func TestMaybe(t *testing.T) {
|
|
|
|
t.Run("matches sequence", func(t *testing.T) {
|
2017-08-06 15:32:10 +02:00
|
|
|
node, p2 := runParser("hello world", Maybe("hello"))
|
2017-08-07 10:25:23 +02:00
|
|
|
require.Equal(t, "hello", node.Token)
|
2017-08-06 09:02:39 +02:00
|
|
|
require.Equal(t, " world", p2.Get())
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("returns no errors", func(t *testing.T) {
|
2017-08-06 15:32:10 +02:00
|
|
|
node, p3 := runParser("hello world", Maybe("world"))
|
2017-08-09 13:18:14 +02:00
|
|
|
require.Equal(t, Result{}, node)
|
2017-08-06 15:32:10 +02:00
|
|
|
require.False(t, p3.Errored())
|
|
|
|
require.Equal(t, 0, p3.Pos)
|
2017-08-06 09:02:39 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-08-06 06:31:35 +02:00
|
|
|
func TestAny(t *testing.T) {
|
|
|
|
t.Run("Matches any", func(t *testing.T) {
|
2017-08-06 15:32:10 +02:00
|
|
|
node, p2 := runParser("hello world!", Any("hello", "world"))
|
2017-08-07 10:25:23 +02:00
|
|
|
require.Equal(t, "hello", node.Token)
|
2017-08-06 15:32:10 +02:00
|
|
|
require.Equal(t, 5, p2.Pos)
|
2017-08-06 06:31:35 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("Returns longest error", func(t *testing.T) {
|
2017-08-06 15:32:10 +02:00
|
|
|
_, p2 := runParser("hello world!", Any(
|
|
|
|
"nope",
|
2017-08-09 11:35:15 +02:00
|
|
|
Seq("hello", "world", "."),
|
|
|
|
Seq("hello", "brother"),
|
2017-08-06 15:32:10 +02:00
|
|
|
))
|
2017-08-09 13:18:14 +02:00
|
|
|
require.Equal(t, "offset 11: expected .", p2.Error.Error())
|
2017-08-06 15:32:10 +02:00
|
|
|
require.Equal(t, 11, p2.Error.Pos())
|
|
|
|
require.Equal(t, 0, p2.Pos)
|
2017-08-06 06:31:35 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("Accepts nil matches", func(t *testing.T) {
|
2017-08-07 13:20:30 +02:00
|
|
|
node, p2 := runParser("hello world!", Any(Exact("ffffff")))
|
2017-08-09 13:18:14 +02:00
|
|
|
require.Equal(t, Result{}, node)
|
2017-08-06 15:32:10 +02:00
|
|
|
require.Equal(t, 0, p2.Pos)
|
2017-08-06 06:31:35 +02:00
|
|
|
})
|
2017-08-13 07:28:43 +02:00
|
|
|
|
|
|
|
t.Run("branch prediction", func(t *testing.T) {
|
|
|
|
p := Any("hello", Seq("{", Cut(), "world", "}"), Seq("[", Cut(), "a", "]"))
|
|
|
|
// warm up the predictor
|
|
|
|
_, _ = Run(p, "hello")
|
|
|
|
_, _ = Run(p, "{world}")
|
|
|
|
|
|
|
|
t.Run("matches", func(t *testing.T) {
|
|
|
|
node, ps := runParser("hello world!", p)
|
|
|
|
require.Equal(t, "hello", node.Token)
|
|
|
|
require.Equal(t, 5, ps.Pos)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("errors", func(t *testing.T) {
|
|
|
|
_, ps := runParser("help world!", p)
|
|
|
|
require.Equal(t, "offset 0: expected [", ps.Error.Error())
|
|
|
|
require.Equal(t, 0, ps.Error.Pos())
|
|
|
|
require.Equal(t, 0, ps.Pos)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("errors with cuts", func(t *testing.T) {
|
|
|
|
_, ps := runParser("{world", p)
|
|
|
|
require.Equal(t, "offset 6: expected }", ps.Error.Error())
|
|
|
|
require.Equal(t, 6, ps.Error.Pos())
|
|
|
|
require.Equal(t, 0, ps.Pos)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("misprededicted cut", func(t *testing.T) {
|
|
|
|
// This should probably only happen when the predictor is cold
|
|
|
|
_, ps := runParser("[a", p)
|
|
|
|
require.Equal(t, "offset 2: expected ]", ps.Error.Error())
|
|
|
|
require.Equal(t, 2, ps.Error.Pos())
|
|
|
|
require.Equal(t, 0, ps.Pos)
|
|
|
|
})
|
|
|
|
})
|
2017-08-06 06:31:35 +02:00
|
|
|
}
|
|
|
|
|
2017-08-09 11:35:15 +02:00
|
|
|
func TestSome(t *testing.T) {
|
2017-08-06 06:31:35 +02:00
|
|
|
t.Run("Matches sequence with sep", func(t *testing.T) {
|
2017-08-09 11:35:15 +02:00
|
|
|
node, p2 := runParser("a,b,c,d,e,", Some(Chars("a-g"), ","))
|
2017-08-06 15:32:10 +02:00
|
|
|
require.False(t, p2.Errored())
|
2017-08-07 10:25:23 +02:00
|
|
|
assertSequence(t, node, "a", "b", "c", "d", "e")
|
2017-08-06 15:32:10 +02:00
|
|
|
require.Equal(t, 10, p2.Pos)
|
2017-08-06 06:31:35 +02:00
|
|
|
})
|
|
|
|
|
2017-08-09 13:58:36 +02:00
|
|
|
t.Run("Matches sequence without trailing sep", func(t *testing.T) {
|
|
|
|
node, p2 := runParser("a,b,c,d,e1111", Some(Chars("a-g"), ","))
|
|
|
|
require.False(t, p2.Errored())
|
|
|
|
assertSequence(t, node, "a", "b", "c", "d", "e")
|
|
|
|
require.Equal(t, "1111", p2.Get())
|
|
|
|
})
|
|
|
|
|
2017-08-06 06:31:35 +02:00
|
|
|
t.Run("Matches sequence without sep", func(t *testing.T) {
|
2017-08-09 11:35:15 +02:00
|
|
|
node, p2 := runParser("a,b,c,d,e,", Some(Any(Chars("a-g"), ",")))
|
2017-08-07 10:25:23 +02:00
|
|
|
assertSequence(t, node, "a", ",", "b", ",", "c", ",", "d", ",", "e", ",")
|
2017-08-06 15:32:10 +02:00
|
|
|
require.Equal(t, 10, p2.Pos)
|
2017-08-06 06:31:35 +02:00
|
|
|
})
|
|
|
|
|
2017-08-07 13:20:30 +02:00
|
|
|
t.Run("splits words automatically on space", func(t *testing.T) {
|
2017-08-09 11:35:15 +02:00
|
|
|
node, p2 := runParser("hello world", Some(Chars("a-z")))
|
2017-08-07 13:20:30 +02:00
|
|
|
assertSequence(t, node, "hello", "world")
|
|
|
|
require.Equal(t, "", p2.Get())
|
|
|
|
})
|
|
|
|
|
2017-08-06 06:31:35 +02:00
|
|
|
t.Run("Stops on error", func(t *testing.T) {
|
2017-08-09 11:35:15 +02:00
|
|
|
node, p2 := runParser("a,b,c,d,e,", Some(Chars("a-c"), ","))
|
2017-08-07 10:25:23 +02:00
|
|
|
assertSequence(t, node, "a", "b", "c")
|
2017-08-06 15:32:10 +02:00
|
|
|
require.Equal(t, 6, p2.Pos)
|
2017-08-06 06:31:35 +02:00
|
|
|
require.Equal(t, "d,e,", p2.Get())
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMany(t *testing.T) {
|
|
|
|
t.Run("Matches sequence with sep", func(t *testing.T) {
|
2017-08-06 15:32:10 +02:00
|
|
|
node, p2 := runParser("a,b,c,d,e,", Many(Chars("a-g"), Exact(",")))
|
2017-08-07 10:25:23 +02:00
|
|
|
assertSequence(t, node, "a", "b", "c", "d", "e")
|
2017-08-06 15:32:10 +02:00
|
|
|
require.Equal(t, 10, p2.Pos)
|
2017-08-06 06:31:35 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("Matches sequence without sep", func(t *testing.T) {
|
2017-08-06 15:32:10 +02:00
|
|
|
node, p2 := runParser("a,b,c,d,e,", Many(Any(Chars("abcdefg"), Exact(","))))
|
2017-08-07 10:25:23 +02:00
|
|
|
assertSequence(t, node, "a", ",", "b", ",", "c", ",", "d", ",", "e", ",")
|
2017-08-06 15:32:10 +02:00
|
|
|
require.Equal(t, 10, p2.Pos)
|
2017-08-06 06:31:35 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("Stops on error", func(t *testing.T) {
|
2017-08-06 15:32:10 +02:00
|
|
|
node, p2 := runParser("a,b,c,d,e,", Many(Chars("abc"), Exact(",")))
|
2017-08-07 10:25:23 +02:00
|
|
|
assertSequence(t, node, "a", "b", "c")
|
2017-08-06 15:32:10 +02:00
|
|
|
require.Equal(t, 6, p2.Pos)
|
2017-08-06 06:31:35 +02:00
|
|
|
require.Equal(t, "d,e,", p2.Get())
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("Returns error if nothing matches", func(t *testing.T) {
|
2017-08-06 15:32:10 +02:00
|
|
|
_, p2 := runParser("a,b,c,d,e,", Many(Chars("def"), Exact(",")))
|
2017-08-09 13:18:14 +02:00
|
|
|
require.Equal(t, "offset 0: expected def", p2.Error.Error())
|
2017-08-06 06:31:35 +02:00
|
|
|
require.Equal(t, "a,b,c,d,e,", p2.Get())
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-08-06 09:02:39 +02:00
|
|
|
type htmlTag struct {
|
|
|
|
Name string
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMap(t *testing.T) {
|
2017-08-13 09:30:10 +02:00
|
|
|
parser := Seq("<", Chars("a-zA-Z0-9"), ">").Map(func(n *Result) {
|
|
|
|
n.Result = htmlTag{n.Child[1].Token}
|
2017-08-06 09:02:39 +02:00
|
|
|
})
|
|
|
|
|
2017-08-10 16:08:08 +02:00
|
|
|
t.Run("success", func(t *testing.T) {
|
2017-08-06 15:32:10 +02:00
|
|
|
result, _ := runParser("<html>", parser)
|
2017-08-07 10:25:23 +02:00
|
|
|
require.Equal(t, htmlTag{"html"}, result.Result)
|
2017-08-06 09:02:39 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("error", func(t *testing.T) {
|
2017-08-06 15:32:10 +02:00
|
|
|
_, ps := runParser("<html", parser)
|
2017-08-09 13:18:14 +02:00
|
|
|
require.Equal(t, "offset 5: expected >", ps.Error.Error())
|
2017-08-06 15:32:10 +02:00
|
|
|
require.Equal(t, 0, ps.Pos)
|
2017-08-06 09:02:39 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-08-09 13:58:36 +02:00
|
|
|
func TestBind(t *testing.T) {
|
|
|
|
parser := Bind("true", true)
|
|
|
|
|
2017-08-10 16:08:08 +02:00
|
|
|
t.Run("success", func(t *testing.T) {
|
2017-08-09 13:58:36 +02:00
|
|
|
result, _ := runParser("true", parser)
|
|
|
|
require.Equal(t, true, result.Result)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("error", func(t *testing.T) {
|
|
|
|
result, ps := runParser("nil", parser)
|
|
|
|
require.Nil(t, result.Result)
|
|
|
|
require.Equal(t, "offset 0: expected true", ps.Error.Error())
|
|
|
|
require.Equal(t, 0, ps.Pos)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-08-10 13:04:14 +02:00
|
|
|
func TestCut(t *testing.T) {
|
|
|
|
t.Run("test any", func(t *testing.T) {
|
2017-08-10 14:10:30 +02:00
|
|
|
_, ps := runParser("var world", Any(Seq("var", Cut(), "hello"), "var world"))
|
2017-08-10 13:04:14 +02:00
|
|
|
require.Equal(t, "offset 4: expected hello", ps.Error.Error())
|
|
|
|
require.Equal(t, 0, ps.Pos)
|
|
|
|
})
|
2017-08-10 13:58:14 +02:00
|
|
|
|
|
|
|
t.Run("test many", func(t *testing.T) {
|
2017-08-10 14:10:30 +02:00
|
|
|
_, ps := runParser("hello <world", Many(Any(Seq("<", Cut(), Chars("a-z"), ">"), Chars("a-z"))))
|
2017-08-10 13:58:14 +02:00
|
|
|
require.Equal(t, "offset 12: expected >", ps.Error.Error())
|
|
|
|
require.Equal(t, 0, ps.Pos)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("test maybe", func(t *testing.T) {
|
2017-08-10 14:10:30 +02:00
|
|
|
_, ps := runParser("var", Maybe(Seq("var", Cut(), "hello")))
|
2017-08-10 13:58:14 +02:00
|
|
|
require.Equal(t, "offset 3: expected hello", ps.Error.Error())
|
|
|
|
require.Equal(t, 0, ps.Pos)
|
|
|
|
})
|
2017-08-10 13:04:14 +02:00
|
|
|
}
|
|
|
|
|
2017-08-06 09:02:39 +02:00
|
|
|
func TestMerge(t *testing.T) {
|
|
|
|
var bracer Parser
|
2017-08-09 11:35:15 +02:00
|
|
|
bracer = Seq("(", Maybe(&bracer), ")")
|
2017-08-06 09:02:39 +02:00
|
|
|
parser := Merge(bracer)
|
|
|
|
|
2017-08-10 16:08:08 +02:00
|
|
|
t.Run("success", func(t *testing.T) {
|
2017-08-06 15:32:10 +02:00
|
|
|
result, _ := runParser("((()))", parser)
|
2017-08-07 10:25:23 +02:00
|
|
|
require.Equal(t, "((()))", result.Token)
|
2017-08-06 09:02:39 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("error", func(t *testing.T) {
|
2017-08-06 15:32:10 +02:00
|
|
|
_, ps := runParser("((())", parser)
|
2017-08-09 13:18:14 +02:00
|
|
|
require.Equal(t, "offset 5: expected )", ps.Error.Error())
|
2017-08-06 15:32:10 +02:00
|
|
|
require.Equal(t, 0, ps.Pos)
|
2017-08-06 09:02:39 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-08-13 04:56:46 +02:00
|
|
|
func TestMapShorthand(t *testing.T) {
|
2017-08-13 09:30:10 +02:00
|
|
|
Chars("a-z").Map(func(n *Result) {
|
|
|
|
n.Result = n.Token
|
2017-08-13 04:56:46 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-08-09 13:18:14 +02:00
|
|
|
func assertSequence(t *testing.T, node Result, expected ...string) {
|
2017-08-07 13:20:30 +02:00
|
|
|
require.NotNil(t, node)
|
2017-08-07 10:25:23 +02:00
|
|
|
actual := []string{}
|
|
|
|
|
2017-08-08 15:11:47 +02:00
|
|
|
for _, child := range node.Child {
|
2017-08-07 10:25:23 +02:00
|
|
|
actual = append(actual, child.Token)
|
|
|
|
}
|
|
|
|
|
|
|
|
require.Equal(t, expected, actual)
|
|
|
|
}
|