hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

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 };