hare

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

expr.ha (26201B)


      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::{binarithm_op};
      7 use hare::lex;
      8 use hare::lex::{ltok};
      9 use io;
     10 use strings;
     11 
     12 // Unparses a [[hare::ast::expr]].
     13 export fn expr(
     14 	out: io::handle,
     15 	syn: *synfunc,
     16 	e: *ast::expr,
     17 ) (size | io::error) = {
     18 	let ctx = context {
     19 		out = out,
     20 		...
     21 	};
     22 	return _expr(&ctx, syn, e);
     23 };
     24 
     25 fn _expr(ctx: *context, syn: *synfunc, e: *ast::expr) (size | io::error) = {
     26 	ctx.stack = &stack {
     27 		cur = e,
     28 		up = ctx.stack,
     29 		...
     30 	};
     31 	defer {
     32 		let stack = &(ctx.stack as *stack);
     33 		match (stack.extra) {
     34 		case let p: *opaque =>
     35 			free(p);
     36 		case null => void;
     37 		};
     38 		ctx.stack = stack.up;
     39 	};
     40 
     41 	match (e.expr) {
     42 	case let e: ast::access_expr =>
     43 		match (e) {
     44 		case let id: ast::access_identifier =>
     45 			return _ident(ctx, syn, id, synkind::IDENT);
     46 		case let ix: ast::access_index =>
     47 			let z = 0z;
     48 			const needs_parens = !is_postfix(ix.object);
     49 			if (needs_parens) {
     50 				z += syn(ctx, "(", synkind::PUNCTUATION)?;
     51 			};
     52 			z += _expr(ctx, syn, ix.object)?;
     53 			if (needs_parens) {
     54 				z += syn(ctx, ")", synkind::PUNCTUATION)?;
     55 			};
     56 			z += syn(ctx, "[", synkind::PUNCTUATION)?;
     57 			z += _expr(ctx, syn, ix.index)?;
     58 			z += syn(ctx, "]", synkind::PUNCTUATION)?;
     59 			return z;
     60 		case let fi: ast::access_field =>
     61 			let z = 0z;
     62 			const needs_parens = !is_postfix(fi.object);
     63 			if (needs_parens) {
     64 				z += syn(ctx, "(", synkind::PUNCTUATION)?;
     65 			};
     66 			z += _expr(ctx, syn, fi.object)?;
     67 			if (needs_parens) {
     68 				z += syn(ctx, ")", synkind::PUNCTUATION)?;
     69 			};
     70 			z += syn(ctx, ".", synkind::OPERATOR)?;
     71 			z += syn(ctx, fi.field, synkind::SECONDARY)?;
     72 			return z;
     73 		case let tp: ast::access_tuple =>
     74 			let z = 0z;
     75 			const needs_parens = !is_postfix(tp.object);
     76 			if (needs_parens) {
     77 				z += syn(ctx, "(", synkind::PUNCTUATION)?;
     78 			};
     79 			z += _expr(ctx, syn, tp.object)?;
     80 			if (needs_parens) {
     81 				z += syn(ctx, ")", synkind::PUNCTUATION)?;
     82 			};
     83 			z += syn(ctx, ".", synkind::OPERATOR)?;
     84 			z += _expr(ctx, syn, tp.value)?;
     85 			return z;
     86 		};
     87 	case let e: ast::align_expr =>
     88 		let z = syn(ctx, "align", synkind::KEYWORD)?;
     89 		z += syn(ctx, "(", synkind::PUNCTUATION)?;
     90 		z += __type(ctx, syn, e)?;
     91 		z += syn(ctx, ")", synkind::PUNCTUATION)?;
     92 		return z;
     93 	case let e: ast::alloc_expr =>
     94 		let z = syn(ctx, "alloc", synkind::KEYWORD)?;
     95 		z += syn(ctx, "(", synkind::PUNCTUATION)?;
     96 		z += _expr(ctx, syn, e.init)?;
     97 		match (e.capacity) {
     98 		case null =>
     99 			if (e.form == ast::alloc_form::COPY) {
    100 				z += syn(ctx, "...", synkind::OPERATOR)?;
    101 			};
    102 		case let e: *ast::expr =>
    103 			z += syn(ctx, ",", synkind::PUNCTUATION)?;
    104 			z += space(ctx)?;
    105 			z += _expr(ctx, syn, e)?;
    106 		};
    107 		z += syn(ctx, ")", synkind::PUNCTUATION)?;
    108 		return z;
    109 	case ast::append_expr =>
    110 		return append_insert_expr(ctx, syn, e);
    111 	case let e: ast::assert_expr =>
    112 		return assert_expr(ctx, syn, &e);
    113 	case let e: ast::assign_expr =>
    114 		let z = 0z;
    115 		z += _expr(ctx, syn, e.object)?;
    116 		const op = match (e.op) {
    117 		case void =>
    118 			yield "=";
    119 		case let op: binarithm_op =>
    120 			yield switch (op) {
    121 			case binarithm_op::BAND =>
    122 				yield "&=";
    123 			case binarithm_op::LAND =>
    124 				yield "&&=";
    125 			case binarithm_op::BOR =>
    126 				yield "|=";
    127 			case binarithm_op::LOR =>
    128 				yield "||=";
    129 			case binarithm_op::DIV =>
    130 				yield "/=";
    131 			case binarithm_op::LSHIFT =>
    132 				yield "<<=";
    133 			case binarithm_op::MINUS =>
    134 				yield "-=";
    135 			case binarithm_op::MODULO =>
    136 				yield "%=";
    137 			case binarithm_op::PLUS =>
    138 				yield "+=";
    139 			case binarithm_op::RSHIFT =>
    140 				yield ">>=";
    141 			case binarithm_op::TIMES =>
    142 				yield "*=";
    143 			case binarithm_op::BXOR =>
    144 				yield "^=";
    145 			case binarithm_op::LXOR =>
    146 				yield "^^=";
    147 			case binarithm_op::GT, binarithm_op::GTEQ,
    148 				binarithm_op::LESS, binarithm_op::LESSEQ,
    149 				binarithm_op::LEQUAL, binarithm_op::NEQUAL =>
    150 				abort(); // unreachable
    151 			};
    152 		};
    153 		z += space(ctx)?;
    154 		z += syn(ctx, op, synkind::OPERATOR)?;
    155 		z += space(ctx)?;
    156 		z += _expr(ctx, syn, e.value)?;
    157 		return z;
    158 	case let e: ast::binarithm_expr =>
    159 		const prec = binprecedence(e.op);
    160 		let z = binexprval(ctx, syn, e.lvalue, prec)?;
    161 		z += space(ctx)?;
    162 		z += syn(ctx, switch (e.op) {
    163 		case binarithm_op::BAND =>
    164 			yield "&";
    165 		case binarithm_op::BOR =>
    166 			yield "|";
    167 		case binarithm_op::DIV =>
    168 			yield "/";
    169 		case binarithm_op::GT =>
    170 			yield ">";
    171 		case binarithm_op::GTEQ =>
    172 			yield ">=";
    173 		case binarithm_op::LAND =>
    174 			yield "&&";
    175 		case binarithm_op::LEQUAL =>
    176 			yield "==";
    177 		case binarithm_op::LESS =>
    178 			yield "<";
    179 		case binarithm_op::LESSEQ =>
    180 			yield "<=";
    181 		case binarithm_op::LOR =>
    182 			yield "||";
    183 		case binarithm_op::LSHIFT =>
    184 			yield "<<";
    185 		case binarithm_op::LXOR =>
    186 			yield "^^";
    187 		case binarithm_op::MINUS =>
    188 			yield "-";
    189 		case binarithm_op::MODULO =>
    190 			yield "%";
    191 		case binarithm_op::NEQUAL =>
    192 			yield "!=";
    193 		case binarithm_op::PLUS =>
    194 			yield "+";
    195 		case binarithm_op::RSHIFT =>
    196 			yield ">>";
    197 		case binarithm_op::TIMES =>
    198 			yield "*";
    199 		case binarithm_op::BXOR =>
    200 			yield "^";
    201 		}, synkind::OPERATOR)?;
    202 		z += space(ctx)?;
    203 		z += binexprval(ctx, syn, e.rvalue, prec)?;
    204 		return z;
    205 	case let e: ast::binding_expr =>
    206 		let z = 0z;
    207 		if (e.is_static) {
    208 			z += syn(ctx, "static", synkind::KEYWORD)?;
    209 			z += space(ctx)?;
    210 		};
    211 		switch (e.kind) {
    212 		case ast::binding_kind::DEF =>
    213 			z += syn(ctx, "def", synkind::KEYWORD)?;
    214 		case ast::binding_kind::CONST =>
    215 			z += syn(ctx, "const", synkind::KEYWORD)?;
    216 		case ast::binding_kind::LET =>
    217 			z += syn(ctx, "let", synkind::KEYWORD)?;
    218 		};
    219 		z += space(ctx)?;
    220 		for (let i = 0z; i < len(e.bindings); i += 1) {
    221 			let binding = e.bindings[i];
    222 			match (binding.name) {
    223 			case let s: str =>
    224 				z += syn(ctx, s, synkind::IDENT)?;
    225 			case let u: ast::binding_unpack =>
    226 				z += syn(ctx, "(", synkind::PUNCTUATION)?;
    227 				for (let i = 0z; i < len(u); i += 1) {
    228 					match (u[i]) {
    229 					case let s: str =>
    230 						z += syn(ctx, s,
    231 							synkind::IDENT)?;
    232 					case void =>
    233 						z += syn(ctx, "_",
    234 							synkind::OPERATOR)?;
    235 					};
    236 					if (i + 1 < len(u)) {
    237 						z += syn(ctx, ",",
    238 							synkind::PUNCTUATION)?;
    239 						z += space(ctx)?;
    240 					};
    241 				};
    242 				z += syn(ctx, ")", synkind::PUNCTUATION)?;
    243 			};
    244 			match (binding._type) {
    245 			case let t: *ast::_type =>
    246 				z += syn(ctx, ":", synkind::PUNCTUATION)?;
    247 				z += space(ctx)?;
    248 				z += __type(ctx, syn, t)?;
    249 			case null => void;
    250 			};
    251 			z += space(ctx)?;
    252 			z += syn(ctx, "=", synkind::OPERATOR)?;
    253 			z += space(ctx)?;
    254 			z += _expr(ctx, syn, binding.init)?;
    255 			if (i + 1 < len(e.bindings)) {
    256 				z += syn(ctx, ",", synkind::PUNCTUATION)?;
    257 				z += space(ctx)?;
    258 			};
    259 		};
    260 		return z;
    261 	case let e: ast::break_expr =>
    262 		let z = syn(ctx, "break", synkind::KEYWORD)?;
    263 		if (e != "") {
    264 			z += space(ctx)?;
    265 			z += syn(ctx, ":", synkind::LABEL)?;
    266 			z += syn(ctx, e, synkind::LABEL)?;
    267 		};
    268 		return z;
    269 	case let e: ast::call_expr =>
    270 		let z = 0z;
    271 		const needs_parens = !is_postfix(e.lvalue);
    272 		if (needs_parens) {
    273 			z += syn(ctx, "(", synkind::PUNCTUATION)?;
    274 		};
    275 		z += _expr(ctx, syn, e.lvalue)?;
    276 		if (needs_parens) {
    277 			z += syn(ctx, ")", synkind::PUNCTUATION)?;
    278 		};
    279 		z += syn(ctx, "(", synkind::PUNCTUATION)?;
    280 		for (let i = 0z; i < len(e.args); i += 1) {
    281 			z += _expr(ctx, syn, e.args[i])?;
    282 			if (i + 1 < len(e.args)) {
    283 				z += syn(ctx, ",", synkind::PUNCTUATION)?;
    284 				z += space(ctx)?;
    285 			};
    286 		};
    287 		if (e.variadic) {
    288 			z += syn(ctx, "...", synkind::OPERATOR)?;
    289 		};
    290 		z += syn(ctx, ")", synkind::PUNCTUATION)?;
    291 		return z;
    292 	case let e: ast::cast_expr =>
    293 		let z = 0z;
    294 		const needs_parens = !is_cast(e.value);
    295 		if (needs_parens) {
    296 			z += syn(ctx, "(", synkind::PUNCTUATION)?;
    297 		};
    298 		z += _expr(ctx, syn, e.value)?;
    299 		if (needs_parens) {
    300 			z += syn(ctx, ")", synkind::PUNCTUATION)?;
    301 		};
    302 		switch (e.kind) {
    303 		case ast::cast_kind::CAST =>
    304 			z += syn(ctx, ":", synkind::OPERATOR)?;
    305 			z += space(ctx)?;
    306 		case ast::cast_kind::ASSERTION =>
    307 			z += space(ctx)?;
    308 			z += syn(ctx, "as", synkind::OPERATOR)?;
    309 			z += space(ctx)?;
    310 		case ast::cast_kind::TEST =>
    311 			z += space(ctx)?;
    312 			z += syn(ctx, "is", synkind::OPERATOR)?;
    313 			z += space(ctx)?;
    314 		};
    315 		z += __type(ctx, syn, e._type)?;
    316 		return z;
    317 	case let e: ast::constant_expr =>
    318 		return constant(ctx, syn, e)?;
    319 	case let e: ast::continue_expr =>
    320 		let z = syn(ctx, "continue", synkind::KEYWORD)?;
    321 		if (e != "") {
    322 			z += space(ctx)?;
    323 			z += syn(ctx, ":", synkind::LABEL)?;
    324 			z += syn(ctx, e, synkind::LABEL)?;
    325 		};
    326 		return z;
    327 	case let e: ast::defer_expr =>
    328 		let z = syn(ctx, "defer", synkind::KEYWORD)?;
    329 		z += space(ctx)?;
    330 		z += _expr(ctx, syn, e)?;
    331 		return z;
    332 	case let e: ast::delete_expr =>
    333 		let z = 0z;
    334 		if (e.is_static) {
    335 			z += syn(ctx, "static", synkind::KEYWORD)?;
    336 			z += space(ctx)?;
    337 		};
    338 		z += syn(ctx, "delete", synkind::KEYWORD)?;
    339 		z += syn(ctx, "(", synkind::PUNCTUATION)?;
    340 		z += _expr(ctx, syn, e.object)?;
    341 		z += syn(ctx, ")", synkind::PUNCTUATION)?;
    342 		return z;
    343 	case let e: ast::error_assert_expr =>
    344 		let z = 0z;
    345 		const needs_parens = !is_postfix(e);
    346 		if (needs_parens) {
    347 			z += syn(ctx, "(", synkind::PUNCTUATION)?;
    348 		};
    349 		z += _expr(ctx, syn, e)?;
    350 		if (needs_parens) {
    351 			z += syn(ctx, ")", synkind::PUNCTUATION)?;
    352 		};
    353 		z += syn(ctx, "!", synkind::OPERATOR)?;
    354 		return z;
    355 	case let e: ast::for_expr =>
    356 		return for_expr(ctx, syn, &e)?;
    357 	case let e: ast::free_expr =>
    358 		let z = syn(ctx, "free", synkind::KEYWORD)?;
    359 		z += syn(ctx, "(", synkind::PUNCTUATION)?;
    360 		z += _expr(ctx, syn, e)?;
    361 		z += syn(ctx, ")", synkind::PUNCTUATION)?;
    362 		return z;
    363 	case let e: ast::if_expr =>
    364 		let z = syn(ctx, "if", synkind::KEYWORD)?;
    365 		z += space(ctx)?;
    366 		z += syn(ctx, "(", synkind::PUNCTUATION)?;
    367 		z += _expr(ctx, syn, e.cond)?;
    368 		z += syn(ctx, ")", synkind::PUNCTUATION)?;
    369 		z += space(ctx)?;
    370 		z += _expr(ctx, syn, e.tbranch)?;
    371 		match (e.fbranch) {
    372 		case null => void;
    373 		case let e: *ast::expr =>
    374 			z += space(ctx)?;
    375 			z += syn(ctx, "else", synkind::KEYWORD)?;
    376 			z += space(ctx)?;
    377 			z += _expr(ctx, syn, e)?;
    378 		};
    379 		return z;
    380 	case ast::insert_expr =>
    381 		return append_insert_expr(ctx, syn, e);
    382 	case let e: ast::compound_expr =>
    383 		let z = 0z;
    384 		if (e.label != "") {
    385 			z += syn(ctx, ":", synkind::LABEL)?;
    386 			z += syn(ctx, e.label, synkind::LABEL)?;
    387 			z += space(ctx)?;
    388 		};
    389 		z += syn(ctx, "{", synkind::PUNCTUATION)?;
    390 		ctx.indent += 1;
    391 		for (let i = 0z; i < len(e.exprs); i += 1) {
    392 			z += newline(ctx)?;
    393 			z += stmt(ctx, syn, e.exprs[i])?;
    394 		};
    395 		ctx.indent -= 1;
    396 		z += newline(ctx)?;
    397 		z += syn(ctx, "}", synkind::PUNCTUATION)?;
    398 		return z;
    399 	case let e: ast::match_expr =>
    400 		return match_expr(ctx, syn, &e)?;
    401 	case let e: ast::len_expr =>
    402 		let z = syn(ctx, "len", synkind::KEYWORD)?;
    403 		z += syn(ctx, "(", synkind::PUNCTUATION)?;
    404 		z += _expr(ctx, syn, e)?;
    405 		z += syn(ctx, ")", synkind::PUNCTUATION)?;
    406 		return z;
    407 	case let e: ast::size_expr =>
    408 		let z = syn(ctx, "size", synkind::KEYWORD)?;
    409 		z += syn(ctx, "(", synkind::PUNCTUATION)?;
    410 		z += __type(ctx, syn, e)?;
    411 		z += syn(ctx, ")", synkind::PUNCTUATION)?;
    412 		return z;
    413 	case let e: ast::offset_expr =>
    414 		let z = syn(ctx, "offset", synkind::KEYWORD)?;
    415 		z += syn(ctx, "(", synkind::PUNCTUATION)?;
    416 		z += _expr(ctx, syn, e)?;
    417 		z += syn(ctx, ")", synkind::PUNCTUATION)?;
    418 		return z;
    419 	case let e: ast::propagate_expr =>
    420 		let z = 0z;
    421 		const needs_parens = !is_postfix(e);
    422 		if (needs_parens) {
    423 			z += syn(ctx, "(", synkind::PUNCTUATION)?;
    424 		};
    425 		z += _expr(ctx, syn, e)?;
    426 		if (needs_parens) {
    427 			z += syn(ctx, ")", synkind::PUNCTUATION)?;
    428 		};
    429 		z += syn(ctx, "?", synkind::OPERATOR)?;
    430 		return z;
    431 	case let e: ast::return_expr =>
    432 		let z = syn(ctx, "return", synkind::KEYWORD)?;
    433 		match (e) {
    434 		case null => void;
    435 		case let e: *ast::expr =>
    436 			z += space(ctx)?;
    437 			z += _expr(ctx, syn, e)?;
    438 		};
    439 		return z;
    440 	case let e: ast::slice_expr =>
    441 		let z = 0z;
    442 		const needs_parens = !is_postfix(e.object);
    443 		if (needs_parens) {
    444 			z += syn(ctx, "(", synkind::PUNCTUATION)?;
    445 		};
    446 		z += _expr(ctx, syn, e.object)?;
    447 		if (needs_parens) {
    448 			z += syn(ctx, ")", synkind::PUNCTUATION)?;
    449 		};
    450 		z += syn(ctx, "[", synkind::PUNCTUATION)?;
    451 		match (e.start) {
    452 		case null => void;
    453 		case let e: *ast::expr =>
    454 			z += _expr(ctx, syn, e)?;
    455 		};
    456 		z += syn(ctx, "..", synkind::OPERATOR)?;
    457 		match (e.end) {
    458 		case null => void;
    459 		case let e: *ast::expr =>
    460 			z += _expr(ctx, syn, e)?;
    461 		};
    462 		z += syn(ctx, "]", synkind::PUNCTUATION)?;
    463 		return z;
    464 	case let e: ast::switch_expr =>
    465 		return switch_expr(ctx, syn, &e)?;
    466 	case let e: ast::unarithm_expr =>
    467 		let z = syn(ctx, switch (e.op) {
    468 		case ast::unarithm_op::ADDR =>
    469 			yield "&";
    470 		case ast::unarithm_op::BNOT =>
    471 			yield "~";
    472 		case ast::unarithm_op::DEREF =>
    473 			yield "*";
    474 		case ast::unarithm_op::LNOT =>
    475 			yield "!";
    476 		case ast::unarithm_op::MINUS =>
    477 			yield "-";
    478 		}, synkind::OPERATOR)?;
    479 		const needs_parens = match (e.operand.expr) {
    480 		case let inner: ast::unarithm_expr =>
    481 			yield e.op == ast::unarithm_op::ADDR
    482 				&& inner.op == e.op;
    483 		case =>
    484 			yield !is_unary(e.operand);
    485 		};
    486 		if (needs_parens) {
    487 			z += syn(ctx, "(", synkind::PUNCTUATION)?;
    488 		};
    489 		z += _expr(ctx, syn, e.operand)?;
    490 		if (needs_parens) {
    491 			z += syn(ctx, ")", synkind::PUNCTUATION)?;
    492 		};
    493 		return z;
    494 	case let e: ast::variadic_expr =>
    495 		match (e) {
    496 		case ast::vastart_expr =>
    497 			let z = syn(ctx, "vastart", synkind::KEYWORD)?;
    498 			z += syn(ctx, "(", synkind::PUNCTUATION)?;
    499 			z += syn(ctx, ")", synkind::PUNCTUATION)?;
    500 			return z;
    501 		case let e: ast::vaarg_expr =>
    502 			let z = syn(ctx, "vaarg", synkind::KEYWORD)?;
    503 			z += syn(ctx, "(", synkind::PUNCTUATION)?;
    504 			z += _expr(ctx, syn, e)?;
    505 			z += syn(ctx, ")", synkind::PUNCTUATION)?;
    506 			return z;
    507 		case let e: ast::vaend_expr =>
    508 			let z = syn(ctx, "vaend", synkind::KEYWORD)?;
    509 			z += syn(ctx, "(", synkind::PUNCTUATION)?;
    510 			z += _expr(ctx, syn, e)?;
    511 			z += syn(ctx, ")", synkind::PUNCTUATION)?;
    512 			return z;
    513 		};
    514 	case let e: ast::yield_expr =>
    515 		let z = syn(ctx, "yield", synkind::KEYWORD)?;
    516 		if (e.label != "") {
    517 			z += space(ctx)?;
    518 			z += syn(ctx, ":", synkind::LABEL)?;
    519 			z += syn(ctx, e.label, synkind::LABEL)?;
    520 		};
    521 		match (e.value) {
    522 		case null => void;
    523 		case let v: *ast::expr =>
    524 			if (e.label != "") {
    525 				z += syn(ctx, ",", synkind::PUNCTUATION)?;
    526 			};
    527 			z += space(ctx)?;
    528 			z += _expr(ctx, syn, v)?;
    529 		};
    530 		return z;
    531 	};
    532 };
    533 
    534 fn binprecedence(op: binarithm_op) uint = {
    535 	switch (op) {
    536 	case binarithm_op::DIV, binarithm_op::MODULO, binarithm_op::TIMES =>
    537 		return 10;
    538 	case binarithm_op::MINUS, binarithm_op::PLUS =>
    539 		return 9;
    540 	case binarithm_op::LSHIFT, binarithm_op::RSHIFT =>
    541 		return 8;
    542 	case binarithm_op::BAND =>
    543 		return 7;
    544 	case binarithm_op::BXOR =>
    545 		return 6;
    546 	case binarithm_op::BOR =>
    547 		return 5;
    548 	case binarithm_op::GT, binarithm_op::GTEQ,
    549 		binarithm_op::LESS, binarithm_op::LESSEQ =>
    550 		return 4;
    551 	case binarithm_op::LEQUAL, binarithm_op::NEQUAL =>
    552 		return 3;
    553 	case binarithm_op::LAND =>
    554 		return 2;
    555 	case binarithm_op::LXOR =>
    556 		return 1;
    557 	case binarithm_op::LOR =>
    558 		return 0;
    559 	};
    560 };
    561 
    562 fn binexprval(
    563 	ctx: *context,
    564 	syn: *synfunc,
    565 	e: *ast::expr,
    566 	prec: uint,
    567 ) (size | io::error) = {
    568 	let z = 0z;
    569 	match (e.expr) {
    570 	case let b: ast::binarithm_expr =>
    571 		if (binprecedence(b.op) < prec) {
    572 			z += syn(ctx, "(", synkind::PUNCTUATION)?;
    573 			z += _expr(ctx, syn, e)?;
    574 			z += syn(ctx, ")", synkind::PUNCTUATION)?;
    575 			return z;
    576 		};
    577 	case => void;
    578 	};
    579 	const needs_parens = !is_cast(e) && !(e.expr is ast::binarithm_expr);
    580 	if (needs_parens) {
    581 		z += syn(ctx, "(", synkind::PUNCTUATION)?;
    582 	};
    583 	z += _expr(ctx, syn, e)?;
    584 	if (needs_parens) {
    585 		z += syn(ctx, ")", synkind::PUNCTUATION)?;
    586 	};
    587 	return z;
    588 };
    589 
    590 fn stmt(ctx: *context, syn: *synfunc, e: *ast::expr) (size | io::error) = {
    591 	let n = _expr(ctx, syn, e)?;
    592 	n += syn(ctx, ";", synkind::PUNCTUATION)?;
    593 	return n;
    594 };
    595 
    596 fn constant(
    597 	ctx: *context,
    598 	syn: *synfunc,
    599 	e: ast::constant_expr,
    600 ) (size | io::error) = {
    601 	match (e) {
    602 	case void =>
    603 		return syn(ctx, "void", synkind::KEYWORD)?;
    604 	case let v: ast::value =>
    605 		match (v) {
    606 		case void => abort();
    607 		case ast::_null =>
    608 			return syn(ctx, "null", synkind::KEYWORD)?;
    609 		case let b: bool =>
    610 			return syn(ctx, if (b) "true" else "false",
    611 				synkind::KEYWORD)?;
    612 		case let s: str =>
    613 			const s = strings::multireplace(s,
    614 				(`\`, `\\`), (`"`, `\"`));
    615 			defer free(s);
    616 			const s = fmt::asprintf(`"{}"`, s);
    617 			defer free(s);
    618 			return syn(ctx, s, synkind::RUNE_STRING)?;
    619 		case let r: rune =>
    620 			// 4 for unicode codepoint + 2 's
    621 			let buf: [6]u8 = [0...];
    622 			if (r == '\'' || r == '\\') {
    623 				return syn(ctx, fmt::bsprintf(buf, `'\{}'`, r),
    624 					synkind::RUNE_STRING)?;
    625 			} else {
    626 				return syn(ctx, fmt::bsprintf(buf, "'{}'", r),
    627 					synkind::RUNE_STRING)?;
    628 			};
    629 		};
    630 	case let ac: ast::array_constant =>
    631 		let z = syn(ctx, "[", synkind::PUNCTUATION)?;
    632 		for (let i = 0z; i < len(ac.values); i += 1) {
    633 			z += _expr(ctx, syn, ac.values[i])?;
    634 			if (i + 1 < len(ac.values)) {
    635 				z += syn(ctx, ",", synkind::PUNCTUATION)?;
    636 				z += space(ctx)?;
    637 			};
    638 		};
    639 		if (ac.expand) {
    640 			z += syn(ctx, "...", synkind::OPERATOR)?;
    641 		};
    642 		z += syn(ctx, "]", synkind::PUNCTUATION)?;
    643 		return z;
    644 	case let v: ast::number_constant =>
    645 		const s = fmt::asprintf("{}{}", v.value, switch (v.suff) {
    646 		case ltok::LIT_U8 =>
    647 			yield "u8";
    648 		case ltok::LIT_U16 =>
    649 			yield "u16";
    650 		case ltok::LIT_U32 =>
    651 			yield "u32";
    652 		case ltok::LIT_U64 =>
    653 			yield "u64";
    654 		case ltok::LIT_UINT =>
    655 			yield "u";
    656 		case ltok::LIT_SIZE =>
    657 			yield "z";
    658 		case ltok::LIT_I8 =>
    659 			yield "i8";
    660 		case ltok::LIT_I16 =>
    661 			yield "i16";
    662 		case ltok::LIT_I32 =>
    663 			yield "i32";
    664 		case ltok::LIT_I64 =>
    665 			yield "i64";
    666 		case ltok::LIT_INT =>
    667 			yield "i";
    668 		case ltok::LIT_ICONST, ltok::LIT_FCONST =>
    669 			yield "";
    670 		case ltok::LIT_F32 =>
    671 			yield "f32";
    672 		case ltok::LIT_F64 =>
    673 			yield "f64";
    674 		case => abort();
    675 		});
    676 		defer free(s);
    677 		return syn(ctx, s, synkind::NUMBER)?;
    678 	case let sc: ast::struct_constant =>
    679 		return struct_constant(ctx, syn, sc)?;
    680 	case let tu: ast::tuple_constant =>
    681 		let z = syn(ctx, "(", synkind::PUNCTUATION)?;
    682 		for (let i = 0z; i < len(tu); i += 1) {
    683 			z += _expr(ctx, syn, tu[i])?;
    684 			if (i + 1 < len(tu)) {
    685 				z += syn(ctx, ",", synkind::PUNCTUATION)?;
    686 				z += space(ctx)?;
    687 			};
    688 		};
    689 		z += syn(ctx, ")", synkind::PUNCTUATION)?;
    690 		return z;
    691 	};
    692 };
    693 
    694 fn struct_constant(
    695 	ctx: *context,
    696 	syn: *synfunc,
    697 	sc: ast::struct_constant,
    698 ) (size | io::error) = {
    699 	let z = 0z;
    700 	z += if (len(sc.alias) != 0) {
    701 		yield _ident(ctx, syn, sc.alias, synkind::IDENT)?;
    702 	} else {
    703 		yield syn(ctx, "struct", synkind::KEYWORD)?;
    704 	};
    705 	z += space(ctx)?;
    706 	z += syn(ctx, "{", synkind::PUNCTUATION)?;
    707 	ctx.indent += 1;
    708 	for (let i = 0z; i < len(sc.fields); i += 1) {
    709 		z += newline(ctx)?;
    710 		match (sc.fields[i]) {
    711 		case let sv: ast::struct_value =>
    712 			z += syn(ctx, sv.name, synkind::SECONDARY)?;
    713 			match (sv._type) {
    714 			case null => void;
    715 			case let t: *ast::_type =>
    716 				z += syn(ctx, ":", synkind::PUNCTUATION)?;
    717 				z += space(ctx)?;
    718 				z += __type(ctx, syn, t)?;
    719 			};
    720 			z += space(ctx)?;
    721 			z += syn(ctx, "=", synkind::OPERATOR)?;
    722 			z += space(ctx)?;
    723 			z += _expr(ctx, syn, sv.init)?;
    724 		case let sc: *ast::struct_constant =>
    725 			z += constant(ctx, syn, *sc)?;
    726 		};
    727 		z += syn(ctx, ",", synkind::PUNCTUATION)?;
    728 	};
    729 	if (sc.autofill) {
    730 		z += newline(ctx)?;
    731 		z += syn(ctx, "...", synkind::OPERATOR)?;
    732 	};
    733 	ctx.indent -= 1;
    734 	z += newline(ctx)?;
    735 	z += syn(ctx, "}", synkind::PUNCTUATION)?;
    736 	return z;
    737 };
    738 
    739 fn for_expr(
    740 	ctx: *context,
    741 	syn: *synfunc,
    742 	e: *ast::for_expr,
    743 ) (size | io::error) = {
    744 	let z = syn(ctx, "for", synkind::KEYWORD)?;
    745 	z += space(ctx)?;
    746 	z += syn(ctx, "(", synkind::PUNCTUATION)?;
    747 	match (e.bindings) {
    748 	case null => void;
    749 	case let e: *ast::expr =>
    750 		z += _expr(ctx, syn, e)?;
    751 		z += syn(ctx, ";", synkind::PUNCTUATION)?;
    752 		z += space(ctx)?;
    753 	};
    754 
    755 	z += _expr(ctx, syn, e.cond)?;
    756 
    757 	match (e.afterthought) {
    758 	case null => void;
    759 	case let e: *ast::expr =>
    760 		z += syn(ctx, ";", synkind::PUNCTUATION)?;
    761 		z += space(ctx)?;
    762 		z += _expr(ctx, syn, e)?;
    763 	};
    764 
    765 	z += syn(ctx, ")", synkind::PUNCTUATION)?;
    766 	z += space(ctx)?;
    767 	z += _expr(ctx, syn, e.body)?;
    768 	return z;
    769 };
    770 
    771 fn switch_expr(
    772 	ctx: *context,
    773 	syn: *synfunc,
    774 	e: *ast::switch_expr,
    775 ) (size | io::error) = {
    776 	let z = syn(ctx, "switch", synkind::KEYWORD)?;
    777 	z += space(ctx)?;
    778 	z += syn(ctx, "(", synkind::PUNCTUATION)?;
    779 	z += _expr(ctx, syn, e.value)?;
    780 	z += syn(ctx, ")", synkind::PUNCTUATION)?;
    781 	z += space(ctx)?;
    782 	z += syn(ctx, "{", synkind::PUNCTUATION)?;
    783 
    784 	for (let i = 0z; i < len(e.cases); i += 1) {
    785 		z += newline(ctx)?;
    786 		const item = e.cases[i];
    787 		z += syn(ctx, "case", synkind::KEYWORD)?;
    788 		z += space(ctx)?;
    789 		if (len(item.options) == 0) {
    790 			z += syn(ctx, "=>", synkind::OPERATOR)?;
    791 		} else {
    792 			for (let j = 0z; j < len(item.options); j += 1) {
    793 				const opt = item.options[j];
    794 				z += _expr(ctx, syn, opt)?;
    795 				if (j + 1 < len(item.options)) {
    796 					z += syn(ctx, ",",
    797 						synkind::PUNCTUATION)?;
    798 					z += space(ctx)?;
    799 				};
    800 			};
    801 			z += space(ctx)?;
    802 			z += syn(ctx, "=>", synkind::OPERATOR)?;
    803 		};
    804 		z += case_exprs(ctx, syn, item.exprs)?;
    805 	};
    806 
    807 	z += newline(ctx)?;
    808 	z += syn(ctx, "}", synkind::PUNCTUATION)?;
    809 	return z;
    810 };
    811 
    812 fn match_expr(
    813 	ctx: *context,
    814 	syn: *synfunc,
    815 	e: *ast::match_expr,
    816 ) (size | io::error) = {
    817 	let z = syn(ctx, "match", synkind::KEYWORD)?;
    818 	z += space(ctx)?;
    819 	z += syn(ctx, "(", synkind::PUNCTUATION)?;
    820 	z += _expr(ctx, syn, e.value)?;
    821 	z += syn(ctx, ")", synkind::PUNCTUATION)?;
    822 	z += space(ctx)?;
    823 	z += syn(ctx, "{", synkind::PUNCTUATION)?;
    824 
    825 	for (let i = 0z; i < len(e.cases); i += 1) {
    826 		z += newline(ctx)?;
    827 		z += syn(ctx, "case", synkind::KEYWORD)?;
    828 		const item = e.cases[i];
    829 		if (len(item.name) > 0) {
    830 			z += space(ctx)?;
    831 			z += syn(ctx, "let", synkind::KEYWORD)?;
    832 			z += space(ctx)?;
    833 			z += syn(ctx, item.name, synkind::IDENT)?;
    834 		};
    835 		match (item._type) {
    836 		case let typ: *ast::_type =>
    837 			if (len(item.name) > 0) {
    838 				z += syn(ctx, ":", synkind::PUNCTUATION)?;
    839 			};
    840 			z += space(ctx)?;
    841 			z += __type(ctx, syn, typ)?;
    842 		case null => void;
    843 		};
    844 		z += space(ctx)?;
    845 		z += syn(ctx, "=>", synkind::OPERATOR)?;
    846 		z += case_exprs(ctx, syn, item.exprs)?;
    847 	};
    848 
    849 	z += newline(ctx)?;
    850 	z += syn(ctx, "}", synkind::PUNCTUATION)?;
    851 	return z;
    852 };
    853 
    854 fn case_exprs(
    855 	ctx: *context,
    856 	syn: *synfunc,
    857 	exprs: []*ast::expr,
    858 ) (size | io::error) = {
    859 	let z = 0z;
    860 
    861 	if (len(exprs) == 1) match (exprs[0].expr) {
    862 	case let e: ast::assert_expr =>
    863 		if (e.cond == null) {
    864 			// abort() expression
    865 			z += space(ctx)?;
    866 			z += assert_expr(ctx, syn, &e)?;
    867 			z += syn(ctx, ";", synkind::PUNCTUATION)?;
    868 			return z;
    869 		};
    870 	case let e: ast::value =>
    871 		if (e is void) {
    872 			z += space(ctx)?;
    873 			{
    874 				ctx.stack = &stack {
    875 					cur = exprs[0],
    876 					up = ctx.stack,
    877 					...
    878 				};
    879 				defer ctx.stack = (ctx.stack as *stack).up;
    880 				z += syn(ctx, "void", synkind::KEYWORD)?;
    881 			};
    882 			z += syn(ctx, ";", synkind::PUNCTUATION)?;
    883 			return z;
    884 		};
    885 	case => void;
    886 	};
    887 	ctx.indent += 1;
    888 	for (let j = 0z; j < len(exprs); j += 1) {
    889 		z += newline(ctx)?;
    890 		z += stmt(ctx, syn, exprs[j])?;
    891 	};
    892 	ctx.indent -= 1;
    893 
    894 	return z;
    895 };
    896 
    897 fn is_plain(e: *ast::expr) bool = {
    898 	match (e.expr) {
    899 	case ast::constant_expr =>
    900 		return true;
    901 	case ast::access_identifier =>
    902 		return true;
    903 	case =>
    904 		return false;
    905 	};
    906 };
    907 
    908 fn is_postfix(e: *ast::expr) bool = {
    909 	if (is_plain(e)) {
    910 		return true;
    911 	};
    912 
    913 	match (e.expr) {
    914 	case ast::call_expr =>
    915 		return true;
    916 	case ast::access_expr =>
    917 		return true;
    918 	case ast::slice_expr =>
    919 		return true;
    920 	case ast::error_assert_expr =>
    921 		return true;
    922 	case ast::propagate_expr =>
    923 		return true;
    924 	case =>
    925 		return false;
    926 	};
    927 };
    928 
    929 fn is_builtin(e: *ast::expr) bool = {
    930 	if (is_postfix(e)) {
    931 		return true;
    932 	};
    933 
    934 	match (e.expr) {
    935 	case ast::alloc_expr =>
    936 		return true;
    937 	case ast::assert_expr =>
    938 		return true;
    939 	case ast::variadic_expr =>
    940 		return true;
    941 	// measurement-expression
    942 	case ast::len_expr =>
    943 		return true;
    944 	case ast::align_expr =>
    945 		return true;
    946 	case ast::size_expr =>
    947 		return true;
    948 	case ast::offset_expr =>
    949 		return true;
    950 	// slice-mutation-expression
    951 	case ast::append_expr =>
    952 		return true;
    953 	case ast::insert_expr =>
    954 		return true;
    955 	case =>
    956 		return false;
    957 	};
    958 };
    959 
    960 fn is_unary(e: *ast::expr) bool = {
    961 	if (is_builtin(e)) {
    962 		return true;
    963 	};
    964 
    965 	match (e.expr) {
    966 	case ast::compound_expr =>
    967 		return true;
    968 	case ast::match_expr =>
    969 		return true;
    970 	case ast::switch_expr =>
    971 		return true;
    972 	case ast::unarithm_expr =>
    973 		return true;
    974 	case =>
    975 		return false;
    976 	};
    977 };
    978 
    979 fn is_cast(e: *ast::expr) bool = {
    980 	return is_unary(e) || (e.expr is ast::cast_expr);
    981 };
    982 
    983 fn assert_expr(
    984 	ctx: *context,
    985 	syn: *synfunc,
    986 	e: *ast::assert_expr,
    987 ) (size | io::error) = {
    988 	let z = 0z;
    989 	if (e.is_static) {
    990 		z += syn(ctx, "static", synkind::KEYWORD)?;
    991 		z += space(ctx)?;
    992 	};
    993 	// assert without a condition = abort
    994 	match (e.cond) {
    995 	case let e: *ast::expr =>
    996 		z += syn(ctx, "assert", synkind::KEYWORD)?;
    997 		z += syn(ctx, "(", synkind::PUNCTUATION)?;
    998 		z += _expr(ctx, syn, e)?;
    999 	case null =>
   1000 		z += syn(ctx, "abort", synkind::KEYWORD)?;
   1001 		z += syn(ctx, "(", synkind::PUNCTUATION)?;
   1002 	};
   1003 	match (e.message) {
   1004 	case let m: *ast::expr =>
   1005 		match (e.cond) {
   1006 		case null => void;
   1007 		case *ast::expr =>
   1008 			z += syn(ctx, ",", synkind::PUNCTUATION)?;
   1009 			z += space(ctx)?;
   1010 		};
   1011 		z += _expr(ctx, syn, m)?;
   1012 	case null => void;
   1013 	};
   1014 	z += syn(ctx, ")", synkind::PUNCTUATION)?;
   1015 	return z;
   1016 };
   1017 
   1018 fn append_insert_expr(
   1019 	ctx: *context,
   1020 	syn: *synfunc,
   1021 	e: *ast::expr,
   1022 ) (size | io::error) = {
   1023 	let z = 0z;
   1024 	const e: *ast::append_expr = match (e.expr) {
   1025 	case let e: ast::append_expr =>
   1026 		if (e.is_static) {
   1027 			z += syn(ctx, "static", synkind::KEYWORD)?;
   1028 			z += space(ctx)?;
   1029 		};
   1030 		z += syn(ctx, "append", synkind::KEYWORD)?;
   1031 		yield &e;
   1032 	case let e: ast::insert_expr =>
   1033 		if (e.is_static) {
   1034 			z += syn(ctx, "static", synkind::KEYWORD)?;
   1035 			z += space(ctx)?;
   1036 		};
   1037 		z += syn(ctx, "insert", synkind::KEYWORD)?;
   1038 		yield &e;
   1039 	case => abort(); // unreachable
   1040 	};
   1041 	z += syn(ctx, "(", synkind::PUNCTUATION)?;
   1042 	z += _expr(ctx, syn, e.object)?;
   1043 	z += syn(ctx, ",", synkind::PUNCTUATION)?;
   1044 	z += space(ctx)?;
   1045 	z += _expr(ctx, syn, e.value)?;
   1046 	if (e.variadic) {
   1047 		z += syn(ctx, "...", synkind::OPERATOR)?;
   1048 	};
   1049 	match (e.length) {
   1050 	case null => void;
   1051 	case let l: *ast::expr =>
   1052 		z += syn(ctx, ",", synkind::PUNCTUATION)?;
   1053 		z += space(ctx)?;
   1054 		z += _expr(ctx, syn, l)?;
   1055 	};
   1056 	z += syn(ctx, ")", synkind::PUNCTUATION)?;
   1057 	return z;
   1058 };