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