#+feature dynamic-literals package parsing_test import "../../ast" import "../../expect" import "../../parsing" import "../../token" import "core:testing" @(test) error :: proc(t: ^testing.T) { tests := map[string]struct { input: [dynamic]token.Token, output: string, } { "let missing assignment" = { input = [dynamic]token.Token { {type = .Let, literal = "let"}, {type = .Identifier, literal = "x"}, {type = .Integer, literal = "0", line = 1, column = 2}, {type = .Semicolon, literal = ";"}, {type = .End_Of_File}, }, output = "1:2: expected Assignment, but got Integer", }, "let missing identifier" = { input = [dynamic]token.Token { {type = .Let, literal = "let"}, {type = .Assignment, literal = "=", line = 1, column = 2}, {type = .Integer, literal = "0"}, {type = .Semicolon, literal = ";"}, {type = .End_Of_File}, }, output = "1:2: expected Identifier, but got Assignment", }, "let missing integer" = { input = [dynamic]token.Token { {type = .Let, literal = "let"}, {type = .Identifier, literal = "x"}, {type = .Assignment, literal = "="}, {type = .Semicolon, literal = ";", line = 1, column = 2}, {type = .End_Of_File}, }, output = "1:2: expected Integer, but got Semicolon", }, "let missing semicolon" = { input = [dynamic]token.Token { {type = .Let, literal = "let"}, {type = .Identifier, literal = "x"}, {type = .Assignment, literal = "="}, {type = .Integer, literal = "0"}, {type = .End_Of_File, line = 1, column = 2}, }, output = "1:2: expected Semicolon, but got End_Of_File", }, "return missing integer" = { input = [dynamic]token.Token { {type = .Return, literal = "return"}, {type = .Semicolon, literal = ";", line = 1, column = 2}, {type = .End_Of_File}, }, output = "1:2: expected Integer, but got Semicolon", }, "return missing semicolon" = { input = [dynamic]token.Token { {type = .Return, literal = "return"}, {type = .Integer, literal = "0"}, {type = .End_Of_File, line = 1, column = 2}, }, output = "1:2: expected Semicolon, but got End_Of_File", }, "identifier missing semicolon" = { input = [dynamic]token.Token { {type = .Identifier, literal = "x"}, {type = .End_Of_File, line = 1, column = 2}, }, output = "1:2: expected Semicolon, but got End_Of_File", }, "integer missing semicolon" = { input = [dynamic]token.Token { {type = .Integer, literal = "0"}, {type = .End_Of_File, line = 1, column = 2}, }, output = "1:2: expected Semicolon, but got End_Of_File", }, "boolean missing semicolon" = { input = [dynamic]token.Token { {type = .True, literal = "true"}, {type = .End_Of_File, line = 1, column = 2}, }, output = "1:2: expected Semicolon, but got End_Of_File", }, "bang missing semicolon" = { input = [dynamic]token.Token { {type = .Bang, literal = "!"}, {type = .Integer, literal = "0"}, {type = .End_Of_File, line = 1, column = 2}, }, output = "1:2: expected Semicolon, but got End_Of_File", }, "minus missing semicolon" = { input = [dynamic]token.Token { {type = .Minus, literal = "-"}, {type = .Integer, literal = "0"}, {type = .End_Of_File, line = 1, column = 2}, }, output = "1:2: expected Semicolon, but got End_Of_File", }, } defer free_all() for name, data in tests { statements, errors := parsing.run(data.input[:]) expect.value(t, name, len(statements), 0) expect.value(t, name, len(errors), 1) expect.value(t, name, string(errors[0]), data.output) } } @(test) statement :: proc(t: ^testing.T) { tests := map[string]struct { input: [dynamic]token.Token, output: ast.Statement, } { "let" = { input = [dynamic]token.Token { {type = .Let, literal = "let"}, {type = .Identifier, literal = "x"}, {type = .Assignment, literal = "="}, {type = .Integer, literal = "0"}, {type = .Semicolon, literal = ";"}, }, output = ast.Let_Statement { name = ast.Identifier{value = "x"}, value = ast.Integer{value = 0}, }, }, "return" = { input = [dynamic]token.Token { {type = .Return, literal = "return"}, {type = .Integer, literal = "0"}, {type = .Semicolon, literal = ";"}, }, output = ast.Return_Statement{value = ast.Integer{value = 0}}, }, } defer free_all() for name, data in tests { statements, errors := parsing.run(data.input[:]) expect.value(t, name, len(statements), 1) expect.value(t, name, len(errors), 0) expect.value(t, name, statements[0], data.output) } } @(test) expression :: proc(t: ^testing.T) { tests := map[string]struct { input: [dynamic]token.Token, output: ast.Expression, } { "identifier" = { input = [dynamic]token.Token { {type = .Identifier, literal = "x"}, {type = .Semicolon, literal = ";"}, }, output = ast.Identifier{value = "x"}, }, "integer" = { input = [dynamic]token.Token { {type = .Integer, literal = "0"}, {type = .Semicolon, literal = ";"}, }, output = ast.Integer{value = 0}, }, "true" = { input = [dynamic]token.Token { {type = .True, literal = "true"}, {type = .Semicolon, literal = ";"}, }, output = ast.Boolean{value = true}, }, "false" = { input = [dynamic]token.Token { {type = .False, literal = "false"}, {type = .Semicolon, literal = ";"}, }, output = ast.Boolean{value = false}, }, } defer free_all() for name, data in tests { statements, errors := parsing.run(data.input[:]) expect.value(t, name, len(statements), 1) expect.value(t, name, len(errors), 0) statement := statements[0].(ast.Expression_Statement) expect.value(t, name, statement.expression, data.output) } } @(test) unary_expression :: proc(t: ^testing.T) { tests := map[string]struct { input: [dynamic]token.Token, output: ast.Unary_Expression, } { "bang" = { input = [dynamic]token.Token { {type = .Bang, literal = "!"}, {type = .Integer, literal = "0"}, {type = .Semicolon, literal = ";"}, }, output = ast.Unary_Expression{operator = "!", right = ast.Integer{value = 0}}, }, "minus" = { input = [dynamic]token.Token { {type = .Minus, literal = "-"}, {type = .Integer, literal = "0"}, {type = .Semicolon, literal = ";"}, }, output = ast.Unary_Expression{operator = "-", right = ast.Integer{value = 0}}, }, } defer free_all() for name, data in tests { statements, errors := parsing.run(data.input[:]) expect.value(t, name, len(statements), 1) expect.value(t, name, len(errors), 0) statement := statements[0].(ast.Expression_Statement) expression := statement.expression.(^ast.Unary_Expression) expect.value(t, name, expression^, data.output) } } @(test) binary_expression :: proc(t: ^testing.T) { tests := map[string]struct { input: [dynamic]token.Token, output: ast.Binary_Expression, } { "plus" = { input = [dynamic]token.Token { {type = .Integer, literal = "0"}, {type = .Plus, literal = "+"}, {type = .Integer, literal = "0"}, {type = .Semicolon, literal = ";"}, }, output = ast.Binary_Expression { operator = "+", left = ast.Integer{value = 0}, right = ast.Integer{value = 0}, }, }, "minus" = { input = [dynamic]token.Token { {type = .Integer, literal = "0"}, {type = .Minus, literal = "-"}, {type = .Integer, literal = "0"}, {type = .Semicolon, literal = ";"}, }, output = ast.Binary_Expression { operator = "-", left = ast.Integer{value = 0}, right = ast.Integer{value = 0}, }, }, "multiply by" = { input = [dynamic]token.Token { {type = .Integer, literal = "0"}, {type = .Asterisk, literal = "*"}, {type = .Integer, literal = "0"}, {type = .Semicolon, literal = ";"}, }, output = ast.Binary_Expression { operator = "*", left = ast.Integer{value = 0}, right = ast.Integer{value = 0}, }, }, "divide by" = { input = [dynamic]token.Token { {type = .Integer, literal = "0"}, {type = .Slash, literal = "/"}, {type = .Integer, literal = "0"}, {type = .Semicolon, literal = ";"}, }, output = ast.Binary_Expression { operator = "/", left = ast.Integer{value = 0}, right = ast.Integer{value = 0}, }, }, "greater than" = { input = [dynamic]token.Token { {type = .Integer, literal = "0"}, {type = .Greater_Than, literal = ">"}, {type = .Integer, literal = "0"}, {type = .Semicolon, literal = ";"}, }, output = ast.Binary_Expression { operator = ">", left = ast.Integer{value = 0}, right = ast.Integer{value = 0}, }, }, "less than" = { input = [dynamic]token.Token { {type = .Integer, literal = "0"}, {type = .Less_Than, literal = "<"}, {type = .Integer, literal = "0"}, {type = .Semicolon, literal = ";"}, }, output = ast.Binary_Expression { operator = "<", left = ast.Integer{value = 0}, right = ast.Integer{value = 0}, }, }, "equal" = { input = [dynamic]token.Token { {type = .Integer, literal = "0"}, {type = .Equal, literal = "=="}, {type = .Integer, literal = "0"}, {type = .Semicolon, literal = ";"}, }, output = ast.Binary_Expression { operator = "==", left = ast.Integer{value = 0}, right = ast.Integer{value = 0}, }, }, "not equal" = { input = [dynamic]token.Token { {type = .Integer, literal = "0"}, {type = .Not_Equal, literal = "!="}, {type = .Integer, literal = "0"}, {type = .Semicolon, literal = ";"}, }, output = ast.Binary_Expression { operator = "!=", left = ast.Integer{value = 0}, right = ast.Integer{value = 0}, }, }, "group" = { input = [dynamic]token.Token { {type = .Left_Parenthesis, literal = "("}, {type = .Integer, literal = "0"}, {type = .Plus, literal = "+"}, {type = .Integer, literal = "1"}, {type = .Right_Parenthesis, literal = ")"}, {type = .Asterisk, literal = "*"}, {type = .Integer, literal = "2"}, {type = .Semicolon, literal = ";"}, }, output = ast.Binary_Expression { operator = "*", left = &ast.Binary_Expression { operator = "+", left = ast.Integer{value = 0}, right = ast.Integer{value = 1}, }, right = ast.Integer{value = 2}, }, }, } defer free_all() for name, data in tests { statements, errors := parsing.run(data.input[:]) expect.value(t, name, len(statements), 1) expect.value(t, name, len(errors), 0) statement := statements[0].(ast.Expression_Statement) expression := statement.expression.(^ast.Binary_Expression) expect.binary(t, name, expression^, data.output) } }