hare

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

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