hare

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

expr.ha (27551B)


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