hare

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

expr.ha (26958B)


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