hare

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

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