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