decl.ha (5813B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use ascii; 5 use hare::ast; 6 use hare::lex; 7 use hare::lex::{ltok}; 8 use strings; 9 10 fn attr_symbol(lexer: *lex::lexer) (str | error) = { 11 want(lexer, ltok::LPAREN)?; 12 let t = want(lexer, ltok::LIT_STR)?; 13 let s = t.1 as str; 14 let d = strings::iter(s); 15 match (strings::next(&d)) { 16 case void => void; 17 case let r: rune => 18 synassert(t.2, ascii::isalpha(r) || r == '.' 19 || r == '_', "Invalid symbol")?; 20 }; 21 for (true) match (strings::next(&d)) { 22 case void => 23 break; 24 case let r: rune => 25 synassert(t.2, ascii::isalnum(r) || r == '$' 26 || r == '.' || r == '_', "Invalid symbol")?; 27 }; 28 want(lexer, ltok::RPAREN)?; 29 return s; 30 }; 31 32 // Parses a command-line definition 33 export fn define(lexer: *lex::lexer) (ast::decl_const | error) = { 34 const ident = ident(lexer)?; 35 const _type: nullable *ast::_type = match (try(lexer, ltok::COLON)?) { 36 case lex::token => yield alloc(_type(lexer)?); 37 case void => yield null; 38 }; 39 want(lexer, ltok::EQUAL)?; 40 const init: *ast::expr = alloc(expr(lexer)?); 41 return ast::decl_const { 42 ident = ident, 43 _type = _type, 44 init = init, 45 }; 46 }; 47 48 fn decl_const( 49 lexer: *lex::lexer, 50 tok: ltok, 51 ) ([]ast::decl_const | error) = { 52 let decl: []ast::decl_const = []; 53 for (true) { 54 append(decl, define(lexer)?); 55 56 if (try(lexer, ltok::COMMA)? is void) { 57 break; 58 }; 59 }; 60 return decl; 61 62 }; 63 64 fn decl_global( 65 lexer: *lex::lexer, 66 tok: ltok, 67 ) ([]ast::decl_global | error) = { 68 let decl: []ast::decl_global = []; 69 for (true) { 70 const (symbol, threadlocal) = match (try(lexer, 71 ltok::ATTR_SYMBOL, ltok::ATTR_THREADLOCAL)?) { 72 case void => 73 yield ("", false); 74 case let t: lex::token => 75 yield if (t.0 == ltok::ATTR_SYMBOL) { 76 yield (attr_symbol(lexer)?, false); 77 } else { 78 yield ("", true); 79 }; 80 }; 81 const ident = ident(lexer)?; 82 const _type: nullable *ast::_type = 83 match (try(lexer, ltok::COLON)?) { 84 case lex::token => 85 yield alloc(_type(lexer)?); 86 case void => 87 yield null; 88 }; 89 const init: nullable *ast::expr = 90 match (try(lexer, ltok::EQUAL)?) { 91 case lex::token => 92 yield alloc(expr(lexer)?); 93 case void => 94 yield null; 95 }; 96 const btok = try(lexer, ltok::COMMA)?; 97 append(decl, ast::decl_global { 98 is_const = tok == ltok::CONST, 99 is_threadlocal = threadlocal, 100 symbol = symbol, 101 ident = ident, 102 _type = _type, 103 init = init, 104 }); 105 if (btok is void) { 106 break; 107 }; 108 }; 109 return decl; 110 }; 111 112 fn decl_type(lexer: *lex::lexer) ([]ast::decl_type | error) = { 113 let decl: []ast::decl_type = []; 114 for (true) { 115 let ident = ident(lexer)?; 116 want(lexer, ltok::EQUAL)?; 117 let _type = _type(lexer)?; 118 let btok = try(lexer, ltok::COMMA)?; 119 append(decl, ast::decl_type { 120 ident = ident, 121 _type = alloc(_type), 122 }); 123 if (btok is void) { 124 break; 125 }; 126 }; 127 return decl; 128 }; 129 130 fn decl_func(lexer: *lex::lexer) (ast::decl_func | error) = { 131 let attr = ast::fndecl_attr::NONE, sym = ""; 132 const attrs = [ 133 ltok::ATTR_FINI, ltok::ATTR_INIT, ltok::ATTR_TEST, 134 ltok::ATTR_SYMBOL 135 ]; 136 for (true) match (try(lexer, attrs...)?) { 137 case void => 138 break; 139 case let t: lex::token => 140 switch (t.0) { 141 case ltok::ATTR_FINI => 142 attr = ast::fndecl_attr::FINI; 143 case ltok::ATTR_INIT => 144 attr = ast::fndecl_attr::INIT; 145 case ltok::ATTR_TEST => 146 attr = ast::fndecl_attr::TEST; 147 case ltok::ATTR_SYMBOL => 148 sym = attr_symbol(lexer)?; 149 case => 150 abort("unreachable"); 151 }; 152 }; 153 154 want(lexer, ltok::FN)?; 155 let ident_loc = lex::mkloc(lexer); 156 let ident = ident(lexer)?; 157 let proto_start = lex::mkloc(lexer); 158 let prototype = prototype(lexer)?; 159 let proto_end = lex::prevloc(lexer); 160 161 let tok = want(lexer, ltok::EQUAL, ltok::SEMICOLON)?; 162 let body = switch (tok.0) { 163 case ltok::EQUAL => 164 const params = prototype.params; 165 for (let i = 0z; i < len(params); i += 1) { 166 synassert(params[i].loc, 167 len(params[i].name) > 0, 168 "Expected parameter name in function declaration")?; 169 }; 170 yield alloc(expr(lexer)?); 171 case ltok::SEMICOLON => 172 lex::unlex(lexer, tok); 173 yield null; 174 case => abort(); // unreachable 175 }; 176 177 return ast::decl_func { 178 symbol = sym, 179 ident = ident, 180 prototype = alloc(ast::_type { 181 start = proto_start, 182 end = proto_end, 183 flags = 0, 184 repr = prototype, 185 }), 186 body = body, 187 attrs = attr, 188 }; 189 }; 190 191 // Parses a declaration. 192 export fn decl(lexer: *lex::lexer) (ast::decl | error) = { 193 const start = lex::mkloc(lexer); 194 let comment = ""; 195 if (try(lexer, ltok::STATIC)? is lex::token) { 196 comment = strings::dup(lex::comment(lexer)); 197 let expr = assert_expr(lexer, true)?; 198 want(lexer, ltok::SEMICOLON)?; 199 return ast::decl { 200 exported = false, 201 start = start, 202 end = expr.end, 203 decl = expr.expr as ast::assert_expr, 204 docs = comment, 205 }; 206 }; 207 let exported = match (try(lexer, ltok::EXPORT)?) { 208 case void => 209 yield false; 210 case lex::token => 211 comment = strings::dup(lex::comment(lexer)); 212 yield true; 213 }; 214 const toks = [ltok::CONST, ltok::LET, ltok::DEF, ltok::TYPE]; 215 const next = try(lexer, toks...)?; 216 if (comment == "") { 217 comment = strings::dup(lex::comment(lexer)); 218 }; 219 let decl = match (next) { 220 case void => 221 yield decl_func(lexer)?; 222 case let t: lex::token => 223 yield switch (t.0) { 224 case ltok::TYPE => 225 yield decl_type(lexer)?; 226 case ltok::LET, ltok::CONST => 227 yield decl_global(lexer, t.0)?; 228 case ltok::DEF => 229 yield decl_const(lexer, t.0)?; 230 case => abort(); 231 }; 232 }; 233 want(lexer, ltok::SEMICOLON)?; 234 return ast::decl { 235 exported = exported, 236 start = start, 237 end = lex::mkloc(lexer), 238 decl = decl, 239 docs = comment, 240 }; 241 }; 242 243 // Parses the declarations for a sub-unit. 244 export fn decls(lexer: *lex::lexer) ([]ast::decl | error) = { 245 let decls: []ast::decl = []; 246 for (true) { 247 if (peek(lexer, ltok::EOF)? is lex::token) break; 248 append(decls, decl(lexer)?); 249 }; 250 return decls; 251 };