1 module buffer.compiler; 2 3 import std.string; 4 import std.conv; 5 import std.array; 6 import std.typecons; 7 import std.algorithm.searching; 8 import std.uni; 9 import std.exception; 10 11 template LoadBufferFile(string fileName) 12 { 13 pragma(msg, "Compiling file: ", fileName, "..."); 14 const char[] LoadBufferFile = compiler!(import(fileName)); 15 } 16 17 template LoadBufferScript(string src) 18 { 19 pragma(msg, "Compiling script: ", extractScriptfragment(src), "..."); 20 const char[] LoadBufferScript = compiler!src; 21 } 22 23 private string compiler(string source)() 24 { 25 Token[] tokens = lexer(source); 26 Sentence[] sentences = parser(tokens); 27 28 Appender!string code; 29 code.put("import buffer;\r\n\r\n"); 30 31 foreach (sentence; sentences) 32 { 33 code.put("final static class " ~ sentence.name ~ " : Message\r\n"); 34 code.put("{\r\n"); 35 36 foreach (field; sentence.fields) 37 { 38 code.put("\t" ~ field.type ~ " " ~ field.name ~ ";\r\n"); 39 } 40 41 code.put("\r\n"); 42 code.put("\tubyte[] serialize(string method = string.init)\r\n"); 43 code.put("\t{\r\n"); 44 code.put("\t\treturn super.serialize!(typeof(this))(this, method);\r\n"); 45 code.put("\t}\r\n"); 46 code.put("}\r\n"); 47 code.put("\r\n"); 48 } 49 50 return code.data; 51 } 52 53 /// lexer 54 55 enum TokenType 56 { 57 Define = 1, // message 58 Keyword = 2, // type: int8... 59 Identifier = 3, 60 SentenceEnd = 110, // ; 61 DelimiterOpen = 120, // { 62 DelimiterClose = 121 // } 63 } 64 65 private const string[] keywords = [ 66 "int8", "uint8", "int16", "uint16", "int32", "uint32", "int64", "uint64", 67 "float32", "float64", "float128", "bool", "char", "string" 68 ]; 69 70 struct Token 71 { 72 TokenType type; 73 string name; 74 75 this(string name) 76 { 77 if (name == "message") 78 { 79 this.type = TokenType.Define; 80 } 81 else if (keywords.any!(x => x == name)) 82 { 83 this.type = TokenType.Keyword; 84 } 85 else 86 { 87 this.type = TokenType.Identifier; 88 } 89 90 this.name = name; 91 } 92 93 this(TokenType type, string name) 94 { 95 this.type = type; 96 this.name = name; 97 } 98 } 99 100 Token[] lexer(string source) 101 { 102 /* State transition diagram: 103 0: none 1: word 2: { 3: ; 4: } 104 -1: / -2: // -3: /* 105 106 0 -> \s[ \f\n\r\t\v] 0 107 -> A..Za..z_ 1 108 -> { 2 -> add token -> 0 109 -> ; 3 -> add token -> 0 110 -> } 4 -> add token -> 0 111 -> / hang state, -1 112 -> other Exception 113 1 -> \s[ \f\n\r\t\v] 1 -> add token -> 0 114 -> A..Za..z0..9_ 1 115 -> { 1 -> add token -> 2 -> add token -> 0 116 -> ; 1 -> add token -> 3 -> add token -> 0 117 -> } 1 -> add token -> 4 -> add token -> 0 118 -> / hang state, -1 119 -> other Exception 120 2 -> 0 121 3 -> 0 122 4 -> 0 123 -1 -> / -2 124 -> * -3 125 -> other Exception 126 -2 -> \n restore state, hang = 0 127 -> other skip 128 -3 -> / if last is * then restore state & hang = 0, else skip 129 -> other skip 130 */ 131 132 Token[] tokens; 133 int state = 0; 134 int stateHang; 135 char last; 136 string token = string.init; 137 138 source ~= "\r\n"; 139 foreach (i, ch; source) 140 { 141 switch (state) 142 { 143 case 0: 144 if (isWhite(ch)) 145 continue; 146 else if (isIdentifierFirstChar(ch)) 147 { 148 token = ch.to!string; 149 state = 1; 150 } 151 else if (ch == '{') 152 { 153 tokens ~= Token(TokenType.DelimiterOpen, "{"); 154 state = 0; 155 } 156 else if (ch == ';') 157 { 158 tokens ~= Token(TokenType.SentenceEnd, ";"); 159 state = 0; 160 } 161 else if (ch == '}') 162 { 163 tokens ~= Token(TokenType.DelimiterClose, "}"); 164 state = 0; 165 } 166 else if (ch == '/') 167 { 168 stateHang = state; 169 state = -1; 170 } 171 else 172 { 173 enforce(0, "Invalid character: " ~ ch.to!string); 174 } 175 break; 176 case 1: 177 if (isWhite(ch)) 178 { 179 tokens ~= Token(token); 180 token = string.init; 181 state = 0; 182 } 183 else if (isIdentifierChar(ch)) 184 { 185 token ~= ch.to!string; 186 } 187 else if (ch == '{') 188 { 189 tokens ~= Token(token); 190 tokens ~= Token(TokenType.DelimiterOpen, "{"); 191 token = string.init; 192 state = 0; 193 } 194 else if (ch == ';') 195 { 196 tokens ~= Token(token); 197 tokens ~= Token(TokenType.SentenceEnd, ";"); 198 token = string.init; 199 state = 0; 200 } 201 else if (ch == '}') 202 { 203 tokens ~= Token(token); 204 tokens ~= Token(TokenType.DelimiterClose, "}"); 205 token = string.init; 206 state = 0; 207 } 208 else if (ch == '/') 209 { 210 stateHang = state; 211 state = -1; 212 } 213 else 214 { 215 enforce(0, "Invalid character: " ~ ch.to!string); 216 } 217 break; 218 case -1: 219 if (ch == '/') 220 { 221 state = -2; 222 } 223 else if (ch == '*') 224 { 225 state = -3; 226 } 227 else 228 { 229 enforce(0, "Invalid character: " ~ ch.to!string); 230 } 231 break; 232 case -2: 233 if (ch == '\n') 234 { 235 state = stateHang; 236 stateHang = 0; 237 } 238 else 239 { 240 continue; 241 } 242 break; 243 case -3: 244 if ((ch == '/') && (last == '*')) 245 { 246 state = stateHang; 247 stateHang = 0; 248 } 249 else 250 { 251 continue; 252 } 253 break; 254 default: 255 break; 256 } 257 258 last = ch; 259 } 260 261 return tokens; 262 } 263 264 private bool isIdentifierFirstChar(char ch) 265 { 266 return isAlpha(ch) || ch == '_'; 267 } 268 269 private bool isIdentifierChar(char ch) 270 { 271 return isAlphaNum(ch) || ch == '_'; 272 } 273 274 private string extractScriptfragment(string source) 275 { 276 string ret = string.init; 277 278 foreach (ch; source) 279 { 280 if (ret.length >= 50) 281 break; 282 if (!isWhite(ch)) 283 ret ~= ch.to!string; 284 else if ((ret.length > 0) && (ret[$ - 1] != ' ')) 285 ret ~= " "; 286 } 287 288 return ret; 289 } 290 291 /// parser 292 293 struct Field 294 { 295 string type; 296 string name; 297 } 298 299 struct Sentence 300 { 301 string name; 302 Field[] fields; 303 } 304 305 Sentence[] parser(Token[] tokens) 306 { 307 Sentence[] sentences; 308 int pos; 309 while (pos < cast(int)tokens.length - 1) 310 { 311 if (tokens[pos].type != TokenType.Define) 312 { 313 enforce(0, "Syntax error at " ~ tokens[pos].name); 314 } 315 316 sentences ~= parser_define(tokens, pos); 317 } 318 319 return sentences; 320 } 321 322 private Sentence parser_define(Token[] tokens, ref int pos) 323 { 324 if ((cast(int)tokens.length - pos < 4) || (tokens[pos].type != TokenType.Define) || (tokens[pos + 1].type != TokenType.Identifier) || (tokens[pos + 2].type != TokenType.DelimiterOpen)) 325 { 326 enforce(0, "Syntax error at " ~ tokens[pos].name); 327 } 328 329 Sentence sentence; 330 sentence.name = tokens[pos + 1].name; 331 pos += 3; 332 333 while (pos < tokens.length) 334 { 335 Nullable!Field field = parser_field(tokens, pos); 336 337 if (field.isNull) 338 return sentence; 339 340 sentence.fields ~= field; 341 } 342 343 return sentence; 344 } 345 346 private Nullable!Field parser_field(Token[] tokens, ref int pos) 347 { 348 if ((cast(int)tokens.length - pos >= 1) && (tokens[pos].type == TokenType.DelimiterClose)) 349 { 350 pos++; 351 return Nullable!Field(); 352 } 353 354 if ((cast(int)tokens.length - pos < 3) || (tokens[pos].type != TokenType.Keyword) 355 || (tokens[pos + 1].type != TokenType.Identifier) 356 || (tokens[pos + 2].type != TokenType.SentenceEnd)) 357 { 358 enforce(0, "Syntax error at " ~ tokens[pos].name); 359 } 360 361 Field field; 362 field.type = tokens[pos].name; 363 field.name = tokens[pos + 1].name; 364 pos += 3; 365 366 return Nullable!Field(field); 367 } 368 369 /* 370 import buffer; 371 372 final static class Sample : Message 373 { 374 string name; 375 int32 age; 376 int16 sex; 377 378 ubyte[] serialize(string method = string.init) 379 { 380 return super.serialize!(typeof(this))(this, method); 381 } 382 } 383 */