diff --git a/compile.cmd b/compile.cmd index 334d167..fb371bb 100644 --- a/compile.cmd +++ b/compile.cmd @@ -1,5 +1,5 @@ @echo off -pushd C:\Users\Owner\Documents\Projects\VoicemenuGenerator\src +pushd C:\Users\Owner\Documents\Projects\CmdMenuGenerator\src g++ -m32 binds.cpp commandmenu.cpp launchoptions.cpp compiler.cpp tokens.cpp main.cpp -o ../cvm_generate_x32.exe -std=c++2a -Wall if NOT %%ERRORLEVEL%% EQU 0 ( pause diff --git a/compile.sh b/compile.sh index 6811c9c..b89817a 100644 --- a/compile.sh +++ b/compile.sh @@ -1,10 +1,10 @@ -pushd src -g++ -m32 binds.cpp commandmenu.cpp launchoptions.cpp compiler.cpp tokens.cpp main.cpp -o ../cvm_generate_x32.exe -std=c++2a -Wall +pushd C:/Users/Owner/Documents/Projects/CmdMenuGenerator/src +g++ -g -m32 * -o ../cmg-x32.exe -std=c++2a -Wall if [ ! $? -eq 0 ] then read -n1 -r -p "Error(s) have occurred here! Press a key to continue." fi -g++ -m64 binds.cpp commandmenu.cpp launchoptions.cpp compiler.cpp tokens.cpp main.cpp -o ../cvm_generate_x64.exe -std=c++2a -Wall +g++ -g -m64 * -o ../cmg-x64.exe -std=c++2a -Wall if [ ! $? -eq 0 ] then read -n1 -r -p "Error(s) have occurred here! Press a key to continue." diff --git a/src/binds.cpp b/src/bind.cpp similarity index 98% rename from src/binds.cpp rename to src/bind.cpp index 74cd433..18d77d7 100644 --- a/src/binds.cpp +++ b/src/bind.cpp @@ -1,5 +1,6 @@ -#include "binds.hpp" +#include "bind.hpp" #include "compiler.hpp" +#include "lex.hpp" #include extern std::map KVMap; Bind::Bind() {} @@ -28,6 +29,7 @@ Bind::Bind(const unsigned char& p_cKey, const Parser::BindToken& p_Token) if (p_Token.bNoExit==true) CmdStrContainer.push_back(p_Token.sCmdStr); else CmdStrContainer.push_back("cmenu.exitmenu; cmenu.on_cmenu_exit; "+p_Token.sCmdStr); } + Bind::Bind(const unsigned char& p_cKey, const Parser::ToggleBindToken& p_Token) : bToggleBind(true), cKey(p_cKey) { for (unsigned short i=0; i < p_Token.ToggleStates; i++) { diff --git a/src/binds.hpp b/src/bind.hpp similarity index 100% rename from src/binds.hpp rename to src/bind.hpp diff --git a/src/commandmenu.cpp b/src/commandmenu.cpp index 057a6b2..a37913e 100644 --- a/src/commandmenu.cpp +++ b/src/commandmenu.cpp @@ -1,12 +1,25 @@ +#include #include "commandmenu.hpp" #include "compiler.hpp" +#include "lex.hpp" + +extern std::map KVMap; CommandMenu::CommandMenu() {} CommandMenu::CommandMenu(const std::string& p_sName) - : sRawName(formatRaw(p_sName)), sName(p_sName) {} +: sRawName(formatRaw(p_sName)), sName(p_sName) { + if (KVMap["display"]=="caption") Display=CMenuDisplayType::CAPTIONS; + else if (KVMap["display"]=="console") Display=CMenuDisplayType::CONSOLE; + else if (KVMap["display"]=="none") Display=CMenuDisplayType::NONE; +} CommandMenu::CommandMenu(const std::string& p_sRawName, const std::string& p_sName) -: sRawName(p_sRawName), sName(p_sName) {} +: sRawName(p_sRawName), sName(p_sName) { + if (KVMap["display"]=="caption") Display=CMenuDisplayType::CAPTIONS; + else if (KVMap["display"]=="console") Display=CMenuDisplayType::CONSOLE; + else if (KVMap["display"]=="none") Display=CMenuDisplayType::NONE; + else Display=CMenuDisplayType::CAPTIONS; +} CommandMenu::~CommandMenu() {} \ No newline at end of file diff --git a/src/commandmenu.hpp b/src/commandmenu.hpp index d74cd5a..a0e2612 100644 --- a/src/commandmenu.hpp +++ b/src/commandmenu.hpp @@ -1,15 +1,21 @@ -#ifndef PAGE_HPP -#define PAGE_HPP +#ifndef COMMANDMENU_HPP +#define COMMANDMENU_HPP #include #include #include -#include "binds.hpp" #include "compiler.hpp" +#include "bind.hpp" + +enum class CMenuDisplayType { + NONE=0, + CONSOLE, + CAPTIONS +}; struct CommandMenu { std::string sRawName, sName; std::vector binds; - + CMenuDisplayType Display; CommandMenu(); CommandMenu(const std::string& p_sName); CommandMenu(const std::string& p_sRawName, const std::string& p_sName); diff --git a/src/compiler.cpp b/src/compiler.cpp index 7e5133e..c9c0228 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -1,354 +1,128 @@ -#include "Tokens.hpp" -#include "binds.hpp" -#include "commandmenu.hpp" -#include "compiler.hpp" #include #include #include #include #include -std::size_t iLineNum=1u; -std::size_t iLineColumn=1u; +#include "compiler.hpp" +#include "lex.hpp" +#include "token.hpp" +#include "bind.hpp" +#include "commandmenu.hpp" + extern std::map KVMap; -#define Error(error) ErrorTokens.push_back(Token(0u,0u,TokenType::COMPILER_ERROR,error)) std::deque TokenContainer; std::deque ErrorTokens; std::deque CMenuTokens; extern std::deque CMenuContainer; // Made in main.cpp -// Convert to a safer string format for file and caption names. -std::string formatRaw(std::string p_sInStr) { - for (unsigned long long i=0; i < p_sInStr.length(); i++) { - if (p_sInStr.at(i)=='<' && p_sInStr.find('>',i)!=std::string::npos) { - p_sInStr.erase(i,(p_sInStr.find('>',i+1)-i)+1); - if (i>0) i--; - } - // remove punctuation - if (ispunct(p_sInStr.at(i))) { - p_sInStr.erase(i,1); - i--; - continue; - } - // and non-ascii characters - if (i0) TokenContainer.back().sValue.shrink_to_fit(); - // EOF Token - if (i==p_sInStr.length()-1) { - TokenContainer.push_back(Token(p_sInStr.length()-1,iLineNum,TokenType::END_OF_FILE,"")); - TokenContainer.back().sValue.shrink_to_fit(); - break; - } - // Nonterminal - if (IsIdentChar(p_sInStr.at(i))) { - TokenContainer.push_back(Token(iLineColumn,iLineNum,TokenType::IDENTIFIER,"")); - while (IsIdentChar(p_sInStr.at(i))) - { - TokenContainer.back().sValue.push_back(p_sInStr.at(i)); - i++; - iLineColumn++; - } - // Check for terminals. - if (TokenContainer.back().sValue=="TOGGLE") TokenContainer.back().Type=TokenType::TOGGLE; - else if (TokenContainer.back().sValue=="BIND") TokenContainer.back().Type=TokenType::BIND; - else if (TokenContainer.back().sValue=="NOEXIT") TokenContainer.back().Type=TokenType::NOEXIT; - else if (TokenContainer.back().sValue=="NOFORMAT") TokenContainer.back().Type=TokenType::NOFORMAT; - continue; - } - switch (p_sInStr.at(i)) - { - // comments - case '/': - // Line comments - if (p_sInStr.at(i+1)=='/') { - for (auto t=p_sInStr.begin()+i; t!=p_sInStr.end(); t++, i++) { - if (*t=='\t') iLineColumn+=5-(iLineColumn%4==0 ? 4 : iLineColumn%4); - else iLineColumn++; - if (*t=='\n' || *t=='\r') break; - } - } - /* Block comments */ - else if (p_sInStr.at(i+1)=='*') { // - std::size_t tempi=i, templinecolumn=iLineColumn, templinenumber=iLineNum; - for (auto t=p_sInStr.begin()+i; t!=p_sInStr.end(); t++, tempi++) { - if (*t=='\t') templinecolumn+=5-(iLineColumn%4==0 ? 4 : iLineColumn%4); - else templinecolumn++; - if (*t=='\n') { - if (*(t-1)!='\r') templinecolumn=1u; - templinenumber++; - } - else if (*t=='\r') { - templinecolumn=1u; - if (*(t+1)!='\n') templinenumber++; - } - if (*t=='*' && *(t+1)=='/') { - i=tempi+2; - iLineColumn=templinecolumn+2; - iLineNum=templinenumber+2; - break; - } - if (t==p_sInStr.end()-1) { - Error("error: Unclosed comment. ("+std::to_string(iLineNum)+':'+std::to_string(iLineColumn)+')'); - i=tempi; - break; - } - } - } - else { - TokenContainer.push_back(Token(iLineColumn,iLineNum,TokenType::UNDEFINED,"/")); - iLineColumn++; - i++; - } - break; - // strings - case '\"': - { - std::size_t tempcol=iLineColumn,templn=iLineNum; - // New lines or carriage returns cannot be in strings. (I don't mean the '\r' or '\n' character.) - for (i++, iLineColumn++; i < p_sInStr.length(); i++, iLineColumn++) { - if (p_sInStr.at(i)=='\r') { - Error("error: Missing a quote at ("); - ErrorTokens.back().iLineNum=iLineNum; - ErrorTokens.back().iLineColumn=iLineColumn; - ErrorTokens.back().sValue+=ErrorTokens.back().GetFileLoc()+")"; - // - TokenContainer.push_back(Token(iLineColumn,iLineNum,TokenType::STRING,"")); - t_sStrTemp=""; - iLineColumn=1u; - if (p_sInStr.at(i+1)!='\n') { - iLineNum++; - i++; - } - break; - } - else if (p_sInStr.at(i)=='\n') { - Error("error: Missing a quote at ("); - ErrorTokens.back().iLineNum=iLineNum; - ErrorTokens.back().iLineColumn=iLineColumn; - ErrorTokens.back().sValue+=ErrorTokens.back().GetFileLoc()+")"; - // - TokenContainer.push_back(Token(iLineColumn,iLineNum,TokenType::STRING,"")); - t_sStrTemp=""; - iLineNum++; - if (p_sInStr.at(i-1)!='\r') { - iLineColumn=1u; - i++; - } - break; - } - else if (i==p_sInStr.length()-1){ - Error("error: Missing a quote at ("); - ErrorTokens.back().sValue+=ErrorTokens.back().GetFileLoc()+")"; - t_sStrTemp=""; - break; - } - else if (p_sInStr.at(i)=='\"') { - TokenContainer.push_back(Token(tempcol,templn,TokenType::STRING,t_sStrTemp)); - t_sStrTemp=""; - break; - } - else { - if (p_sInStr.at(i)=='\t') iLineColumn+=5-(iLineColumn%4==0 ? 4 : iLineColumn%4); - t_sStrTemp+=p_sInStr.at(i); - } - } - i++; // Starts at the ending quote if this doesn't exist. - iLineColumn++; - } - break; - //terminals - case '=': - TokenContainer.push_back(Token(iLineColumn,iLineNum,TokenType::EQUALS,"=")); - i++; - iLineColumn++; - break; - case '|': - TokenContainer.push_back(Token(iLineColumn,iLineNum,TokenType::VBAR,"|")); - i++; - iLineColumn++; - break; - case '{': - TokenContainer.push_back(Token(iLineColumn,iLineNum,TokenType::LCBRACKET,"{")); - i++; - iLineColumn++; - break; - case '}': - TokenContainer.push_back(Token(iLineColumn,iLineNum,TokenType::RCBRACKET,"}")); - i++; - iLineColumn++; - break; - //spaces and colon - case '\t': - iLineColumn+=5-(iLineColumn%4==0 ? 4 : iLineColumn%4); - i++; - break; - case ' ': - iLineColumn++; - i++; - break; - case '\r': - iLineColumn=1u; - if (i+1==p_sInStr.length() || p_sInStr.at(i+1)!='\n') iLineNum++; - i++; - break; - case '\v': - i++; - iLineNum++; - break; - case '\n': - iLineNum++; - if (i>0 && p_sInStr.at(i-1)!='\r') iLineColumn=1u; - i++; - break; - default: - TokenContainer.push_back(Token(iLineColumn,iLineNum,TokenType::UNDEFINED,"")); - TokenContainer.back().sValue.push_back(p_sInStr.at(i)); - iLineColumn++; - i++; - break; - } - } - if (ErrorTokens.size()>=1) bErrorsFound=true; - return !bErrorsFound; -} + namespace Parser { - unsigned int depth=0u; + int iCMenuDepth=0; // Records how nested is a CMenu. bool bEOFFound=false, bErrorsFound=false; bool ParseTokens() { bool bNoExit=false, bFormatted=true; for (auto token=TokenContainer.begin(); token!=TokenContainer.end(); ) { switch (token->Type) { case TokenType::NOEXIT: - if (bNoExit==true) Error("error: Duplicate modifier \"NOEXIT\". ("+token->GetFileLoc()+')'); + if (bNoExit==true) ErrorTokens.push_back(Token(token->iLineNum,token->iLineColumn,TokenType::COMPILER_ERROR,token->GetFileLoc()+": error: Duplicate modifier \"NOEXIT\".")); else bNoExit=true; token++; break; case TokenType::NOFORMAT: - if (bFormatted==false) Error("error: Duplicate modifier \"NOFORMAT\". ("+token->GetFileLoc()+')'); + if (bFormatted==false) ErrorTokens.push_back(Token(token->iLineNum,token->iLineColumn,TokenType::COMPILER_ERROR,token->GetFileLoc()+": error: Duplicate modifier \"NOFORMAT\".")); else bFormatted=false; token++; break; case TokenType::TOGGLE: { // Check for toggle bind. - std::string namelist[MAX_TOGGLE_STATES]; - std::string cmdlist[MAX_TOGGLE_STATES]; + std::string NameList[MAX_TOGGLE_STATES]; + std::string CmdList[MAX_TOGGLE_STATES]; unsigned short i=1; - if (depth<=0) - Error("error: Toggle bind must be set in a command menu. ("+token->GetFileLoc()+')'); + if (iCMenuDepth<=0) + ErrorTokens.push_back(Token(token->iLineNum,token->iLineColumn,TokenType::COMPILER_ERROR,token->GetFileLoc()+": error: Toggle bind must be set in a command menu.")); if ((token+i)->Type!=TokenType::BIND) - Error("Expected \'BIND\' ("+(token+i)->GetFileLoc()+")"); + ErrorTokens.push_back(Token(token->iLineNum,token->iLineColumn,TokenType::COMPILER_ERROR,token->GetFileLoc()+": error: Expected \'BIND\' keyword here.")); else i++; while ((token+i)->Type==TokenType::STRING) { - if (i % 2 == 0) namelist[(i-2)/2]=(token+i)->sValue; - else if (i % 2 == 1) cmdlist[(i-2)/2]=(token+i)->sValue; + if (i % 2 == 0) NameList[(i-2)/2]=(token+i)->sValue; + else if (i % 2 == 1) CmdList[(i-2)/2]=(token+i)->sValue; i++; } if (i-1<=1 || i % 2!=0) // Even amount of strings indicate that the toggle bind has names and cmdstrs - Error("error: Expected string! ("+(token+i)->GetFileLoc()+")"); + ErrorTokens.push_back(Token(token->iLineNum,token->iLineColumn,TokenType::COMPILER_ERROR,token->GetFileLoc()+": error: Expected string here.")); if ((token+i)->Type!=TokenType::VBAR) - Error("error: Expected '|' ("+(token+i-1)->GetFileLoc()+")"); - CMenuTokens.push_back(new Parser::ToggleBindToken(namelist,cmdlist,static_cast((i-2)/2),bNoExit,bFormatted)); - if (bNoExit==true) bNoExit=false; + ErrorTokens.push_back(Token(token->iLineNum,token->iLineColumn,TokenType::COMPILER_ERROR,(token+i)->GetFileLoc()+"error: Expected '|'.")); + if (!bErrorsFound) CMenuTokens.push_back(new Parser::ToggleBindToken(NameList,CmdList,static_cast((i-2)/2),bNoExit,bFormatted)); + bNoExit=false, bFormatted=true; token+=i; } break; case TokenType::BIND: // Check for bind. { unsigned short i=1u; - if (depth<=0) - Error("error: Bind must be set in a commandmenu. ("+token->GetFileLoc()+')'); + if (iCMenuDepth<=0) + ErrorTokens.push_back(Token(token->iLineNum,token->iLineColumn,TokenType::COMPILER_ERROR,token->GetFileLoc()+": error: Bind must be set in a command menu.")); if ((token+i)->Type!=TokenType::STRING) - Error("error: Expected string. ("+(token+i)->GetFileLoc()+")"); + ErrorTokens.push_back(Token((token+i)->iLineNum,(token+i)->iLineColumn,TokenType::COMPILER_ERROR,(token+i)->GetFileLoc()+": error: Expected a string.")); else { i++; if ((token+i)->Type!=TokenType::STRING) - Error("error: Expected string. ("+(token+i)->GetFileLoc()+")"); + ErrorTokens.push_back(Token((token+i)->iLineNum,(token+i)->iLineColumn,TokenType::COMPILER_ERROR,(token+i)->GetFileLoc()+": error: Expected a string.")); else i++; } if ((token+i)->Type!=TokenType::VBAR) - Error("error: Expected '|' ("+(token+i)->GetFileLoc()+")"); + ErrorTokens.push_back(Token((token+i)->iLineNum,(token+i)->iLineColumn,TokenType::COMPILER_ERROR,(token+i)->GetFileLoc()+": error: Expected '|'.")); else i++; - CMenuTokens.push_back(new Parser::BindToken((token+1)->sValue, (token+2)->sValue,bNoExit,bFormatted)); + if (!bErrorsFound) CMenuTokens.push_back(new Parser::BindToken((token+1)->sValue, (token+2)->sValue,bNoExit,bFormatted)); bNoExit=false, bFormatted=true; token+=i; } - break; + break; case TokenType::STRING: // Check for new commandmenu. { unsigned short i=1u; - if ((token+i)->Type!=TokenType::LCBRACKET) - Error("error: Expected '{' ("+(token+i)->GetFileLoc()+")"); - else if (bNoExit==true) { - Error("error: Expected a bind. ("+token->GetFileLoc()+')'); - bNoExit=false; + if (bNoExit==true) { + ErrorTokens.push_back(Token(token->iLineNum,token->iLineColumn,TokenType::COMPILER_ERROR,token->GetFileLoc()+": error: 'NOEXIT' modifier cannot be used with CMenus.")); + bNoExit = false; } - CMenuTokens.push_back(new Parser::CMenuToken(token->sValue,bFormatted)); + if ((token+i)->Type!=TokenType::LCBRACKET) + ErrorTokens.push_back(Token((token+i)->iLineNum,(token+i)->iLineColumn,TokenType::COMPILER_ERROR,(token+i)->GetFileLoc()+": error: Expected '{' here.")); + if (!bErrorsFound) CMenuTokens.push_back(new Parser::CMenuToken(token->sValue,bFormatted)); bFormatted=true; token+=i; - depth++; } break; case TokenType::IDENTIFIER: // Check for set keymaps { unsigned short i=1u; - // Maybe they were trying to make a commandmenu… - if ((token+i)->Type==TokenType::LCBRACKET) { - token++; - break; - }; - if ((token+i)->Type!=TokenType::EQUALS) { - Error("error: Expected '='! ("+(token+i)->GetFileLoc()+")"); - TokenContainer.insert(token+i,Token((token+i)->iLineColumn,(token+i)->iLineNum,TokenType::EQUALS,"=")); - } - if ((token+i+1)->Type!=TokenType::LCBRACKET) i++; - if ((token+i)->Type!=TokenType::STRING || (token+i+1)->Type==TokenType::LCBRACKET) { - Error("error: Expected string! ("+(token+i)->GetFileLoc()+")"); - TokenContainer.insert(token+i,Token((token+i)->iLineColumn,(token+i)->iLineNum,TokenType::STRING,"")); + ErrorTokens.push_back(Token((token+i)->iLineNum,(token+i)->iLineColumn,TokenType::COMPILER_ERROR,(token+i)->GetFileLoc()+": error: Expected a '=' here.")); + } + else i++; + if ((token+i)->Type!=TokenType::STRING) { + ErrorTokens.push_back(Token((token+i)->iLineNum,(token+i)->iLineColumn,TokenType::COMPILER_ERROR,(token+i)->GetFileLoc()+": error: Expected a string here.")); } - if ((token+i+1)->Type!=TokenType::LCBRACKET) i++; - CMenuTokens.push_back(new Parser::KVToken(token->sValue,(token+2)->sValue)); + else i++; + if (!bErrorsFound) CMenuTokens.push_back(new Parser::KVToken(token->sValue,(token+2)->sValue)); token+=i; } break; case TokenType::LCBRACKET: - depth++; + iCMenuDepth++; if (token>TokenContainer.begin() && (token-1)->Type!=TokenType::STRING) { - Error("error: Expected string! ("+(token-1)->GetFileLoc()+")"); - depth--; + ErrorTokens.push_back(Token((token-1)->iLineNum,(token-1)->iLineColumn,TokenType::COMPILER_ERROR,(token-1)->GetFileLoc()+": error: Expected a string here.")); + iCMenuDepth--; } token++; break; case TokenType::RCBRACKET: - depth--; - if (depth==UINT32_MAX) { - Error("error: Stray '}' ("+token->GetFileLoc()+")"); - depth++; + iCMenuDepth--; + if (iCMenuDepth<0) { + ErrorTokens.push_back(Token(token->iLineNum,token->iLineColumn,TokenType::COMPILER_ERROR,token->GetFileLoc()+": error: Stray '}'")); + iCMenuDepth++; } - CMenuTokens.push_back(new Parser::PageEndToken()); + if (!bErrorsFound) CMenuTokens.push_back(new Parser::CMenuEndToken()); token++; break; case TokenType::END_OF_FILE: @@ -356,22 +130,29 @@ namespace Parser { token=TokenContainer.end(); break; case TokenType::UNDEFINED: - Error("error: Unrecognized token '"+token->sValue+"' ("+token->GetFileLoc()+")"); + ErrorTokens.push_back(Token(token->iLineNum,token->iLineColumn,TokenType::COMPILER_ERROR,token->GetFileLoc()+": error: Unrecognized character '"+token->sValue+"'.")); token++; break; default: token++; - break; - } + break; + } } if (bEOFFound==false) std::cout<<"warning: EOF not found!\n"; if (ErrorTokens.size()>=1) bErrorsFound=true; - return !bErrorsFound; + return bErrorsFound; } } -// Convert menu TokenContainer into something useful. -void ParseMenuTokens(unsigned short& p_iBindCount) { +/* Convert menu TokenContainer into something useful. + * p_iBindCount (unsigned short) stores how many binds were counted + * p_bUsedDisplayFlags (a flag) stores what display types were used. + - 3rd bit: Is the "none" display method used? + - 2nd bit: Is the "console" display method used? + - 1st bit: Is the "captions" display method used? +*/ + +bool ParseMenuTokens(unsigned short& p_iBindCount, unsigned char& p_bUsedDisplayFlags) { std::deque CMenuStack; std::stack NumKeyStack; // Variable is here to reduce the amount of goddamn downcasts I would have to do. @@ -383,6 +164,11 @@ void ParseMenuTokens(unsigned short& p_iBindCount) { { Parser::KVToken temp=static_cast(**token); KVMap.insert_or_assign(temp.Key,temp.Value); + + // Store what display types were used in p_bUsedDisplayFlags. + if (KVMap["display"]=="none") p_bUsedDisplayFlags |= 1; + else if (KVMap["display"]=="console") p_bUsedDisplayFlags |= 2; + else if (KVMap["display"]=="caption") p_bUsedDisplayFlags |= 4; } break; case Parser::CMenuTokenType::DECLARE_CMENU: @@ -410,27 +196,56 @@ void ParseMenuTokens(unsigned short& p_iBindCount) { } break; case Parser::CMenuTokenType::END_CMENU: - // Warning: - if (CMenuStack.front().binds.size()>10) std::cout<<"Warning: More than ten binds in CMenu \'"< 0) { + // Warning: + if (CMenuStack.front().binds.size()>10) std::cout<<"Warning: More than ten binds in CMenu \'"<(**token))); - NumKeyStack.top()=(NumKeyStack.top()+1)%10; - p_iBindCount++; + if (CMenuStack.size() > 0) { + CMenuStack.front().binds.push_back(Bind(NumKeyStack.top(),static_cast(**token))); + NumKeyStack.top()=(NumKeyStack.top()+1)%10; + p_iBindCount++; + } + else { + std::cerr<<"ParseMenuTokens():CMenuTokens+"<(**token))); - NumKeyStack.top()=(NumKeyStack.top()+1)%10; - p_iBindCount++; - break; + if (CMenuStack.size() > 0) { + CMenuStack.front().binds.push_back(Bind(NumKeyStack.top(),static_cast(**token))); + if (!NumKeyStack.empty()) { + NumKeyStack.top()=(NumKeyStack.top()+1)%10; + } + else { + std::cerr<<"ParseMenuTokens():CMenuTokens+"< #include -#include "Tokens.hpp" -// Convert to a safer string format for file and caption names. (Yes the duplicate input string is intentional.) -std::string formatRaw(std::string p_sInStr); -// Tokenize any string into needed Tokens for parsing. -bool Tokenize(const std::string& str); -void ParseMenuTokens(unsigned short& p_iBindCount); +#include "token.hpp" +bool ParseMenuTokens(unsigned short& p_iBindCount, unsigned char& p_bUsedDisplayFlags); namespace Parser { // For handling multi Tokens. enum CMenuTokenType {MENU_BIND=0, MENU_TOGGLE_BIND, DECLARE_CMENU, END_CMENU, KV_SET}; @@ -66,8 +62,8 @@ namespace Parser { Type=CMenuTokenType::DECLARE_CMENU; } }; - struct PageEndToken : public MenuToken { - PageEndToken() { + struct CMenuEndToken : public MenuToken { + CMenuEndToken() { Type=CMenuTokenType::END_CMENU; } }; diff --git a/src/lex.cpp b/src/lex.cpp new file mode 100644 index 0000000..36128f0 --- /dev/null +++ b/src/lex.cpp @@ -0,0 +1,249 @@ +#include +#include +#include "lex.hpp" +#include "compiler.hpp" +#include "token.hpp" + +std::size_t iLineNum=1u; +std::size_t iLineColumn=1u; + +extern std::deque ErrorTokens; +extern std::deque TokenContainer; +extern std::deque CMenuTokens; + +// Convert to a safer string format for file and caption names. +std::string formatRaw(std::string p_sInStr) { + for (unsigned long long i=0; i < p_sInStr.length(); i++) { + if (p_sInStr.at(i)=='<' && p_sInStr.find('>',i)!=std::string::npos) { + p_sInStr.erase(i,(p_sInStr.find('>',i+1)-i)+1); + if (i>0) i--; + } + // remove punctuation + if (ispunct(p_sInStr.at(i))) { + p_sInStr.erase(i,1); + i--; + continue; + } + // and non-ascii characters + if (i0 && p_sInStr.at(i-1)!='\r') iLineColumn=1u; + i++; + break; + default: + i++; + iLineColumn++; + break; + } + } + else { + if (TokenContainer.size()>0) TokenContainer.back().sValue.shrink_to_fit(); + // EOF Token + if (i == p_sInStr.length() - 1) { + TokenContainer.push_back(Token(iLineNum,p_sInStr.length()-1,TokenType::END_OF_FILE,"")); + TokenContainer.back().sValue.shrink_to_fit(); + break; + } + // Nonterminal + if (IsIdentChar(p_sInStr.at(i))) { + TokenContainer.push_back(Token(iLineNum,iLineColumn,TokenType::IDENTIFIER,"")); + while (IsIdentChar(p_sInStr.at(i))) + { + TokenContainer.back().sValue.push_back(p_sInStr.at(i)); + i++; + iLineColumn++; + } + // Check for terminals. + if (TokenContainer.back().sValue=="TOGGLE") TokenContainer.back().Type=TokenType::TOGGLE; + else if (TokenContainer.back().sValue=="BIND") TokenContainer.back().Type=TokenType::BIND; + else if (TokenContainer.back().sValue=="NOEXIT") TokenContainer.back().Type=TokenType::NOEXIT; + else if (TokenContainer.back().sValue=="NOFORMAT") TokenContainer.back().Type=TokenType::NOFORMAT; + continue; + } + switch (p_sInStr.at(i)) + { + // comments + case '/': + // Line comments + if (p_sInStr.at(i+1)=='/') { + for (auto t=p_sInStr.begin()+i; t!=p_sInStr.end(); t++, i++) { + if (*t=='\t') iLineColumn += 4 - (iLineColumn % 4); + else iLineColumn++; + if (*t=='\n' || *t=='\r') break; + } + } + /* Block comments */ + else if (p_sInStr.at(i+1)=='*') { // + bInBlockComment=true; + i+=2; + iLineColumn+=2; + } + else { + TokenContainer.push_back(Token(iLineNum,iLineColumn,TokenType::UNDEFINED,"/")); + iLineColumn++; + i++; + } + break; + // strings + case '\"': + { + // New lines or carriage returns cannot be in strings. (I don't mean the '\r' or '\n' character.) + for (i++, iLineColumn++; i < p_sInStr.length(); i++, iLineColumn++) { + if (p_sInStr.at(i)=='\r') { + ErrorTokens.push_back(Token(iLineNum,iLineColumn,TokenType::COMPILER_ERROR,std::to_string(iLineNum)+':'+std::to_string(iLineColumn)+": error: Ending '\"' is missing.")); + bErrorsFound=true; + TokenContainer.push_back(Token(iLineNum,iLineColumn,TokenType::STRING,"")); + t_sStrTemp=""; + iLineColumn=1u; + if (p_sInStr.at(i+1)!='\n') { + iLineNum++; + i++; + } + break; + } + else if (p_sInStr.at(i)=='\n') { + ErrorTokens.push_back(Token(iLineNum,iLineColumn,TokenType::COMPILER_ERROR,std::to_string(iLineNum)+':'+std::to_string(iLineColumn)+": error: Ending '\"' is missing.")); + bErrorsFound=true; + TokenContainer.push_back(Token(iLineNum,iLineColumn,TokenType::STRING,"")); + t_sStrTemp=""; + iLineNum++; + if (p_sInStr.at(i-1)!='\r') { + iLineColumn=1u; + i++; + } + break; + } + else if (i==p_sInStr.length()-1){ + ErrorTokens.push_back(Token(iLineNum,iLineColumn,TokenType::COMPILER_ERROR,std::to_string(iLineNum)+':'+std::to_string(iLineColumn)+": error: Ending '\"' is missing.")); + bErrorsFound=true; + t_sStrTemp=""; + break; + } + else if (p_sInStr.at(i)=='\"') { + TokenContainer.push_back(Token(iLineNum,iLineColumn,TokenType::STRING,t_sStrTemp)); + t_sStrTemp=""; + break; + } + else { + if (p_sInStr.at(i)=='\t') iLineColumn += 4 - (iLineColumn % 4); + t_sStrTemp+=p_sInStr.at(i); + } + } + i++; // Starts at the ending quote if this doesn't exist. + iLineColumn++; + } + break; + //terminals + case '=': + TokenContainer.push_back(Token(iLineNum,iLineColumn,TokenType::EQUALS,"=")); + i++; + iLineColumn++; + break; + case '|': + TokenContainer.push_back(Token(iLineNum,iLineColumn,TokenType::VBAR,"|")); + i++; + iLineColumn++; + break; + case '{': + TokenContainer.push_back(Token(iLineNum,iLineColumn,TokenType::LCBRACKET,"{")); + i++; + iLineColumn++; + break; + case '}': + TokenContainer.push_back(Token(iLineNum,iLineColumn,TokenType::RCBRACKET,"}")); + i++; + iLineColumn++; + break; + //spaces and colon + case '\t': + iLineColumn += 4 - (iLineColumn % 4); + i++; + break; + case ' ': + iLineColumn++; + i++; + break; + case '\r': + iLineColumn=1u; + if (i+1==p_sInStr.length() || p_sInStr.at(i+1)!='\n') iLineNum++; + i++; + break; + case '\v': + i++; + iLineNum++; + break; + case '\n': + iLineNum++; + if (i>0 && p_sInStr.at(i-1)!='\r') iLineColumn=1u; + i++; + break; + default: + ErrorTokens.push_back(Token(iLineNum,iLineColumn,TokenType::UNDEFINED,"")); + ErrorTokens.back().sValue.push_back(p_sInStr.at(i)); + iLineColumn++; + i++; + break; + } + } + } + return bErrorsFound; + } +} \ No newline at end of file diff --git a/src/lex.hpp b/src/lex.hpp new file mode 100644 index 0000000..eb026b1 --- /dev/null +++ b/src/lex.hpp @@ -0,0 +1,7 @@ +#include +// Convert to a safer string format for file and caption names. (Yes the duplicate input string is intentional.) +std::string formatRaw(std::string p_sInStr); +namespace Lexer { + // Tokenize any string into needed Tokens for parsing. + bool Tokenize(const std::string& str); +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 3a019e2..1fa305b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,7 +7,8 @@ #include #include #include -#include "Tokens.hpp" +#include "lex.hpp" +#include "token.hpp" #include "commandmenu.hpp" #include "compiler.hpp" #include "launchoptions.hpp" @@ -19,7 +20,7 @@ std::map KVMap={ {"linger_time","0"}, {"predisplay_time","0.25"}, {"format","$(nkey). $(str)"}, - {"consolemode","false"}, + {"display","caption"}, {"resetkeys","bind 1 slot1; bind 2 slot2; bind 3 slot3; bind 4 slot4; bind 5 slot5; bind 6 slot6; bind 7 slot7; bind 8 slot8; bind 9 slot9; bind 0 slot10"} }; std::wstring_convert,wchar_t> convert; @@ -27,70 +28,54 @@ std::wstring_convert,wchar_t> convert; extern std::filesystem::path InputFilePath; extern std::filesystem::path sOutputDir; int main(int argc, char** argv) { - unsigned short iBindCount=0u; + // Evaluate launch options. if (!EvaluateLaunchOptions(argc,argv)) return -1; + // Get content from input file. std::string Line, InFileContent; std::ifstream inputf(InputFilePath,std::ios_base::binary); while (std::getline(inputf,Line)) { InFileContent+=Line+'\n'; } - // If tokenization and parsing process failed, then error out and return. - if (!Tokenize(InFileContent) || !Parser::ParseTokens()) { + // If tokenization and parsing process failed then error out and return. Also get the compiled bind count. + unsigned short iBindCount=0u; + unsigned char bUsedDisplayFlags=0; // Flag used to mark if and what display types were used. + if (Lexer::Tokenize(InFileContent) + || Parser::ParseTokens() + || ParseMenuTokens(iBindCount,bUsedDisplayFlags)) { + for (auto& e : ErrorTokens) { - std::cout<sRawName+".cfg"; - std::ofstream CMenuCFG(CMenuCFGPath); - // Write to cfg - CMenuCFG<<"_cmenu.menusettings\n_cvm.nullkeys\n"; - for (auto& kbind : CMenu->binds) { - if (kbind.bToggleBind) { - InitRoutineFile<<"alias _cmenu.toggle_"<sRawName+".cfg"; std::ofstream CMenuCFG(CMenuCFGPath); + /* Disable other displays if this CMenu isnt using it and there are other CMenus that use it.*/ + + if (CMenu->Display!=CMenuDisplayType::CONSOLE && (bUsedDisplayFlags & 2)) + CMenuCFG<<"developer 0\n"; + if (CMenu->Display!=CMenuDisplayType::CAPTIONS && (bUsedDisplayFlags & 4)) + CMenuCFG<<"cc_emit _#cmenu.clear_screen\n"; + /* Create the actual binds needed for the declared binds. */ - CMenuCFG<<"_cmenu.menusettings\ncc_emit _#cmenu.clear_screen\ncc_emit _#cmenu."+CMenu->sRawName+'\n'; + if (CMenu->Display==CMenuDisplayType::CAPTIONS) CMenuCFG<<"cc_linger_time 10000\ncc_predisplay_time 0\ncc_emit _#cmenu.clear_screen\ncc_emit _#cmenu."+CMenu->sRawName+'\n'; + else if (CMenu->Display==CMenuDisplayType::CONSOLE) CMenuCFG<<"developer 1\nclear\n"; + CMenuCFG<<"bind 1 _cmenu.1\nbind 2 _cmenu.2\nbind 3 _cmenu.3\nbind 4 _cmenu.4\nbind 5 _cmenu.5\nbind 6 _cmenu.6\nbind 7 _cmenu.7\nbind 8 _cmenu.8\nbind 9 _cmenu.9\nbind 0 _cmenu.0\n"; { unsigned int t_ToggleNum=iToggleNumber; - for (auto kbind=CMenu->binds.begin(); kbind!=CMenu->binds.end(); kbind++) { - if (kbind->bToggleBind==true) { + for (auto kTBind=CMenu->binds.begin(); kTBind!=CMenu->binds.end(); kTBind++) { + if (CMenu->Display != CMenuDisplayType::NONE && kTBind->bToggleBind==true) { CMenuCFG<<"_#cmenu.toggle_"+std::to_string(t_ToggleNum)<<'\n'; t_ToggleNum++; } - if (kbind!=CMenu->binds.begin() && kbind->bToggleBind==false && (kbind-1)->bToggleBind==true) { + if (CMenu->Display == CMenuDisplayType::CAPTIONS && kTBind!=CMenu->binds.begin() && kTBind->bToggleBind==false && (kTBind-1)->bToggleBind==true) { CMenuCFG<<"cc_emit _#cmenu."<sRawName<<"_seg_"<binds.begin(); kbind!=CMenu->binds.end(); kbind++) { - if (kbind==CMenu->binds.begin() && kbind->bToggleBind!=true) - CMenuCaptionFile<sRawName+"\" \""); - if (kbind->bToggleBind==true) { - InitRoutineFile<<"alias _cmenu.toggle_"<NameContainer.size(); sti++) { - InitRoutineFile<<"alias _cmenu.toggle_"<NameContainer.size())<<';'<CmdStrContainer.at(sti)<<"; alias _cmenu.toggle_"<NameContainer.size())<<"\"\n"; - InitRoutineFile<<"alias _#cmenu.toggle_"<NameContainer.at(sti)+"\"\n"); + for (auto kBind = CMenu->binds.begin(); kBind != CMenu->binds.end(); kBind++) { + if (CMenu->Display==CMenuDisplayType::CAPTIONS) { + if (kBind==CMenu->binds.begin() && kBind->bToggleBind!=true) + CMenuCaptionFile<sRawName+"\" \""); + if (kBind->bToggleBind==true) { + InitRoutineFile<<"alias _cmenu.toggle_"<NameContainer.size(); sti++) { + InitRoutineFile<<"alias _cmenu.toggle_"<NameContainer.size())<<';'<CmdStrContainer.at(sti)<<"; alias _cmenu.toggle_"<NameContainer.size())<<"\"\n"; + InitRoutineFile<<"alias _#cmenu.toggle_"<NameContainer.at(sti)+"\"\n"); + } + CMenuCFG<<"alias _cmenu."<cKey)<<"\"_cmenu.toggle_"<binds.begin() && (kBind-1)->bToggleBind==true) { + CMenuCaptionFile<sRawName+"_seg_"+std::to_string(iTBindSegmentNum)+"\" \""); + iTBindSegmentNum++; + } + CMenuCaptionFile<NameContainer.front()); + if (kBind==CMenu->binds.end()-1 || (kBind+1)->bToggleBind==true) CMenuCaptionFile<cKey)<<"\""<CmdStrContainer.at(0)<<"\"\n"; } - CMenuCFG<<"alias _cmenu."<cKey)<<" \"_cmenu.toggle_"<binds.begin() && (kbind-1)->bToggleBind==true) { - CMenuCaptionFile<sRawName+"_seg_"+std::to_string(iTBindSegmentNum)+"\" \""); - iTBindSegmentNum++; + else if (CMenu->Display==CMenuDisplayType::CONSOLE) { + if (kBind->bToggleBind) { + InitRoutineFile<<"alias _cmenu.toggle_"<NameContainer.size(); sti++) { + InitRoutineFile<<"alias _cmenu.toggle_"<CmdStrContainer.at(sti)<<"; alias _cmenu.toggle_"<NameContainer.size())<<"; alias _#cmenu.toggle_"<NameContainer.size())<<"\"\n"; + InitRoutineFile<<"alias _#cmenu.toggle_"<NameContainer.at(sti)<<"\"\n"; + } + CMenuCFG<<"_#cmenu.toggle_"<cKey)<<" \"_cmenu.toggle_"<NameContainer.front()<<'\n'<<"alias _cmenu."<cKey)<<" \""<CmdStrContainer.front()<<"\"\n";; + } + } + else if (CMenu->Display==CMenuDisplayType::NONE) { + if (kBind->bToggleBind) { + InitRoutineFile<<"alias _cmenu.toggle_"<NameContainer.size(); sti++) { + InitRoutineFile<<"alias _cmenu.toggle_"<CmdStrContainer.at(sti)<<"; alias _cmenu.toggle_"<NameContainer.size())<<"\n"; + } + CMenuCFG<<"alias _cmenu."<cKey)<<" \"_cmenu.toggle_"<cKey)<<" \""<CmdStrContainer.front()<<"\"\n";; } - CMenuCaptionFile<NameContainer.front()); - if (kbind==CMenu->binds.end()-1 || (kbind+1)->bToggleBind==true) CMenuCaptionFile<cKey)<<" \""<CmdStrContainer.at(0)<<"\"\n"; } } } + // We're done compiling so close the file streams. CMenuCaptionFile<<"\t}\n}"; CMenuCaptionFile.close(); - } InitRoutineFile<<"\necho [Command Menu Generator] Initialized command menus!"; InitRoutineFile.close(); //Done! diff --git a/src/tokens.cpp b/src/token.cpp similarity index 93% rename from src/tokens.cpp rename to src/token.cpp index e4b8021..c76060a 100644 --- a/src/tokens.cpp +++ b/src/token.cpp @@ -1,4 +1,4 @@ -#include "Tokens.hpp" +#include "token.hpp" #include "compiler.hpp" #include #include diff --git a/src/tokens.hpp b/src/token.hpp similarity index 96% rename from src/tokens.hpp rename to src/token.hpp index e3de202..fe781dc 100644 --- a/src/tokens.hpp +++ b/src/token.hpp @@ -21,7 +21,7 @@ class Token { unsigned char Type=TokenType::UNDEFINED; std::size_t iLineColumn=1u; // Location std::size_t iLineNum=1u; // Location from newline - std::string sValue; + std::string sValue=""; Token(); Token(const std::size_t& p_iLineNum, const std::size_t& p_iColumn, const unsigned char p_TokenType, const std::string& p_sVal);