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