diff --git a/CMakeLists.txt b/CMakeLists.txt index 67f1b96c41..f46cdec399 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,4 +69,5 @@ add_subdirectory(Launcher) else() add_subdirectory(MakeAPPL) add_subdirectory(ASFilter) +add_subdirectory(Rez) endif() diff --git a/Rez/CMakeLists.txt b/Rez/CMakeLists.txt new file mode 100644 index 0000000000..ae7c33bf55 --- /dev/null +++ b/Rez/CMakeLists.txt @@ -0,0 +1,49 @@ +# Copyright 2012 Wolfgang Thaller. +# +# This file is part of Retro68. +# +# Retro68 is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Retro68 is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Retro68. If not, see . + +cmake_minimum_required(VERSION 2.8) + +set(CMAKE_CXX_FLAGS "--std=c++11") + +find_package(Boost COMPONENTS wave filesystem system thread regex) + + +find_package(BISON REQUIRED) + +include_directories(. ${CMAKE_CURRENT_BINARY_DIR}) + +add_custom_command( + DEPENDS RezParser.yy + COMMAND ${BISON_EXECUTABLE} + ARGS -o ${CMAKE_CURRENT_BINARY_DIR}/RezParser.generated.cc + ${CMAKE_CURRENT_SOURCE_DIR}/RezParser.yy --graph + COMMENT "Generating parser.cpp" + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/RezParser.generated.cc ${CMAKE_CURRENT_BINARY_DIR}/RezParser.generated.hh +) +add_executable(Rez + Rez.cc + + RezParser.yy RezParser.generated.hh RezParser.generated.cc + + RezLexer.h + RezLexer.cc + RezLexerWaveToken.h + RezLexerNextToken.cc + ) +target_link_libraries(Rez ${Boost_LIBRARIES}) + +install(TARGETS Rez RUNTIME DESTINATION bin) diff --git a/Rez/Rez.cc b/Rez/Rez.cc new file mode 100644 index 0000000000..91560796d7 --- /dev/null +++ b/Rez/Rez.cc @@ -0,0 +1,67 @@ +#include +#include + + +#include "RezParser.generated.hh" +#include "RezLexer.h" + + +int main() +{ + //RezLexer lexer("/home/wolfgang/Projects/Retro68/RIncludes/Types.r"); + RezLexer lexer("/home/wolfgang/Projects/Retro68/Rez/Test.r"); + + RezParser parser(lexer); + + parser.parse(); + + /* + // The following preprocesses a given input file. + // Open the file and read it into a string variable + std::ifstream instream("/home/wolfgang/Projects/Retro68/RIncludes/Types.r"); + + std::string input( + std::istreambuf_iterator(instream.rdbuf()), + std::istreambuf_iterator()); + + context_type ctx(input.begin(), input.end(), "Types.r"); + + // At this point you may want to set the parameters of the + // preprocessing as include paths and/or predefined macros. + ctx.add_include_path("/home/wolfgang/Projects/Retro68/RIncludes"); + // ctx.add_macro_definition(...); + + auto first = ctx.begin(); + auto last = ctx.end(); + + std::ostringstream out; + + try + { + while(first != last) + { + out << (*first).get_value(); + ++first; + } + } + catch(boost::wave::preprocess_exception& e) + { + std::cout << e.file_name() << ":" << e.line_no() << ": "; + std::cout << e.description() << std::endl; + } + + std::string str = out.str(); + + + std::cout << str.substr(0,100) << std::endl;*/ + +/* + int i = 0; + while (first != last) { + std::cout << i << ": " << get_token_name(token_id(*first)) << " <<" << (*first).get_value() << ">>\n"; + ++first; + if(++i > 10) + break; + }*/ + return 0; +} diff --git a/Rez/RezLexer.cc b/Rez/RezLexer.cc new file mode 100644 index 0000000000..739664b524 --- /dev/null +++ b/Rez/RezLexer.cc @@ -0,0 +1,116 @@ +#include "RezLexer.h" + +#include +#include +#include +#include + +#include "RezLexerWaveToken.h" + +namespace wave = boost::wave; + +using namespace boost::wave; + +struct load_file_to_string_filtered +{ + template + class inner + { + public: + template + static void init_iterators(IterContextT &iter_ctx, + PositionT const &act_pos, language_support language) + { + typedef typename IterContextT::iterator_type iterator_type; + + // read in the file + std::ifstream instream(iter_ctx.filename.c_str()); + if (!instream.is_open()) { + BOOST_WAVE_THROW_CTX(iter_ctx.ctx, preprocess_exception, + bad_include_file, iter_ctx.filename.c_str(), act_pos); + return; + } + instream.unsetf(std::ios::skipws); + + std::string str(std::istreambuf_iterator(instream.rdbuf()), + std::istreambuf_iterator()); + + boost::regex endif("#endif[^\r\n]*"); + str = boost::regex_replace(str, endif, "#endif"); + + boost::regex dollar_escape("\\\\\\$([a-zA-Z0-9][a-zA-Z0-9])"); + str = boost::regex_replace(str, dollar_escape, "\\x$1"); + + + iter_ctx.instring = str; + + iter_ctx.first = iterator_type( + iter_ctx.instring.begin(), iter_ctx.instring.end(), + PositionT(iter_ctx.filename), language); + iter_ctx.last = iterator_type(); + } + + private: + std::string instring; + }; +}; + + + +typedef wave::cpplexer::lex_iterator< + wave::cpplexer::lex_token<> > + lex_iterator_type; +typedef wave::context< + std::string::iterator, lex_iterator_type, + load_file_to_string_filtered> + context_type; +typedef context_type::iterator_type pp_iterator_type; + +struct RezLexer::Priv +{ + std::string input; + context_type ctx; + pp_iterator_type iter; + + Priv(std::string data, std::string name) + : input(data), ctx(input.begin(), input.end(), name.c_str()) + { + } +}; + +RezLexer::RezLexer(std::string filename) +{ + std::ifstream instream(filename); + + pImpl.reset(new Priv(std::string( + std::istreambuf_iterator(instream.rdbuf()), + std::istreambuf_iterator()), + filename)); + + pImpl->ctx.add_include_path("/home/wolfgang/Projects/Retro68/RIncludes"); + // ctx.add_macro_definition(...); + pImpl->ctx.add_macro_definition("DeRez", "0"); + + pImpl->iter = pImpl->ctx.begin(); +} + +RezLexer::~RezLexer() +{ + +} + +bool RezLexer::atEnd() +{ + return pImpl->iter == pImpl->ctx.end(); +} + +RezLexer::WaveToken RezLexer::nextWave() +{ + return pImpl->iter == pImpl->ctx.end() ? WaveToken() : (*pImpl->iter++); +} + +RezLexer::WaveToken RezLexer::peekWave() +{ + return pImpl->iter == pImpl->ctx.end() ? WaveToken() : *pImpl->iter; +} + diff --git a/Rez/RezLexer.h b/Rez/RezLexer.h new file mode 100644 index 0000000000..c0e7f12cea --- /dev/null +++ b/Rez/RezLexer.h @@ -0,0 +1,28 @@ +#ifndef REZLEXER_H +#define REZLEXER_H + +#include + +class RezSymbol; + +class RezLexer +{ + struct Priv; + std::unique_ptr pImpl; + + std::string curFile; + + class WaveToken; + + bool atEnd(); + WaveToken nextWave(); + WaveToken peekWave(); + +public: + RezLexer(std::string filename); + ~RezLexer(); + + RezSymbol nextToken(); +}; + +#endif // REZLEXER_H diff --git a/Rez/RezLexerNextToken.cc b/Rez/RezLexerNextToken.cc new file mode 100644 index 0000000000..f862edbf02 --- /dev/null +++ b/Rez/RezLexerNextToken.cc @@ -0,0 +1,192 @@ +#include "RezLexer.h" +#include "RezLexerWaveToken.h" +#include "RezParser.generated.hh" +#include + +#include + +using namespace boost::wave; + +static int readInt(const char *str) +{ + int x = 0; + + int base = 10; + + if(*str == '0') + { + base = 8; + ++str; + if(*str == 'x' || *str == 'X') + { + base = 16; + ++str; + } + if(*str == 'b' || *str == 'B') + { + base = 2; + ++str; + } + } + else if(*str == 'b' || *str == 'B') + { + base = 2; + ++str; + } + + while(*str) + { + x *= base; + if(*str >= 'a' && *str <= 'z') + x += *str - 'a'; + else if(*str >= 'A' && *str <= 'Z') + x += *str - 'A' + 10; + else if(*str >= '0' && *str <= '9') + x += *str - '0'; + *str++; + } + + return x; +} + +RezSymbol RezLexer::nextToken() +{ + for(auto tok = nextWave(); tok != T_EOI && tok != T_EOF; tok = nextWave()) + { + if(IS_CATEGORY(tok, WhiteSpaceTokenType)) + continue; + else if(IS_CATEGORY(tok, EOLTokenType)) + continue; + else if(tok == T_PP_LINE) + { + while(tok != T_EOI && tok != T_EOF && !IS_CATEGORY(tok, EOLTokenType)) + tok = nextWave(); + continue; + } + else + { + //std::cout << "{" << std::hex << (token_id)tok << std::dec << "|" << tok.get_value() << "}\n"; + + auto pos = tok.get_position(); + curFile = pos.get_file().c_str(); + auto yypos = yy::position(&curFile, pos.get_line(), pos.get_column()); + yy::location loc(yypos); + + if(tok == (UnknownTokenType | '"')) + { + return RezParser::make_STRINGLIT("Hello, world.", loc); + } + else if(IS_CATEGORY(tok, IdentifierTokenType) || IS_CATEGORY(tok, KeywordTokenType) || IS_CATEGORY(tok, BoolLiteralTokenType)) + { + typedef decltype(&RezParser::make_TYPE) memfun; +#define KEYWORD(upper, lower) \ +{ lower, &RezParser::make_ ## upper } + + static std::unordered_map keywords = { + KEYWORD(TYPE, "type"), + KEYWORD(RESOURCE, "resource"), + + KEYWORD(ARRAY,"array"), + KEYWORD(SWITCH, "switch"), + KEYWORD(CASE, "case"), + KEYWORD(AS, "as"), + KEYWORD(FILL,"fill"), + KEYWORD(ALIGN, "align"), + KEYWORD(HEX,"hex"), + KEYWORD(KEY, "key"), + KEYWORD(WIDE,"wide"), + KEYWORD(UNSIGNED, "unsigned"), + KEYWORD(LITERAL, "literal"), + KEYWORD(BOOLEAN, "boolean"), + KEYWORD(BIT, "bit"), + KEYWORD(BYTE, "byte"), + KEYWORD(CHAR, "char"), + KEYWORD(WORD, "word"), + KEYWORD(INTEGER, "integer"), + KEYWORD(LONG, "long"), + KEYWORD(LONGINT, "longint"), + KEYWORD(PSTRING, "pstring"), + KEYWORD(PSTRING, "wstring"), + KEYWORD(STRING, "string"), + KEYWORD(POINT, "point"), + KEYWORD(RECT, "rect"), + KEYWORD(BITSTRING, "bitstring"), + + KEYWORD(INTEGER, "int"), + + }; + + std::string s = tok.get_value().c_str(); + std::string lower = s; + std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower); + auto p = keywords.find(lower); + if(p == keywords.end()) + { + //std::cout << "id: " << s << std::endl; + return RezParser::make_IDENTIFIER(lower, loc); + } + else + { + //std::cout << "key: " << s << std::endl; + return (*p->second)(loc); + } + } + else if(tok == T_INTLIT) + { + if(tok.get_value() == "0") + { + auto tok2 = peekWave(); + while(tok2 != T_EOI && tok2 != T_EOF && IS_CATEGORY(tok2, WhiteSpaceTokenType)) + nextWave(), tok2 = peekWave(); + + //std::cout << "!" << std::hex << (token_id)tok2 << std::dec << "|" << tok2.get_value() << "!\n"; + static boost::regex binlit("[bB][01]+"); + if(tok2 == T_IDENTIFIER && boost::regex_match(tok2.get_value().c_str(), binlit)) + tok = nextWave(); + } + return RezParser::make_INTLIT(readInt(tok.get_value().c_str()), loc); + } + else + { +#define NOVAL_TOK(name) \ +case T_ ## name: /*std::cout << #name << std::endl;*/ return RezParser::make_ ## name(loc) + switch(token_id(tok)) + { + case T_INTLIT: return RezParser::make_INTLIT(readInt(tok.get_value().c_str()), loc); + + case T_CHARLIT: return RezParser::make_CHARLIT(tok.get_value().c_str(), loc); + case T_STRINGLIT: return RezParser::make_STRINGLIT(tok.get_value().c_str(), loc); + + NOVAL_TOK(LEFTBRACE); + NOVAL_TOK(RIGHTBRACE); + NOVAL_TOK(LEFTBRACKET); + NOVAL_TOK(RIGHTBRACKET); + NOVAL_TOK(LEFTPAREN); + NOVAL_TOK(RIGHTPAREN); + NOVAL_TOK(SEMICOLON); + NOVAL_TOK(COMMA); + NOVAL_TOK(PLUS); + NOVAL_TOK(MINUS); + NOVAL_TOK(DIVIDE); + NOVAL_TOK(STAR); + NOVAL_TOK(ASSIGN); + NOVAL_TOK(COLON); + NOVAL_TOK(SHIFTLEFT); + NOVAL_TOK(SHIFTRIGHT); + NOVAL_TOK(EQUAL); + NOVAL_TOK(NOTEQUAL); + NOVAL_TOK(AND); + NOVAL_TOK(OR); + NOVAL_TOK(XOR); + NOVAL_TOK(COMPL); + + default: + + return RezParser::make_BADTOKEN(tok.get_value().c_str(), loc); + } + + } + } + } + return RezSymbol(); +} diff --git a/Rez/RezLexerWaveToken.h b/Rez/RezLexerWaveToken.h new file mode 100644 index 0000000000..3d11903b0b --- /dev/null +++ b/Rez/RezLexerWaveToken.h @@ -0,0 +1,15 @@ +#ifndef REZLEXERWAVETOKEN_H +#define REZLEXERWAVETOKEN_H + +#include "RezLexer.h" + +#include + +class RezLexer::WaveToken : public boost::wave::cpplexer::lex_token<> +{ +public: + WaveToken() = default; + WaveToken(const boost::wave::cpplexer::lex_token<> & o) : boost::wave::cpplexer::lex_token<>(o) {} +}; + +#endif // REZLEXERWAVETOKEN_H diff --git a/Rez/RezParser.yy b/Rez/RezParser.yy new file mode 100644 index 0000000000..ed5dacad8e --- /dev/null +++ b/Rez/RezParser.yy @@ -0,0 +1,271 @@ +%require "3.0.2" +%defines +%define parser_class_name {RezParser} +%skeleton "lalr1.cc" + +%locations; + +%define api.token.constructor +%define api.value.type variant +%define parse.assert + +%token IDENTIFIER; +%token CHARLIT; +%token STRINGLIT; +%token INTLIT; + +%token BADTOKEN; + + +%token LEFTBRACE "{"; +%token RIGHTBRACE "}"; +%token LEFTBRACKET "["; +%token RIGHTBRACKET "]"; +%token LEFTPAREN "("; +%token RIGHTPAREN ")"; +%token SEMICOLON ";"; +%token COMMA ","; +%token PLUS "+"; +%token MINUS "-"; +%token DIVIDE "/"; +%token STAR "*"; +%token ASSIGN "="; +%token COLON ":"; +%token SHIFTLEFT "<<"; +%token SHIFTRIGHT ">>"; +%token EQUAL "=="; +%token NOTEQUAL "!="; +%token AND "&"; +%token OR "|"; +%token XOR "^"; +%token COMPL "~"; + + +%token TYPE "type"; +%token RESOURCE "resource"; +%token ARRAY "array"; +%token SWITCH "switch"; +%token CASE "case"; +%token AS "as"; +%token FILL "fill"; +%token ALIGN "align"; +%token HEX "hex"; +%token KEY "key"; +%token WIDE "wide"; +%token LITERAL "literal"; +%token UNSIGNED "unsigned"; + +%token BOOLEAN "boolean"; +%token BIT "bit"; +%token BYTE "byte"; +%token CHAR "char"; +%token WORD "word"; +%token INTEGER "integer"; +%token LONG "long"; +%token LONGINT "longint"; +%token PSTRING "pstring"; +%token WSTRING "wstring"; +%token STRING "string"; +%token POINT "point"; +%token RECT "rect"; +%token BITSTRING "bitstring"; + +/* +%left "|"; +%left "^"; +%left "&"; +%left "==" "!="; +%left ">>" "<<"; +%left "+" "-"; +%left "*" "/"; +*/ + +%param { RezLexer& lexer } + +%code requires { + #define YY_NULLPTR nullptr + class RezLexer; +} + +%code provides { + using yy::RezParser; + //using RezSymbol = yy::RezParser::symbol_type; + + class RezSymbol : public yy::RezParser::symbol_type + { + public: + RezSymbol() = default; + RezSymbol(const yy::RezParser::symbol_type& x) : yy::RezParser::symbol_type(x) {} + }; +} + +%code { + #include "RezLexer.h" + static yy::RezParser::symbol_type yylex(RezLexer& lexer) + { + return lexer.nextToken(); + } + + void yy::RezParser::error(const location_type& loc, std::string const& err) + { + std::cerr << loc << ": " << err << std::endl; + } +} + +%% +%start rez; + +rez : %empty + | rez type_definition ";" + | rez resource ";" + ; + +simpletype : "boolean" + | "bit" | "byte" | "word" | "integer" | "long" | "longint" | "rect" + | "point" + | "char" + | "pstring" array_count_opt + | "wstring" array_count_opt + | "string" array_count_opt; + | "bitstring" "[" expression "]"; + +type_definition : "type" type_spec "{" field_definitions "}" + { std::cout << "TYPE " << $2 << std::endl; } + | "type" type_spec "as" type_spec + { std::cout << "TYPE " << $2 << std::endl; } + ; + +%type type_spec; +type_spec : CHARLIT { $$ = $1; } + | CHARLIT "(" INTLIT ")" { $$ = $1; } + ; + +field_definitions : %empty + | field_definitions IDENTIFIER ":" + | field_definitions ";" + | field_definitions field_definition ";" ; + +field_definition: simple_field_definition + | array_definition + | switch_definition + | fill_statement + | align_statement; + +fill_statement : "fill" fill_unit array_count_opt; +align_statement : "align" fill_unit; + +fill_unit : "bit" | "byte" | "word" | "long"; + +simple_field_definition: field_attributes simpletype value_spec; + +value_spec : %empty + | named_values + | "=" expression; + +named_values: named_value + | named_values "," named_value + | named_values named_value; + +named_value : IDENTIFIER + | IDENTIFIER "=" expression ; + +array_definition: array_attributes "array" array_name_opt array_count_opt "{" field_definitions "}" ; + +array_count : "[" expression "]" ; +array_count_opt : %empty | array_count ; + +array_name_opt : %empty | IDENTIFIER ; + +array_attributes: %empty | "wide" ; +field_attributes: %empty | field_attributes field_attribute; +field_attribute : "hex" | "key" | "unsigned" | "literal"; + +switch_definition: "switch" "{" + switch_cases + "}" ; + +switch_cases : %empty | switch_cases switch_case ; + +switch_case : "case" IDENTIFIER ":" field_definitions ; + +/* +expression + | expression "^" expression + | expression "&" expression + | expression "|" expression + | "~" expression + | expression "==" expression + | expression "!=" expression + | expression ">>" expression + | expression "<<" expression + | expression "+" expression + | expression "-" expression + | "-" expression + | expression "/" expression + | expression "*" expression + ; +*/ + +expression : expression1 + | expression "^" expression1 + ; + +expression1 : expression2 + | expression1 "&" expression2 + ; + +expression2 : expression3 + | expression2 "|" expression3 + ; + +expression3 : expression4 + | expression3 "==" expression4 + | expression3 "!=" expression4 + ; + +expression4 : expression5 + | expression4 ">>" expression5 + | expression4 "<<" expression5 + ; + +expression5 : expression6 + | expression5 "+" expression6 + | expression5 "-" expression6 + ; + +expression6 : expression7 + | expression6 "*" expression7 + | expression6 "/" expression7 + ; +expression7 : expression8 + | "-" expression7 + | "+" expression7 + | "~" expression7 + ; + +expression8 : INTLIT + | CHARLIT + | STRINGLIT + | IDENTIFIER + | IDENTIFIER "(" function_argument_list ")" + | IDENTIFIER "[" function_argument_list1 "]" + | "(" expression ")" + | "{" resource_body "}" + ; + +function_argument_list : %empty | function_argument_list1 ; +function_argument_list1 : expression | function_argument_list "," expression ; + +resource: "resource" CHARLIT "(" function_argument_list ")" "{" resource_body "}" + { std::cout << "RESOURCE " << $2 << std::endl; } + +resource_body : %empty | resource_body1 ; +resource_body1 : resource_item + | resource_body1 "," resource_item + | resource_body1 ";" resource_item + | resource_body1 ";" + ; + +resource_item : expression | IDENTIFIER "{" resource_body "}" ; + +%% diff --git a/Rez/Test.r b/Rez/Test.r new file mode 100644 index 0000000000..173917529b --- /dev/null +++ b/Rez/Test.r @@ -0,0 +1,11 @@ +/*#include "Types.r" + + +*/ + +#include "/home/wolfgang/Projects/Retro68/CExamples/Sample.r" + +type 'TEST' { +boolean itemUnlocked = false, // defined attributes bits... + itemLocked = true; +};