type.ha (11079B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use fmt; 5 use hare::ast; 6 use hare::ast::{variadism}; 7 use hare::lex; 8 use io; 9 use memio; 10 use strings; 11 12 // Returns a builtin type as a string. 13 export fn builtin_type(b: ast::builtin_type) str = switch (b) { 14 case ast::builtin_type::FCONST, ast::builtin_type::ICONST, 15 ast::builtin_type::RCONST => 16 abort("ICONST, FCONST, and RCONST have no lexical representation"); 17 case ast::builtin_type::BOOL => 18 yield "bool"; 19 case ast::builtin_type::DONE => 20 yield "done"; 21 case ast::builtin_type::F32 => 22 yield "f32"; 23 case ast::builtin_type::F64 => 24 yield "f64"; 25 case ast::builtin_type::I16 => 26 yield "i16"; 27 case ast::builtin_type::I32 => 28 yield "i32"; 29 case ast::builtin_type::I64 => 30 yield "i64"; 31 case ast::builtin_type::I8 => 32 yield "i8"; 33 case ast::builtin_type::INT => 34 yield "int"; 35 case ast::builtin_type::NEVER => 36 yield "never"; 37 case ast::builtin_type::NULL => 38 yield "null"; 39 case ast::builtin_type::OPAQUE => 40 yield "opaque"; 41 case ast::builtin_type::RUNE => 42 yield "rune"; 43 case ast::builtin_type::SIZE => 44 yield "size"; 45 case ast::builtin_type::STR => 46 yield "str"; 47 case ast::builtin_type::U16 => 48 yield "u16"; 49 case ast::builtin_type::U32 => 50 yield "u32"; 51 case ast::builtin_type::U64 => 52 yield "u64"; 53 case ast::builtin_type::U8 => 54 yield "u8"; 55 case ast::builtin_type::UINT => 56 yield "uint"; 57 case ast::builtin_type::UINTPTR => 58 yield "uintptr"; 59 case ast::builtin_type::VALIST => 60 yield "valist"; 61 case ast::builtin_type::VOID => 62 yield "void"; 63 }; 64 65 fn prototype( 66 ctx: *context, 67 syn: *synfunc, 68 t: *ast::func_type, 69 ) (size | io::error) = { 70 let n = 0z; 71 n += syn(ctx, "(", synkind::PUNCTUATION)?; 72 73 for (let i = 0z; i < len(t.params); i += 1) { 74 const param = &t.params[i]; 75 if (param.name != "") { 76 n += syn(ctx, param.name, synkind::SECONDARY)?; 77 n += syn(ctx, ":", synkind::PUNCTUATION)?; 78 n += space(ctx)?; 79 }; 80 n += __type(ctx, syn, param._type)?; 81 match (param.default_value) { 82 case void => 83 yield; 84 case let e: ast::expr => 85 n += space(ctx)?; 86 n += syn(ctx, "=", synkind::PUNCTUATION)?; 87 n += space(ctx)?; 88 n += _expr(ctx, syn, &e)?; 89 }; 90 if (i + 1 < len(t.params) || t.variadism == variadism::C) { 91 n += syn(ctx, ",", synkind::PUNCTUATION)?; 92 n += space(ctx)?; 93 }; 94 }; 95 if (t.variadism != variadism::NONE) { 96 n += syn(ctx, "...", synkind::OPERATOR)?; 97 }; 98 99 n += syn(ctx, ")", synkind::PUNCTUATION)?; 100 n += space(ctx)?; 101 n += __type(ctx, syn, t.result)?; 102 return n; 103 }; 104 105 fn struct_union_type( 106 ctx: *context, 107 syn: *synfunc, 108 t: *ast::_type, 109 ) (size | io::error) = { 110 let z = 0z; 111 let membs = match (t.repr) { 112 case let st: ast::struct_type => 113 z += syn(ctx, "struct", synkind::TYPE)?; 114 z += space(ctx)?; 115 if (st.packed) { 116 z += syn(ctx, "@packed", synkind::ATTRIBUTE)?; 117 z += space(ctx)?; 118 }; 119 z += syn(ctx, "{", synkind::PUNCTUATION)?; 120 yield st.members: []ast::struct_member; 121 case let ut: ast::union_type => 122 z += syn(ctx, "union", synkind::TYPE)?; 123 z += space(ctx)?; 124 z += syn(ctx, "{", synkind::PUNCTUATION)?; 125 yield ut: []ast::struct_member; 126 case => abort(); // unreachable 127 }; 128 129 ctx.indent += 1z; 130 for (let memb .. membs) { 131 z += fmt::fprintln(ctx.out)?; 132 ctx.linelen = 0; 133 if (memb.docs != "") { 134 z += comment(ctx, syn, memb.docs)?; 135 }; 136 for (let i = 0z; i < ctx.indent; i += 1) { 137 z += fmt::fprint(ctx.out, "\t")?; 138 ctx.linelen += 8; 139 }; 140 141 match (memb._offset) { 142 case null => void; 143 case let ex: *ast::expr => 144 z += syn(ctx, "@offset(", synkind::ATTRIBUTE)?; 145 z += _expr(ctx, syn, ex)?; 146 z += syn(ctx, ")", synkind::ATTRIBUTE)?; 147 z += space(ctx)?; 148 }; 149 150 match (memb.member) { 151 case let se: ast::struct_embedded => 152 z += __type(ctx, syn, se)?; 153 case let sa: ast::struct_alias => 154 z += _ident(ctx, syn, sa, synkind::IDENT)?; 155 case let sf: ast::struct_field => 156 z += syn(ctx, sf.name, synkind::SECONDARY)?; 157 z += syn(ctx, ":", synkind::PUNCTUATION)?; 158 z += space(ctx)?; 159 z += __type(ctx, syn, sf._type)?; 160 }; 161 162 z += syn(ctx, ",", synkind::PUNCTUATION)?; 163 }; 164 165 ctx.indent -= 1; 166 z += newline(ctx)?; 167 z += syn(ctx, "}", synkind::PUNCTUATION)?; 168 return z; 169 }; 170 171 fn multiline_comment(s: str) bool = 172 strings::byteindex(s, '\n') as size != len(s) - 1; 173 174 // Unparses a [[hare::ast::_type]]. 175 export fn _type( 176 out: io::handle, 177 syn: *synfunc, 178 t: *ast::_type, 179 ) (size | io::error) = { 180 let ctx = context { 181 out = out, 182 ... 183 }; 184 return __type(&ctx, syn, t); 185 }; 186 187 fn __type(ctx: *context, syn: *synfunc, t: *ast::_type) (size | io::error) = { 188 ctx.stack = &stack { 189 cur = t, 190 up = ctx.stack, 191 ... 192 }; 193 defer { 194 let stack = &(ctx.stack as *stack); 195 match (stack.extra) { 196 case let p: *opaque => 197 free(p); 198 case null => void; 199 }; 200 ctx.stack = stack.up; 201 }; 202 203 let n = 0z; 204 if (t.flags & ast::type_flag::CONST != 0) { 205 n += syn(ctx, "const", synkind::TYPE)?; 206 n += space(ctx)?; 207 }; 208 if (t.flags & ast::type_flag::ERROR != 0) { 209 n += syn(ctx, "!", synkind::TYPE)?; 210 }; 211 match (t.repr) { 212 case let a: ast::alias_type => 213 if (a.unwrap) { 214 n += syn(ctx, "...", synkind::TYPE)?; 215 }; 216 n += _ident(ctx, syn, a.ident, synkind::TYPE)?; 217 case let b: ast::builtin_type => 218 n += syn(ctx, builtin_type(b), synkind::TYPE)?; 219 case let e: ast::enum_type => 220 n += syn(ctx, "enum", synkind::TYPE)?; 221 n += space(ctx)?; 222 if (e.storage != ast::builtin_type::INT) { 223 n += syn(ctx, builtin_type(e.storage), synkind::TYPE)?; 224 n += space(ctx)?; 225 }; 226 n += syn(ctx, "{", synkind::PUNCTUATION)?; 227 ctx.indent += 1; 228 n += fmt::fprintln(ctx.out)?; 229 ctx.linelen = 0; 230 for (let value .. e.values) { 231 let wrotedocs = false; 232 if (value.docs != "") { 233 // Check if comment should go above or next to 234 // field 235 if (multiline_comment(value.docs)) { 236 n += comment(ctx, syn, value.docs)?; 237 wrotedocs = true; 238 }; 239 }; 240 for (let i = 0z; i < ctx.indent; i += 1) { 241 n += fmt::fprint(ctx.out, "\t")?; 242 ctx.linelen += 8; 243 }; 244 n += syn(ctx, value.name, synkind::SECONDARY)?; 245 match (value.value) { 246 case null => void; 247 case let e: *ast::expr => 248 n += space(ctx)?; 249 n += syn(ctx, "=", synkind::OPERATOR)?; 250 n += space(ctx)?; 251 n += _expr(ctx, syn, e)?; 252 }; 253 n += syn(ctx, ",", synkind::PUNCTUATION)?; 254 if (value.docs != "" && !wrotedocs) { 255 n += space(ctx)?; 256 const oldindent = ctx.indent; 257 ctx.indent = 0; 258 n += comment(ctx, syn, value.docs)?; 259 ctx.indent = oldindent; 260 } else { 261 n += fmt::fprintln(ctx.out)?; 262 ctx.linelen = 0; 263 }; 264 }; 265 ctx.indent -= 1; 266 for (let i = 0z; i < ctx.indent; i += 1) { 267 n += fmt::fprint(ctx.out, "\t")?; 268 ctx.linelen += 8; 269 }; 270 n += syn(ctx, "}", synkind::PUNCTUATION)?; 271 case let f: ast::func_type => 272 n += syn(ctx, "fn", synkind::TYPE)?; 273 n += prototype(ctx, syn, &f)?; 274 case let l: ast::list_type => 275 n += syn(ctx, "[", synkind::TYPE)?; 276 match (l.length) { 277 case ast::len_slice => void; 278 case ast::len_unbounded => 279 n += syn(ctx, "*", synkind::TYPE)?; 280 case ast::len_contextual => 281 n += syn(ctx, "_", synkind::TYPE)?; 282 case let e: *ast::expr => 283 n += _expr(ctx, syn, e)?; 284 }; 285 n += syn(ctx, "]", synkind::TYPE)?; 286 n += __type(ctx, syn, l.members)?; 287 case let p: ast::pointer_type => 288 if (p.flags & ast::pointer_flag::NULLABLE != 0) { 289 n += syn(ctx, "nullable", synkind::TYPE)?; 290 n += space(ctx)?; 291 }; 292 n += syn(ctx, "*", synkind::TYPE)?; 293 n += __type(ctx, syn, p.referent)?; 294 case ast::struct_type => 295 n += struct_union_type(ctx, syn, t)?; 296 case ast::union_type => 297 n += struct_union_type(ctx, syn, t)?; 298 case let t: ast::tagged_type => 299 n += syn(ctx, "(", synkind::TYPE)?; 300 for (let i = 0z; i < len(t); i += 1) { 301 n += __type(ctx, syn, t[i])?; 302 if (i + 1 == len(t)) break; 303 n += space(ctx)?; 304 n += syn(ctx, "|", synkind::TYPE)?; 305 n += space(ctx)?; 306 }; 307 n += syn(ctx, ")", synkind::TYPE)?; 308 case let t: ast::tuple_type => 309 n += syn(ctx, "(", synkind::TYPE)?; 310 for (let i = 0z; i < len(t); i += 1) { 311 n += __type(ctx, syn, t[i])?; 312 if (i + 1 == len(t)) break; 313 n += syn(ctx, ",", synkind::TYPE)?; 314 n += space(ctx)?; 315 }; 316 n += syn(ctx, ")", synkind::TYPE)?; 317 }; 318 return n; 319 }; 320 321 fn type_test(t: *ast::_type, expected: str) void = { 322 let buf = memio::dynamic(); 323 _type(&buf, &syn_nowrap, t)!; 324 let s = memio::string(&buf)!; 325 defer free(s); 326 if (s != expected) { 327 fmt::errorfln("=== wanted\n{}", expected)!; 328 fmt::errorfln("=== got\n{}", s)!; 329 abort(); 330 }; 331 }; 332 333 @test fn _type() void = { 334 let loc = lex::location { 335 path = "<test>", 336 line = 0, 337 col = 0, 338 }; 339 let t = ast::_type { 340 start = loc, 341 end = loc, 342 flags = ast::type_flag::CONST, 343 repr = ast::alias_type { 344 unwrap = false, 345 ident = ["foo", "bar"], 346 }, 347 }; 348 let type_int = ast::_type { 349 start = loc, 350 end = loc, 351 flags = 0, 352 repr = ast::builtin_type::INT, 353 }; 354 let expr_void = ast::expr { 355 start = lex::location { ... }, 356 end = lex::location { ... }, 357 expr = void, 358 }; 359 360 type_test(&t, "const foo::bar"); 361 t.flags = 0; 362 t.repr = ast::alias_type { 363 unwrap = true, 364 ident = ["baz"], 365 }; 366 type_test(&t, "...baz"); 367 368 t.flags = ast::type_flag::ERROR; 369 t.repr = ast::builtin_type::INT; 370 type_test(&t, "!int"); 371 372 t.flags = ast::type_flag::CONST | ast::type_flag::ERROR; 373 t.repr = ast::enum_type { 374 storage = ast::builtin_type::U32, 375 values = [ 376 ast::enum_field { 377 name = "FOO", 378 value = null, 379 loc = loc, 380 docs = "", 381 }, 382 ast::enum_field { 383 name = "BAR", 384 value = &expr_void, 385 loc = loc, 386 docs = "", 387 }, 388 ], 389 }; 390 type_test(&t, "const !enum u32 {\n\tFOO,\n\tBAR = void,\n}"); 391 392 t.flags = 0; 393 394 t.repr = ast::func_type { 395 result = &type_int, 396 variadism = variadism::NONE, 397 params = [], 398 }; 399 type_test(&t, "fn() int"); 400 t.repr = ast::func_type { 401 result = &type_int, 402 variadism = variadism::C, 403 params = [ 404 ast::func_param { 405 loc = loc, 406 name = "", 407 _type = &type_int, 408 default_value = void, 409 }, 410 ], 411 }; 412 type_test(&t, "fn(int, ...) int"); 413 t.repr = ast::func_type { 414 result = &type_int, 415 variadism = variadism::HARE, 416 params = [ 417 ast::func_param { 418 loc = loc, 419 name = "foo", 420 _type = &type_int, 421 default_value = void, 422 }, 423 ast::func_param { 424 loc = loc, 425 name = "bar", 426 _type = &type_int, 427 default_value = void, 428 }, 429 ], 430 }; 431 type_test(&t, "fn(foo: int, bar: int...) int"); 432 433 t.repr = ast::list_type { 434 length = ast::len_slice, 435 members = &type_int, 436 }; 437 type_test(&t, "[]int"); 438 t.repr = ast::list_type { 439 length = ast::len_unbounded, 440 members = &type_int, 441 }; 442 type_test(&t, "[*]int"); 443 t.repr = ast::list_type { 444 length = ast::len_contextual, 445 members = &type_int, 446 }; 447 type_test(&t, "[_]int"); 448 t.repr = ast::list_type { 449 length = &expr_void, 450 members = &type_int, 451 }; 452 type_test(&t, "[void]int"); 453 454 t.repr = ast::pointer_type { 455 referent = &type_int, 456 flags = 0, 457 }; 458 type_test(&t, "*int"); 459 t.repr = ast::pointer_type { 460 referent = &type_int, 461 flags = ast::pointer_flag::NULLABLE, 462 }; 463 type_test(&t, "nullable *int"); 464 465 t.repr = [&type_int, &type_int]: ast::tagged_type; 466 type_test(&t, "(int | int)"); 467 468 t.repr = [&type_int, &type_int]: ast::tuple_type; 469 type_test(&t, "(int, int)"); 470 };