#include <assert.h>
#include <iostream>
#include <peglib.h>
#include <ctime>

using namespace peg;
using namespace std;

int main(void) {
  // (2) Make a parser
  parser parser;
  parser.set_logger([](size_t line, size_t col, const string& msg, const string &rule) {
	  cerr << line << ":" << col << ": " << msg << "\n";
	});
  auto grammar = (R"---(
		command_list <- command  (";" command)*
		command <- "color" atom_specifier < "#" [a-f0-9]{6} >
		atom_specifier <- as_term "&" atom_specifier / as_term "|" atom_specifier / as_term
		as_term <- "(" atom_specifier ")" zone_selector? / "~" as_term zone_selector? / SELECTOR_NAME / model_list
		model_list <- model+
		model <- ("#!" / "#") model_hierarchy ("##" attribute_list)? model_parts* zone_selector? / attribute_list model_parts* zone_selector* / model_parts zone_selector*
		# should be negative lookbehind for white space before the '.' in the below...
		model_hierarchy <- < model_range_list (!Space "."  !Space model_hierarchy)* >
		model_range_list <- model_range ("," model_range_list)*
		model_range <- MODEL_SPEC_START "-" MODEL_SPEC_END / MODEL_SPEC_ANY
		model_parts <- chain+
		chain <- "/" part_list ("//" attribute_list)? residue* / "//" attribute_list residue* / residue+
		residue <- ":" part_list ("::" attribute_list)? atom* / "::" attribute_list atom* / atom+
		part_list <- PART_RANGE_LIST "," part_list / PART_RANGE_LIST
		# atom ranges are not allowed
		atom <- "@" atom_list ("@@" attribute_list)? / "@@" attribute_list
		atom_list <- ATOM_NAME "," atom_list / ATOM_NAME
		attribute_list <- attr_test ("," attr_test)*
		attr_test <- ATTR_NAME ATTR_OPERATOR ATTR_VALUE / ATTR_NAME / "~" ATTR_NAME
		zone_selector <- ZONE_OPERATOR < [0-9]* "." [0-9]+ >
		# think about ranges in these character sets
		ATOM_NAME <- < [^#/:@; \t\n]+ >
		ATTR_NAME <- < [a-zA-Z_] [a-zA-Z0-9]* >
		ATTR_OPERATOR <- ">=" | ">" | "<=" | "<" | "==" | "=" | "!==" | "!=" | "<>"
		EndOfLine <- "\r\n" / "\n" / "\r"
		Space <- ' ' / '\t' / EndOfLine
		# Outer token delimiters to prevent automatic whitespace elimination inside quotes
		ATTR_VALUE <- < '"' < [^"]+ > '"' > / < "'" < [^']+ > "'" > / < [^#/:@,;"']+ >
		# limit model numbers to 5 digits to avoid conflicts with hex colors
		MODEL_SPEC <- < < [0-9]{1,5} > ![0-9A-Fa-f] >
		MODEL_SPEC_ANY <- MODEL_SPEC / "*"
		MODEL_SPEC_END <- MODEL_SPEC / "end" / "*"
		MODEL_SPEC_START <- MODEL_SPEC / "start" / "*"
		RANGE_CHAR <- [^#/:@,;- \t\n]
		PART_RANGE_LIST <- < "-"? RANGE_CHAR+ ("-" RANGE_CHAR+)? >
		SELECTOR_NAME <- < [a-zA-Z_][-+a-zA-Z0-9_]* >
		ZONE_OPERATOR <- "@>" | "@<" | ":>" | ":<" | "/>" | "/<" | "#>" | "#<"
		%whitespace  <-  [ \t\r\n]*
    )---");
auto t0 = clock();
	auto ok = parser.load_grammar(grammar);
auto t1 = clock();
std::cerr << "load grammar: " << (t1-t0) / (float)CLOCKS_PER_SEC << " seconds\n";
	assert(ok);

  assert(static_cast<bool>(parser) == true);

  // (4) Parse
  auto do_packrat = true;
  if (do_packrat)
    parser.enable_packrat_parsing(); // Enable packrat parsing.

  string test_cmd = "";
  test_cmd += "color @@serial_number=1";
  for (int i = 2; i <= 8956; ++i) {
  	test_cmd += "|@@serial_number=";
	test_cmd += to_string(i);
  }
  test_cmd += " #ff8654";
  std::cerr << "command string is " << test_cmd.size() << " characters\n";
  	

t0 = clock();
  parser.parse(test_cmd);
t1 = clock();
std::cerr << "parse (" << (do_packrat ? "" : "no ") << "packrat): " << (t1-t0) / (float)CLOCKS_PER_SEC << " seconds\n";
}
