Skip to content

Instantly share code, notes, and snippets.

@agustingianni
Created June 23, 2017 21:46
Show Gist options
  • Select an option

  • Save agustingianni/825ea8b7d7a867b3f44c63c93623a448 to your computer and use it in GitHub Desktop.

Select an option

Save agustingianni/825ea8b7d7a867b3f44c63c93623a448 to your computer and use it in GitHub Desktop.
// Copyright (c) 2017 Agustin Gianni
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <string>
#include <iostream>
#include <tao/pegtl.hpp>
#include <tao/pegtl/analyze.hpp>
namespace pegtl = tao::TAOCPP_PEGTL_NAMESPACE;
// clang-format off
// Tokens.
struct LPAR : pegtl::one<'('> {};
struct RPAR : pegtl::one<')'> {};
struct LBRACK : pegtl::one<'['> {};
struct RBRACK : pegtl::one<']'> {};
struct LBRACE : pegtl::one<'{'> {};
struct RBRACE : pegtl::one<'}'> {};
struct SEMI : pegtl::one<';'> {};
struct COMMA : pegtl::one<','> {};
struct COLON : pegtl::one<':'> {};
struct EQUALS : pegtl::one<'='> {};
struct LANGLE : pegtl::one<'<'> {};
struct RANGLE : pegtl::one<'>'> {};
struct DOT : pegtl::one<'.'> {};
struct QUOTE : pegtl::one<'\'', '\"'> {};
struct QUESTION_MARK : pegtl::one<'?'> {};
// Unary operators.
struct unary_minus : pegtl::one<'-'> {};
struct unary_plus : pegtl::one<'+'> {};
struct unary_negate : pegtl::one<'!'> {};
struct unary_invert : pegtl::one<'~'> {};
// Binary operators.
struct binary_plus : TAOCPP_PEGTL_STRING("+") {};
struct binary_minus : TAOCPP_PEGTL_STRING("-") {};
struct binary_mul : TAOCPP_PEGTL_STRING("*") {};
struct binary_div : TAOCPP_PEGTL_STRING("/") {};
struct binary_mod : TAOCPP_PEGTL_STRING("MOD") {};
struct binary_idiv : TAOCPP_PEGTL_STRING("DIV") {};
struct binary_shift_left : TAOCPP_PEGTL_STRING("<<") {};
struct binary_shift_right : TAOCPP_PEGTL_STRING(">>") {};
struct binary_lt : TAOCPP_PEGTL_STRING("<") {};
struct binary_le : TAOCPP_PEGTL_STRING("<=") {};
struct binary_gt : TAOCPP_PEGTL_STRING(">") {};
struct binary_ge : TAOCPP_PEGTL_STRING(">=") {};
struct binary_eq : TAOCPP_PEGTL_STRING("==") {};
struct binary_ne : TAOCPP_PEGTL_STRING("!=") {};
struct binary_bit_and : TAOCPP_PEGTL_STRING("AND") {};
struct binary_bit_eor : TAOCPP_PEGTL_STRING("EOR") {};
struct binary_bit_or : TAOCPP_PEGTL_STRING("OR") {};
struct binary_bool_and : TAOCPP_PEGTL_STRING("&&") {};
struct binary_bool_or : TAOCPP_PEGTL_STRING("||") {};
struct binary_in : TAOCPP_PEGTL_STRING("IN") {};
struct binary_assignment : TAOCPP_PEGTL_STRING("=") {};
struct binary_bit_concat : TAOCPP_PEGTL_STRING(":") {};
// Operator groups.
struct unary_operator : pegtl::sor<unary_negate, unary_minus, unary_invert, unary_plus> {};
struct multiplicative_operator : pegtl::sor<binary_mul, binary_div, binary_idiv, binary_mod> {};
struct additive_operator : pegtl::sor<binary_plus, binary_minus> {};
struct shift_operator : pegtl::sor<binary_shift_left, binary_shift_right> {};
struct relational_operator : pegtl::sor<binary_le, binary_lt, binary_ge, binary_gt> {};
struct equality_operator : pegtl::sor<binary_eq, binary_ne> {};
struct and_operator : binary_bit_and {};
struct eor_operator : binary_bit_eor {};
struct or_operator : binary_bit_or {};
struct logical_and_operator : binary_bool_and {};
struct logical_or_operator : binary_bool_or {};
struct concatenation_operator : binary_bit_concat {};
struct inclusion_operator : binary_in {};
struct assignment_operator : binary_assignment {};
// Comments.
struct comment : pegtl::seq<pegtl::two<'/'>, pegtl::until<pegtl::eolf>> {};
// Ignore whitespaces and comments.
struct sep : pegtl::sor<pegtl::space, comment> {};
struct seps : pegtl::star<sep> {};
template <typename R>
struct pad : pegtl::pad<R, sep> {};
// Keywords.
struct keyword_array : TAOCPP_PEGTL_KEYWORD("array") {};
struct keyword_bit : TAOCPP_PEGTL_KEYWORD("bit") {};
struct keyword_bitstring : TAOCPP_PEGTL_KEYWORD("bitstring") {};
struct keyword_boolean : TAOCPP_PEGTL_KEYWORD("boolean") {};
struct keyword_case : TAOCPP_PEGTL_KEYWORD("case") {};
struct keyword_do : TAOCPP_PEGTL_KEYWORD("do") {};
struct keyword_else : TAOCPP_PEGTL_KEYWORD("else") {};
struct keyword_elsif : TAOCPP_PEGTL_KEYWORD("elsif") {};
struct keyword_enumeration : TAOCPP_PEGTL_KEYWORD("enumeration") {};
struct keyword_for : TAOCPP_PEGTL_KEYWORD("for") {};
struct keyword_if : TAOCPP_PEGTL_KEYWORD("if") {};
struct keyword_integer : TAOCPP_PEGTL_KEYWORD("integer") {};
struct keyword_list : TAOCPP_PEGTL_KEYWORD("list") {};
struct keyword_of : TAOCPP_PEGTL_KEYWORD("of") {};
struct keyword_otherwise : TAOCPP_PEGTL_KEYWORD("otherwise") {};
struct keyword_real : TAOCPP_PEGTL_KEYWORD("real") {};
struct keyword_repeat : TAOCPP_PEGTL_KEYWORD("repeat") {};
struct keyword_return : TAOCPP_PEGTL_KEYWORD("return") {};
struct keyword_then : TAOCPP_PEGTL_KEYWORD("then") {};
struct keyword_to : TAOCPP_PEGTL_KEYWORD("to") {};
struct keyword_until : TAOCPP_PEGTL_KEYWORD("until") {};
struct keyword_when : TAOCPP_PEGTL_KEYWORD("when") {};
struct keyword_while : TAOCPP_PEGTL_KEYWORD("while") {};
struct keyword : pegtl::sor <
keyword_array,
keyword_bit,
keyword_bitstring,
keyword_boolean,
keyword_case,
keyword_do,
keyword_else,
keyword_elsif,
keyword_enumeration,
keyword_for,
keyword_if,
keyword_integer,
keyword_list,
keyword_of,
keyword_otherwise,
keyword_real,
keyword_repeat,
keyword_return,
keyword_then,
keyword_to,
keyword_until,
keyword_when,
keyword_while
>
{
};
// Values.
struct value_ignored : TAOCPP_PEGTL_STRING("-") {};
struct value_false : TAOCPP_PEGTL_STRING("FALSE") {};
struct value_true : TAOCPP_PEGTL_STRING("TRUE") {};
struct value_unknown : TAOCPP_PEGTL_STRING("UNKNOWN") {};
struct value_undefined : TAOCPP_PEGTL_STRING("UNDEFINED") {};
struct value_unpredictable : TAOCPP_PEGTL_STRING("UNPREDICTABLE") {};
// Boolean values.
struct boolean : pegtl::sor<value_false, value_true> {};
// Digits for the different integer bases.
struct base_two_digit : pegtl::one<'0', '1'> {};
struct base_two_digit_masked : pegtl::one<'0', '1', 'x'> {};
struct base_eight_digit : pegtl::range<'0', '7'> {};
// Integers for the different bases.
struct base_two_integer : pegtl::seq<QUOTE, pegtl::plus<base_two_digit> , QUOTE> {};
struct base_two_integer_masked : pegtl::seq<QUOTE, pegtl::plus<base_two_digit_masked> , QUOTE> {};
struct base_eight_integer : pegtl::seq<pegtl::one<'0'>, pegtl::star<base_eight_digit>> {};
struct base_sixteen_integer : pegtl::seq<pegtl::one<'0'>, pegtl::one<'x', 'X'>, pegtl::plus<pegtl::xdigit>> {};
struct base_ten_integer : pegtl::seq<pegtl::range<'1', '9'>, pegtl::star<pegtl::digit>> {};
// Generic integer.
struct number : pegtl::sor<base_two_integer, base_two_integer_masked, base_ten_integer, base_sixteen_integer, base_eight_integer> {};
// An identifier is a name that is not a keyword.
struct identifier : pegtl::seq<pegtl::not_at<keyword>, pegtl::identifier> {};
// Enumeration is either a list enclosed by curly brackets or a masked binary integer.
struct enumeration_atom : pegtl::sor<identifier, number> {};
struct enumeration_list : pegtl::list<enumeration_atom, COMMA, sep> {};
struct enumeration_element : pegtl::seq<LBRACE, enumeration_list, RBRACE> {};
struct enumeration : pegtl::sor<base_two_integer_masked, enumeration_element> {};
// TODO: Missing these.
// struct array_access;
// struct procedure_call;
// list_atom = MatchFirst([ignored, procedure_call_expr, array_access_expr, boolean, identifier, number])
// List.
struct list_atom : pegtl::sor<value_ignored, boolean, identifier, number> {};
struct list_list : pegtl::list<list_atom, COMMA, sep> {};
struct list : pegtl::seq<LPAR, list_list, RPAR> {};
// TODO: Is this the right thing to do?.
struct SEE : TAOCPP_PEGTL_STRING("SEE") {};
struct IMPLEMENTATION_DEFINED : TAOCPP_PEGTL_STRING("IMPLEMENTATION_DEFINED") {};
struct SUBARCHITECTURE_DEFINED : TAOCPP_PEGTL_STRING("SUBARCHITECTURE_DEFINED") {};
// https://github.com/antlr/grammars-v4/blob/master/c/C.g4#L32
struct expression;
struct parenthesized_expression : pegtl::seq<pad<LPAR>, expression, pad<RPAR>> {};
struct primary_expression : pegtl::sor<identifier, number, parenthesized_expression> {};
struct assignment_expression;
struct argument_expression_list : pegtl::list<assignment_expression, COMMA, sep> {};
// NOTE: We are using `primary_expression` instead of `postfix_expression` to break left-recursion.
// This probably breaks nested rules like `id0.field0.field1`. Test this.
struct array_expression : pegtl::seq<primary_expression, pad<LBRACK>, expression, pad<RBRACK>> {};
struct function_call_expression : pegtl::seq<primary_expression, pad<LPAR>, pegtl::opt<argument_expression_list>, pad<RPAR>> {};
struct field_access_expression : pegtl::seq<primary_expression, pad<DOT>, identifier> {};
// https://github.com/antlr/grammars-v4/blob/master/c/C.g4#L57
struct postfix_expression : pegtl::sor<
primary_expression,
array_expression,
function_call_expression,
field_access_expression
> {};
struct unary_expression : pegtl::sor<
postfix_expression,
pegtl::seq<unary_operator, seps, unary_expression>
> {};
// https://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B#Operator_precedence
template<typename Expression, typename Operator>
struct left_associative : pegtl::seq<Expression, seps, pegtl::star<pegtl::if_must<Operator, seps, Expression, seps>>> {};
struct concatenation_expression : left_associative< unary_expression , concatenation_operator > {};
struct multiplicative_expression : left_associative< concatenation_expression , multiplicative_operator > {};
struct additive_expression : left_associative< multiplicative_expression , additive_operator > {};
struct shift_expression : left_associative< additive_expression , shift_operator > {};
struct inclusion_expression : left_associative< shift_expression , inclusion_operator > {};
struct relational_expression : left_associative< inclusion_expression , relational_operator > {};
struct equality_expression : left_associative< relational_expression , equality_operator > {};
struct and_expression : left_associative< equality_expression , and_operator > {};
struct eor_expression : left_associative< and_expression , eor_operator > {};
struct or_expression : left_associative< eor_expression , or_operator > {};
struct logical_and_expression : left_associative< or_expression , logical_and_operator > {};
struct logical_or_expression : left_associative< logical_and_expression , logical_or_operator > {};
// Right associative.
struct conditional_expression : pegtl::sor<
pegtl::seq<logical_or_expression, pegtl::opt<pad<QUESTION_MARK>, expression, pad<COLON>, conditional_expression>>
> {};
struct assignment_expression : pegtl::sor<
pegtl::seq<unary_expression, pad<assignment_operator>, assignment_expression>,
conditional_expression
> {};
struct expression : pegtl::list<assignment_expression, COMMA, sep> {};
struct grammar : pegtl::seq<expression, pegtl::eolf> {};
// clang-format on
// Do nothing on generic types.
template <typename T>
struct TestAction {
template <typename Input>
static void apply(const Input& in, std::string& v)
{
// std::cout << "UNMATCHED -> " << typeid(T).name() << " -> " << in.string() << std::endl;
}
};
template <typename GrammarRule, typename Iterator>
void test_parsing(Iterator&& tests)
{
std::string value;
for (auto test : tests) {
pegtl::string_input<> in(test, __PRETTY_FUNCTION__);
pegtl::parse<GrammarRule, TestAction>(in, value);
}
}
#define ADD_TEST(TypeName, ...) \
static void test_##TypeName##_parsing() \
{ \
auto tests = { __VA_ARGS__ }; \
test_parsing<TypeName>(tests); \
} \
\
template <> \
struct TestAction<TypeName> { \
template <typename Input> \
static void apply(const Input& in, std::string& v) \
{ \
std::cout << "TypeName ->" << typeid(TypeName).name() << " -> " << in.string() << std::endl; \
} \
};
#define DO_TEST(TypeName) test_##TypeName##_parsing()
// ADD_TEST(comment, "// AAAA", "// BBBB", "// CCCCDDDD");
// ADD_TEST(identifier, "while", "while_something");
// ADD_TEST(number, "\"0\"", "\"1\"", "\"x\"", "\"00\"", "\"01\"", "\"10\"", "\"11\"", "\"0x\"", "\"x0\"", "\"1x\"", "\"x1\"", "\"xx\"", "0", "1", "12", "123", "011", "0x10000");
// ADD_TEST(enumeration, "{0}", "{0, 1}", "{0, 1, 2}", "\"0000xxxx\"");
// ADD_TEST(list, "(0)", "(0, 1)", "(TRUE, FALSE, -, hola)");
ADD_TEST(
grammar,
// "example_identifier",
// "10101010",
// "-1",
// "+1",
// "!1",
// "~1",
// "-object",
// "+object",
// "!object",
// "~object",
"2222 = 3333",
"2222 = 3333 = 4444",
// "1 : hola",
// "1 IN hola",
// "1 * hola",
// "1 / hola",
// "1 MOD hola",
// "1 + hola",
// "1 - hola",
// "1 << hola",
// "1 >> hola",
// "1 < hola",
// "1 > hola",
// "1 <= hola",
// "1 >= hola",
// "1 == hola",
// "1 != hola",
// "1 AND hola",
// "1 OR hola",
// "1 EOR hola",
// "1 && hola",
// "1 || hola",
"1212 ? 2323 : 3434",
// "my_array[0]",
// "my_array[hola]",
// "my_array[1+1]",
// "my_array[1+hola]",
// "my_array()",
// "my_array(1, 2)",
// "my_array(1, 2, 3)",
// "my_array(hola, hola)",
// "my_array(1, hola)",
// "my_array(hola, 1)",
// "object.field",
// "object.field.fald",
// "-object.field",
// "+object.field",
// "!object.field",
// "~object.field"
);
int main(int argc, char** argv)
{
const size_t issues_found = pegtl::analyze<grammar>();
if (issues_found) {
std::cout << "There are " << issues_found << " errors in the grammar." << std::endl;
return -1;
}
// DO_TEST(comment);
// DO_TEST(identifier);
// DO_TEST(number);
// DO_TEST(enumeration);
// DO_TEST(list);
DO_TEST(grammar);
// std::string value;
// pegtl::argv_input<> in(argv, 1);
// pegtl::parse<grammar, TestAction>(in, value);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment