hare

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

parse.ha (2395B)


      1 // License: MPL-2.0
      2 // (c) 2022 Alexey Yerin <yyp@disroot.org>
      3 // (c) 2021 Byron Torres <b@torresjrjr.com>
      4 // (c) 2021 Drew DeVault <sir@cmpwn.com>
      5 // (c) 2021 Ember Sawady <ecs@d2evs.net>
      6 use fmt;
      7 use hare::lex::{ltok};
      8 use hare::lex;
      9 use io;
     10 use memio;
     11 
     12 // All possible error types.
     13 export type error = !lex::error;
     14 
     15 // Convert an error into a human-friendly string.
     16 export fn strerror(err: error) const str = lex::strerror(err: lex::error);
     17 
     18 fn syntaxerr(
     19 	loc: lex::location,
     20 	fmt: str,
     21 	args: fmt::field...
     22 ) lex::error = {
     23 	let why = fmt::asprintf(fmt, args...);
     24 	return lex::syntaxerr(loc, why);
     25 };
     26 
     27 // Requires the next token to have a matching ltok. Returns that token, or an
     28 // error.
     29 fn want(lexer: *lex::lexer, want: lex::ltok...) (lex::token | error) = {
     30 	let tok = lex::lex(lexer)?;
     31 	if (len(want) == 0) {
     32 		return tok;
     33 	};
     34 	for (let i = 0z; i < len(want); i += 1) {
     35 		if (tok.0 == want[i]) {
     36 			return tok;
     37 		};
     38 	};
     39 
     40 	let buf = memio::dynamic();
     41 	defer io::close(&buf)!;
     42 	for (let i = 0z; i < len(want); i += 1) {
     43 		const tstr = if (want[i] == ltok::NAME) "name"
     44 			else lex::tokstr((want[i], void, lex::mkloc(lexer)));
     45 		fmt::fprintf(&buf, "'{}'", tstr)!;
     46 		if (i + 1 < len(want)) {
     47 			fmt::fprint(&buf, ", ")!;
     48 		};
     49 	};
     50 	lex::unlex(lexer, tok);
     51 	return syntaxerr(lex::mkloc(lexer), "Unexpected '{}', was expecting {}",
     52 		lex::tokstr(tok), memio::string(&buf)!);
     53 };
     54 
     55 // Looks for a matching ltok from the lexer, and if not present, unlexes the
     56 // token and returns void. If found, the token is consumed from the lexer and is
     57 // returned.
     58 fn try(
     59 	lexer: *lex::lexer,
     60 	want: lex::ltok...
     61 ) (lex::token | error | void) = {
     62 	let tok = lex::lex(lexer)?;
     63 	assert(len(want) > 0);
     64 	for (let i = 0z; i < len(want); i += 1) {
     65 		if (tok.0 == want[i]) {
     66 			return tok;
     67 		};
     68 	};
     69 	lex::unlex(lexer, tok);
     70 };
     71 
     72 // Looks for a matching ltok from the lexer, unlexes the token, and returns
     73 // it; or void if it was not an ltok.
     74 fn peek(
     75 	lexer: *lex::lexer,
     76 	want: lex::ltok...
     77 ) (lex::token | error | void) = {
     78 	let tok = lex::lex(lexer)?;
     79 	lex::unlex(lexer, tok);
     80 	if (len(want) == 0) {
     81 		return tok;
     82 	};
     83 	for (let i = 0z; i < len(want); i += 1) {
     84 		if (tok.0 == want[i]) {
     85 			return tok;
     86 		};
     87 	};
     88 };
     89 
     90 // Returns a syntax error if cond is false and void otherwise
     91 fn synassert(loc: lex::location, cond: bool, msg: str) (void | error) = {
     92 	if (!cond) {
     93 		return syntaxerr(loc, "{}", msg);
     94 	};
     95 };