package core import ( "math/big" "strconv" "testing" ) func TestNewParser(t *testing.T) { tokens := make([]Token, 0) p := NewParser("", []string{}, tokens) if p == nil { t.Fatal("parser should not be nil") } if p.pos != 0 { t.Error("parser should initialize position at 0") } if len(p.tokens) != len(tokens) { t.Error("parser should have the passed token list") } for i, v := range tokens { if p.tokens[i] != v { t.Error("parser should have the passed token list") } } } func BenchmarkNewParser(b *testing.B) { tokens := make([]Token, 0) for i := 0; i < b.N; i++ { _ = NewParser("", []string{}, tokens) } } type TokenTestData struct { tokens []Token tree Node } func GetTokenTestData() map[string]TokenTestData { return map[string]TokenTestData{ "empty": { []Token{ NewToken(TokenEOF, 0, 0, 0, ""), }, &BlockNode{}, }, "addition": { []Token{ NewToken(TokenName, 0, 1, 0, "_"), NewToken(TokenAssign, 1, 1, 0, "="), NewToken(TokenFloat, 3, 1, 0, "1"), NewToken(TokenPlus, 4, 1, 0, "+"), NewToken(TokenFloat, 5, 1, 0, "2"), NewToken(TokenEOF, 6, 0, 0, ""), }, &BlockNode{ []Node{ &AssignNode{ &ReferenceNode{"_", 0, 0}, &BinaryNode{ BinaryAddition, &FloatNode{ 1, 0, 0, }, &FloatNode{ 2, 0, 0, }, nil, 0, 0, }, false, 0, 0, }, }, 0, 0, }, }, "assignment": { []Token{ NewToken(TokenName, 0, 5, 0, "hello"), NewToken(TokenAssign, 5, 1, 0, "="), NewToken(TokenString, 6, 12, 0, "\"Hello world!\""), NewToken(TokenEOF, 18, 0, 0, ""), }, &BlockNode{ []Node{ &AssignNode{ &ReferenceNode{"hello", 0, 0}, &StringNode{ "Hello world!", "\"Hello world!\"", 0, 0, }, false, 0, 0, }, }, 0, 0, }, }, "declaration": { []Token{ NewToken(TokenName, 0, 1, 0, "a"), NewToken(TokenDeclare, 1, 2, 0, ":="), NewToken(TokenFloat, 3, 1, 0, "1"), NewToken(TokenPlus, 4, 1, 0, "+"), NewToken(TokenName, 5, 1, 0, "b"), NewToken(TokenEOF, 6, 0, 0, ""), }, &BlockNode{ []Node{ &AssignNode{ &ReferenceNode{"a", 0, 0}, &BinaryNode{ BinaryAddition, &FloatNode{ 1, 0, 0, }, &ReferenceNode{ "b", 0, 0, }, nil, 0, 0, }, true, 0, 0, }, }, 0, 0, }, }, // (2 + 1) * 5 + 3 / (6 - 2) - 10 / 2 "arithmetic_order": { []Token{ NewToken(TokenName, 0, 1, 0, "_"), NewToken(TokenAssign, 1, 2, 0, "="), NewToken(TokenOpenParenthesis, 3, 1, 0, "("), NewToken(TokenFloat, 4, 1, 0, "2"), NewToken(TokenPlus, 5, 1, 0, "+"), NewToken(TokenFloat, 6, 1, 0, "1"), NewToken(TokenCloseParenthesis, 7, 1, 0, ")"), NewToken(TokenStar, 8, 1, 0, "*"), NewToken(TokenFloat, 9, 1, 0, "5"), NewToken(TokenPlus, 10, 1, 0, "+"), NewToken(TokenFloat, 11, 1, 0, "3"), NewToken(TokenSlash, 12, 1, 0, "/"), NewToken(TokenOpenParenthesis, 13, 1, 0, "("), NewToken(TokenFloat, 14, 1, 0, "6"), NewToken(TokenMinus, 15, 1, 0, "-"), NewToken(TokenFloat, 16, 1, 0, "2"), NewToken(TokenCloseParenthesis, 17, 1, 0, ")"), NewToken(TokenMinus, 18, 1, 0, "-"), NewToken(TokenFloat, 19, 2, 0, "10"), NewToken(TokenSlash, 20, 1, 0, "/"), NewToken(TokenFloat, 21, 1, 0, "2"), NewToken(TokenEOF, 22, 0, 0, ""), }, // (2 + 1) * 5 + 3 / (6 - 2) - 10 / 2 &BlockNode{ []Node{ &AssignNode{ &ReferenceNode{"_", 0, 0}, &BinaryNode{ BinarySubtraction, &BinaryNode{ BinaryAddition, &BinaryNode{ BinaryMultiplication, &BinaryNode{ BinaryAddition, &FloatNode{ 2, 0, 0, }, &FloatNode{ 1, 0, 0, }, nil, 0, 0, }, &FloatNode{ 5, 0, 0, }, nil, 0, 0, }, &BinaryNode{ BinaryDivision, &FloatNode{ 3, 0, 0, }, &BinaryNode{ BinarySubtraction, &FloatNode{ 6, 0, 0, }, &FloatNode{ 2, 0, 0, }, nil, 0, 0, }, nil, 0, 0, }, nil, 0, 0, }, &BinaryNode{ BinaryDivision, &FloatNode{ 10, 0, 0, }, &FloatNode{ 2, 0, 0, }, nil, 0, 0, }, nil, 0, 0, }, false, 0, 0, }, }, 0, 0, }, }, "condition_equal": { []Token{ NewToken(TokenName, 0, 1, 0, "_"), NewToken(TokenAssign, 1, 1, 0, "="), NewToken(TokenFloat, 2, 2, 0, "20"), NewToken(TokenEquals, 4, 2, 0, "=="), NewToken(TokenFloat, 6, 2, 0, "15"), NewToken(TokenEOF, 8, 0, 0, ""), }, &BlockNode{ []Node{ &AssignNode{ &ReferenceNode{"_", 0, 0}, &BinaryNode{ BinaryEquality, &FloatNode{ 20, 0, 0, }, &FloatNode{ 15, 0, 0, }, nil, 0, 0, }, false, 0, 0, }, }, 0, 0, }, }, "if_statement": { []Token{ NewToken(TokenIf, 0, 2, 0, "if"), NewToken(TokenName, 2, 1, 0, "a"), NewToken(TokenEquals, 3, 2, 0, "=="), NewToken(TokenFloat, 5, 1, 0, "0"), NewToken(TokenOpenBrace, 6, 1, 0, "{"), NewToken(TokenName, 7, 1, 1, "b"), NewToken(TokenAssign, 8, 1, 1, "="), NewToken(TokenFloat, 9, 1, 1, "1"), NewToken(TokenCloseBrace, 10, 1, 2, "}"), NewToken(TokenEOF, 11, 0, 2, ""), }, &BlockNode{ []Node{ &ConditionalNode{ condition: &BinaryNode{ BinaryEquality, &ReferenceNode{ "a", 0, 0, }, &FloatNode{ 0, 0, 0, }, nil, 0, 0, }, do: &BlockNode{ []Node{ &AssignNode{ &ReferenceNode{"b", 0, 0}, &FloatNode{ 1, 0, 0, }, false, 0, 0, }, }, 0, 0, }, }, }, 0, 0, }, }, "if_else_statement": { []Token{ NewToken(TokenIf, 0, 2, 0, "if"), NewToken(TokenName, 2, 1, 0, "a"), NewToken(TokenEquals, 3, 2, 0, "=="), NewToken(TokenFloat, 5, 1, 0, "0"), NewToken(TokenOpenBrace, 6, 1, 0, "{"), NewToken(TokenName, 7, 1, 1, "b"), NewToken(TokenAssign, 8, 1, 1, "="), NewToken(TokenFloat, 9, 1, 1, "1"), NewToken(TokenCloseBrace, 10, 1, 2, "}"), NewToken(TokenElse, 11, 4, 2, "else"), NewToken(TokenOpenBrace, 15, 1, 2, "{"), NewToken(TokenName, 16, 1, 2, "b"), NewToken(TokenAssign, 17, 1, 2, "="), NewToken(TokenFloat, 18, 1, 2, "0"), NewToken(TokenCloseBrace, 19, 1, 2, "}"), NewToken(TokenEOF, 20, 0, 2, ""), }, &BlockNode{ []Node{ &ConditionalNode{ condition: &BinaryNode{ BinaryEquality, &ReferenceNode{ "a", 0, 0, }, &FloatNode{ 0, 0, 0, }, nil, 0, 0, }, do: &BlockNode{ []Node{ &AssignNode{ &ReferenceNode{"b", 0, 0}, &FloatNode{ 1, 0, 0, }, false, 0, 0, }, }, 0, 0, }, otherwise: &BlockNode{ []Node{ &AssignNode{ &ReferenceNode{"b", 0, 0}, &FloatNode{ 0, 0, 0, }, false, 0, 0, }, }, 0, 0, }, }, }, 0, 0, }, }, "empty_block": { []Token{ NewToken(TokenOpenBrace, 0, 1, 0, "{"), NewToken(TokenCloseBrace, 1, 1, 0, "}"), NewToken(TokenEOF, 2, 0, 0, ""), }, &BlockNode{ []Node{ &BlockNode{ []Node{}, 0, 0, }, }, 0, 0, }, }, "lambda": { // a := fn(a: float, b: float) -> float { return a + b } []Token{ NewToken(TokenName, 0, 1, 0, "a"), NewToken(TokenDeclare, 1, 2, 0, ":="), NewToken(TokenFunc, 3, 2, 0, "fn"), NewToken(TokenOpenParenthesis, 5, 1, 0, "("), NewToken(TokenName, 6, 1, 0, "a"), NewToken(TokenColon, 6, 1, 0, ":"), NewToken(TokenName, 10, 5, 0, "float"), NewToken(TokenComma, 9, 1, 0, ","), NewToken(TokenName, 10, 1, 0, "b"), NewToken(TokenColon, 9, 1, 0, ":"), NewToken(TokenName, 10, 5, 0, "float"), NewToken(TokenCloseParenthesis, 11, 1, 0, ")"), NewToken(TokenArrow, 12, 1, 0, "->"), NewToken(TokenName, 10, 5, 0, "float"), NewToken(TokenOpenBrace, 12, 1, 1, "{"), NewToken(TokenReturn, 13, 6, 1, "return"), NewToken(TokenName, 19, 1, 1, "a"), NewToken(TokenPlus, 20, 1, 1, "+"), NewToken(TokenName, 21, 1, 1, "b"), NewToken(TokenCloseBrace, 22, 1, 2, "}"), NewToken(TokenEOF, 23, 0, 2, ""), }, &BlockNode{ []Node{ &AssignNode{ &ReferenceNode{"a", 0, 0}, &FunctionNode{ "*", []FunctionParameter{ { "a", &FloatSignature{}, }, { "b", &FloatSignature{}, }, }, &FloatSignature{}, &BlockNode{ []Node{ &ReturnNode{ &BinaryNode{ BinaryAddition, &ReferenceNode{ "a", 0, 0, }, &ReferenceNode{ "b", 0, 0, }, nil, 0, 0, }, 0, 0, }, }, 0, 0, }, 0, 0, }, true, 0, 0, }, }, 0, 0, }, }, "function_declaration": { []Token{ NewToken(TokenFunc, 0, 2, 0, "fn"), NewToken(TokenName, 4, 3, 0, "a"), NewToken(TokenOpenParenthesis, 7, 1, 0, "("), NewToken(TokenName, 8, 1, 0, "a"), NewToken(TokenColon, 9, 1, 0, ":"), NewToken(TokenName, 8, 1, 0, "float"), NewToken(TokenComma, 9, 1, 0, ","), NewToken(TokenName, 10, 1, 0, "b"), NewToken(TokenColon, 9, 1, 0, ":"), NewToken(TokenName, 8, 1, 0, "float"), NewToken(TokenCloseParenthesis, 11, 1, 0, ")"), NewToken(TokenArrow, 9, 1, 0, "->"), NewToken(TokenName, 8, 1, 0, "float"), NewToken(TokenOpenBrace, 12, 1, 1, "{"), NewToken(TokenReturn, 13, 6, 1, "return"), NewToken(TokenName, 19, 1, 1, "a"), NewToken(TokenPlus, 20, 1, 1, "+"), NewToken(TokenName, 21, 1, 1, "b"), NewToken(TokenCloseBrace, 22, 1, 2, "}"), NewToken(TokenEOF, 23, 0, 2, ""), }, &BlockNode{ []Node{ &AssignNode{ &ReferenceNode{"a", 0, 0}, &FunctionNode{ "a", []FunctionParameter{ { "a", &FloatSignature{}, }, { "b", &FloatSignature{}, }, }, &FloatSignature{}, &BlockNode{ []Node{ &ReturnNode{ &BinaryNode{ BinaryAddition, &ReferenceNode{ "a", 0, 0, }, &ReferenceNode{ "b", 0, 0, }, nil, 0, 0, }, 0, 0, }, }, 0, 0, }, 0, 0, }, true, 0, 0, }, }, 0, 0, }, }, "prop_getting": { []Token{ NewToken(TokenName, 0, 1, 0, "p"), NewToken(TokenDeclare, 1, 2, 0, ":="), NewToken(TokenName, 3, 1, 0, "a"), NewToken(TokenDot, 4, 1, 0, "."), NewToken(TokenName, 5, 1, 0, "b"), NewToken(TokenEOF, 23, 0, 2, ""), }, &BlockNode{ []Node{ &AssignNode{ &ReferenceNode{"p", 0, 0}, &AccessNode{ &ReferenceNode{ "a", 0, 0, }, &Token{TokenName, 0, 1, 0, "b"}, 0, 0, }, true, 0, 0, }, }, 0, 0, }, }, "list_init": { []Token{ NewToken(TokenName, 0, 4, 0, "data"), NewToken(TokenDeclare, 4, 2, 0, ":="), NewToken(TokenOpenBracket, 6, 1, 0, "["), NewToken(TokenName, 8, 1, 0, "a"), NewToken(TokenComma, 11, 1, 0, ","), NewToken(TokenFloat, 8, 1, 0, "3.141"), NewToken(TokenComma, 11, 1, 0, ","), NewToken(TokenString, 6, 1, 0, "\"Hello world!\""), NewToken(TokenComma, 11, 1, 0, ","), NewToken(TokenTrue, 6, 1, 0, "true"), NewToken(TokenComma, 11, 1, 0, ","), NewToken(TokenOpenBracket, 6, 1, 0, "["), NewToken(TokenFloat, 6, 1, 0, "2"), NewToken(TokenComma, 11, 1, 0, ","), NewToken(TokenFloat, 6, 1, 0, "3"), NewToken(TokenCloseBracket, 6, 1, 0, "]"), NewToken(TokenCloseBracket, 6, 1, 0, "]"), NewToken(TokenEOF, 23, 0, 2, ""), }, &BlockNode{ []Node{ &AssignNode{ &ReferenceNode{"data", 0, 0}, &ListNode{ []Node{ &ReferenceNode{ "a", 0, 0, }, &FloatNode{ 3.141, 0, 0, }, &StringNode{ "Hello world!", "\"Hello world!\"", 0, 0, }, &BooleanNode{ true, 0, 0, }, &ListNode{ []Node{ &FloatNode{ 2, 0, 0, }, &FloatNode{ 3, 0, 0, }, }, nil, 0, 0, }, }, nil, 0, 0, }, true, 0, 0, }, }, 0, 0, }, }, "single_tuple": { []Token{ NewToken(TokenOpenParenthesis, 0, 0, 0, "("), NewToken(TokenInteger, 0, 0, 0, "1"), NewToken(TokenComma, 0, 0, 0, ","), NewToken(TokenCloseParenthesis, 0, 0, 0, ")"), NewToken(TokenEOF, 0, 0, 0, ""), }, &BlockNode{ []Node{ &TupleNode{ []Node{ &IntegerNode{ big.NewInt(1), 0, 0, }, }, 0, 0, }, }, 0, 0, }, }, "tuple": { []Token{ NewToken(TokenOpenParenthesis, 0, 0, 0, "("), NewToken(TokenInteger, 0, 0, 0, "1"), NewToken(TokenComma, 0, 0, 0, ","), NewToken(TokenInteger, 0, 0, 0, "2"), NewToken(TokenCloseParenthesis, 0, 0, 0, ")"), NewToken(TokenEOF, 0, 0, 0, ""), }, &BlockNode{ []Node{ &TupleNode{ []Node{ &IntegerNode{ big.NewInt(1), 0, 0, }, &IntegerNode{ big.NewInt(2), 0, 0, }, }, 0, 0, }, }, 0, 0, }, }, } } func NodeEquality(t *testing.T, n1 Node, n2 Node) { if n1 == n2 { return } if n1 == nil || n2 == nil { t.Fatalf("one of the nodes are nil (1: %s; 2: %s)", n1, n2) } if n1.Type() != n2.Type() { t.Fatalf("node types (%s and %s) don't match", n1.Type(), n2.Type()) } t.Logf("Nodes have same non-nil type (%s)", n1.Type()) switch n1.Type() { case NilNodeType: case StringNodeType: if n1.(*StringNode).value != n2.(*StringNode).value { t.Errorf("String node values don't match (%s and %s)", n1.(*StringNode).value, n2.(*StringNode).value) } else { t.Logf("String node values match (%s)", n1.(*StringNode).value) } if n1.(*StringNode).quoted != n2.(*StringNode).quoted { t.Errorf("String node quoted values don't match (%s and %s)", n1.(*StringNode).quoted, n2.(*StringNode).quoted) } else { t.Logf("String node quoted values match (%s)", n1.(*StringNode).value) } case FloatNodeType: if n1.(*FloatNode).value != n2.(*FloatNode).value { t.Errorf("Float node values don't match (%f and %f)", n1.(*FloatNode).value, n2.(*FloatNode).value) } else { t.Logf("Float node values match (%f)", n1.(*FloatNode).value) } case IntegerNodeType: if n1.(*IntegerNode).value.Cmp(n2.(*IntegerNode).value) != 0 { t.Errorf("Integer node values don't match (%d and %d)", n1.(*IntegerNode).value, n2.(*IntegerNode).value) } else { t.Logf("Integer node values match (%d)", n1.(*IntegerNode).value) } case ReferenceNodeType: if n1.(*ReferenceNode).name != n2.(*ReferenceNode).name { t.Errorf("Reference node values don't match (%s and %s)", n1.(*ReferenceNode).name, n2.(*ReferenceNode).name) } else { t.Logf("Reference node values match (%s)", n1.(*ReferenceNode).name) } case BinaryNodeType: if n1.(*BinaryNode).BinaryOperation != n2.(*BinaryNode).BinaryOperation { t.Errorf("Binary node operation not same (%s and %s)", n1.(*BinaryNode).BinaryOperation, n2.(*BinaryNode).BinaryOperation) } else { t.Logf("Binary node operation matches (%s)", n1.(*BinaryNode).BinaryOperation) } t.Log("Checking equality of binary left side") NodeEquality(t, n1.(*BinaryNode).Left, n2.(*BinaryNode).Left) t.Log("Checking equality of binary right side") NodeEquality(t, n1.(*BinaryNode).Right, n2.(*BinaryNode).Right) case BooleanNodeType: if n1.(*BooleanNode).Boolean != n2.(*BooleanNode).Boolean { t.Errorf("Boolean node values don't match (%s and %s)", strconv.FormatBool(n1.(*BooleanNode).Boolean), strconv.FormatBool(n2.(*BooleanNode).Boolean)) } else { t.Logf("Boolean node values match (%s)", strconv.FormatBool(n1.(*BooleanNode).Boolean)) } case BlockNodeType: if len(n1.(*BlockNode).statements) != len(n2.(*BlockNode).statements) { t.Errorf("Block node statement count is not equal (%d and %d)", len(n1.(*BlockNode).statements), len(n2.(*BlockNode).statements)) } else { t.Logf("Block node statement count is equal (%d) ", len(n1.(*BlockNode).statements)) } for i, n := range n1.(*BlockNode).statements { t.Logf("Checking equality of statements at %d", i) NodeEquality(t, n, n2.(*BlockNode).statements[i]) } case ConditionalNodeType: t.Log("Checking equality of conditions") NodeEquality(t, n1.(*ConditionalNode).condition, n2.(*ConditionalNode).condition) t.Log("Checking equality of do statement(s)") NodeEquality(t, n1.(*ConditionalNode).do, n2.(*ConditionalNode).do) t.Log("Checking equality of else statement(s)") NodeEquality(t, n1.(*ConditionalNode).otherwise, n2.(*ConditionalNode).otherwise) case LoopNodeType: t.Log("Checking equality of loop conditions") NodeEquality(t, n1.(*LoopNode).condition, n2.(*LoopNode).condition) t.Log("Checking equality of do loop statement(s)") NodeEquality(t, n1.(*LoopNode).do, n2.(*LoopNode).do) case AssignNodeType: t.Logf("Checking if value destination matches") NodeEquality(t, n1.(*AssignNode).dest, n2.(*AssignNode).dest) if n1.(*AssignNode).declare != n2.(*AssignNode).declare { t.Errorf("Not same type of assigning (1: %v; 2: %v)", n1.(*AssignNode).declare, n2.(*AssignNode).declare) } t.Logf("Checking equality of assignment values") NodeEquality(t, n1.(*AssignNode).value, n2.(*AssignNode).value) case InvokeNodeType: n := n1.(*InvokeNode) m := n2.(*InvokeNode) NodeEquality(t, n.source, m.source) if len(n.args) != len(m.args) { t.Fatalf("Call node arguments count does not match (%d and %d)", len(n.args), m.args) } for i, arg := range m.args { NodeEquality(t, n1.(*InvokeNode).args[i], arg) } case FunctionNodeType: n := n1.(*FunctionNode) m := n2.(*FunctionNode) if n.name != m.name { t.Errorf("Function node names don't match (%s and %s)", n.name, m.name) } else { t.Logf("Function node names match (%s)", n.name) } if len(n.parameters) != len(m.parameters) { t.Fatalf("Function node parameters count does not match (%d and %d)", len(n.parameters), len(m.parameters)) } else { t.Logf("Function node parameters count is equal (%d) ", len(n.parameters)) } for i, p := range m.parameters { if !n.parameters[i].Signature.Contains(p.Signature) { t.Errorf("Function node parameter signature %d does not match: %s and %s", i, p.Signature, n.parameters[i].Signature) } else if n.parameters[i].Name != p.Name { t.Errorf("Function node parameter name %d does not match: %s and %s", i, p.Name, n.parameters[i].Name) } else { t.Logf("Function node parameter %d matches (%s)", i, p) } } NodeEquality(t, n.logic, m.logic) case ReturnNodeType: NodeEquality(t, n1.(*ReturnNode).value, n2.(*ReturnNode).value) case AccessNodeType: a1 := n1.(*AccessNode) a2 := n2.(*AccessNode) // only care about lexeme; the rest is debug info if a1.property.Lexeme != a2.property.Lexeme { t.Errorf("Access node property does not match: .%s != .%s", a1.property.Lexeme, a2.property.Lexeme) } else { t.Logf("Access node property matches: .%s", a1.property.Lexeme) } NodeEquality(t, a1.source, a2.source) case ListNodeType: l1 := n1.(*ListNode) l2 := n2.(*ListNode) if l1.content == nil && l2.content == nil { // fine t.Logf("Both content types are yet to be determined") } else if l1.content != nil || l2.content != nil { t.Errorf("one is nil, one is not") } else if !l1.content.Contains(l2.content) { t.Errorf("signature doesn't match") } for i, v1 := range l1.items { t.Logf("Checking item %d", i) NodeEquality(t, v1, l2.items[i]) } case TupleNodeType: t1 := n1.(*TupleNode) t2 := n2.(*TupleNode) if len(t1.items) != len(t2.items) { t.Fatalf("tuple item count does not match") } for i, v1 := range t1.items { t.Logf("Checking item %d", i) NodeEquality(t, v1, t2.items[i]) } default: panic("unimplemented node equality") } } func TestParser_Parse(t *testing.T) { t.Logf("Getting test data") tokenData := GetTokenTestData() for name, data := range tokenData { t.Run(name, func(t *testing.T) { t.Logf("Initializing parser") p := NewParser("", []string{}, data.tokens) t.Logf("Parsing main") tree, err := p.Parse("") if err != nil { t.Fatalf("Unexpected error(s): %s", err.(ParsingError).Format()) } t.Logf("Checking parsed tree") NodeEquality(t, tree.Block, data.tree) }) } } func TestParser_AcceptAll(t *testing.T) { p := NewParser("a:", []string{}, []Token{ NewToken(TokenName, 0, 1, 0, "a"), NewToken(TokenColon, 1, 2, 0, "a"), }) if !p.acceptAll(TokenName, TokenColon) { t.Fatalf("tokens were not accepted") } t.Logf("tokens were accepted") } func TestParser_AcceptAll_TooFew(t *testing.T) { p := NewParser("a", []string{}, []Token{ NewToken(TokenName, 0, 1, 0, "a"), }) if p.acceptAll(TokenName, TokenColon) { t.Fatalf("tokens were incorrectly accepted") } t.Logf("tokens were, as expected, not accepted") } func BenchmarkParser_Parse(b *testing.B) { tokenData := GetTokenTestData() for name, data := range tokenData { b.Run(name, func(b *testing.B) { for i := 0; i < b.N; i++ { p := NewParser("", []string{}, data.tokens) _, _ = p.Parse("") } }) } }