hare

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

decl.ha (5225B)


      1 // License: MPL-2.0
      2 // (c) 2021 Drew DeVault <sir@cmpwn.com>
      3 // (c) 2021 Eyal Sawady <ecs@d2evs.net>
      4 use ascii;
      5 use hare::ast;
      6 use hare::lex::{ltok};
      7 use hare::lex;
      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 fn decl_const(
     33 	lexer: *lex::lexer,
     34 	tok: ltok,
     35 ) ([]ast::decl_const | error) = {
     36 	let decl: []ast::decl_const = [];
     37 	for (true) {
     38 		const ident = ident(lexer)?;
     39 		want(lexer, ltok::COLON)?;
     40 		const _type = _type(lexer)?;
     41 		want(lexer, ltok::EQUAL)?;
     42 		const init: *ast::expr = alloc(expr(lexer)?);
     43 		append(decl, ast::decl_const {
     44 			ident = ident,
     45 			_type = _type,
     46 			init = init,
     47 		});
     48 
     49 		if (try(lexer, ltok::COMMA)? is void) {
     50 			break;
     51 		};
     52 	};
     53 	return decl;
     54 
     55 };
     56 
     57 fn decl_global(
     58 	lexer: *lex::lexer,
     59 	tok: ltok,
     60 ) ([]ast::decl_global | error) = {
     61 	let decl: []ast::decl_global = [];
     62 	for (true) {
     63 		const symbol = match (try(lexer, ltok::ATTR_SYMBOL)?) {
     64 		case void =>
     65 			yield "";
     66 		case lex::token =>
     67 			yield attr_symbol(lexer)?;
     68 		};
     69 		const ident = ident(lexer)?;
     70 		want(lexer, ltok::COLON)?;
     71 		const _type = _type(lexer)?;
     72 		const init: nullable *ast::expr =
     73 			match (try(lexer, ltok::EQUAL)?) {
     74 			case lex::token =>
     75 				yield alloc(expr(lexer)?);
     76 			case void =>
     77 				yield null;
     78 			};
     79 		const btok = try(lexer, ltok::COMMA)?;
     80 		append(decl, ast::decl_global {
     81 			is_const = tok == ltok::CONST,
     82 			symbol = symbol,
     83 			ident = ident,
     84 			_type = _type,
     85 			init = init,
     86 		});
     87 		if (btok is void) {
     88 			break;
     89 		};
     90 	};
     91 	return decl;
     92 };
     93 
     94 fn decl_type(lexer: *lex::lexer) ([]ast::decl_type | error) = {
     95 	let decl: []ast::decl_type = [];
     96 	for (true) {
     97 		let ident = ident(lexer)?;
     98 		want(lexer, ltok::EQUAL)?;
     99 		let _type = _type(lexer)?;
    100 		let btok = try(lexer, ltok::COMMA)?;
    101 		append(decl, ast::decl_type {
    102 			ident = ident,
    103 			_type = _type,
    104 		});
    105 		if (btok is void) {
    106 			break;
    107 		};
    108 	};
    109 	return decl;
    110 };
    111 
    112 fn decl_func(lexer: *lex::lexer) (ast::decl_func | error) = {
    113 	let attr = ast::fndecl_attrs::NONE, noreturn = false, sym = "";
    114 	const attrs = [
    115 		ltok::ATTR_FINI, ltok::ATTR_INIT, ltok::ATTR_TEST,
    116 		ltok::ATTR_NORETURN, ltok::ATTR_SYMBOL
    117 	];
    118 	for (true) match (try(lexer, attrs...)?) {
    119 	case void =>
    120 		break;
    121 	case let t: lex::token =>
    122 		switch (t.0) {
    123 		case ltok::ATTR_FINI =>
    124 			attr = ast::fndecl_attrs::FINI;
    125 		case ltok::ATTR_INIT =>
    126 			attr = ast::fndecl_attrs::INIT;
    127 		case ltok::ATTR_TEST =>
    128 			attr = ast::fndecl_attrs::TEST;
    129 		case ltok::ATTR_NORETURN =>
    130 			noreturn = true;
    131 		case ltok::ATTR_SYMBOL =>
    132 			sym = attr_symbol(lexer)?;
    133 		case =>
    134 			abort("unreachable");
    135 		};
    136 	};
    137 
    138 	want(lexer, ltok::FN)?;
    139 	let ident_loc = lex::mkloc(lexer);
    140 	let ident = ident(lexer)?;
    141 	let proto_start = lex::mkloc(lexer);
    142 	let prototype = prototype(lexer)?;
    143 	let proto_end = lex::prevloc(lexer);
    144 	if (noreturn) {
    145 		prototype.attrs |= ast::func_attrs::NORETURN;
    146 	};
    147 
    148 	let tok = want(lexer, ltok::EQUAL, ltok::SEMICOLON)?;
    149 	let body = switch (tok.0) {
    150 	case ltok::EQUAL =>
    151 		synassert(ident_loc, len(ident) == 1,
    152 			"Unexpected identifier, was expecting name")?;
    153 		const params = prototype.params;
    154 		for (let i = 0z; i < len(params); i += 1) {
    155 			synassert(params[i].loc,
    156 				len(params[i].name) > 0,
    157 				"Expected parameter name in function declaration")?;
    158 		};
    159 		yield expr(lexer)?;
    160 	case ltok::SEMICOLON =>
    161 		yield lex::unlex(lexer, tok);
    162 	};
    163 
    164 	return ast::decl_func {
    165 		symbol = sym,
    166 		ident = ident,
    167 		prototype = ast::_type {
    168 			start = proto_start,
    169 			end = proto_end,
    170 			flags = ast::type_flags::CONST,
    171 			repr = prototype,
    172 		},
    173 		body = body,
    174 		attrs = attr,
    175 	};
    176 };
    177 
    178 // Parses a declaration.
    179 export fn decl(lexer: *lex::lexer) (ast::decl | error) = {
    180 	const start = lex::mkloc(lexer);
    181 	let comment = "";
    182 	let exported = match (try(lexer, ltok::EXPORT)?) {
    183 	case void =>
    184 		yield false;
    185 	case lex::token =>
    186 		comment = strings::dup(lex::comment(lexer));
    187 		yield true;
    188 	};
    189 	const toks = [ltok::CONST, ltok::LET, ltok::DEF, ltok::TYPE];
    190 	const next = try(lexer, toks...)?;
    191 	if (comment == "") {
    192 		comment = strings::dup(lex::comment(lexer));
    193 	};
    194 	let decl = match (next) {
    195 	case void =>
    196 		yield decl_func(lexer)?;
    197 	case let t: lex::token =>
    198 		yield switch (t.0) {
    199 		case ltok::TYPE =>
    200 			yield decl_type(lexer)?;
    201 		case ltok::LET, ltok::CONST =>
    202 			yield decl_global(lexer, t.0)?;
    203 		case ltok::DEF =>
    204 			yield decl_const(lexer, t.0)?;
    205 		case => abort();
    206 		};
    207 	};
    208 	want(lexer, ltok::SEMICOLON)?;
    209 	return ast::decl {
    210 		exported = exported,
    211 		start = start,
    212 		end = lex::mkloc(lexer),
    213 		decl = decl,
    214 		docs = comment,
    215 	};
    216 };
    217 
    218 // Parses the declarations for a sub-unit.
    219 export fn decls(lexer: *lex::lexer) ([]ast::decl | error) = {
    220 	let decls: []ast::decl = [];
    221 	for (true) {
    222 		if (peek(lexer, ltok::EOF)? is lex::token) break;
    223 		append(decls, decl(lexer)?);
    224 	};
    225 	return decls;
    226 };