odin-html

HTML Parsing library in Odin.
Log | Files | Refs | README | LICENSE

commit 860ab567592943a5624aa8d1b42f2917c457f0d1
parent b36945cfe53e383a0e0b3a4f54367f3c7ba06bb0
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date:   Thu, 23 Jul 2026 20:28:50 -0300

lex: use flat token struct

Instead of a union prefer the data-oriented flat style.

Diffstat:
Mlex.odin | 343+++++++++++++++++++++++++++++++++++++++----------------------------------------
1 file changed, 168 insertions(+), 175 deletions(-)

diff --git a/lex.odin b/lex.odin @@ -12,22 +12,16 @@ import "core:unicode" DOCTYPE_LITERAL :: "<!DOCTYPE" -Token_Span :: struct { +// Token is a flat descriptor of a lexical token. +// +// `start` and `end` are inclusive byte offsets into the source string. +// For atoms (single-character tokens) `end == start`. +Token :: struct { kind: Token_Kind, start: u32, end: u32, } -Token_Atom :: struct { - kind: Token_Kind, - start: u32, -} - -Token :: union { - Token_Atom, - Token_Span, -} - Token_Kind :: enum u8 { Open_Tag_Start, Tag_End, @@ -53,12 +47,12 @@ lexer_next :: proc(l: ^Lexer) -> (t: Token, ok: bool) { if !lexer_advance_until(l, `>`) do return t, false end := l.cursor defer lexer_advance(l) - return Token_Span{kind = .Doctype, start = start, end = end}, true + return Token{kind = .Doctype, start = start, end = end}, true } peek := lexer_peek(l) if peek == "" { - return nil, false + return t, false } if t, ok := _lexer_next(l); ok { @@ -83,19 +77,19 @@ lexer_next :: proc(l: ^Lexer) -> (t: Token, ok: bool) { } end := l.cursor - 1 - return Token_Span{kind = .Raw_Text, start = start, end = end}, true + return Token{kind = .Raw_Text, start = start, end = end}, true } @(private = "file") _lexer_next :: proc(l: ^Lexer) -> (t: Token, ok: bool) { if peek := lexer_peek(l, 4); peek == "<!--" { defer lexer_advance(l, 4) - return Token_Span{kind = .Comment_Start, start = l.cursor, end = l.cursor + 3}, true + return Token{kind = .Comment_Start, start = l.cursor, end = l.cursor + 3}, true } if peek := lexer_peek(l, 3); peek == "-->" { defer lexer_advance(l, 3) - return Token_Span{kind = .Comment_End, start = l.cursor, end = l.cursor + 2}, true + return Token{kind = .Comment_End, start = l.cursor, end = l.cursor + 2}, true } if peek := lexer_peek(l, 2); peek != "" && peek[0] == '<' && unicode.is_letter(rune(peek[1])) { @@ -104,7 +98,7 @@ _lexer_next :: proc(l: ^Lexer) -> (t: Token, ok: bool) { if !lexer_advance_while_letters(l) do return end := l.cursor defer lexer_advance(l) - return Token_Span{kind = .Open_Tag_Start, start = start, end = end}, true + return Token{kind = .Open_Tag_Start, start = start, end = end}, true } if peek := lexer_peek(l, 2); peek == "</" { @@ -113,22 +107,22 @@ _lexer_next :: proc(l: ^Lexer) -> (t: Token, ok: bool) { if !lexer_advance_while_letters(l) do return end := l.cursor defer lexer_advance(l) - return Token_Span{kind = .Close_Tag_Start, start = start, end = end}, true + return Token{kind = .Close_Tag_Start, start = start, end = end}, true } if peek := lexer_peek(l, 2); peek == "/>" { defer lexer_advance(l, 2) - return Token_Span{kind = .Self_Closing_Tag_End, start = l.cursor, end = l.cursor + 1}, true + return Token{kind = .Self_Closing_Tag_End, start = l.cursor, end = l.cursor + 1}, true } if peek := lexer_peek(l, 1); peek == ">" { defer lexer_advance(l, 1) - return Token_Atom{kind = .Tag_End, start = l.cursor}, true + return Token{kind = .Tag_End, start = l.cursor, end = l.cursor}, true } if peek := lexer_peek(l, 1); peek == "=" { defer lexer_advance(l, 1) - return Token_Atom{kind = .Assign, start = l.cursor}, true + return Token{kind = .Assign, start = l.cursor, end = l.cursor}, true } if peek := lexer_peek(l, 1); peek == `"` { @@ -137,7 +131,7 @@ _lexer_next :: proc(l: ^Lexer) -> (t: Token, ok: bool) { if !lexer_advance_until(l, `"`) do return t, false end := l.cursor defer lexer_advance(l, 1) - return Token_Span{kind = .Double_Quote_String_Literal, start = start, end = end}, true + return Token{kind = .Double_Quote_String_Literal, start = start, end = end}, true } if peek := lexer_peek(l, 1); peek == `'` { @@ -146,7 +140,7 @@ _lexer_next :: proc(l: ^Lexer) -> (t: Token, ok: bool) { if !lexer_advance_until(l, `'`) do return t, false end := l.cursor defer lexer_advance(l, 1) - return Token_Span{kind = .Single_Quote_String_Literal, start = start, end = end}, true + return Token{kind = .Single_Quote_String_Literal, start = start, end = end}, true } return @@ -163,56 +157,63 @@ lexer_advance :: proc(l: ^Lexer, step: u32 = 1) -> (ok: bool) { return l.cursor < u32(len(l.source)) } -lexer_advance_until :: proc { - lexer_advance_until_string, - lexer_advance_until_proc, +// lexer_advance_until advances the cursor until the source at the cursor +// matches `target`. Returns true if a match was found, false on EOF. +lexer_advance_until :: proc(l: ^Lexer, target: string) -> bool { + for { + if lexer_peek(l, u32(len(target))) == target { + return true + } + if !lexer_advance(l) { + return false + } + } } +// lexer_advance_while_letters advances the cursor while the byte at cursor+1 +// is a letter, hyphen, or underscore. Returns true if stopped at a non-identifier +// byte, false on EOF. lexer_advance_while_letters :: proc(l: ^Lexer) -> bool { - return lexer_advance_until_proc(l, proc(l: ^Lexer) -> bool { - peek := lexer_peek(l, 2) - if peek == "" { + for { + b, ok := lexer_peek_byte(l, 1) + if !ok { return false } - return !(unicode.is_letter(rune(peek[1])) || peek[1] == '-' || peek[1] == '_') - }) + if !(unicode.is_letter(rune(b)) || b == '-' || b == '_') { + return true + } + if !lexer_advance(l) { + return false + } + } } +// lexer_advance_until_space advances the cursor until the byte at cursor+1 +// is whitespace. Returns true if stopped, false on EOF. lexer_advance_until_space :: proc(l: ^Lexer) -> bool { - return lexer_advance_until_proc(l, proc(l: ^Lexer) -> bool { - peek := lexer_peek(l, 2) - if peek == "" { + for { + b, ok := lexer_peek_byte(l, 1) + if !ok { return false } - return unicode.is_space(rune(peek[1])) - }) -} - -lexer_advance_until_space_or :: proc(l: ^Lexer, or: byte) -> bool { - or := or - context.user_ptr = &or - return lexer_advance_until_proc(l, proc(l: ^Lexer) -> bool { - or: ^byte = auto_cast context.user_ptr - peek := lexer_peek(l, 2) - if peek == "" { + if unicode.is_space(rune(b)) { + return true + } + if !lexer_advance(l) { return false } - return unicode.is_space(rune(peek[1])) || peek[1] == or^ - }) -} - -lexer_advance_until_string :: proc(l: ^Lexer, target: string) -> bool { - target := target - context.user_ptr = &target - return lexer_advance_until_proc(l, proc(l: ^Lexer) -> bool { - target := cast(^string)(context.user_ptr) - return lexer_peek(l) == target^ - }) + } } -lexer_advance_until_proc :: proc(l: ^Lexer, predicate: proc(l: ^Lexer) -> bool) -> bool { +// lexer_advance_until_space_or advances the cursor until the byte at cursor+1 +// is whitespace or equals `or`. Returns true if stopped, false on EOF. +lexer_advance_until_space_or :: proc(l: ^Lexer, or: byte) -> bool { for { - if predicate(l) { + b, ok := lexer_peek_byte(l, 1) + if !ok { + return false + } + if unicode.is_space(rune(b)) || b == or { return true } if !lexer_advance(l) { @@ -232,6 +233,15 @@ lexer_peek :: proc(l: ^Lexer, step: u32 = 1) -> (p: string) { return l.source[start:end] } +// lexer_peek_byte returns the byte at cursor+offset without advancing. +lexer_peek_byte :: proc(l: ^Lexer, offset: u32 = 0) -> (b: byte, ok: bool) { + pos := l.cursor + offset + if pos >= u32(len(l.source)) { + return 0, false + } + return l.source[pos], true +} + // lexer_peek_token returns the next token without advancing the lexer. lexer_peek_token :: proc(l: ^Lexer) -> (t: Token, ok: bool) { start := l.cursor @@ -240,37 +250,30 @@ lexer_peek_token :: proc(l: ^Lexer) -> (t: Token, ok: bool) { } lexer_skip_space :: proc(l: ^Lexer) { - lexer_advance_until_proc(l, proc(l: ^Lexer) -> bool { - peek := lexer_peek(l) - - if peek == "" { - return true + for { + b, ok := lexer_peek_byte(l) + if !ok { + return } - - return !unicode.is_space(rune(peek[0])) - }) + if !unicode.is_space(rune(b)) { + return + } + if !lexer_advance(l) { + return + } + } } // token_kind returns the kind for the token. token_kind :: proc(t: Token) -> Token_Kind { - switch t in t { - case Token_Atom: - return t.kind - case Token_Span: - return t.kind - } - return nil + return t.kind } // token_lookup returns the substring corresponding to the token. +// For atoms (end == start) this is the single byte at `start`; +// for spans it is the inclusive range [start, end]. token_lookup :: proc(t: Token, text: string) -> string { - switch t in t { - case Token_Atom: - return text[t.start:t.start + 1] - case Token_Span: - return text[t.start:t.end + 1] - } - return "" + return text[t.start:t.end + 1] } @(private = "file") @@ -319,9 +322,9 @@ _lexer_test :: proc(t: ^testing.T, source: string, want: []Token, loc := #caller test_lex_comment :: proc(t: ^testing.T) { source := "<!-- comment -->" want := []Token { - Token_Span{kind = .Comment_Start, start = 0, end = 3}, - Token_Span{kind = .Raw_Text, start = 4, end = 12}, - Token_Span{kind = .Comment_End, start = 13, end = 15}, + Token{kind = .Comment_Start, start = 0, end = 3}, + Token{kind = .Raw_Text, start = 4, end = 12}, + Token{kind = .Comment_End, start = 13, end = 15}, } _lexer_test(t, source, want) } @@ -330,9 +333,9 @@ test_lex_comment :: proc(t: ^testing.T) { test_lex_comment_2 :: proc(t: ^testing.T) { source := "<!--comment-->" want := []Token { - Token_Span{kind = .Comment_Start, start = 0, end = 3}, - Token_Span{kind = .Raw_Text, start = 4, end = 10}, - Token_Span{kind = .Comment_End, start = 11, end = 13}, + Token{kind = .Comment_Start, start = 0, end = 3}, + Token{kind = .Raw_Text, start = 4, end = 10}, + Token{kind = .Comment_End, start = 11, end = 13}, } _lexer_test(t, source, want) } @@ -341,9 +344,9 @@ test_lex_comment_2 :: proc(t: ^testing.T) { test_lex_comment_3 :: proc(t: ^testing.T) { source := "<!-- \ncomment\n -->" want := []Token { - Token_Span{kind = .Comment_Start, start = 0, end = 3}, - Token_Span{kind = .Raw_Text, start = 4, end = 14}, - Token_Span{kind = .Comment_End, start = 15, end = 17}, + Token{kind = .Comment_Start, start = 0, end = 3}, + Token{kind = .Raw_Text, start = 4, end = 14}, + Token{kind = .Comment_End, start = 15, end = 17}, } _lexer_test(t, source, want) } @@ -351,14 +354,14 @@ test_lex_comment_3 :: proc(t: ^testing.T) { @(test) test_lex_doctype :: proc(t: ^testing.T) { source := "<!DOCTYPE html>" - want := []Token{Token_Span{kind = .Doctype, start = 0, end = 14}} + want := []Token{Token{kind = .Doctype, start = 0, end = 14}} _lexer_test(t, source, want) } @(test) test_lex_exotic_doctype :: proc(t: ^testing.T) { source := "<!DOCTYPE foobar>" - want := []Token{Token_Span{kind = .Doctype, start = 0, end = 16}} + want := []Token{Token{kind = .Doctype, start = 0, end = 16}} _lexer_test(t, source, want) } @@ -366,9 +369,9 @@ test_lex_exotic_doctype :: proc(t: ^testing.T) { test_lex_self_closing_tag :: proc(t: ^testing.T) { source := `<tag />` want := []Token { - Token_Span{kind = .Open_Tag_Start, start = 1, end = 3}, - Token_Span{kind = .Raw_Text, start = 4, end = 4}, - Token_Span{kind = .Self_Closing_Tag_End, start = 5, end = 6}, + Token{kind = .Open_Tag_Start, start = 1, end = 3}, + Token{kind = .Raw_Text, start = 4, end = 4}, + Token{kind = .Self_Closing_Tag_End, start = 5, end = 6}, } _lexer_test(t, source, want) } @@ -377,8 +380,8 @@ test_lex_self_closing_tag :: proc(t: ^testing.T) { test_lex_self_closing_tag_2 :: proc(t: ^testing.T) { source := `<tag/>` want := []Token { - Token_Span{kind = .Open_Tag_Start, start = 1, end = 3}, - Token_Span{kind = .Self_Closing_Tag_End, start = 4, end = 5}, + Token{kind = .Open_Tag_Start, start = 1, end = 3}, + Token{kind = .Self_Closing_Tag_End, start = 4, end = 5}, } _lexer_test(t, source, want) } @@ -387,8 +390,8 @@ test_lex_self_closing_tag_2 :: proc(t: ^testing.T) { test_lex_open_tag :: proc(t: ^testing.T) { source := "<tag>" want := []Token { - Token_Span{kind = .Open_Tag_Start, start = 1, end = 3}, - Token_Atom{kind = .Tag_End, start = 4}, + Token{kind = .Open_Tag_Start, start = 1, end = 3}, + Token{kind = .Tag_End, start = 4, end = 4}, } _lexer_test(t, source, want) } @@ -397,8 +400,8 @@ test_lex_open_tag :: proc(t: ^testing.T) { test_lex_close_tag :: proc(t: ^testing.T) { source := "</tag>" want := []Token { - Token_Span{kind = .Close_Tag_Start, start = 2, end = 4}, - Token_Atom{kind = .Tag_End, start = 5}, + Token{kind = .Close_Tag_Start, start = 2, end = 4}, + Token{kind = .Tag_End, start = 5, end = 5}, } _lexer_test(t, source, want) } @@ -407,10 +410,10 @@ test_lex_close_tag :: proc(t: ^testing.T) { test_lex_tag_pair :: proc(t: ^testing.T) { source := "<tag></tag>" want := []Token { - Token_Span{kind = .Open_Tag_Start, start = 1, end = 3}, - Token_Atom{kind = .Tag_End, start = 4}, - Token_Span{kind = .Close_Tag_Start, start = 7, end = 9}, - Token_Atom{kind = .Tag_End, start = 10}, + Token{kind = .Open_Tag_Start, start = 1, end = 3}, + Token{kind = .Tag_End, start = 4, end = 4}, + Token{kind = .Close_Tag_Start, start = 7, end = 9}, + Token{kind = .Tag_End, start = 10, end = 10}, } _lexer_test(t, source, want) } @@ -419,13 +422,13 @@ test_lex_tag_pair :: proc(t: ^testing.T) { test_lex_tag_pair_with_comment :: proc(t: ^testing.T) { source := "<tag><!--comment--></tag>" want := []Token { - Token_Span{kind = .Open_Tag_Start, start = 1, end = 3}, - Token_Atom{kind = .Tag_End, start = 4}, - Token_Span{kind = .Comment_Start, start = 5, end = 8}, - Token_Span{kind = .Raw_Text, start = 9, end = 15}, - Token_Span{kind = .Comment_End, start = 16, end = 18}, - Token_Span{kind = .Close_Tag_Start, start = 21, end = 23}, - Token_Atom{kind = .Tag_End, start = 24}, + Token{kind = .Open_Tag_Start, start = 1, end = 3}, + Token{kind = .Tag_End, start = 4, end = 4}, + Token{kind = .Comment_Start, start = 5, end = 8}, + Token{kind = .Raw_Text, start = 9, end = 15}, + Token{kind = .Comment_End, start = 16, end = 18}, + Token{kind = .Close_Tag_Start, start = 21, end = 23}, + Token{kind = .Tag_End, start = 24, end = 24}, } _lexer_test(t, source, want) } @@ -434,11 +437,11 @@ test_lex_tag_pair_with_comment :: proc(t: ^testing.T) { test_lex_open_tag_value_attribute :: proc(t: ^testing.T) { source := `<tag class=".style">` want := []Token { - Token_Span{kind = .Open_Tag_Start, start = 1, end = 3}, - Token_Span{kind = .Raw_Text, start = 4, end = 9}, - Token_Atom{kind = .Assign, start = 10}, - Token_Span{kind = .Double_Quote_String_Literal, start = 11, end = 18}, - Token_Atom{kind = .Tag_End, start = 19}, + Token{kind = .Open_Tag_Start, start = 1, end = 3}, + Token{kind = .Raw_Text, start = 4, end = 9}, + Token{kind = .Assign, start = 10, end = 10}, + Token{kind = .Double_Quote_String_Literal, start = 11, end = 18}, + Token{kind = .Tag_End, start = 19, end = 19}, } _lexer_test(t, source, want) } @@ -447,12 +450,12 @@ test_lex_open_tag_value_attribute :: proc(t: ^testing.T) { test_lex_open_tag_naked_attribute :: proc(t: ^testing.T) { source := `<tag class=".style" foobar>` want := []Token { - Token_Span{kind = .Open_Tag_Start, start = 1, end = 3}, - Token_Span{kind = .Raw_Text, start = 4, end = 9}, - Token_Atom{kind = .Assign, start = 10}, - Token_Span{kind = .Double_Quote_String_Literal, start = 11, end = 18}, - Token_Span{kind = .Raw_Text, start = 19, end = 25}, - Token_Atom{kind = .Tag_End, start = 26}, + Token{kind = .Open_Tag_Start, start = 1, end = 3}, + Token{kind = .Raw_Text, start = 4, end = 9}, + Token{kind = .Assign, start = 10, end = 10}, + Token{kind = .Double_Quote_String_Literal, start = 11, end = 18}, + Token{kind = .Raw_Text, start = 19, end = 25}, + Token{kind = .Tag_End, start = 26, end = 26}, } _lexer_test(t, source, want) } @@ -461,24 +464,24 @@ test_lex_open_tag_naked_attribute :: proc(t: ^testing.T) { test_lex_nested_tags :: proc(t: ^testing.T) { source := `<tag><ul><li>one</li><li>two</li></ul></tag>` want := []Token { - Token_Span{kind = .Open_Tag_Start, start = 1, end = 3}, - Token_Atom{kind = .Tag_End, start = 4}, - Token_Span{kind = .Open_Tag_Start, start = 6, end = 7}, - Token_Atom{kind = .Tag_End, start = 8}, - Token_Span{kind = .Open_Tag_Start, start = 10, end = 11}, - Token_Atom{kind = .Tag_End, start = 12}, - Token_Span{kind = .Raw_Text, start = 13, end = 15}, - Token_Span{kind = .Close_Tag_Start, start = 18, end = 19}, - Token_Atom{kind = .Tag_End, start = 20}, - Token_Span{kind = .Open_Tag_Start, start = 22, end = 23}, - Token_Atom{kind = .Tag_End, start = 24}, - Token_Span{kind = .Raw_Text, start = 25, end = 27}, - Token_Span{kind = .Close_Tag_Start, start = 30, end = 31}, - Token_Atom{kind = .Tag_End, start = 32}, - Token_Span{kind = .Close_Tag_Start, start = 35, end = 36}, - Token_Atom{kind = .Tag_End, start = 37}, - Token_Span{kind = .Close_Tag_Start, start = 40, end = 42}, - Token_Atom{kind = .Tag_End, start = 43}, + Token{kind = .Open_Tag_Start, start = 1, end = 3}, + Token{kind = .Tag_End, start = 4, end = 4}, + Token{kind = .Open_Tag_Start, start = 6, end = 7}, + Token{kind = .Tag_End, start = 8, end = 8}, + Token{kind = .Open_Tag_Start, start = 10, end = 11}, + Token{kind = .Tag_End, start = 12, end = 12}, + Token{kind = .Raw_Text, start = 13, end = 15}, + Token{kind = .Close_Tag_Start, start = 18, end = 19}, + Token{kind = .Tag_End, start = 20, end = 20}, + Token{kind = .Open_Tag_Start, start = 22, end = 23}, + Token{kind = .Tag_End, start = 24, end = 24}, + Token{kind = .Raw_Text, start = 25, end = 27}, + Token{kind = .Close_Tag_Start, start = 30, end = 31}, + Token{kind = .Tag_End, start = 32, end = 32}, + Token{kind = .Close_Tag_Start, start = 35, end = 36}, + Token{kind = .Tag_End, start = 37, end = 37}, + Token{kind = .Close_Tag_Start, start = 40, end = 42}, + Token{kind = .Tag_End, start = 43, end = 43}, } _lexer_test(t, source, want) } @@ -487,26 +490,26 @@ test_lex_nested_tags :: proc(t: ^testing.T) { test_lex_raw_text_less_than :: proc(t: ^testing.T) { source := `<tag> 1 < 2 </tag>` want := []Token { - Token_Span{kind = .Open_Tag_Start, start = 1, end = 3}, - Token_Atom{kind = .Tag_End, start = 4}, - Token_Span{kind = .Raw_Text, start = 5, end = 11}, - Token_Span{kind = .Close_Tag_Start, start = 14, end = 16}, - Token_Atom{kind = .Tag_End, start = 17}, + Token{kind = .Open_Tag_Start, start = 1, end = 3}, + Token{kind = .Tag_End, start = 4, end = 4}, + Token{kind = .Raw_Text, start = 5, end = 11}, + Token{kind = .Close_Tag_Start, start = 14, end = 16}, + Token{kind = .Tag_End, start = 17, end = 17}, } _lexer_test(t, source, want) } @(test) test_lex_invalid_tag :: proc(t: ^testing.T) { - source := `<tag> one <two </tag>` + source := `<tag> one <two three </tag>` want := []Token { - Token_Span{kind = .Open_Tag_Start, start = 1, end = 3}, - Token_Atom{kind = .Tag_End, start = 4}, - Token_Span{kind = .Raw_Text, start = 5, end = 9}, - Token_Span{kind = .Open_Tag_Start, start = 11, end = 13}, - Token_Span{kind = .Raw_Text, start = 14, end = 14}, - Token_Span{kind = .Close_Tag_Start, start = 17, end = 19}, - Token_Atom{kind = .Tag_End, start = 20}, + Token{kind = .Open_Tag_Start, start = 1, end = 3}, + Token{kind = .Tag_End, start = 4, end = 4}, + Token{kind = .Raw_Text, start = 5, end = 9}, + Token{kind = .Open_Tag_Start, start = 11, end = 13}, + Token{kind = .Raw_Text, start = 14, end = 20}, + Token{kind = .Close_Tag_Start, start = 23, end = 25}, + Token{kind = .Tag_End, start = 26, end = 26}, } _lexer_test(t, source, want) } @@ -533,21 +536,13 @@ test_lex_fuzz_lexer :: proc(t: ^testing.T) { name := strconv.itoa(buf[:], int(time.time_to_unix(time.now()))) - if err := os.make_directory("fuzzdata", 0o755); err != nil && err != os.EEXIST { + if err := os.make_directory("fuzzdata"); err != nil && err != .Exist { log.errorf("preparing fuzzdata directory: %w", err) } - file_path := filepath.join([]string{"fuzzdata", name}) - defer delete(file_path) - - f, err := os.open(file_path, os.O_CREATE | os.O_RDWR | os.O_APPEND, 0o644) - if err != os.ERROR_NONE { - log.errorf("cannot open fuzz corpus file: %v", err) - } - - defer os.close(f) + file_path, _ := filepath.join([]string{"fuzzdata", name}) - if _, err := os.write_string(f, target^); err != nil { + if err := os.write_entire_file_from_string(file_path, target^); err != nil { log.errorf("writing to fuzz corpus file: %v", err) } @@ -558,28 +553,28 @@ test_lex_fuzz_lexer :: proc(t: ^testing.T) { // Run the known failed fuzz corpus. if FUZZ_RUN_FAILED { dir, err := os.open("fuzzdata") - if err != os.ERROR_NONE { + if err != nil { log.errorf("opening fuzzdata directory: %v", err) } defer os.close(dir) - entries, entries_err := os.read_dir(dir, -1) - if entries_err != os.ERROR_NONE { + entries, entries_err := os.read_directory(dir, -1, context.allocator) + if entries_err != nil { log.errorf("reading fuzzdata entries: %v", err) } defer { for entry in entries { - os.file_info_delete(entry) + os.file_info_delete(entry, context.allocator) } delete(entries) } for entry in entries { - input, input_err := os.read_entire_file_or_err(entry.fullpath) - if input_err != os.ERROR_NONE { - log.errorf("reading fuzzdata entry: %v", input_err) + input, input_err := os.read_entire_file_from_path(entry.fullpath, context.allocator) + if input_err != nil { + log.errorf("reading fuzzdata entry: %v", err) continue } @@ -624,13 +619,12 @@ lexer_print_tokens :: proc(l: ^Lexer) { for token in lexer_next(l) { context.allocator = context.temp_allocator defer free_all() - switch token in token { - case Token_Atom: + if token.start == token.end { log.debug(token) log.debugf("\t: %q \t(%v)", rune(l.source[token.start]), token) log.debugf("\t: %v", l.source) log.debugf("\t: %v^", strings.repeat(" ", int(token.start))) - case Token_Span: + } else { log.debugf("\t: %q \t(%v)", l.source[token.start:token.end + 1], token) log.debugf("\t: %v", l.source) padding := strings.repeat(" ", int(token.start)) @@ -652,4 +646,3 @@ random_string :: proc(char_set: string, length: int) -> strings.Builder { return b } -