parse.odin (8869B)
1 package main 2 3 import "core:bytes" 4 import "core:fmt" 5 import "core:strconv" 6 import "core:strings" 7 import "core:unicode" 8 9 Struct :: struct { 10 name: string, 11 fields: [dynamic]Struct_Field, 12 } 13 14 Struct_Field :: struct { 15 type: string, 16 name: string, 17 is_pointer: bool, 18 // Because we don't do generic expression parsing we must explicitly 19 // allow for function pointers. 20 function: Maybe(Function), 21 } 22 23 Function :: struct { 24 name: string, 25 return_type: string, 26 return_is_pointer: bool, 27 is_type: bool, 28 args: [dynamic]Function_Argument, 29 } 30 31 Function_Argument :: struct { 32 type: string, 33 name: string, 34 is_pointer: bool, 35 function: Maybe(Function), 36 } 37 38 Enum :: struct { 39 name: string, 40 type: string, 41 variants: [dynamic]Enum_Variant, 42 } 43 44 Enum_Variant :: struct { 45 name: string, 46 value: Maybe(uint), 47 } 48 49 Parser :: struct { 50 data: []byte, 51 offset: int, 52 line: int, 53 column: int, 54 enum_defs: [dynamic]Enum, 55 struct_defs: [dynamic]Struct, 56 function_defs: [dynamic]Function, 57 } 58 59 Parser_Proc :: #type proc(_: ^Parser) 60 61 parser_parse_struct :: proc(p: ^Parser) { 62 struct_def: Struct 63 parser_consume(p, "struct") 64 parser_skip_space(p) 65 struct_def.name = parser_consume_identifier(p) 66 parser_skip_space(p) 67 if !parser_check(p, "{") do return // not all structs have bodies 68 parser_consume(p, "{") 69 for { 70 parser_skip_space(p) 71 if parser_check(p, "};") { 72 break 73 } 74 append(&struct_def.fields, parser_parse_struct_field(p)) 75 } 76 parser_consume(p, "};") 77 append(&p.struct_defs, struct_def) 78 } 79 80 parser_check :: proc(p: ^Parser, text: string) -> bool { 81 return parser_peek(p, len(text)) == text 82 } 83 84 parser_consume :: proc(p: ^Parser, text: string) { 85 got := p.data[p.offset:p.offset + len(text)] 86 if !bytes.equal(got, transmute([]u8)text) { 87 panic( 88 fmt.tprintf( 89 "expected %q, got %q (offset=%d) (line=%d, col=%d) (context=%q)", 90 text, 91 got, 92 p.offset, 93 p.line + 1, 94 p.column, 95 p.data[p.offset - 10:p.offset + 10], 96 ), 97 ) 98 } 99 parser_skip_n(p, len(text)) 100 } 101 102 parser_consume_identifier :: proc(p: ^Parser) -> string { 103 parser_skip_text(p, "const ") 104 parser_skip_text(p, "struct ") 105 start := p.offset 106 for { 107 if !is_ident(p.data[p.offset]) { 108 return cast(string)p.data[start:p.offset] 109 } 110 if !parser_advance(p) { 111 break 112 } 113 } 114 panic("EOF before identifier") 115 } 116 117 is_ident :: proc(b: byte) -> bool { 118 r := rune(b) 119 return unicode.is_letter(r) || unicode.is_digit(r) || b == '_' 120 } 121 122 parser_skip_space :: proc(p: ^Parser) -> bool { 123 start := p.offset 124 for { 125 if p.data[p.offset] == '/' { 126 parser_skip_comment(p) 127 } 128 if !unicode.is_space(rune(p.data[p.offset])) { 129 return p.offset != start 130 } 131 if !parser_advance(p) { 132 break 133 } 134 } 135 return false 136 } 137 138 parser_parse_struct_field :: proc(p: ^Parser) -> (field: Struct_Field) { 139 ident := parser_consume_identifier(p) 140 parser_skip_space(p) 141 if parser_check(p, "*") { 142 parser_consume(p, "*") 143 field.is_pointer = true 144 } 145 parser_skip_space(p) 146 if !parser_check(p, "(") { 147 field.name = parser_consume_identifier(p) 148 } else { 149 field.function = parser_parse_function_pointer(p) 150 } 151 parser_consume(p, ";") 152 // Special case to ensure that the return type is appropriately labeled. 153 if field.function == nil { 154 field.type = ident 155 } else { 156 if f, ok := &field.function.?; ok { 157 f.return_type = ident 158 } 159 } 160 return 161 } 162 163 parser_parse_function_pointer :: proc(p: ^Parser) -> (f: Function) { 164 f.return_type = parser_consume_identifier(p) 165 parser_skip_space(p) 166 parser_consume(p, "(") 167 parser_skip_space(p) 168 parser_consume(p, "*") 169 parser_skip_space(p) 170 f.name = parser_consume_identifier(p) 171 parser_consume(p, ")") 172 parser_skip_space(p) 173 f.args = parser_parse_function_arguments(p) 174 return 175 } 176 177 parser_parse_function :: proc(p: ^Parser) { 178 f: Function 179 parser_consume(p, "_UI_EXTERN") 180 parser_skip_space(p) 181 f.return_type = parser_consume_identifier(p) 182 parser_skip_space(p) 183 if parser_check(p, "*") { 184 f.return_is_pointer = true 185 parser_consume(p, "*") 186 } 187 parser_skip_space(p) 188 f.name = parser_consume_identifier(p) 189 f.args = parser_parse_function_arguments(p) 190 parser_skip_space(p) 191 parser_consume(p, ";") 192 append(&p.function_defs, f) 193 return 194 } 195 196 parser_parse_function_arguments :: proc(p: ^Parser) -> (args: [dynamic]Function_Argument) { 197 parser_consume(p, "(") 198 for { 199 arg: Function_Argument 200 if parser_check(p, ")") { 201 break 202 } 203 parser_skip_space(p) 204 arg.type = parser_consume_identifier(p) 205 parser_skip_space(p) 206 if parser_check(p, "*") { 207 arg.is_pointer = true 208 parser_consume(p, "*") 209 } 210 parser_skip_space(p) 211 if parser_check(p, "(") { 212 fn := parser_parse_function_pointer(p) 213 fn.return_type = arg.type 214 arg.type = "" 215 arg.function = fn 216 } else { 217 arg.name = parser_consume_identifier(p) 218 } 219 220 if parser_check(p, ",") { 221 parser_consume(p, ",") 222 } 223 parser_skip_space(p) 224 append(&args, arg) 225 } 226 parser_consume(p, ")") 227 return 228 } 229 230 parser_skip_n :: proc(p: ^Parser, n: int = 1) { 231 for ii in 0 ..< n { 232 if !parser_advance(p) do return 233 } 234 } 235 236 parser_skip_text :: proc(p: ^Parser, text: string) { 237 if parser_check(p, text) { 238 parser_consume(p, text) 239 } 240 } 241 242 // TODO: might need to handle multiline preprocessor things 243 parser_skip_preproccessor_stuff :: proc(p: ^Parser) -> bool { 244 if !parser_check(p, "#") { 245 return false 246 } 247 parser_skip_until(p, "\n") 248 return true 249 } 250 251 parser_parse_enum :: proc(p: ^Parser) { 252 enum_def: Enum 253 parser_consume(p, "_UI_ENUM") 254 parser_consume(p, "(") 255 enum_def.name = parser_consume_identifier(p) 256 parser_consume(p, ")") 257 parser_skip_space(p) 258 parser_consume(p, "{") 259 for { 260 variant: Enum_Variant 261 parser_skip_space(p) 262 if parser_check(p, "};") { 263 break 264 } 265 append(&enum_def.variants, parser_parse_enum_variant(p)) 266 } 267 parser_consume(p, "};") 268 append(&p.enum_defs, enum_def) 269 } 270 271 parser_skip_comment :: proc(p: ^Parser) -> bool { 272 line_comment := parser_check(p, "//") 273 inline_comment := parser_check(p, "/*") 274 275 if line_comment { 276 parser_skip_until(p, "\n") 277 return true 278 } 279 280 if inline_comment { 281 parser_skip_until(p, "*/") 282 return true 283 } 284 285 return false 286 } 287 288 parser_skip_until :: proc(p: ^Parser, text: string) { 289 for { 290 if parser_check(p, text) { 291 defer parser_consume(p, text) 292 return 293 } 294 if !parser_advance(p) do return 295 } 296 } 297 298 parser_advance :: proc(p: ^Parser) -> bool { 299 if p.offset > len(p.data) - 1 { 300 return false 301 } 302 if p.data[p.offset] == '\n' { 303 p.line += 1 304 p.column = 0 305 } 306 p.column += 1 307 p.offset += 1 308 return p.offset < len(p.data) 309 } 310 311 parser_peek :: proc(p: ^Parser, n: int) -> string { 312 end := p.offset + n 313 if end > len(p.data) - 1 do return "" 314 return cast(string)p.data[p.offset:end] 315 } 316 317 bitwise_operators :: [?]string{"<<", ">>"} 318 319 parser_parse_enum_variant :: proc(p: ^Parser) -> (variant: Enum_Variant) { 320 variant.name = parser_consume_identifier(p) 321 parser_skip_space(p) 322 if parser_check(p, "=") { 323 parser_consume(p, "=") 324 parser_skip_space(p) 325 n_1 := parser_parse_int(p) 326 parser_skip_space(p) 327 // Because some enums use bitshifting we have to account for that, 328 // but because we aren't doing proper expresion parsing it's total 329 // jank. 330 for op in bitwise_operators { 331 if !parser_check(p, op) do continue 332 parser_consume(p, op) 333 parser_skip_space(p) 334 n_2 := parser_parse_int(p) 335 switch op { 336 case "<<": 337 n_1 = n_1 << n_2 338 case ">>": 339 n_1 = n_1 << n_2 340 } 341 } 342 variant.value = n_1 343 } 344 parser_consume(p, ",") 345 return 346 } 347 348 parser_parse_int :: proc(p: ^Parser) -> uint { 349 start := p.offset 350 index: int 351 for unicode.is_digit(rune(p.data[p.offset + index])) { 352 index += 1 353 } 354 n, n_ok := strconv.parse_uint(cast(string)p.data[p.offset:p.offset + index]) 355 if !n_ok { 356 panic("not a number") 357 } 358 parser_skip_n(p, index) 359 return n 360 } 361 362 // We are only handling typedef'd function pointers. 363 parser_parse_typedef :: proc(p: ^Parser) { 364 parser_consume(p, "typedef") 365 parser_skip_space(p) 366 if parser_check(p, "struct") { 367 parser_skip_until(p, ";") 368 return 369 } 370 function := parser_parse_function_pointer(p) 371 function.is_type = true 372 parser_skip_until(p, ";") 373 append(&p.function_defs, function) 374 } 375 376 keywords :: [?]string{"_UI_ENUM", "_UI_EXTERN", "typedef", "struct"} 377 378 parser_parse :: proc(p: ^Parser, by: []byte) { 379 p.data = by 380 381 for p.offset < len(p.data) - 1 { 382 skipped_space := parser_skip_space(p) 383 skipped_comment := parser_skip_comment(p) 384 skipped_preprocessor := parser_skip_preproccessor_stuff(p) 385 386 start := p.offset 387 388 389 defer if !(skipped_space || skipped_comment || skipped_preprocessor) { 390 end := p.offset 391 392 if start == end { 393 parser_skip_n(p, 1) 394 } 395 } 396 397 for keyword in keywords { 398 if !parser_check(p, keyword) { 399 continue 400 } 401 switch keyword { 402 case "struct": 403 parser_parse_struct(p) 404 case "_UI_ENUM": 405 parser_parse_enum(p) 406 case "_UI_EXTERN": 407 parser_parse_function(p) 408 case "typedef": 409 parser_parse_typedef(p) 410 } 411 } 412 } 413 } 414