Skip to content

Instantly share code, notes, and snippets.

@sfan5
Last active August 29, 2015 13:59
Show Gist options
  • Save sfan5/10993910 to your computer and use it in GitHub Desktop.
Save sfan5/10993910 to your computer and use it in GitHub Desktop.
does stuff using C++
#include <iostream>
#include <fstream>
#include <sstream>
#include <map>
#include <list>
#include <utility>
#include <stdlib.h> // for exit()
#include <vector>
template <typename T, typename U> class create_map {
private:
std::map<T, U> m_map;
public:
create_map(const T& key, const U& val)
{
m_map[key] = val;
}
create_map<T, U>& operator()(const T& key, const U& val)
{
m_map[key] = val;
return *this;
}
operator std::map<T, U>()
{
return m_map;
}
};
struct type {
const char *pf; // Function name
const char *type; // C++ type
unsigned int flags;
};
class create_st {
private:
struct type s;
public:
create_st(const char *pf, const char *type, unsigned int flags)
{
s.pf = pf; s.type = type; s.flags = flags;
}
operator struct type()
{
return s;
}
};
#define FLG_SER 1 // Uses serialize... function instead of write...
#define FLG_REF 2 // Passed by reference
#define LEX_WHITESPACE 1 // [ \t]+
#define LEX_IDENT 2 // [A-Za-z0-9_]+
#define LEX_TOPEN 3 // [
#define LEX_TCLOSE 4 // ]
#define LEX_COPEN 5 // {
#define LEX_CCLOSE 6 // }
#define LEX_SEMIC 7 // ;
static inline const char *lex2human(unsigned int t)
{
switch(t) {
case LEX_WHITESPACE:
return "whitespace";
case LEX_IDENT:
return "identifier";
case LEX_TOPEN:
return "'['";
case LEX_TCLOSE:
return "']'";
case LEX_COPEN:
return "'{'";
case LEX_CCLOSE:
return "'}'";
case LEX_SEMIC:
return "semicolon";
default:
return "~ERROR~";
}
}
struct lex_item {
unsigned int type;
unsigned int line;
unsigned int chri;
const char *file;
std::string ident_v; // Only when type = LEX_IDENT
std::vector<std::string> lines; // Only in first element, contains all the lines of the file this lex item is in
};
const std::map<std::string, struct type> types = create_map<std::string, struct type>
//name struct type
("u64", create_st("U64", "u64", 0))
("u32", create_st("U32", "u32", 0))
("u16", create_st("U16", "u16", 0))
("u8", create_st("U8", "u8", 0))
("s32", create_st("S32", "s32", 0))
("s16", create_st("S16", "s16", 0))
("s8", create_st("S8", "s8", 0))
("v3s32", create_st("V3S32", "v3s32", FLG_REF))
("v3s16", create_st("V3S16", "v3s16", FLG_REF))
("v2s32", create_st("V2S32", "v2s32", FLG_REF))
("v2s16", create_st("V2S16", "v2s16", FLG_REF))
("argb8", create_st("ARGB8", "irr::SColor", FLG_REF))
("f32", create_st("F1000", "float", 0))
("v3f", create_st("V3F1000", "v3f", FLG_REF))
("v2f", create_st("V2F1000", "v2f", FLG_REF))
("string", create_st("String", "std::string", FLG_SER | FLG_REF))
("lstring", create_st("LongString", "std::string", FLG_SER | FLG_REF))
("jstring", create_st("JsonString", "std::string", FLG_SER | FLG_REF))
("wstring", create_st("WideString", "std::wstring", FLG_SER | FLG_REF))
;
void writeHeader(std::ofstream &ofs)
{
ofs << "#include \"clientserver.h\"\n";
ofs << "#include \"util/serialize.h\"\n";
ofs << "\n";
ofs << "namespace protocol {\n";
ofs << "\n";
}
void writeFooter(std::ofstream &ofs)
{
ofs << "} // namespace protocol\n";
ofs << "\n";
}
void writeFunctionHeader1(std::ostream &os, std::string tag)
{
os << "SharedBuffer<u8> create_" << tag << "(\n";
os << "\t\tu16 net_proto_version, // Always\n";
}
void writeFunctionHeaderType(std::ostream &os, std::string name, struct type t)
{
if(t.flags & FLG_REF)
os << "\t\tconst " << t.type << " &" << name << ",\n";
else
os << "\t\t" << t.type << " " << name << ",\n";
}
void writeFunctionHeader2(std::ostream &os, std::string tag)
{
os << ") {\n";
os << "\tstd::ostringstream os(std::ios_base::binary);\n";
os << "\n";
os << "\twriteU16(os, " << tag << ");\n";
}
void writeFunctionBody(std::ostream &os, std::string name, struct type t)
{
if(t.flags & FLG_SER)
os << "\tos << serialize" << t.pf << "(" << name << ");\n";
else
os << "\twrite" << t.pf << "(os, " << name << ");\n";
}
void writeFunctionFooter(std::ostream &os)
{
os << "\n";
os << "\tstd::string s = os.str();\n";
os << "\treturn SharedBuffer<u8>((u8*) s.c_str(), s.size());\n";
os << "}\n";
os << "\n";
}
void print_file_err(const char *line, unsigned int lnum, unsigned int chri, const char *fn, const char *err)
{
std::cerr << fn << ":" << lnum << ":" << chri << ": " << err << std::endl;
std::cerr << "| ";
char *p = (char*) line;
while(*p) {
if(*p == '\t')
std::cerr << " "; // Makes sure alignment for tabs is right
else
std::cerr << *p;
p++;
}
std::cerr << std::endl;
std::cerr << "| ";
for(int i = 0; i < chri; i++)
std::cerr << " ";
std::cerr << "^" << std::endl;
}
#define IDENT_COND ((line[i] >= 'a' && line[i] <= 'z') || (line[i] >= 'A' && line[i] <= 'Z') || (line[i] >= '0' && line[i] <= '9') || line[i] == '_')
std::list<struct lex_item> lex(std::ifstream &ifs, const char *fn)
{
unsigned int ln = 0;
std::list<struct lex_item> out;
std::vector<std::string> lines;
while(1) {
char line[250];
if(ifs.eof())
break;
ifs.getline(line, 250);
ln++;
lines.push_back(std::string(line));
unsigned int i, currtype, starti;
i = currtype = 0;
std::ostringstream oss;
while(1) {
unsigned int newtype;
if(line[i] == '#' || line[i] == '\0')
goto finish; // Finish off the last element...
else if(line[i] == ' ' || line[i] == '\t')
newtype = LEX_WHITESPACE;
else if(IDENT_COND)
newtype = LEX_IDENT;
else if(line[i] == '[')
newtype = LEX_TOPEN;
else if(line[i] == ']')
newtype = LEX_TCLOSE;
else if(line[i] == '{')
newtype = LEX_COPEN;
else if(line[i] == '}')
newtype = LEX_CCLOSE;
else if(line[i] == ';')
newtype = LEX_SEMIC;
else {
print_file_err(line, ln, i, fn, "???");
exit(2);
}
if(currtype == newtype)
goto finish_end;
finish:
if(currtype != 0) {
struct lex_item l;
l.type = currtype;
l.line = ln;
l.chri = starti;
l.file = fn;
if(currtype == LEX_IDENT) {
l.ident_v = oss.str();
oss.str(""); // Clear string for ident value
}
out.push_back(l);
}
finish_end:
if(line[i] == '#' || line[i] == '\0')
break; // ...and then exit
if(currtype != newtype) {
starti = i; // Set the starting index for the new element
currtype = newtype;
}
if(currtype == LEX_IDENT)
oss << line[i];
i++;
}
}
out.front().lines = lines;
return out;
}
/*inline std::string int2str(int i)
{
std::ostringstream oss;
oss << i;
return oss.str();
}*/
#define NEXT_EL(lines) \
if(ll.size() == 0) { \
print_file_err(lines[el.line-1].c_str(), el.line, el.chri, el.file, "error: Unexpected EOF"); \
exit(2); \
} \
el = ll.front(); \
ll.pop_front();
#define SKIP(typ) \
while(el.type == typ) { \
NEXT_EL(lines); \
} \
#define EXPECT(typ) \
if(el.type != typ) { \
print_file_err(lines[el.line-1].c_str(), el.line, el.chri, el.file, (std::string("error: Expected ") + lex2human(typ) + " got " + lex2human(el.type)/* + " (" + int2str(__LINE__) + ")"*/).c_str()); \
exit(2); \
}
#define FILE_ERR(err) { \
print_file_err(lines[el.line-1].c_str(), el.line, el.chri, el.file, (std::string("error: ") + (err)).c_str()); \
exit(2); \
}
#define FILE_WARN(err) \
print_file_err(lines[el.line-1].c_str(), el.line, el.chri, el.file, (std::string("warning: ") + (err)).c_str());
void gen_file(std::list<struct lex_item> ll, std::ofstream &ofs)
{
if(ll.size() == 0)
return;
std::string c_tag, c_type, c_name, c_special1, c_special2;
std::ostringstream oss; // Contains the function body
struct lex_item el;
el = ll.front();
ll.pop_front();
std::vector<std::string> lines = el.lines;
while(1)
{
SKIP(LEX_WHITESPACE);
if(ll.size() == 0)
break;
newtag:
// TOPEN IDENT TCLOSE | [ foobar ]
EXPECT(LEX_TOPEN); NEXT_EL(lines);
SKIP(LEX_WHITESPACE);
EXPECT(LEX_IDENT);
c_tag = el.ident_v;
NEXT_EL(lines);
SKIP(LEX_WHITESPACE);
EXPECT(LEX_TCLOSE); NEXT_EL(lines);
writeFunctionHeader1(ofs, c_tag);
oss.str("");
newcontent:
// IDENT IDENT | v2s16 foo;
SKIP(LEX_WHITESPACE);
EXPECT(LEX_IDENT);
c_type = el.ident_v;
if(c_type == "list") {
// IDENT IDENT IDENT IDENT { <repeating contents> } | list bar u16 SomeCXXType { u8 abc; s32 def; }
NEXT_EL(lines);
EXPECT(LEX_WHITESPACE); SKIP(LEX_WHITESPACE);
EXPECT(LEX_IDENT);
c_name = el.ident_v;
NEXT_EL(lines);
EXPECT(LEX_WHITESPACE); SKIP(LEX_WHITESPACE);
EXPECT(LEX_IDENT);
c_special1 = el.ident_v;
NEXT_EL(lines);
EXPECT(LEX_WHITESPACE); SKIP(LEX_WHITESPACE);
EXPECT(LEX_IDENT);
c_special2 = std::string("std::list<") + el.ident_v + ">";
NEXT_EL(lines);
struct type t;
t.type = c_special2.c_str();
t.flags = FLG_REF;
writeFunctionHeaderType(ofs, c_name, t);
SKIP(LEX_WHITESPACE);
EXPECT(LEX_COPEN);
NEXT_EL(lines);
writeFunctionBody(oss, std::string(c_name) + ".size()", types.find(c_special1)->second);
oss << "\tfor(" << c_special2 << "::iterator it = " << c_name << ".begin(); it != " << c_name << ".end(); it++) {\n";
while(1) {
// IDENT IDENT | v2s16 foo;
SKIP(LEX_WHITESPACE);
if(el.type == LEX_CCLOSE)
break;
EXPECT(LEX_IDENT);
c_type = el.ident_v;
if(types.find(c_type) == types.end())
FILE_ERR((c_type + " does not name a valid type").c_str());
NEXT_EL(lines);
EXPECT(LEX_WHITESPACE); SKIP(LEX_WHITESPACE);
EXPECT(LEX_IDENT);
c_name = el.ident_v;
NEXT_EL(lines);
oss << "\t";
writeFunctionBody(oss, "it->" + c_name, types.find(c_type)->second);
SKIP(LEX_WHITESPACE);
EXPECT(LEX_SEMIC);
NEXT_EL(lines);
}
oss << "\t}\n";
SKIP(LEX_WHITESPACE);
EXPECT(LEX_CCLOSE);
NEXT_EL(lines);
} else {
if(types.find(c_type) == types.end())
FILE_ERR((c_type + " does not name a valid type").c_str());
NEXT_EL(lines);
EXPECT(LEX_WHITESPACE); SKIP(LEX_WHITESPACE);
EXPECT(LEX_IDENT);
c_name = el.ident_v;
NEXT_EL(lines);
writeFunctionHeaderType(ofs, c_name, types.find(c_type)->second);
writeFunctionBody(oss, c_name, types.find(c_type)->second);
}
SKIP(LEX_WHITESPACE);
EXPECT(LEX_SEMIC);
if(ll.size() == 0) {
writeFunctionHeader2(ofs, c_tag); // Write the remaining tag to file...
ofs << oss.str();
writeFunctionFooter(ofs);
break; // ...and break
}
NEXT_EL(lines);
if(el.type == LEX_TOPEN) {
writeFunctionHeader2(ofs, c_tag);
ofs << oss.str();
writeFunctionFooter(ofs);
goto newtag;
} else
goto newcontent;
}
}
int main(int argc, char *argv[])
{
if(argc <= 2) {
std::cerr << "Usage: " << argv[0] << " <input> <output>" << std::endl;
return 1;
} else {
std::ifstream ifs;
ifs.open(argv[1]);
if(!ifs) {
std::cerr << "Failed to open input file!" << std::endl;
return 1;
}
std::ofstream ofs;
ofs.open(argv[2]);
if(!ofs) {
std::cerr << "Failed to open output file!" << std::endl;
return 1;
}
writeHeader(ofs);
gen_file(lex(ifs, argv[1]), ofs);
writeFooter(ofs);
ifs.close();
ofs.close();
}
}
#test comment
#test comment
[ TOCLIENT_INIT ]
u8 deployed_block_version;
v3s16 player_pos;
u64 map_seed;
f32 recommended_send_interval;
# above stuff was only for testing the parser
# here's a packet with everything we can use
[TOCLIENT_TEST]
u64 a;
u32 b;
u16 c;
u8 d;
s32 e;
s16 f;
s8 g;
v3s32 h;
v3s16 i;
v2s32 j;
v2s16 k;
argb8 this_is_a_color;
f32 this_is_a_float;
v3f l;
v2f m;
string n;
lstring longstr;
jstring jsonstr;
wstring widestr;
# here's a packet to test lists
[TOCLIENT_LISTTEST]
#list <name> <type for length> <C++ type>
list a u16 SomeListContainer {
# the names here are equivalent to the names in the C++ type
v3s16 pos;
u8 flags;
string name;
};
u16 general_settings;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment