hare

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

decl.ha (7418B)


      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::lex;
      7 use io;
      8 use memio;
      9 use strings;
     10 
     11 // Unparses a [[hare::ast::decl]].
     12 export fn decl(
     13 	out: io::handle,
     14 	syn: *synfunc,
     15 	d: *ast::decl,
     16 ) (size | io::error) = {
     17 	let n = 0z;
     18 	let ctx = context {
     19 		out = out,
     20 		stack = &stack {
     21 			cur = d,
     22 			...
     23 		},
     24 		...
     25 	};
     26 	if (len(d.docs) > 0) {
     27 		n += comment(&ctx, syn, d.docs)?;
     28 	};
     29 	if (d.exported) {
     30 		n += syn(&ctx, "export", synkind::KEYWORD)?;
     31 		n += space(&ctx)?;
     32 	};
     33 	match (d.decl) {
     34 	case let c: []ast::decl_const =>
     35 		n += syn(&ctx, "def", synkind::KEYWORD)?;
     36 		n += space(&ctx)?;
     37 		for (let i = 0z; i < len(c); i += 1) {
     38 			n += _ident(&ctx, syn, c[i].ident, synkind::CONSTANT)?;
     39 			match (c[i]._type) {
     40 			case null => void;
     41 			case let ty: *ast::_type =>
     42 				n += syn(&ctx, ":", synkind::PUNCTUATION)?;
     43 				n += space(&ctx)?;
     44 				n += __type(&ctx, syn, ty)?;
     45 			};
     46 			n += space(&ctx)?;
     47 			n += syn(&ctx, "=", synkind::OPERATOR)?;
     48 			n += space(&ctx)?;
     49 			n += _expr(&ctx, syn, c[i].init)?;
     50 			if (i + 1 < len(c)) {
     51 				n += syn(&ctx, ",", synkind::PUNCTUATION)?;
     52 				n += space(&ctx)?;
     53 			};
     54 		};
     55 	case let g: []ast::decl_global =>
     56 		n += syn(&ctx,
     57 			if (g[0].is_const) "const" else "let",
     58 			synkind::KEYWORD)?;
     59 		n += space(&ctx)?;
     60 		for (let i = 0z; i < len(g); i += 1) {
     61 			if (len(g[i].symbol) != 0) {
     62 				n += syn(&ctx, "@symbol(", synkind::ATTRIBUTE)?;
     63 				n += literal(&ctx, syn, g[i].symbol)?;
     64 				n += syn(&ctx, ")", synkind::ATTRIBUTE)?;
     65 				n += space(&ctx)?;
     66 			} else if (g[i].is_threadlocal) {
     67 				n += syn(&ctx, "@threadlocal",
     68 					synkind::ATTRIBUTE)?;
     69 				n += space(&ctx)?;
     70 			};
     71 			n += _ident(&ctx, syn, g[i].ident, synkind::GLOBAL)?;
     72 			match (g[i]._type) {
     73 			case null => void;
     74 			case let ty: *ast::_type =>
     75 				n += syn(&ctx, ":", synkind::PUNCTUATION)?;
     76 				n += space(&ctx)?;
     77 				n += __type(&ctx, syn, ty)?;
     78 			};
     79 			match (g[i].init) {
     80 			case null => void;
     81 			case let ex: *ast::expr =>
     82 				n += space(&ctx)?;
     83 				n += syn(&ctx, "=", synkind::OPERATOR)?;
     84 				n += space(&ctx)?;
     85 				n += _expr(&ctx, syn, ex)?;
     86 			};
     87 			if (i + 1 < len(g)) {
     88 				n += syn(&ctx, ",", synkind::OPERATOR)?;
     89 				n += space(&ctx)?;
     90 			};
     91 		};
     92 	case let t: []ast::decl_type =>
     93 		n += syn(&ctx, "type", synkind::KEYWORD)?;
     94 		n += space(&ctx)?;
     95 		for (let i = 0z; i < len(t); i += 1) {
     96 			n += _ident(&ctx, syn, t[i].ident, synkind::TYPEDEF)?;
     97 			n += space(&ctx)?;
     98 			n += syn(&ctx, "=", synkind::OPERATOR)?;
     99 			n += space(&ctx)?;
    100 			n += __type(&ctx, syn, t[i]._type)?;
    101 			if (i + 1 < len(t)) {
    102 				n += syn(&ctx, ",", synkind::PUNCTUATION)?;
    103 				n += space(&ctx)?;
    104 			};
    105 		};
    106 	case let f: ast::decl_func =>
    107 		ctx.stack = &stack {
    108 			cur = f.prototype,
    109 			up = ctx.stack,
    110 			...
    111 		};
    112 		defer {
    113 			let stack = &(ctx.stack as *stack);
    114 			match (stack.extra) {
    115 			case let p: *opaque =>
    116 				free(p);
    117 			case null => void;
    118 			};
    119 			ctx.stack = stack.up;
    120 		};
    121 
    122 		switch (f.attrs) {
    123 		case ast::fndecl_attr::NONE => void;
    124 		case ast::fndecl_attr::FINI =>
    125 			n += syn(&ctx, "@fini", synkind::ATTRIBUTE)?;
    126 			n += space(&ctx)?;
    127 		case ast::fndecl_attr::INIT =>
    128 			n += syn(&ctx, "@init", synkind::ATTRIBUTE)?;
    129 			n += space(&ctx)?;
    130 		case ast::fndecl_attr::TEST =>
    131 			n += syn(&ctx, "@test", synkind::ATTRIBUTE)?;
    132 			n += space(&ctx)?;
    133 		};
    134 		let p = f.prototype.repr as ast::func_type;
    135 		if (len(f.symbol) != 0) {
    136 			n += syn(&ctx, "@symbol(", synkind::ATTRIBUTE)?;
    137 			n += literal(&ctx, syn, f.symbol)?;
    138 			n += syn(&ctx, ")", synkind::ATTRIBUTE)?;
    139 			n += space(&ctx)?;
    140 		};
    141 		n += syn(&ctx, "fn", synkind::KEYWORD)?;
    142 		n += space(&ctx)?;
    143 		n += _ident(&ctx, syn, f.ident, synkind::FUNCTION)?;
    144 		const fntype = f.prototype.repr as ast::func_type;
    145 		n += prototype(&ctx, syn, &fntype)?;
    146 		match (f.body) {
    147 		case null => void;
    148 		case let e: *ast::expr =>
    149 			n += space(&ctx)?;
    150 			n += syn(&ctx, "=", synkind::OPERATOR)?;
    151 			n += space(&ctx)?;
    152 			n += _expr(&ctx, syn, e)?;
    153 		};
    154 	case let e: ast::assert_expr =>
    155 		n += assert_expr(&ctx, syn, &e)?;
    156 	};
    157 	n += syn(&ctx, ";", synkind::PUNCTUATION)?;
    158 	return n;
    159 };
    160 
    161 fn comment(ctx: *context, syn: *synfunc, s: str) (size | io::error) = {
    162 	let n = 0z;
    163 	let s = strings::trimsuffix(s, "\n");
    164 	let s = strings::tokenize(s, "\n");
    165 
    166 	for (let line => strings::next_token(&s)) {
    167 		for (let i = 0z; i < ctx.indent; i += 1) {
    168 			n += syn(ctx, "\t", synkind::COMMENT)?;
    169 			ctx.linelen += 8;
    170 		};
    171 		n += syn(ctx, "//", synkind::COMMENT)?;
    172 		n += syn(ctx, line, synkind::COMMENT)?;
    173 		n += syn(ctx, "\n", synkind::COMMENT)?;
    174 		ctx.linelen = 0;
    175 	};
    176 	return n;
    177 };
    178 
    179 fn decl_test(d: *ast::decl, expected: str) bool = {
    180 	let buf = memio::dynamic();
    181 	decl(&buf, &syn_nowrap, d)!;
    182 	let s = memio::string(&buf)!;
    183 	defer free(s);
    184 	fmt::println(s)!;
    185 	return s == expected;
    186 };
    187 
    188 @test fn decl() void = {
    189 	let loc = lex::location {
    190 		path = "<test>",
    191 		line = 0,
    192 		col = 0,
    193 	};
    194 	let type_int = ast::_type {
    195 		start = loc,
    196 		end = loc,
    197 		flags = 0,
    198 		repr = ast::builtin_type::INT,
    199 	};
    200 	let type_fn = ast::_type {
    201 		start = loc,
    202 		end = loc,
    203 		flags = 0,
    204 		repr = ast::func_type {
    205 			result = &type_int,
    206 			variadism = ast::variadism::HARE,
    207 			params = [
    208 				ast::func_param {
    209 					loc = loc,
    210 					name = "foo",
    211 					_type = &type_int,
    212 					default_value = void,
    213 				},
    214 				ast::func_param {
    215 					loc = loc,
    216 					name = "bar",
    217 					_type = &type_int,
    218 					default_value = void,
    219 				},
    220 			],
    221 		},
    222 	};
    223 	let expr_void = ast::expr {
    224 		start = lex::location { ... },
    225 		end = lex::location { ... },
    226 		expr = void,
    227 	};
    228 
    229 	let d = ast::decl {
    230 		exported = false,
    231 		start = loc,
    232 		end = loc,
    233 		decl = [
    234 			ast::decl_global {
    235 				is_const = false,
    236 				is_threadlocal = false,
    237 				symbol = "",
    238 				ident = ["foo", "bar"],
    239 				_type = &type_int,
    240 				init = alloc(expr_void),
    241 			},
    242 			ast::decl_global {
    243 				is_const = false,
    244 				is_threadlocal = true,
    245 				symbol = "",
    246 				ident = ["boo"],
    247 				_type = &type_int,
    248 				init = alloc(expr_void),
    249 			},
    250 			ast::decl_global {
    251 				is_const = false,
    252 				is_threadlocal = false,
    253 				symbol = "foobar",
    254 				ident = ["baz"],
    255 				_type = &type_int,
    256 				init = alloc(expr_void),
    257 			},
    258 		],
    259 		...
    260 	};
    261 	assert(decl_test(&d, "let foo::bar: int = void, @threadlocal boo: int = void, @symbol(\"foobar\") baz: int = void;"));
    262 
    263 	d.exported = true;
    264 	d.decl = [
    265 		ast::decl_const {
    266 			ident = ["foo"],
    267 			_type = &type_int,
    268 			init = alloc(expr_void),
    269 		},
    270 	];
    271 	assert(decl_test(&d, "export def foo: int = void;"));
    272 
    273 	d.exported = false;
    274 	d.decl = [
    275 		ast::decl_type {
    276 			ident = ["foo"],
    277 			_type = &type_int,
    278 		},
    279 		ast::decl_type {
    280 			ident = ["bar"],
    281 			_type = &type_int,
    282 		},
    283 	];
    284 	assert(decl_test(&d, "type foo = int, bar = int;"));
    285 
    286 	d.decl = ast::decl_func {
    287 		symbol = "foo",
    288 		ident = ["foo"],
    289 		prototype = &type_fn,
    290 		body = null,
    291 		attrs = ast::fndecl_attr::FINI,
    292 	};
    293 	assert(decl_test(&d, "@fini @symbol(\"foo\") fn foo(foo: int, bar: int...) int;"));
    294 
    295 	type_fn.repr = ast::func_type {
    296 		result = &type_int,
    297 		variadism = ast::variadism::NONE,
    298 		params = [
    299 			ast::func_param {
    300 				loc = loc,
    301 				name = "",
    302 				_type = &type_int,
    303 				default_value = ast::expr {
    304 					expr = ast::number_literal {
    305 						value = 4u64,
    306 						sign = false,
    307 						suff = lex::ltok::LIT_ICONST,
    308 					},
    309 					...
    310 				},
    311 			},
    312 		],
    313 	};
    314 	d.decl = ast::decl_func {
    315 		symbol = "",
    316 		ident = ["foo"],
    317 		prototype = &type_fn,
    318 		body = &expr_void,
    319 		attrs = 0,
    320 	};
    321 	assert(decl_test(&d, "fn foo(int = 4) int = void;"));
    322 };