odin-lang / Odin

Odin Programming Language
https://odin-lang.org
BSD 3-Clause "New" or "Revised" License
6.1k stars 550 forks source link

odin/parser: unable to parse when ternary expressions #3816

Open laytan opened 4 days ago

laytan commented 4 days ago

The core:odin/parser can't parse expressions like this:

LIB :: (
         "lib/cgltf.lib"      when ODIN_OS == .Windows
    else "lib/cgltf.a"        when ODIN_OS == .Linux
    else "lib/darwin/cgltf.a" when ODIN_OS == .Darwin
    else ""
)

as seen in vendor/cgltf/cgltf.odin.

More cases in vendor, but afaik the same backing error/bug:

``` cgltf.odin(5:52): expected 'else', got 'newline' cgltf.odin(6:2): expected an operand cgltf.odin(6:50): expected ')', got 'newline' cgltf.odin(7:2): 'else' unattached to an 'if' statement cgltf.odin(8:2): 'else' unattached to an 'if' statement cgltf.odin(9:1): expected a statement, got ) stb_image.odin(7:59): expected 'else', got 'newline' stb_image.odin(8:2): expected an operand stb_image.odin(8:57): expected ')', got 'newline' stb_image.odin(9:2): 'else' unattached to an 'if' statement stb_image.odin(10:2): 'else' unattached to an 'if' statement stb_image.odin(11:1): expected a statement, got ) stb_image_resize.odin(7:66): expected 'else', got 'newline' stb_image_resize.odin(8:2): expected an operand stb_image_resize.odin(8:64): expected ')', got 'newline' stb_image_resize.odin(9:2): 'else' unattached to an 'if' statement stb_image_resize.odin(10:2): 'else' unattached to an 'if' statement stb_image_resize.odin(11:1): expected a statement, got ) stb_image_write.odin(7:65): expected 'else', got 'newline' stb_image_write.odin(8:2): expected an operand stb_image_write.odin(8:63): expected ')', got 'newline' stb_image_write.odin(9:2): 'else' unattached to an 'if' statement stb_image_write.odin(10:2): 'else' unattached to an 'if' statement stb_image_write.odin(11:1): expected a statement, got ) stb_rect_pack.odin(9:63): expected 'else', got 'newline' stb_rect_pack.odin(10:2): expected an operand stb_rect_pack.odin(10:61): expected ')', got 'newline' stb_rect_pack.odin(11:2): 'else' unattached to an 'if' statement stb_rect_pack.odin(12:2): 'else' unattached to an 'if' statement stb_rect_pack.odin(13:1): expected a statement, got ) stb_truetype.odin(8:62): expected 'else', got 'newline' stb_truetype.odin(9:2): expected an operand stb_truetype.odin(9:60): expected ')', got 'newline' stb_truetype.odin(10:2): 'else' unattached to an 'if' statement stb_truetype.odin(11:2): 'else' unattached to an 'if' statement stb_truetype.odin(12:1): expected a statement, got ) stb_vorbis.odin(7:60): expected 'else', got 'newline' stb_vorbis.odin(8:2): expected an operand stb_vorbis.odin(8:58): expected ')', got 'newline' stb_vorbis.odin(9:2): 'else' unattached to an 'if' statement stb_vorbis.odin(10:2): 'else' unattached to an 'if' statement stb_vorbis.odin(11:1): expected a statement, got ) ```

I wrote this test to check each package in vendor:

```odin @test test_parse_core :: proc(t: ^testing.T) { allocator: virtual.Arena err := virtual.arena_init_growing(&allocator) testing.expect_value(t, err, nil) State :: struct { t: ^testing.T, allocator: runtime.Allocator, } state := State{ t, virtual.arena_allocator(&allocator), } path := filepath.join({ODIN_ROOT, "vendor"}) defer delete(path) errno := filepath.walk(path, walker, &state) testing.expect_value(t, errno, os.ERROR_NONE) handler :: proc(pos: tokenizer.Pos, msg: string, args: ..any) { log.error( fmt.tprintf("%s(%d:%d): ", pos.file, pos.line, pos.column), fmt.tprintf(msg, ..args), ) } walker :: proc(fi: os.File_Info, errno: os.Errno, state: rawptr) -> (os.Errno, bool) { state := (^State)(state) if !fi.is_dir { return os.ERROR_NONE, false } log.debugf("parsing %v", fi.fullpath) context.allocator = state.allocator defer free_all(context.allocator) _, ok := parser.parse_package_from_path(fi.fullpath, &parser.Parser{ flags = {.Optional_Semicolons}, err = handler, warn = handler, }) testing.expect(state.t, ok, fmt.tprintf("failed parsing %v", fi.fullpath)) return os.ERROR_NONE, false } } ```