hare

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

parse.ha (2370B)


      1 // SPDX-License-Identifier: MPL-2.0
      2 // (c) Hare authors <https://harelang.org>
      3 
      4 use fmt;
      5 use hare::lex;
      6 use hare::lex::{ltok};
      7 use io;
      8 use memio;
      9 
     10 // All possible error types.
     11 export type error = !lex::error;
     12 
     13 // Convert an error into a human-friendly string. The result may be statically
     14 // allocated.
     15 export fn strerror(err: error) const str = lex::strerror(err: lex::error);
     16 
     17 fn syntaxerr(
     18 	loc: lex::location,
     19 	fmt: str,
     20 	args: fmt::field...
     21 ) lex::error = {
     22 	static let buf: [4096]u8 = [0...];
     23 	let why = fmt::bsprintf(buf, 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 };