schema_editor/comdel/parser/comdelparser.cpp

984 lines
30 KiB
C++

#include "comdelparser.h"
#include "assert.h"
#include "poly.h"
#include "tokenstype.h"
#include <utility>
#include <algorithm>
#include <fstream>
#include <cctype>
#include <sstream>
ComdelParser::ComdelParser(std::vector<Token> 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<typename T> PResult<std::vector<T>>
ComdelParser::parseList(std::optional<TokenType> openDelim,
TokenType closeDelim,
std::optional<TokenType> separator,
bool allowTrailing,
const std::function<PResult<T>()>& parse_f)
{
std::vector<T> 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<SourceError> &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<Library> ComdelParser::parse()
{
auto spanner = getSpanner();
Library library{};
while ( ! check(TokenType::END_OF_FILE) ) {
PResult<poly<AstNode>> 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<PropertyNode>(std::optional<TokenType>(TokenType::LBRACE),
TokenType::RBRACE,
std::nullopt,
false,
[this]{return parseProperty(std::optional<TokenType>(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<StringNode> 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<IdentifierNode> 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<NumberNode> 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<CountNode> 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<PropertyNode> ComdelParser::parseProperty(std::optional<TokenType> 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<AddressSpace> 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);
}
/****************************************************************************
*
* Component := "@component" + IDENTIFIER + ("processor" | "memory") { COMPONENT_BLOCK }
*
****************************************************************************/
PResult<Component> ComdelParser::parseComponent()
{
auto spanner = getSpanner();
Component component{};
RETURN_IF_NOT_TOKEN(TokenType::KW_COMPONENT);
ASSIGN_OR_RETURN_IF_ERR(component.name, parseIdentifier());
if(check(TokenType::CT_PROCESSOR)) {
bump();
component.type = Component::PROCESSOR;
} else if(check(TokenType::CT_MEMORY)) {
bump();
component.type = Component::MEMORY;
} else {
component.type = Component::OTHER;
}
RETURN_IF_NOT_TOKEN(TokenType::LBRACE);
while(!check(TokenType::RBRACE)) {
PResult<poly<AstNode>> 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);
}
/****************************************************************************
*
* Display := "@display {" + (DISPLAY_ITEM)* + "}"
*
****************************************************************************/
PResult<Display> ComdelParser::parseDisplay() {
auto spanner = getSpanner();
RETURN_IF_NOT_TOKEN(TokenType::KW_DISPLAY);
Display display;
RETURN_IF_NOT_TOKEN(TokenType::LBRACE);
while(!check(TokenType::RBRACE)) {
PResult<DisplayItem> item;
item = parseDisplayItem();
RETURN_IF_ERR(item);
display.items.push_back(*item);
}
RETURN_IF_NOT_TOKEN(TokenType::RBRACE);
return spanner(display);
}
/****************************************************************************
*
* DisplayItem := "TYPE {(KEY + ":" + VALUE + ";")*}
*
****************************************************************************/
PResult<DisplayItem> ComdelParser::parseDisplayItem() {
auto spanner = getSpanner();
DisplayItem displayItem;
ASSIGN_OR_RETURN_IF_ERR(displayItem.type, parseIdentifier());
RETURN_IF_NOT_TOKEN(TokenType::LBRACE);
while(!check(TokenType::RBRACE)) {
PResult<PropertyNode> item{parseProperty()};
RETURN_IF_ERR(item);
displayItem.values.push_back(*item);
}
RETURN_IF_NOT_TOKEN(TokenType::RBRACE);
return spanner(displayItem);
}
/****************************************************************************
*
* Bus := "@bus " + NAME + TYPE + "{" + POPUP + "}"
*
****************************************************************************/
PResult<Bus> ComdelParser::parseBus() {
auto spanner = getSpanner();
Bus 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 = Bus::AUTOMATIC;
} else if(tokenType.value().value == "regular") {
bus.type = Bus::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);
}
/****************************************************************************
*
* Wire := NAME(<SIZE>){0,1} TYPE)*
*
****************************************************************************/
PResult<Wire> ComdelParser::parseWire() {
auto spanner = getSpanner();
Wire 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 = Wire::WIRE;
if(check(TokenType::WIRE_DEFAULT)) {
bump();
wire.type = Wire::WIRE;
} else if(check(TokenType::WIRE_AND)) {
bump();
wire.type = Wire::WIRED_AND;
} else if(check(TokenType::WIRE_OR)) {
bump();
wire.type = Wire::WIRED_OR;
} else if(check(TokenType::R_WIRE)) {
bump();
wire.type = Wire::R_WIRE;
}
return spanner(wire);
}
/****************************************************************************
*
* Pin := "@pin" NAME TYPE "{"
"@tooltip" MESSAGE
"@connection" TYPE "(" MESSAGE ")"
Display
}
*
****************************************************************************/
PResult<Pin> ComdelParser::parsePin() {
auto spanner = getSpanner();
RETURN_IF_NOT_TOKEN(TokenType::KW_PIN);
Pin pin{};
ASSIGN_OR_RETURN_IF_ERR(pin.name, parseIdentifier());
if(check(TokenType::PIN_IN)) {
bump();
pin.type = Pin::IN;
} else if(check(TokenType::PIN_OUT)) {
bump();
pin.type = Pin::OUT;
} else if(check(TokenType::PIN_IN_OUT)) {
bump();
pin.type = Pin::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);
}
/****************************************************************************
*
* PinConnection := "@connection " + ("check_only" | "automatically") + "(" + MESSAGE + ")"
*
****************************************************************************/
PResult<PinConnection> ComdelParser::parsePinConnection() {
auto spanner = getSpanner();
PinConnection connection{};
RETURN_IF_NOT_TOKEN(TokenType::KW_CONNECTION);
if(check(TokenType::IDENTIFIER)) {
auto type = parseIdentifier();
if(type.value().value == "check_only") {
connection.type = PinConnection::CHECK_ONLY;
} else if(type.value().value == "automatically") {
connection.type = PinConnection::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);
}
/****************************************************************************
*
* Attribute := "@attribute " + NAME + TYPE ("default" + VALUE){0,1} ("{" POPUP "}"){0,1}
*
****************************************************************************/
PResult<Attribute> ComdelParser::parseAttribute() {
auto spanner = getSpanner();
Attribute 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>(popup);
RETURN_IF_NOT_TOKEN(TokenType::RBRACE);
}
return spanner(attribute);
}
/****************************************************************************
*
* Enumeration
*
****************************************************************************/
PResult<EnumerationNode> 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<Popup> 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<EnumerationNode>(
std::optional<TokenType>(TokenType::LBRACE),
TokenType::RBRACE,
std::optional<TokenType>(TokenType::COMMA),
true,
[this]{ return parseEnumeration();}
)
);
} else {
return unexpected();
}
}
RETURN_IF_NOT_TOKEN(TokenType::RBRACE);
return spanner(popup);
}
/****************************************************************************
*
* Connection := "@connection (" + COMPONENT + "." + PIN + "," + BUS) {" + CONNECTION + "}"
*
****************************************************************************/
PResult<Connection> ComdelParser::parseConnection() {
auto spanner = getSpanner();
Connection 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<IdentifierNode>(std::optional<TokenType>(TokenType::LBRACE), TokenType::RBRACE, std::optional<TokenType>(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<Rule> 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<IfStmt> 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<Value>(std::optional<TokenType>(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<Value> 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);
}