package parser import "../ast" import "../lexer" import "../token" import "core:fmt" import "core:mem" import "core:testing" Parser :: struct { l: ^lexer.Lexer, cur_token: token.Token, peek_token: token.Token, errors: [dynamic]string, } new :: proc(l: ^lexer.Lexer) -> Parser { p := Parser{l, token.Illegal{}, token.Illegal{}, make([dynamic]string, context.temp_allocator)} next_token(&p) next_token(&p) return p } next_token :: proc(p: ^Parser) { p.cur_token = p.peek_token p.peek_token = lexer.next_token(p.l) } cur_token_is :: proc(p: ^Parser, $T: typeid) -> bool { _, ok := p.cur_token.(T) return ok } peek_token_is :: proc(p: ^Parser, $T: typeid) -> bool { _, ok := p.peek_token.(T) return ok } expect_peek :: proc(p: ^Parser, $T: typeid) -> bool { if peek_token_is(p, T) { next_token(p) return true } else { peek_error(p, T) return false } } peek_error :: proc(p: ^Parser, $T: typeid) { msg := fmt.tprintf("expected next token to be %s, got %v instead", typeid_of(T), p.peek_token) append(&p.errors, msg) } errors :: proc(p: ^Parser) -> []string { return p.errors[:] } parse_program :: proc(p: ^Parser) -> ast.Program { buf := make([dynamic]ast.Statement, context.temp_allocator) for !cur_token_is(p, token.EOF) { if stmt, ok := parse_statement(p); ok { append(&buf, stmt) } next_token(p) } return ast.Program{buf[:]} } parse_statement :: proc(p: ^Parser) -> (stmt: ast.Statement, ok: bool) { #partial switch t in p.cur_token { case token.Let: stmt, ok = parse_let_statement(p) case token.Return: stmt, ok = parse_return_statement(p) case: ok = false } return } parse_let_statement :: proc(p: ^Parser) -> (stmt: ast.Let_Statement, ok: bool) { stmt.token = p.cur_token if !expect_peek(p, token.Identifier) { ok = false return } stmt.name = ast.Identifier{p.cur_token, token.literal(p.cur_token)} if !expect_peek(p, token.Equal_Sign) { ok = false return } for !cur_token_is(p, token.Semicolon) { next_token(p) } ok = true return } parse_return_statement :: proc(p: ^Parser) -> (stmt: ast.Return_Statement, ok: bool) { stmt.token = p.cur_token next_token(p) for !cur_token_is(p, token.Semicolon) { next_token(p) } ok = true return } @(test) test_let_statements :: proc(t: ^testing.T) { input := ` let x = 5; let y = 10; let foobar = 838383; ` l := lexer.new(input) p := new(&l) program := parse_program(&p) defer free_all(context.allocator) check_parse_errors(t, &p) testing.expect_value(t, len(program.statements), 3) expected := []string{"x", "y", "foobar"} for identifier, index in expected { _ = program.statements[index] stmt := program.statements[index] test_let_statement(t, stmt, identifier) } } @(test) test_return_statements :: proc(t: ^testing.T) { input := ` return 5; return 10; return 993322; ` l := lexer.new(input) p := new(&l) program := parse_program(&p) defer free_all(context.allocator) check_parse_errors(t, &p) testing.expect_value(t, len(program.statements), 3) for stmt in program.statements { return_stmt, ok := stmt.(ast.Return_Statement) testing.expectf(t, ok, "Type of s is not `ast.Return_Statement`. got=%T", stmt) testing.expect_value(t, ast.token_literal(return_stmt), "return") } } test_let_statement :: proc(t: ^testing.T, s: ast.Statement, name: string) { testing.expect_value(t, ast.token_literal(s), "let") let_stmt, ok := s.(ast.Let_Statement) testing.expectf(t, ok, "Type of s is not `ast.Let_Statement`. got=%T", s) testing.expect_value(t, let_stmt.name.value, name) testing.expect_value(t, ast.token_literal(let_stmt.name), name) } check_parse_errors :: proc(t: ^testing.T, p: ^Parser) { errs := errors(p) if len(errs) == 0 { return } testing.expectf(t, len(errs) == 0, "parser had %d errors", len(errs)) for msg in errs { testing.expectf(t, false, "parser error: %q", msg) } testing.fail_now(t) }