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 };