hare

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

expr.ha (18225B)


      1 // License: MPL-2.0
      2 // (c) 2021 Alexey Yerin <yyp@disroot.org>
      3 // (c) 2021 Drew DeVault <sir@cmpwn.com>
      4 // (c) 2021 Eyal Sawady <ecs@d2evs.net>
      5 // (c) 2022 Sebastian <sebastian@sebsite.pw>
      6 use io;
      7 use fmt;
      8 use hare::ast;
      9 use hare::ast::{binarithm_op};
     10 use hare::lex::{ltok};
     11 use hare::lex;
     12 use strings;
     13 
     14 // Unparses a [[hare::ast::expr]].
     15 export fn expr(
     16 	out: io::handle,
     17 	indent: size,
     18 	e: ast::expr
     19 ) (size | io::error) = {
     20 	match (e.expr) {
     21 	case let e: ast::access_expr =>
     22 		match (e) {
     23 		case let id: ast::access_identifier =>
     24 			return ident(out, id);
     25 		case let ix: ast::access_index =>
     26 			let z = expr(out, indent, *ix.object)?;
     27 			z += fmt::fprintf(out, "[")?;
     28 			z += expr(out, indent, *ix.index)?;
     29 			z += fmt::fprintf(out, "]")?;
     30 			return z;
     31 		case let fi: ast::access_field =>
     32 			let z = expr(out, indent, *fi.object)?;
     33 			z += fmt::fprintf(out, ".{}", fi.field)?;
     34 			return z;
     35 		case let tp: ast::access_tuple =>
     36 			let z = expr(out, indent, *tp.object)?;
     37 			z += fmt::fprintf(out, ".")?;
     38 			z += expr(out, indent, *tp.value)?;
     39 			return z;
     40 		};
     41 	case let e: ast::alloc_expr =>
     42 		let z = fmt::fprint(out, "alloc(")?;
     43 		z += expr(out, indent, *e.init)?;
     44 		match (e.capacity) {
     45 		case null =>
     46 			if (e.form == ast::alloc_form::COPY) {
     47 				z += fmt::fprint(out, "...")?;
     48 			};
     49 		case let e: *ast::expr =>
     50 			z += fmt::fprint(out, ", ")?;
     51 			z += expr(out, indent, *e)?;
     52 		};
     53 		z += fmt::fprint(out, ")")?;
     54 		return z;
     55 	case let e: ast::append_expr =>
     56 		let z = if (e.is_static) fmt::fprint(out, "static ")? else 0z;
     57 		z += fmt::fprint(out, "append(")?;
     58 		z += expr(out, indent, *e.object)?;
     59 		z += fmt::fprint(out, ", ")?;
     60 		for (let i = 0z; i < len(e.values); i += 1) {
     61 			let val = e.values[i];
     62 			z += expr(out, indent, *val)?;
     63 			if (i + 1 < len(e.values)) {
     64 				z += fmt::fprint(out, ", ")?;
     65 			};
     66 		};
     67 		match (e.variadic) {
     68 		case null => void;
     69 		case let v: *ast::expr =>
     70 			if (len(e.values) != 0) {
     71 				z += fmt::fprint(out, ", ")?;
     72 			};
     73 			z += expr(out, indent, *v)?;
     74 			z += fmt::fprint(out, "...")?;
     75 		};
     76 		z += fmt::fprint(out, ")")?;
     77 		return z;
     78 	case let e: ast::assert_expr =>
     79 		let z = fmt::fprint(
     80 			out, if (e.is_static) "static " else "")?;
     81 		// assert without a condition = abort
     82 		z += match (e.cond) {
     83 		case let e: *ast::expr =>
     84 			yield fmt::fprint(out, "assert(")? +
     85 				expr(out, indent, *e)?;
     86 		case null =>
     87 			yield fmt::fprint(out, "abort(")?;
     88 		};
     89 		z += match (e.message) {
     90 		case let m: *ast::expr =>
     91 			let z = 0z;
     92 			match (e.cond) {
     93 			case null => void;
     94 			case *ast::expr =>
     95 				z += fmt::fprint(out, ", ")?;
     96 			};
     97 			z += expr(out, indent, *m)?;
     98 			yield z;
     99 		case null =>
    100 			yield 0;
    101 		};
    102 		z += fmt::fprint(out, ")")?;
    103 		return z;
    104 	case let e: ast::assign_expr =>
    105 		let z = 0z;
    106 		if (e.indirect) {
    107 			z += fmt::fprint(out, "*")?;
    108 		};
    109 		z += expr(out, indent, *e.object)?;
    110 		const op = match (e.op) {
    111 		case void =>
    112 			yield "=";
    113 		case let op: binarithm_op =>
    114 			yield switch (op) {
    115 			case binarithm_op::BAND =>
    116 				yield "&=";
    117 			case binarithm_op::LAND =>
    118 				yield "&&=";
    119 			case binarithm_op::BOR =>
    120 				yield "|=";
    121 			case binarithm_op::LOR =>
    122 				yield "||=";
    123 			case binarithm_op::DIV =>
    124 				yield "/=";
    125 			case binarithm_op::LSHIFT =>
    126 				yield "<<=";
    127 			case binarithm_op::MINUS =>
    128 				yield "-=";
    129 			case binarithm_op::MODULO =>
    130 				yield "%=";
    131 			case binarithm_op::PLUS =>
    132 				yield "+=";
    133 			case binarithm_op::RSHIFT =>
    134 				yield ">>=";
    135 			case binarithm_op::TIMES =>
    136 				yield "*=";
    137 			case binarithm_op::BXOR =>
    138 				yield "^=";
    139 			case binarithm_op::LXOR =>
    140 				yield "^^=";
    141 			};
    142 		};
    143 		z += fmt::fprintf(out, " {} ", op)?;
    144 		z += expr(out, indent, *e.value)?;
    145 		return z;
    146 	case let e: ast::binarithm_expr =>
    147 		const prec = binprecedence(e.op);
    148 		let z = binexprval(out, indent, *e.lvalue, prec)?;
    149 		z += fmt::fprintf(out, " {} ", switch (e.op) {
    150 		case binarithm_op::BAND =>
    151 			yield "&";
    152 		case binarithm_op::BOR =>
    153 			yield "|";
    154 		case binarithm_op::DIV =>
    155 			yield "/";
    156 		case binarithm_op::GT =>
    157 			yield ">";
    158 		case binarithm_op::GTEQ =>
    159 			yield ">=";
    160 		case binarithm_op::LAND =>
    161 			yield "&&";
    162 		case binarithm_op::LEQUAL =>
    163 			yield "==";
    164 		case binarithm_op::LESS =>
    165 			yield "<";
    166 		case binarithm_op::LESSEQ =>
    167 			yield "<=";
    168 		case binarithm_op::LOR =>
    169 			yield "||";
    170 		case binarithm_op::LSHIFT =>
    171 			yield "<<";
    172 		case binarithm_op::LXOR =>
    173 			yield "^^";
    174 		case binarithm_op::MINUS =>
    175 			yield "-";
    176 		case binarithm_op::MODULO =>
    177 			yield "%";
    178 		case binarithm_op::NEQUAL =>
    179 			yield "!=";
    180 		case binarithm_op::PLUS =>
    181 			yield "+";
    182 		case binarithm_op::RSHIFT =>
    183 			yield ">>";
    184 		case binarithm_op::TIMES =>
    185 			yield "*";
    186 		case binarithm_op::BXOR =>
    187 			yield "^";
    188 		})?;
    189 		z += binexprval(out, indent, *e.rvalue, prec)?;
    190 		return z;
    191 	case let e: ast::binding_expr =>
    192 		let z = fmt::fprintf(out, "{}{}",
    193 			if (e.is_static) "static " else "",
    194 			if (e.is_const) "const " else "let ")?;
    195 		for (let i = 0z; i < len(e.bindings); i += 1) {
    196 			let binding = e.bindings[i];
    197 			match (binding.name) {
    198 			case let s: str =>
    199 				z += fmt::fprint(out, s)?;
    200 			case let u: ast::binding_unpack =>
    201 				z += fmt::fprint(out, "(")?;
    202 				for (let i = 0z; i < len(u); i += 1) {
    203 					match (u[i]) {
    204 					case let s: str =>
    205 						z += fmt::fprint(out, s)?;
    206 					case void =>
    207 						z += fmt::fprint(out, "_")?;
    208 					};
    209 					if (i + 1 < len(u)) {
    210 						z += fmt::fprint(out, ", ")?;
    211 					};
    212 				};
    213 				z += fmt::fprint(out, ")")?;
    214 			};
    215 			z += match (binding._type) {
    216 			case let t: *ast::_type =>
    217 				let z = 0z;
    218 				z += fmt::fprintf(out, ": ")?;
    219 				z += _type(out, indent, *t)?;
    220 				yield z;
    221 			case null =>
    222 				yield 0z;
    223 			};
    224 			z += fmt::fprint(out, " = ")?;
    225 			z += expr(out, indent, *binding.init)?;
    226 			if (i + 1 < len(e.bindings)) {
    227 				z += fmt::fprint(out, ", ")?;
    228 			};
    229 		};
    230 		return z;
    231 	case let e: ast::break_expr =>
    232 		let z = fmt::fprint(out, "break")?;
    233 		if (e != "") {
    234 			z += fmt::fprintf(out, " :{}", e)?;
    235 		};
    236 		return z;
    237 	case let e: ast::call_expr =>
    238 		let z = expr(out, indent, *e.lvalue)?;
    239 		z += fmt::fprintf(out, "(")?;
    240 		for (let i = 0z; i < len(e.args); i += 1) {
    241 			z += expr(out, indent, *e.args[i])?;
    242 			if (i + 1 < len(e.args)) {
    243 				z += fmt::fprintf(out, ", ")?;
    244 			};
    245 		};
    246 		if (e.variadic) {
    247 			z += fmt::fprintf(out, "...")?;
    248 		};
    249 		z += fmt::fprintf(out, ")")?;
    250 		return z;
    251 	case let e: ast::cast_expr =>
    252 		let z = expr(out, indent, *e.value)?;
    253 		const op = switch (e.kind) {
    254 		case ast::cast_kind::CAST =>
    255 			yield ": ";
    256 		case ast::cast_kind::ASSERTION =>
    257 			yield " as ";
    258 		case ast::cast_kind::TEST =>
    259 			yield " is ";
    260 		};
    261 		z += fmt::fprintf(out, "{}", op)?;
    262 		z += _type(out, indent, *e._type)?;
    263 		return z;
    264 	case let e: ast::constant_expr =>
    265 		return constant(out, indent, e)?;
    266 	case let e: ast::continue_expr =>
    267 		let z = fmt::fprint(out, "continue")?;
    268 		if (e != "") {
    269 			z += fmt::fprintf(out, " :{}", e)?;
    270 		};
    271 		return z;
    272 	case let e: ast::defer_expr =>
    273 		return fmt::fprint(out, "defer ")? + expr(out, indent, *e)?;
    274 	case let e: ast::delete_expr =>
    275 		let z = if (e.is_static) fmt::fprint(out, "static ")? else 0z;
    276 		z += fmt::fprint(out, "delete(")?;
    277 		z += expr(out, indent, *e.object)?;
    278 		z += fmt::fprint(out, ")")?;
    279 		return z;
    280 	case let e: ast::for_expr =>
    281 		return for_expr(out, indent, e)?;
    282 	case let e: ast::free_expr =>
    283 		return fmt::fprint(out, "free(")?
    284 			+ expr(out, indent, *e)?
    285 			+ fmt::fprint(out, ")")?;
    286 	case let e: ast::if_expr =>
    287 		let z = fmt::fprint(out, "if (")?;
    288 		z += expr(out, indent, *e.cond)?;
    289 		z += fmt::fprint(out, ") ")?;
    290 		z += expr(out, indent, *e.tbranch)?;
    291 		match (e.fbranch) {
    292 		case null => void;
    293 		case let e: *ast::expr =>
    294 			z += fmt::fprint(out, " else ")?;
    295 			z += expr(out, indent, *e)?;
    296 		};
    297 		return z;
    298 	case let e: ast::insert_expr =>
    299 		let z = if (e.is_static) fmt::fprint(out, "static ")? else 0z;
    300 		z += fmt::fprint(out, "insert(")?;
    301 		z += expr(out, indent, *e.object)?;
    302 		z += fmt::fprint(out, ", ")?;
    303 		for (let i = 0z; i < len(e.values); i += 1) {
    304 			let val = e.values[i];
    305 			z += expr(out, indent, *val)?;
    306 			if (i + 1 < len(e.values)) {
    307 				z += fmt::fprint(out, ", ")?;
    308 			};
    309 		};
    310 		match (e.variadic) {
    311 		case null => void;
    312 		case let v: *ast::expr =>
    313 			if (len(e.values) != 0) {
    314 				z += fmt::fprint(out, ", ")?;
    315 			};
    316 			z += expr(out, indent, *v)?;
    317 			z += fmt::fprint(out, "...")?;
    318 		};
    319 		z += fmt::fprint(out, ")")?;
    320 		return z;
    321 	case let e: ast::compound_expr =>
    322 		let z = 0z;
    323 		if (e.label != "") {
    324 			z += fmt::fprintf(out, ":{} ", e.label)?;
    325 		};
    326 		z += fmt::fprintf(out, "{{")?;
    327 		for (let i = 0z; i < len(e.exprs); i += 1) {
    328 			z += newline(out, indent + 1)?;
    329 			z += stmt(out, indent + 1, *e.exprs[i])?;
    330 		};
    331 		z += newline(out, indent)?;
    332 		z += fmt::fprintf(out, "}}")?;
    333 		return z;
    334 	case let e: ast::match_expr =>
    335 		return match_expr(out, indent, e)?;
    336 	case let e: ast::len_expr =>
    337 		let z = fmt::fprint(out, "len(")?;
    338 		z += expr(out, indent, *e)?;
    339 		z += fmt::fprint(out, ")")?;
    340 		return z;
    341 	case let e: ast::size_expr =>
    342 		let z = fmt::fprint(out, "size(")?;
    343 		z += _type(out, indent, *e)?;
    344 		z += fmt::fprint(out, ")")?;
    345 		return z;
    346 	case let e: ast::offset_expr =>
    347 		let z = fmt::fprint(out, "offset(")?;
    348 		z += expr(out, indent, *e)?;
    349 		z += fmt::fprint(out, ")")?;
    350 		return z;
    351 	case let e: ast::propagate_expr =>
    352 		let z = expr(out, indent, *e.expr)?;
    353 		z += fmt::fprintf(out, if (e.is_abort) "!" else "?")?;
    354 		return z;
    355 	case let e: ast::return_expr =>
    356 		let z = fmt::fprint(out, "return")?;
    357 		match (e) {
    358 		case null => void;
    359 		case let e: *ast::expr =>
    360 			z += fmt::fprint(out, " ")?;
    361 			z += expr(out, indent, *e)?;
    362 		};
    363 		return z;
    364 	case let e: ast::slice_expr =>
    365 		let z = expr(out, indent, *e.object)?;
    366 		z += fmt::fprint(out, "[")?;
    367 		match (e.start) {
    368 		case null => void;
    369 		case let e: *ast::expr =>
    370 			z += expr(out, indent, *e)?;
    371 		};
    372 		z += fmt::fprint(out, "..")?;
    373 		match (e.end) {
    374 		case null => void;
    375 		case let e: *ast::expr =>
    376 			z += expr(out, indent, *e)?;
    377 		};
    378 		z += fmt::fprint(out, "]")?;
    379 		return z;
    380 	case let e: ast::switch_expr =>
    381 		return switch_expr(out, indent, e)?;
    382 	case let e: ast::unarithm_expr =>
    383 		let z = fmt::fprintf(out, "{}", switch (e.op) {
    384 		case ast::unarithm_op::ADDR =>
    385 			yield "&";
    386 		case ast::unarithm_op::BNOT =>
    387 			yield "~";
    388 		case ast::unarithm_op::DEREF =>
    389 			yield "*";
    390 		case ast::unarithm_op::LNOT =>
    391 			yield "!";
    392 		case ast::unarithm_op::MINUS =>
    393 			yield "-";
    394 		case ast::unarithm_op::PLUS =>
    395 			yield "+";
    396 		})?;
    397 		z += expr(out, indent, *e.operand)?;
    398 		return z;
    399 	case let e: ast::variadic_expr =>
    400 		match (e) {
    401 		case ast::vastart_expr =>
    402 			return fmt::fprint(out, "vastart()")?;
    403 		case let e: ast::vaarg_expr =>
    404 			let z = fmt::fprint(out, "vaarg(")?;
    405 			z += expr(out, indent, *e)?;
    406 			z += fmt::fprint(out, ")")?;
    407 			return z;
    408 		case let e: ast::vaend_expr =>
    409 			let z = fmt::fprint(out, "vaend(")?;
    410 			z += expr(out, indent, *e)?;
    411 			z += fmt::fprint(out, ")")?;
    412 			return z;
    413 		};
    414 	case let e: ast::yield_expr =>
    415 		let z = fmt::fprint(out, "yield")?;
    416 		if (e.label != "") {
    417 			z += fmt::fprintf(out, " :{}", e.label)?;
    418 		};
    419 		match (e.value) {
    420 		case null => void;
    421 		case let v: *ast::expr =>
    422 			z += fmt::fprint(out, if (e.label == "")
    423 				" " else ", ")?;
    424 			z += expr(out, indent, *v)?;
    425 		};
    426 		return z;
    427 	};
    428 };
    429 
    430 fn binprecedence(op: binarithm_op) uint = {
    431 	switch (op) {
    432 	case binarithm_op::DIV, binarithm_op::MODULO, binarithm_op::TIMES =>
    433 		return 10;
    434 	case binarithm_op::MINUS, binarithm_op::PLUS =>
    435 		return 9;
    436 	case binarithm_op::LSHIFT, binarithm_op::RSHIFT =>
    437 		return 8;
    438 	case binarithm_op::BAND =>
    439 		return 7;
    440 	case binarithm_op::BXOR =>
    441 		return 6;
    442 	case binarithm_op::BOR =>
    443 		return 5;
    444 	case binarithm_op::GT, binarithm_op::GTEQ,
    445 		binarithm_op::LESS, binarithm_op::LESSEQ =>
    446 		return 4;
    447 	case binarithm_op::LEQUAL, binarithm_op::NEQUAL =>
    448 		return 3;
    449 	case binarithm_op::LAND =>
    450 		return 2;
    451 	case binarithm_op::LXOR =>
    452 		return 1;
    453 	case binarithm_op::LOR =>
    454 		return 0;
    455 	};
    456 };
    457 
    458 fn binexprval(
    459 	out: io::handle,
    460 	indent: size,
    461 	e: ast::expr,
    462 	prec: uint,
    463 ) (size | io::error) = {
    464 	let z = 0z;
    465 	match (e.expr) {
    466 	case let b: ast::binarithm_expr =>
    467 		if (binprecedence(b.op) < prec) {
    468 			z += fmt::fprint(out, "(")?;
    469 			z += expr(out, indent, e)?;
    470 			z += fmt::fprint(out, ")")?;
    471 			return z;
    472 		};
    473 	case => void;
    474 	};
    475 	z += expr(out, indent, e)?;
    476 	return z;
    477 };
    478 
    479 fn stmt(
    480 	out: io::handle,
    481 	indent: size,
    482 	e: ast::expr
    483 ) (size | io::error) = {
    484 	let n = 0z;
    485 	n += expr(out, indent, e)?;
    486 	n += fmt::fprint(out, ";")?;
    487 	return n;
    488 };
    489 
    490 fn constant(
    491 	out: io::handle,
    492 	indent: size,
    493 	e: ast::constant_expr,
    494 ) (size | io::error) = {
    495 	match (e) {
    496 	case void =>
    497 		return fmt::fprint(out, "void");
    498 	case let v: ast::value =>
    499 		return fmt::fprint(out, match (v) {
    500 		case void => abort();
    501 		case ast::_null =>
    502 			yield "null";
    503 		case let b: bool =>
    504 			return fmt::fprint(out, b);
    505 		case let s: str =>
    506 			const s = strings::multireplace(s,
    507 				(`\`, `\\`), (`"`, `\"`));
    508 			defer free(s);
    509 			return fmt::fprintf(out, `"{}"`, s);
    510 		case let r: rune =>
    511 			if (r == '\'' || r == '\\') {
    512 				return fmt::fprintf(out, `'\{}'`, r);
    513 			} else {
    514 				return fmt::fprintf(out, "'{}'", r);
    515 			};
    516 		});
    517 	case let ac: ast::array_constant =>
    518 		let z = fmt::fprint(out, "[")?;
    519 		for (let i = 0z; i < len(ac.values); i += 1) {
    520 			z += expr(out, indent, *ac.values[i])?;
    521 			if (i + 1 < len(ac.values)) {
    522 				z += fmt::fprint(out, ", ")?;
    523 			};
    524 		};
    525 		z += fmt::fprintf(out, "{}]",
    526 			if (ac.expand) "..." else "")?;
    527 		return z;
    528 	case let v: ast::number_constant =>
    529 		return fmt::fprintf(out, "{}{}", v.value, switch (v.suff) {
    530 		case ltok::LIT_U8 =>
    531 			yield "u8";
    532 		case ltok::LIT_U16 =>
    533 			yield "u16";
    534 		case ltok::LIT_U32 =>
    535 			yield "u32";
    536 		case ltok::LIT_U64 =>
    537 			yield "u64";
    538 		case ltok::LIT_UINT =>
    539 			yield "u";
    540 		case ltok::LIT_SIZE =>
    541 			yield "z";
    542 		case ltok::LIT_I8 =>
    543 			yield "i8";
    544 		case ltok::LIT_I16 =>
    545 			yield "i16";
    546 		case ltok::LIT_I32 =>
    547 			yield "i32";
    548 		case ltok::LIT_I64 =>
    549 			yield "i64";
    550 		case ltok::LIT_INT =>
    551 			yield "i";
    552 		case ltok::LIT_ICONST, ltok::LIT_FCONST =>
    553 			yield "";
    554 		case ltok::LIT_F32 =>
    555 			yield "f32";
    556 		case ltok::LIT_F64 =>
    557 			yield "f64";
    558 		case => abort();
    559 		});
    560 	case let sc: ast::struct_constant =>
    561 		return struct_constant(out, indent, sc)?;
    562 	case let tu: ast::tuple_constant =>
    563 		let z = fmt::fprint(out, "(")?;
    564 		for (let i = 0z; i < len(tu); i += 1) {
    565 			z += expr(out, indent, *tu[i])?;
    566 			if (i + 1 < len(tu)) {
    567 				z += fmt::fprint(out, ", ")?;
    568 			};
    569 		};
    570 		z += fmt::fprint(out, ")")?;
    571 		return z;
    572 	};
    573 };
    574 
    575 fn struct_constant(
    576 	out: io::handle,
    577 	indent: size,
    578 	sc: ast::struct_constant,
    579 ) (size | io::error) = {
    580 	let z = 0z;
    581 	z += if (len(sc.alias) != 0) {
    582 		yield ident(out, sc.alias)?;
    583 	} else {
    584 		yield fmt::fprint(out, "struct")?;
    585 	};
    586 	z += fmt::fprint(out, " {")?;
    587 	indent += 1;
    588 	for (let i = 0z; i < len(sc.fields); i += 1) {
    589 		z += newline(out, indent)?;
    590 		match (sc.fields[i]) {
    591 		case let sv: ast::struct_value =>
    592 			match (sv._type) {
    593 			case null =>
    594 				z += fmt::fprintf(out, "{}", sv.name)?;
    595 			case let t: *ast::_type =>
    596 				z += fmt::fprintf(out, "{}: ", sv.name)?;
    597 				z += _type(out, indent, *t)?;
    598 			};
    599 			z += fmt::fprint(out, " = ")?;
    600 			z += expr(out, indent, *sv.init)?;
    601 		case let sc: *ast::struct_constant =>
    602 			z += constant(out, indent, *sc)?;
    603 		};
    604 		z += fmt::fprint(out, ",")?;
    605 	};
    606 	if (sc.autofill) {
    607 		z += newline(out, indent)?;
    608 		z += fmt::fprint(out, "...")?;
    609 	};
    610 	indent -= 1;
    611 	z += newline(out, indent)?;
    612 	z += fmt::fprint(out, "}")?;
    613 	return z;
    614 };
    615 
    616 fn for_expr(
    617 	out: io::handle,
    618 	indent: size,
    619 	e: ast::for_expr,
    620 ) (size | io::error) = {
    621 	let z = fmt::fprintf(out, "for (")?;
    622 	match (e.bindings) {
    623 	case null => void;
    624 	case let e: *ast::expr =>
    625 		z += expr(out, indent, *e)?;
    626 		z += fmt::fprint(out, "; ")?;
    627 	};
    628 
    629 	z += expr(out, indent, *e.cond)?;
    630 
    631 	match (e.afterthought) {
    632 	case null => void;
    633 	case let e: *ast::expr =>
    634 		z += fmt::fprint(out, "; ")?;
    635 		z += expr(out, indent, *e)?;
    636 	};
    637 
    638 	z += fmt::fprintf(out, ") ")?;
    639 	return z + expr(out, indent, *e.body)?;
    640 };
    641 
    642 fn switch_expr(
    643 	out: io::handle,
    644 	indent: size,
    645 	e: ast::switch_expr,
    646 ) (size | io::error) = {
    647 	let z = fmt::fprint(out, "switch (")?;
    648 	z += expr(out, indent, *e.value)?;
    649 	z += fmt::fprint(out, ") {")?;
    650 
    651 	for (let i = 0z; i < len(e.cases); i += 1) {
    652 		z += newline(out, indent)?;
    653 		const item = e.cases[i];
    654 		z += fmt::fprint(out, "case ")?;
    655 		if (len(item.options) == 0) {
    656 			z += fmt::fprint(out, "=>")?;
    657 		} else {
    658 			for (let j = 0z; j < len(item.options); j += 1) {
    659 				const opt = item.options[j];
    660 				z += expr(out, indent, *opt)?;
    661 				if (j + 1 < len(item.options)) {
    662 					z += fmt::fprint(out, ", ")?;
    663 				};
    664 			};
    665 			z += fmt::fprint(out, " =>")?;
    666 		};
    667 		z += case_exprs(out, indent + 1, item.exprs)?;
    668 	};
    669 
    670 	z += newline(out, indent)?;
    671 	z += fmt::fprint(out, "}")?;
    672 	return z;
    673 };
    674 
    675 fn match_expr(
    676 	out: io::handle,
    677 	indent: size,
    678 	e: ast::match_expr,
    679 ) (size | io::error) = {
    680 	let z = fmt::fprint(out, "match (")?;
    681 	z += expr(out, indent, *e.value)?;
    682 	z += fmt::fprint(out, ") {")?;
    683 
    684 	for (let i = 0z; i < len(e.cases); i += 1) {
    685 		z += newline(out, indent)?;
    686 		z += fmt::fprint(out, "case")?;
    687 		const item = e.cases[i];
    688 		if (len(item.name) > 0) {
    689 			z += fmt::fprintf(out, " let {}", item.name)?;
    690 		};
    691 		match (item._type) {
    692 		case let typ: *ast::_type =>
    693 			if (len(item.name) > 0) {
    694 				z += fmt::fprint(out, ":")?;
    695 			};
    696 			z += fmt::fprint(out, " ")?;
    697 			z += _type(out, indent, *typ)?;
    698 		case null => void;
    699 		};
    700 		z += fmt::fprint(out, " =>")?;
    701 		z += case_exprs(out, indent + 1, item.exprs)?;
    702 	};
    703 
    704 	if (len(e.default) != 0) {
    705 		z += newline(out, indent)?;
    706 		z += fmt::fprint(out, "case =>")?;
    707 		z += case_exprs(out, indent + 1, e.default)!;
    708 	};
    709 
    710 	z += newline(out, indent)?;
    711 	z += fmt::fprint(out, "}")?;
    712 	return z;
    713 };
    714 
    715 fn case_exprs(
    716 	out: io::handle,
    717 	indent: size,
    718 	exprs: []*ast::expr,
    719 ) (size | io::error) = {
    720 	let z = 0z;
    721 
    722 	if (len(exprs) == 1) match (exprs[0].expr) {
    723 	case let e: ast::assert_expr =>
    724 		if (e.cond == null) {
    725 			// abort() expression
    726 			z += fmt::fprint(out, " ")?;
    727 			z += stmt(out, indent, *exprs[0])?;
    728 			return z;
    729 		};
    730 	case let e: ast::value =>
    731 		if (e is void) return fmt::fprint(out, " void;")?;
    732 	case => void;
    733 	};
    734 	for (let j = 0z; j < len(exprs); j += 1) {
    735 		z += newline(out, indent)?;
    736 		z += stmt(out, indent, *exprs[j])?;
    737 	};
    738 
    739 	return z;
    740 };