/* * Copyright (c) 2003-2023, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of New Artisans LLC nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "parser.h" namespace ledger { expr_t::ptr_op_t expr_t::parser_t::parse_value_term(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node; token_t& tok = next_token(in, tflags); switch (tok.kind) { case token_t::VALUE: node = new op_t(op_t::VALUE); node->set_value(tok.value); break; case token_t::IDENT: { string ident = tok.value.as_string(); node = new op_t(op_t::IDENT); node->set_ident(ident); break; } case token_t::LPAREN: node = parse_value_expr(in, tflags.plus_flags(PARSE_PARTIAL) .minus_flags(PARSE_SINGLE)); tok = next_token(in, tflags, token_t::RPAREN); break; default: push_token(tok); break; } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_call_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_value_term(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { while (true) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::LPAREN) { ptr_op_t prev(node); node = new op_t(op_t::O_CALL); node->set_left(prev); push_token(tok); // let the parser see the '(' again node->set_right(parse_value_expr(in, tflags.plus_flags(PARSE_SINGLE))); } else { push_token(tok); break; } } } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_dot_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_call_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { while (true) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::DOT) { ptr_op_t prev(node); node = new op_t(op_t::O_LOOKUP); node->set_left(prev); node->set_right(parse_call_expr(in, tflags)); if (! node->right()) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol); } else { push_token(tok); break; } } } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_unary_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node; token_t& tok = next_token(in, tflags); switch (tok.kind) { case token_t::EXCLAM: { ptr_op_t term(parse_dot_expr(in, tflags)); if (! term) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol); // A very quick optimization if (term->kind == op_t::VALUE) { term->as_value_lval().in_place_not(); node = term; } else { node = new op_t(op_t::O_NOT); node->set_left(term); } break; } case token_t::MINUS: { ptr_op_t term(parse_dot_expr(in, tflags)); if (! term) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol); // A very quick optimization if (term->kind == op_t::VALUE) { term->as_value_lval().in_place_negate(); node = term; } else { node = new op_t(op_t::O_NEG); node->set_left(term); } break; } default: push_token(tok); node = parse_dot_expr(in, tflags); break; } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_mul_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_unary_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { while (true) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::STAR || tok.kind == token_t::SLASH || tok.kind == token_t::KW_DIV) { ptr_op_t prev(node); node = new op_t(tok.kind == token_t::STAR ? op_t::O_MUL : op_t::O_DIV); node->set_left(prev); node->set_right(parse_unary_expr(in, tflags)); if (! node->right()) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol); } else { push_token(tok); break; } } } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_add_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_mul_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { while (true) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::PLUS || tok.kind == token_t::MINUS) { ptr_op_t prev(node); node = new op_t(tok.kind == token_t::PLUS ? op_t::O_ADD : op_t::O_SUB); node->set_left(prev); node->set_right(parse_mul_expr(in, tflags)); if (! node->right()) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol); } else { push_token(tok); break; } } } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_logic_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_add_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { while (true) { op_t::kind_t kind = op_t::LAST; parse_flags_t _flags = tflags; token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); bool negate = false; switch (tok.kind) { case token_t::EQUAL: if (tflags.has_flags(PARSE_NO_ASSIGN)) tok.rewind(in); else kind = op_t::O_EQ; break; case token_t::NEQUAL: kind = op_t::O_EQ; negate = true; break; case token_t::MATCH: kind = op_t::O_MATCH; break; case token_t::NMATCH: kind = op_t::O_MATCH; negate = true; break; case token_t::LESS: kind = op_t::O_LT; break; case token_t::LESSEQ: kind = op_t::O_LTE; break; case token_t::GREATER: kind = op_t::O_GT; break; case token_t::GREATEREQ: kind = op_t::O_GTE; break; default: push_token(tok); goto exit_loop; } if (kind != op_t::LAST) { ptr_op_t prev(node); node = new op_t(kind); node->set_left(prev); node->set_right(parse_add_expr(in, _flags)); if (! node->right()) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol); if (negate) { prev = node; node = new op_t(op_t::O_NOT); node->set_left(prev); } } } } exit_loop: return node; } expr_t::ptr_op_t expr_t::parser_t::parse_and_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_logic_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { while (true) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::KW_AND) { ptr_op_t prev(node); node = new op_t(op_t::O_AND); node->set_left(prev); node->set_right(parse_logic_expr(in, tflags)); if (! node->right()) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol); } else { push_token(tok); break; } } } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_or_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_and_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { while (true) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::KW_OR) { ptr_op_t prev(node); node = new op_t(op_t::O_OR); node->set_left(prev); node->set_right(parse_and_expr(in, tflags)); if (! node->right()) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol); } else { push_token(tok); break; } } } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_querycolon_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_or_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::QUERY) { ptr_op_t prev(node); node = new op_t(op_t::O_QUERY); node->set_left(prev); node->set_right(parse_or_expr(in, tflags)); if (! node->right()) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol); next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT), token_t::COLON); prev = node->right(); ptr_op_t subnode = new op_t(op_t::O_COLON); subnode->set_left(prev); subnode->set_right(parse_or_expr(in, tflags)); if (! subnode->right()) throw_(parse_error, _f("%1% operator not followed by argument") % tok.symbol); node->set_right(subnode); } else if (tok.kind == token_t::KW_IF) { ptr_op_t if_op(parse_or_expr(in, tflags)); if (! if_op) throw_(parse_error, _("'if' keyword not followed by argument")); tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::KW_ELSE) { ptr_op_t else_op(parse_or_expr(in, tflags)); if (! else_op) throw_(parse_error, _("'else' keyword not followed by argument")); ptr_op_t subnode = new op_t(op_t::O_COLON); subnode->set_left(node); subnode->set_right(else_op); node = new op_t(op_t::O_QUERY); node->set_left(if_op); node->set_right(subnode); } else { ptr_op_t null_node = new op_t(op_t::VALUE); null_node->set_value(NULL_VALUE); ptr_op_t subnode = new op_t(op_t::O_COLON); subnode->set_left(node); subnode->set_right(null_node); node = new op_t(op_t::O_QUERY); node->set_left(if_op); node->set_right(subnode); push_token(tok); } } else { push_token(tok); } } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_comma_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_querycolon_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { ptr_op_t next; while (true) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::COMMA) { if (! next) { ptr_op_t prev(node); node = new op_t(op_t::O_CONS); node->set_left(prev); next = node; } token_t& ntok = next_token(in, tflags); push_token(ntok); if (ntok.kind == token_t::RPAREN) break; ptr_op_t chain(new op_t(op_t::O_CONS)); chain->set_left(parse_querycolon_expr(in, tflags)); next->set_right(chain); next = chain; } else { push_token(tok); break; } } } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_lambda_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_comma_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::ARROW) { ptr_op_t prev(node); node = new op_t(op_t::O_LAMBDA); node->set_left(prev); ptr_op_t scope(new op_t(op_t::SCOPE)); scope->set_left(parse_querycolon_expr(in, tflags)); node->set_right(scope); } else { push_token(tok); } } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_assign_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_lambda_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::ASSIGN) { ptr_op_t prev(node); node = new op_t(op_t::O_DEFINE); node->set_left(prev); ptr_op_t scope(new op_t(op_t::SCOPE)); scope->set_left(parse_lambda_expr(in, tflags)); node->set_right(scope); } else { push_token(tok); } } return node; } expr_t::ptr_op_t expr_t::parser_t::parse_value_expr(std::istream& in, const parse_flags_t& tflags) const { ptr_op_t node(parse_assign_expr(in, tflags)); if (node && ! tflags.has_flags(PARSE_SINGLE)) { ptr_op_t chain; while (true) { token_t& tok = next_token(in, tflags.plus_flags(PARSE_OP_CONTEXT)); if (tok.kind == token_t::SEMI) { ptr_op_t seq(new op_t(op_t::O_SEQ)); if (! chain) { seq->set_left(node); node = seq; } else { seq->set_left(chain->right()); chain->set_right(seq); } seq->set_right(parse_assign_expr(in, tflags)); chain = seq; } else { push_token(tok); break; } } } return node; } expr_t::ptr_op_t expr_t::parser_t::parse(std::istream& in, const parse_flags_t& flags, const optional& original_string) { try { ptr_op_t top_node = parse_value_expr(in, flags); if (use_lookahead) { use_lookahead = false; lookahead.rewind(in); } lookahead.clear(); return top_node; } catch (const std::exception&) { if (original_string) { add_error_context(_("While parsing value expression:")); std::streamoff end_pos = 0; if (in.good()) end_pos = in.tellg(); std::streamoff pos = end_pos; if (pos > 0) pos -= lookahead.length; DEBUG("parser.error", "original_string = '" << *original_string << "'"); DEBUG("parser.error", " pos = " << pos); DEBUG("parser.error", " end_pos = " << end_pos); DEBUG("parser.error", " token kind = " << int(lookahead.kind)); DEBUG("parser.error", " token length = " << lookahead.length); add_error_context(line_context(*original_string, static_cast(pos), static_cast(end_pos))); } throw; } } } // namespace ledger