hare

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

parse.ha (2379B)


      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 Eyal Sawady <ecs@d2evs.net>
      6 use fmt;
      7 use hare::lex::{ltok};
      8 use hare::lex;
      9 use io;
     10 use strio;
     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 (loc, why): lex::syntax: lex::error;
     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 = strio::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 	return syntaxerr(lex::mkloc(lexer), "Unexpected '{}', was expecting {}",
     51 		lex::tokstr(tok), strio::string(&buf));
     52 };
     53 
     54 // Looks for a matching ltok from the lexer, and if not present, unlexes the
     55 // token and returns void. If found, the token is consumed from the lexer and is
     56 // returned.
     57 fn try(
     58 	lexer: *lex::lexer,
     59 	want: lex::ltok...
     60 ) (lex::token | error | void) = {
     61 	let tok = lex::lex(lexer)?;
     62 	assert(len(want) > 0);
     63 	for (let i = 0z; i < len(want); i += 1) {
     64 		if (tok.0 == want[i]) {
     65 			return tok;
     66 		};
     67 	};
     68 	lex::unlex(lexer, tok);
     69 };
     70 
     71 // Looks for a matching ltok from the lexer, unlexes the token, and returns
     72 // it; or void if it was not an ltok.
     73 fn peek(
     74 	lexer: *lex::lexer,
     75 	want: lex::ltok...
     76 ) (lex::token | error | void) = {
     77 	let tok = lex::lex(lexer)?;
     78 	lex::unlex(lexer, tok);
     79 	if (len(want) == 0) {
     80 		return tok;
     81 	};
     82 	for (let i = 0z; i < len(want); i += 1) {
     83 		if (tok.0 == want[i]) {
     84 			return tok;
     85 		};
     86 	};
     87 };
     88 
     89 // Returns a syntax error if cond is false and void otherwise
     90 fn synassert(loc: lex::location, cond: bool, msg: str) (void | error) = {
     91 	if (!cond) {
     92 		return syntaxerr(loc, "{}", msg);
     93 	};
     94 };