#include "comdelparser.h" #include "assert.h" #include "poly.h" #include "tokenstype.h" #include #include #include #include #include ComdelParser::ComdelParser(std::vector tokens) : tokens(std::move(tokens)) , position(0) {} // Tries to consume a token, returns an error otherwise. // Unlike the regular consume() call this should be used // when a missing token means a syntax error. #define RETURN_IF_NOT_TOKEN(tokenType) \ do { \ if (!consume(tokenType)) { \ return unexpected(); \ } \ } while (0) // Checks if the current token matches the argument. // Otherwise, adds the token to the list of expected tokens (used in // error message). bool ComdelParser::check(TokenType tokenType) { if(current().type == tokenType) return true; expectedTokens.insert(tokenType); return false; } Token &ComdelParser::current() { return tokens[position]; } // attempts to consume a token and returns whether it's successful bool ComdelParser::consume(TokenType tokenType) { bool exists = check(tokenType); if (exists) { bump(); } return exists; } #define ATLAS_ASSERT_FAIL(x) \ do{}while(0) void ComdelParser::bump() { if (tokens[position].type == TokenType::END_OF_FILE) { ATLAS_ASSERT_FAIL("Internal parser error, called bump after EOF"); } else { tokens[++position]; } expectedTokens.clear(); } // Generates an "unexpected token" error that also // includes a list of valid alternatives based on the contents // of the expectedTokens vector // // e.g. "expected one of: ',', ')', found 'c'" // when parsing "func(a, b c);" PError ComdelParser::unexpected() { std::stringstream ss; ss << "Found: `" << current().text << "`. "; ss << "Expected"; if (expectedTokens.size() > 1) { ss << " one of: "; } else { ss << ": "; } uint token_counter = 0; for (auto& type : expectedTokens) { if (type == TokenType::IDENTIFIER || type == TokenType::NUMBER || type == TokenType::STRING || type == TokenType::COLOR) { ss << tokenTypeToString(type); } else { ss << "`" << tokenTypeToString(type) << "`"; } ++token_counter; if (token_counter < expectedTokens.size()) ss << ", "; else ss <<"."; } return PError({current().span, ss.str()}); } // Parses a list of 'nodes' separated by 'separator'. Parsed nodes // are returned in a vector. A list can be empty which will give // an empty vector. // // The list is enclosed in 'openDelim' and 'closeDelim'. // // 'openDelim' and 'separator' are optional (could be nullopt). // // 'closeDelim' is mandatory and it will be consumed. // // Function 'parse_f' should only parse a single node of type T. // // Parameter allowTrailing==true means that the list should ends with // 'separator' and otherwise the list should ends with 'node'. This // parameter is false in every call-site! template PResult> ComdelParser::parseList(std::optional openDelim, TokenType closeDelim, std::optional separator, bool allowTrailing, const std::function()>& parse_f) { std::vector vec; if(openDelim) { RETURN_IF_NOT_TOKEN(*openDelim); } bool first = true; while (true) { if (consume(closeDelim)) { break; } if (!first && separator) { RETURN_IF_NOT_TOKEN(*separator); if (allowTrailing && consume(closeDelim)) { break; } } first = false; auto item = parse_f(); RETURN_IF_ERR(item); vec.push_back(*item); } return std::move(vec); } const std::vector &ComdelParser::getErrors() { return errors; } Span &ComdelParser::getPreviousSpan() { static Span span; if(position == 0) { return span; } else { return tokens[position].span; } } // See comment for Spanner Spanner ComdelParser::getSpanner() { return Spanner(current().span, getPreviousSpan()); } // utility for reading strings #define ASSIGN_OR_SET_ERR(var, presult) \ do { \ auto&& assign_or_return_if_err_temp_ = (presult); \ if (!assign_or_return_if_err_temp_) { \ err = PError(assign_or_return_if_err_temp_.error()); \ } else { \ var = *assign_or_return_if_err_temp_; \ } \ } while (0) // utility for reading strings #define APPEND_OR_SET_ERR(var, presult) \ do { \ auto&& assign_or_return_if_err_temp_ = (presult); \ if (!assign_or_return_if_err_temp_) { \ err = PError(assign_or_return_if_err_temp_.error()); \ } else { \ var.push_back(*assign_or_return_if_err_temp_); \ } \ } while (0) // utility for reading strings #define APPEND_OR_RETURN_IF_ERR(var, presult) \ do { \ auto&& assign_or_return_if_err_temp_ = (presult); \ if (!assign_or_return_if_err_temp_) { \ return PError(assign_or_return_if_err_temp_.error()); \ } else { \ var.push_back(*assign_or_return_if_err_temp_); \ } \ } while (0) /**************************************************************************** * * F U N C T I O N S O F R E C U R S I V E D E S C E N T P A R S E R * ****************************************************************************/ std::optional ComdelParser::parse() { auto spanner = getSpanner(); LibraryNode library{}; while ( ! check(TokenType::END_OF_FILE) ) { PResult> err; if(check(TokenType::KW_NAME)){ bump(); ASSIGN_OR_SET_ERR(library.name, parseString()); } else if(check(TokenType::KW_HEADER)) { bump(); ASSIGN_OR_SET_ERR(library.header, parseString()); } else if(check(TokenType::KW_DIRECTORY)) { bump(); ASSIGN_OR_SET_ERR(library.componentDirectory, parseString()); } else if(check(TokenType::KW_INFO)) { bump(); ASSIGN_OR_SET_ERR(library.libraryInfo, parseString()); } else if(check(TokenType::KW_ADDRESS)) { APPEND_OR_SET_ERR(library.addressSpaces, parseAddress()); } else if(check(TokenType::KW_COMPONENT)) { APPEND_OR_SET_ERR(library.components, parseComponent()); } else if(check(TokenType::KW_BUS)) { APPEND_OR_SET_ERR(library.buses, parseBus()); } else if(check(TokenType::KW_CONNECTION)) { APPEND_OR_SET_ERR(library.connections, parseConnection()); } else if(check(TokenType::KW_MESSAGES)) { bump(); ASSIGN_OR_SET_ERR(library.messages, parseList(std::optional(TokenType::LBRACE), TokenType::RBRACE, std::nullopt, false, [this]{return parseProperty(std::optional(TokenType::STRING));} )); } else { err = unexpected(); } if(!err.has_value()) { errors.push_back(err.error()); break; } } if (errors.size()) return std::nullopt; return spanner(library); } /**************************************************************************** * * StringNode := "\"" + CONTENT + "\"" * ****************************************************************************/ PResult ComdelParser::parseString() { auto spanner = getSpanner(); if (check(TokenType::STRING)) { StringNode node; node.value = current().text; node.span = current().span; bump(); return spanner(node); } return unexpected(); } /**************************************************************************** * * IdentifierNode := IDENTIFIER * ****************************************************************************/ PResult ComdelParser::parseIdentifier() { auto spanner = getSpanner(); if (check(TokenType::IDENTIFIER)) { IdentifierNode node; node.value = current().text; node.span = current().span; bump(); return spanner(node); } return unexpected(); } /**************************************************************************** * * NumberNode := ('0x' | '0b'){0,1}[0-9]* * ****************************************************************************/ PResult ComdelParser::parseNumber() { auto spanner = getSpanner(); if (check(TokenType::NUMBER)) { NumberNode node{current().text}; node.span = current().span; bump(); return spanner(node); } return unexpected(); } /**************************************************************************** * * CountNode := "@size (" + NumberNode + "," + NumberNode + ")" * ****************************************************************************/ PResult ComdelParser::parseCount() { auto spanner = getSpanner(); RETURN_IF_NOT_TOKEN(TokenType::KW_COUNT); RETURN_IF_NOT_TOKEN(TokenType::LPAREN); auto first = parseNumber(); if(!first.has_value()) { return PError(first.error()); } RETURN_IF_NOT_TOKEN(TokenType::COMMA); auto second = parseNumber(); if(!second.has_value()) { return PError(second.error()); } RETURN_IF_NOT_TOKEN(TokenType::RPAREN); return spanner(CountNode{first.value(), second.value()}); } /**************************************************************************** * * PropertyNode := key: value; * ****************************************************************************/ PResult ComdelParser::parseProperty(std::optional valueType = std::nullopt) { auto spanner = getSpanner(); IdentifierNode key; ASSIGN_OR_RETURN_IF_ERR(key, parseIdentifier()); RETURN_IF_NOT_TOKEN(TokenType::COLON); if(valueType.has_value()) { if(valueType == TokenType::BOOL_TYPE) { if(!(check(TokenType::TRUE) || check(TokenType::FALSE))) { return unexpected(); } } else if(!check(*valueType)) { return unexpected(); } } Value value; ASSIGN_OR_RETURN_IF_ERR(value, parseValue()); RETURN_IF_NOT_TOKEN(TokenType::SEMICOLON); PropertyNode node; node.key = key; node.value = value; return spanner(node); } /**************************************************************************** * * AddressSpace := "@address" + IDENTIFIER "(" + NUMER + "," + NUMBER + ")" * ****************************************************************************/ PResult ComdelParser::parseAddress() { auto spanner = getSpanner(); AddressSpace addressSpace{}; RETURN_IF_NOT_TOKEN(TokenType::KW_ADDRESS); ASSIGN_OR_RETURN_IF_ERR(addressSpace.name, parseIdentifier()); RETURN_IF_NOT_TOKEN(TokenType::LPAREN); ASSIGN_OR_RETURN_IF_ERR(addressSpace.start, parseNumber()); RETURN_IF_NOT_TOKEN(TokenType::COMMA); ASSIGN_OR_RETURN_IF_ERR(addressSpace.end, parseNumber()); RETURN_IF_NOT_TOKEN(TokenType::RPAREN); return spanner(addressSpace); } /**************************************************************************** * * ComponentNode := "@component" + IDENTIFIER + ("processor" | "memory") { COMPONENT_BLOCK } * ****************************************************************************/ PResult ComdelParser::parseComponent() { auto spanner = getSpanner(); ComponentNode component{}; RETURN_IF_NOT_TOKEN(TokenType::KW_COMPONENT); ASSIGN_OR_RETURN_IF_ERR(component.name, parseIdentifier()); if(check(TokenType::CT_PROCESSOR)) { bump(); component.type = ComponentNode::PROCESSOR; } else if(check(TokenType::CT_MEMORY)) { bump(); component.type = ComponentNode::MEMORY; } else { component.type = ComponentNode::OTHER; } RETURN_IF_NOT_TOKEN(TokenType::LBRACE); while(!check(TokenType::RBRACE)) { PResult> err; if(check(TokenType::KW_INSTANCE_NAME)) { bump(); ASSIGN_OR_RETURN_IF_ERR(component.instanceName, parseString()); } else if(check(TokenType::KW_TOOLTIP)) { bump(); ASSIGN_OR_RETURN_IF_ERR(component.tooltip, parseString()); } else if(check(TokenType::KW_SOURCE)) { bump(); ASSIGN_OR_RETURN_IF_ERR(component.source, parseString()); } else if(check(TokenType::KW_COUNT)) { ASSIGN_OR_RETURN_IF_ERR(component.count, parseCount()); } else if(check(TokenType::KW_DISPLAY)) { ASSIGN_OR_RETURN_IF_ERR(component.display, parseDisplay()); } else if(check(TokenType::KW_PIN)) { APPEND_OR_RETURN_IF_ERR(component.pin, parsePin()); } else if(check(TokenType::KW_ATTRIBUTE)) { APPEND_OR_RETURN_IF_ERR(component.attributes, parseAttribute()); } else if(check(TokenType::KW_RULE)) { APPEND_OR_RETURN_IF_ERR(component.rules, parseRule()); } else { return unexpected(); } } RETURN_IF_NOT_TOKEN(TokenType::RBRACE); return spanner(component); } /**************************************************************************** * * DisplayNode := "@display {" + (DISPLAY_ITEM)* + "}" * ****************************************************************************/ PResult ComdelParser::parseDisplay() { auto spanner = getSpanner(); RETURN_IF_NOT_TOKEN(TokenType::KW_DISPLAY); DisplayNode display; RETURN_IF_NOT_TOKEN(TokenType::LBRACE); while(!check(TokenType::RBRACE)) { PResult item; item = parseDisplayItem(); RETURN_IF_ERR(item); display.items.push_back(*item); } RETURN_IF_NOT_TOKEN(TokenType::RBRACE); return spanner(display); } /**************************************************************************** * * DisplayItemNode := "TYPE {(KEY + ":" + VALUE + ";")*} * ****************************************************************************/ PResult ComdelParser::parseDisplayItem() { auto spanner = getSpanner(); DisplayItemNode displayItem; ASSIGN_OR_RETURN_IF_ERR(displayItem.type, parseIdentifier()); RETURN_IF_NOT_TOKEN(TokenType::LBRACE); while(!check(TokenType::RBRACE)) { PResult item{parseProperty()}; RETURN_IF_ERR(item); displayItem.values.push_back(*item); } RETURN_IF_NOT_TOKEN(TokenType::RBRACE); return spanner(displayItem); } /**************************************************************************** * * BusNode := "@bus " + NAME + TYPE + "{" + POPUP + "}" * ****************************************************************************/ PResult ComdelParser::parseBus() { auto spanner = getSpanner(); BusNode bus; RETURN_IF_NOT_TOKEN(TokenType::KW_BUS); ASSIGN_OR_RETURN_IF_ERR(bus.name, parseIdentifier()); if(check(TokenType::IDENTIFIER)) { auto tokenType = parseIdentifier(); if(tokenType.value().value == "automatic") { bus.type = BusNode::AUTOMATIC; } else if(tokenType.value().value == "regular") { bus.type = BusNode::REGULAR; } else { return PError(SourceError{current().span, "expected 'automatic' or 'regular'"}); } } else { return PError(SourceError{current().span, "expected 'automatic' or 'regular'"}); } RETURN_IF_NOT_TOKEN(TokenType::LBRACE); while(!check(TokenType::RBRACE)) { if(check(TokenType::KW_TOOLTIP)) { bump(); ASSIGN_OR_RETURN_IF_ERR(bus.tooltip, parseString()); } else if(check(TokenType::KW_COUNT)) { ASSIGN_OR_RETURN_IF_ERR(bus.count, parseCount()); } else if(check(TokenType::KW_DISPLAY)) { ASSIGN_OR_RETURN_IF_ERR(bus.display, parseDisplay()); } else if(check(TokenType::KW_WIRES)) { bump(); RETURN_IF_NOT_TOKEN(TokenType::LBRACE); while(check(TokenType::IDENTIFIER)) { APPEND_OR_RETURN_IF_ERR(bus.wires, parseWire()); if(check(TokenType::COMMA)) { RETURN_IF_NOT_TOKEN(TokenType::COMMA); } } RETURN_IF_NOT_TOKEN(TokenType::RBRACE); } else { return unexpected(); } } RETURN_IF_NOT_TOKEN(TokenType::RBRACE); return spanner(bus); } /**************************************************************************** * * WireNode := NAME(){0,1} TYPE)* * ****************************************************************************/ PResult ComdelParser::parseWire() { auto spanner = getSpanner(); WireNode wire; ASSIGN_OR_RETURN_IF_ERR(wire.name, parseIdentifier()); if(check(TokenType::LT)) { RETURN_IF_NOT_TOKEN(TokenType::LT); ASSIGN_OR_RETURN_IF_ERR(wire.size, parseNumber()); RETURN_IF_NOT_TOKEN(TokenType::GT); } else { wire.size.value = 1; } // default wire.type = WireNode::WIRE; if(check(TokenType::WIRE_DEFAULT)) { bump(); wire.type = WireNode::WIRE; } else if(check(TokenType::WIRE_AND)) { bump(); wire.type = WireNode::WIRED_AND; } else if(check(TokenType::WIRE_OR)) { bump(); wire.type = WireNode::WIRED_OR; } else if(check(TokenType::R_WIRE)) { bump(); wire.type = WireNode::R_WIRE; } return spanner(wire); } /**************************************************************************** * * PinNode := "@pin" NAME TYPE "{" "@tooltip" MESSAGE "@connection" TYPE "(" MESSAGE ")" DisplayNode } * ****************************************************************************/ PResult ComdelParser::parsePin() { auto spanner = getSpanner(); RETURN_IF_NOT_TOKEN(TokenType::KW_PIN); PinNode pin{}; ASSIGN_OR_RETURN_IF_ERR(pin.name, parseIdentifier()); if(check(TokenType::PIN_IN)) { bump(); pin.type = PinNode::IN; } else if(check(TokenType::PIN_OUT)) { bump(); pin.type = PinNode::OUT; } else if(check(TokenType::PIN_IN_OUT)) { bump(); pin.type = PinNode::IN_OUT; } else { return unexpected(); } RETURN_IF_NOT_TOKEN(TokenType::LBRACE); while(!check(TokenType::RBRACE)) { if (check(TokenType::KW_TOOLTIP)) { bump(); ASSIGN_OR_RETURN_IF_ERR(pin.tooltip, parseString()); } else if (check(TokenType::KW_DISPLAY)) { ASSIGN_OR_RETURN_IF_ERR(pin.display, parseDisplay()); } else if (check(TokenType::KW_CONNECTION)) { ASSIGN_OR_RETURN_IF_ERR(pin.connection, parsePinConnection()); } else { return unexpected(); } } RETURN_IF_NOT_TOKEN(TokenType::RBRACE); return spanner(pin); } /**************************************************************************** * * PinConnectionNode := "@connection " + ("check_only" | "automatically") + "(" + MESSAGE + ")" * ****************************************************************************/ PResult ComdelParser::parsePinConnection() { auto spanner = getSpanner(); PinConnectionNode connection{}; RETURN_IF_NOT_TOKEN(TokenType::KW_CONNECTION); if(check(TokenType::IDENTIFIER)) { auto type = parseIdentifier(); if(type.value().value == "check_only") { connection.type = PinConnectionNode::CHECK_ONLY; } else if(type.value().value == "automatically") { connection.type = PinConnectionNode::AUTOMATICALLY; } else { return PError(SourceError{current().span, "expected identifiers 'check_only' or 'automatically'"}); } } else { return PError(SourceError{current().span, "expected identifiers 'check_only' or 'automatically'"}); } RETURN_IF_NOT_TOKEN(TokenType::LPAREN); ASSIGN_OR_RETURN_IF_ERR(connection.message, parseString()); RETURN_IF_NOT_TOKEN(TokenType::RPAREN); return spanner(connection); } /**************************************************************************** * * AttributeNode := "@attribute " + NAME + TYPE ("default" + VALUE){0,1} ("{" POPUP "}"){0,1} * ****************************************************************************/ PResult ComdelParser::parseAttribute() { auto spanner = getSpanner(); AttributeNode attribute; RETURN_IF_NOT_TOKEN(TokenType::KW_ATTRIBUTE); if(check(TokenType::IDENTIFIER)) { ASSIGN_OR_RETURN_IF_ERR(attribute.name, parseIdentifier()); } else { return unexpected(); } if(check(TokenType::INT_TYPE)) { attribute.type = Value::INT; } else if(check(TokenType::STRING_TYPE)) { attribute.type = Value::STRING; } else if(check(TokenType::BOOL_TYPE)) { attribute.type = Value::BOOL; } else if(check(TokenType::WIRE_TYPE)) { attribute.type = Value::IDENTIFIER; } else { return unexpected(); } bump(); RETURN_IF_NOT_TOKEN(TokenType::DEFAULT); if(attribute.type == Value::BOOL) { if(check(TokenType::TRUE)) { attribute.defaultValue = Value::ofBool(true); } else if(check(TokenType::FALSE)) { attribute.defaultValue = Value::ofBool(false); } else { return unexpected(); } } else if(attribute.type == Value::INT) { if(check(TokenType::NUMBER)) { auto number = parseNumber(); attribute.defaultValue = Value::ofInt(number->value); } else { return unexpected(); } } else if(attribute.type == Value::STRING) { if(check(TokenType::STRING)) { auto string = parseString(); attribute.defaultValue = Value::ofString(string->value); } else { return unexpected(); } } else if(attribute.type == Value::IDENTIFIER) { if(check(TokenType::IDENTIFIER)) { auto identifier = parseIdentifier(); attribute.defaultValue = Value::ofIdentifier(identifier->value); } else { return unexpected(); } } if(check(TokenType::LBRACE)) { RETURN_IF_NOT_TOKEN(TokenType::LBRACE); Popup popup; if(!check(TokenType::KW_POPUP)) { return unexpected(); } ASSIGN_OR_RETURN_IF_ERR(popup, parsePopup()); attribute.popup = std::optional(popup); RETURN_IF_NOT_TOKEN(TokenType::RBRACE); } return spanner(attribute); } /**************************************************************************** * * Enumeration * ****************************************************************************/ PResult ComdelParser::parseEnumeration() { auto spanner = getSpanner(); StringNode key; ASSIGN_OR_RETURN_IF_ERR(key, parseString()); RETURN_IF_NOT_TOKEN(TokenType::EQUALS); Value value; ASSIGN_OR_RETURN_IF_ERR(value, parseValue()); EnumerationNode node; node.key = key; node.value = value; return spanner(node); } /**************************************************************************** * * Popup := "@popup " + ("automatic" | "on_demand") { POPUP BODY } * ****************************************************************************/ PResult ComdelParser::parsePopup() { auto spanner = getSpanner(); Popup popup; RETURN_IF_NOT_TOKEN(TokenType::KW_POPUP); if(check(TokenType::IDENTIFIER)) { auto type = parseIdentifier(); if(type.value().value == "automatic") { popup.type = Popup::AUTOMATIC; } else if(type.value().value == "on_demand") { popup.type = Popup::ON_DEMAND; } else { return PError(SourceError{current().span, "expected type 'automatic', 'on_demand'"}); } } else { popup.type = Popup::ON_DEMAND; } RETURN_IF_NOT_TOKEN(TokenType::LBRACE); while(!check(TokenType::RBRACE)) { if(check(TokenType::KW_TITLE)) { bump(); ASSIGN_OR_RETURN_IF_ERR(popup.title, parseString()); } else if(check(TokenType::KW_TEXT)) { bump(); ASSIGN_OR_RETURN_IF_ERR(popup.text, parseString()); } else if(check(TokenType::KW_RULE)) { APPEND_OR_RETURN_IF_ERR(popup.rules, parseRule()); } else if(check(TokenType::KW_ENUMERATED)) { bump(); popup.enumerated = true; ASSIGN_OR_RETURN_IF_ERR(popup.enumeration, parseList( std::optional(TokenType::LBRACE), TokenType::RBRACE, std::optional(TokenType::COMMA), true, [this]{ return parseEnumeration();} ) ); } else { return unexpected(); } } RETURN_IF_NOT_TOKEN(TokenType::RBRACE); return spanner(popup); } /**************************************************************************** * * ConnectionNode := "@connection (" + COMPONENT + "." + PIN + "," + BUS) {" + CONNECTION + "}" * ****************************************************************************/ PResult ComdelParser::parseConnection() { auto spanner = getSpanner(); ConnectionNode connection; RETURN_IF_NOT_TOKEN(TokenType::KW_CONNECTION); RETURN_IF_NOT_TOKEN(TokenType::LPAREN); ASSIGN_OR_RETURN_IF_ERR(connection.component, parseIdentifier()); RETURN_IF_NOT_TOKEN(TokenType::DOT); ASSIGN_OR_RETURN_IF_ERR(connection.pin, parseIdentifier()); RETURN_IF_NOT_TOKEN(TokenType::COMMA); ASSIGN_OR_RETURN_IF_ERR(connection.bus, parseIdentifier()); RETURN_IF_NOT_TOKEN(TokenType::RPAREN); RETURN_IF_NOT_TOKEN(TokenType::LBRACE); while(!check(TokenType::RBRACE)) { if (check(TokenType::KW_ATTRIBUTE)) { APPEND_OR_RETURN_IF_ERR(connection.attributes, parseAttribute()); } else if(check(TokenType::KW_WIRES)) { bump(); auto wires = parseList(std::optional(TokenType::LBRACE), TokenType::RBRACE, std::optional(TokenType::COMMA), false, [this] { return parseIdentifier(); }); RETURN_IF_ERR(wires); connection.wires = *wires; } else { return unexpected(); } } RETURN_IF_NOT_TOKEN(TokenType::RBRACE); return spanner(connection); } /**************************************************************************** * * Rule := "@rule {" + if-else statements + "}" * ****************************************************************************/ PResult ComdelParser::parseRule() { auto spanner = getSpanner(); Rule rule; RETURN_IF_NOT_TOKEN(TokenType::KW_RULE); RETURN_IF_NOT_TOKEN(TokenType::LBRACE); while(!check(TokenType::RBRACE)) { APPEND_OR_RETURN_IF_ERR(rule.statements, parseIfStatement()); if(check(TokenType::RBRACE)) { break; } RETURN_IF_NOT_TOKEN(TokenType::ELSE); } RETURN_IF_NOT_TOKEN(TokenType::RBRACE); return spanner(rule); } /**************************************************************************** * * IfStatement := "if(!function(params...)) { error(MESSAGE) | warning(MESSAGE) } * ****************************************************************************/ PResult ComdelParser::parseIfStatement() { auto spanner = getSpanner(); IfStmt ifStatement; RETURN_IF_NOT_TOKEN(TokenType::IF); RETURN_IF_NOT_TOKEN(TokenType::LPAREN); if(check(TokenType::NOT)) { ifStatement.condition.negated = true; bump(); } else { ifStatement.condition.negated = false; } ASSIGN_OR_RETURN_IF_ERR(ifStatement.condition.functionName, parseIdentifier()); ASSIGN_OR_RETURN_IF_ERR(ifStatement.condition.params, parseList(std::optional(TokenType::LPAREN), TokenType::RPAREN, TokenType::COMMA, false, [this]{return parseValue();} )); RETURN_IF_NOT_TOKEN(TokenType::RPAREN); RETURN_IF_NOT_TOKEN(TokenType::LBRACE); if(check(TokenType::ERROR)) { ifStatement.action.type = Action::ERROR; } else if(check(TokenType::WARNING)) { ifStatement.action.type = Action::WARNING; } else { return unexpected(); } bump(); RETURN_IF_NOT_TOKEN(TokenType::LPAREN); ASSIGN_OR_RETURN_IF_ERR(ifStatement.action.message, parseString()); RETURN_IF_NOT_TOKEN(TokenType::RPAREN); RETURN_IF_NOT_TOKEN(TokenType::RBRACE); return spanner(ifStatement); } PResult ComdelParser::parseValue() { auto spanner = getSpanner(); Value value; if(check(TokenType::IDENTIFIER)) { value = Value::ofIdentifier(parseIdentifier()->value); } else if(check(TokenType::STRING)) { value = Value::ofString(parseString()->value); } else if(check(TokenType::NUMBER)) { value = Value::ofInt(parseNumber()->value); } else if(check(TokenType::TRUE)) { value = Value::ofBool(true); } else if(check(TokenType::FALSE)) { value = Value::ofBool(false); } else { return unexpected(); } return spanner(value); }