hare

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

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