| 1 |
/++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
|---|
| 2 |
This is a template meta-program that generates a recursive decent |
|---|
| 3 |
parser. It is to be mixed into a class or struct. The parameters |
|---|
| 4 |
for the template are: a string containing almost normal BNF |
|---|
| 5 |
reduction definitions, and a string defining the start rule. |
|---|
| 6 |
|
|---|
| 7 |
It extends BNF by allowing "|" for "or" but doesn't implement |
|---|
| 8 |
optional or repeated terms. |
|---|
| 9 |
|
|---|
| 10 |
$(TABLE |
|---|
| 11 |
$(TR $(TD Grammar) $(TD ::= Rule Grammar | Rule)) |
|---|
| 12 |
$(TR $(TD Rule) $(TD ::= ID ":" Cases ";")) |
|---|
| 13 |
$(TR $(TD Cases) $(TD ::= Case "|" Alt | Case)) |
|---|
| 14 |
$(TR $(TD Case) $(TD ::= ID "/" Sequence)) |
|---|
| 15 |
$(TR $(TD Sequence) $(TD ::= ID Sequence | ID)) |
|---|
| 16 |
$(TR $(TD Item) $(TD ::= ID "[?+*]?";)) |
|---|
| 17 |
$(TR $(TD ID) $(TD ::= "[A-Za-z][A-Za-z0-9]*";)) |
|---|
| 18 |
) |
|---|
| 19 |
|
|---|
| 20 |
The term between the ':' and '/' is the name of the reduction |
|---|
| 21 |
action. If a reduction is used but not defined a terminal must |
|---|
| 22 |
be defined for it. |
|---|
| 23 |
|
|---|
| 24 |
Example: |
|---|
| 25 |
|
|---|
| 26 |
----------------------------------------- |
|---|
| 27 |
struct set |
|---|
| 28 |
{ |
|---|
| 29 |
/******* action code ********/ |
|---|
| 30 |
static PObject Action(char[] string : "MULTIPLY")(PObject[3] set) |
|---|
| 31 |
{...} |
|---|
| 32 |
static PObject Action(char[] string : "SUM" )(PObject[3] set) |
|---|
| 33 |
{...} |
|---|
| 34 |
static PObject Action(char[] string : "PASS" )(PObject[1] set) |
|---|
| 35 |
{...} |
|---|
| 36 |
|
|---|
| 37 |
/******** Terminal code *********/ |
|---|
| 38 |
static PObject Terminal(char[] name : "NUM")(IParser p) |
|---|
| 39 |
{...} |
|---|
| 40 |
static PObject Terminal(char[] name : "PLUS")(IParser p) |
|---|
| 41 |
{...} |
|---|
| 42 |
static PObject Terminal(char[] name : "TIMES")(IParser p) |
|---|
| 43 |
{...} |
|---|
| 44 |
|
|---|
| 45 |
// "ADD" indicates the root rule |
|---|
| 46 |
|
|---|
| 47 |
mixin Parser!("ADD", ReduceWhite( |
|---|
| 48 |
"PRIMARY : PASS/ NUM; |
|---|
| 49 |
MUL : MULTIPLY/ PRIMARY TIMES MUL |
|---|
| 50 |
| PASS/ PRIMARY; |
|---|
| 51 |
ADD : SUM/ MUL PLUS ADD |
|---|
| 52 |
| PASS/ MUL; |
|---|
| 53 |
")); |
|---|
| 54 |
} |
|---|
| 55 |
|
|---|
| 56 |
void main() |
|---|
| 57 |
{ |
|---|
| 58 |
auto a = new ExpGrammer; |
|---|
| 59 |
a.data = "1 + 3 * 4 + 5 * 5 "; |
|---|
| 60 |
|
|---|
| 61 |
Value v = cast(Value)set.Parser(a); |
|---|
| 62 |
|
|---|
| 63 |
assert(v !is null,"ERR0R"); |
|---|
| 64 |
writef("%d\n", v.value); |
|---|
| 65 |
} |
|---|
| 66 |
---------------- |
|---|
| 67 |
|
|---|
| 68 |
This meta program is distributed with no warranty of any kind. |
|---|
| 69 |
|
|---|
| 70 |
Author: Benjamin Shropshire (shro8822drop_this at vandals DOT uidaho edu) |
|---|
| 71 |
|
|---|
| 72 |
Please give credit where credit is due. Don't remove this notice. |
|---|
| 73 |
|
|---|
| 74 |
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++/ |
|---|
| 75 |
module syntax.dparse; |
|---|
| 76 |
|
|---|
| 77 |
// Add not grammer, might use non-Action parser |
|---|
| 78 |
private import std.stdio : writef; |
|---|
| 79 |
private import std.string : ToString = toString ; |
|---|
| 80 |
|
|---|
| 81 |
private import glue.templates; |
|---|
| 82 |
|
|---|
| 83 |
|
|---|
| 84 |
/****************************************************************************** |
|---|
| 85 |
********* PObject Hieracrcy |
|---|
| 86 |
******************************************************************************/ |
|---|
| 87 |
|
|---|
| 88 |
|
|---|
| 89 |
/** Base class for parsed objects |
|---|
| 90 |
*/ |
|---|
| 91 |
abstract class PObject |
|---|
| 92 |
{ |
|---|
| 93 |
/// |
|---|
| 94 |
abstract bool fail(); |
|---|
| 95 |
|
|---|
| 96 |
abstract char[] BaseName(); |
|---|
| 97 |
} |
|---|
| 98 |
|
|---|
| 99 |
|
|---|
| 100 |
/************************************************ |
|---|
| 101 |
Common base type for ObjectVector types |
|---|
| 102 |
*/ |
|---|
| 103 |
class PObjectVectorBase : PObject |
|---|
| 104 |
{ |
|---|
| 105 |
char[] BaseName(){return typeof(this).stringof;} |
|---|
| 106 |
abstract char[] string(); |
|---|
| 107 |
} |
|---|
| 108 |
|
|---|
| 109 |
/************************************************ |
|---|
| 110 |
A template sub class of PObject for basic tree building |
|---|
| 111 |
*/ |
|---|
| 112 |
class PObjectVector(uint i) : PObjectVectorBase |
|---|
| 113 |
{ |
|---|
| 114 |
char[] BaseName(){return typeof(this).stringof;} |
|---|
| 115 |
alias Tupleof!(i,PObject) type; |
|---|
| 116 |
type data; |
|---|
| 117 |
/// constructor taking i PObjects |
|---|
| 118 |
this(type s) |
|---|
| 119 |
{ |
|---|
| 120 |
foreach(int j, t; type) |
|---|
| 121 |
data[j] = s[j]; |
|---|
| 122 |
} |
|---|
| 123 |
|
|---|
| 124 |
this(PObject[i] d) |
|---|
| 125 |
{ |
|---|
| 126 |
foreach(uint j, v; data) |
|---|
| 127 |
{ |
|---|
| 128 |
data[j] = d[j]; |
|---|
| 129 |
} |
|---|
| 130 |
} |
|---|
| 131 |
|
|---|
| 132 |
PObject[] Get() |
|---|
| 133 |
{ |
|---|
| 134 |
PObject[i] ret; |
|---|
| 135 |
foreach(uint j, v; data) |
|---|
| 136 |
{ |
|---|
| 137 |
ret[j] = data[j]; |
|---|
| 138 |
} |
|---|
| 139 |
return ret.dup; |
|---|
| 140 |
} |
|---|
| 141 |
|
|---|
| 142 |
/// Get the j'th item |
|---|
| 143 |
PObject Get(uint j) |
|---|
| 144 |
{ |
|---|
| 145 |
switch(j) |
|---|
| 146 |
{ |
|---|
| 147 |
foreach(int k, t; type) |
|---|
| 148 |
case k: return data[k]; |
|---|
| 149 |
default: throw new Error("Out of bound"); |
|---|
| 150 |
} |
|---|
| 151 |
} |
|---|
| 152 |
|
|---|
| 153 |
/// Get the j'th item |
|---|
| 154 |
PObject GetT(uint j)() |
|---|
| 155 |
{ |
|---|
| 156 |
static if(j<data.length) |
|---|
| 157 |
return data[j]; |
|---|
| 158 |
else |
|---|
| 159 |
static assert(false); |
|---|
| 160 |
} |
|---|
| 161 |
|
|---|
| 162 |
char[] string() |
|---|
| 163 |
{ |
|---|
| 164 |
char[] ret = "["; |
|---|
| 165 |
|
|---|
| 166 |
foreach(i,_;type) |
|---|
| 167 |
{ |
|---|
| 168 |
if(auto v = cast(PObjectVectorBase)data[i]) |
|---|
| 169 |
{ |
|---|
| 170 |
ret ~= " " ~ v.string(); |
|---|
| 171 |
} |
|---|
| 172 |
else if(auto v = cast(PObjectBoxBase)data[i]) |
|---|
| 173 |
{ |
|---|
| 174 |
ret ~= " \"" ~ v.string()~\"; |
|---|
| 175 |
} |
|---|
| 176 |
else ret ~= " <UNKNOWN>"; |
|---|
| 177 |
} |
|---|
| 178 |
return ret ~ "]"; |
|---|
| 179 |
} |
|---|
| 180 |
|
|---|
| 181 |
bool fail() { return false; } |
|---|
| 182 |
} |
|---|
| 183 |
|
|---|
| 184 |
/************************************************ |
|---|
| 185 |
Common base type for BoxObject types |
|---|
| 186 |
*/ |
|---|
| 187 |
class PObjectBoxBase : PObject |
|---|
| 188 |
{ |
|---|
| 189 |
char[] BaseName(){return typeof(this).stringof;} |
|---|
| 190 |
abstract char[] string(); |
|---|
| 191 |
} |
|---|
| 192 |
|
|---|
| 193 |
/** |
|---|
| 194 |
*/ |
|---|
| 195 |
class PObjectBox(T,bool str = true) : PObjectBoxBase |
|---|
| 196 |
{ |
|---|
| 197 |
char[] BaseName(){return typeof(this).stringof~"!("~T.stringof~")";} |
|---|
| 198 |
T t; |
|---|
| 199 |
|
|---|
| 200 |
/// |
|---|
| 201 |
T Get() { return t; } |
|---|
| 202 |
|
|---|
| 203 |
/// |
|---|
| 204 |
this(T ti){t=ti;} |
|---|
| 205 |
|
|---|
| 206 |
bool fail() { return false; } |
|---|
| 207 |
|
|---|
| 208 |
/// |
|---|
| 209 |
char[] string() |
|---|
| 210 |
{ |
|---|
| 211 |
static if(str) |
|---|
| 212 |
{ |
|---|
| 213 |
static if(is(T == char[])) |
|---|
| 214 |
return t; |
|---|
| 215 |
else static if(is(ToString(t))) |
|---|
| 216 |
return ToString(t); |
|---|
| 217 |
else |
|---|
| 218 |
return "<<"~T.stringof~">>"; |
|---|
| 219 |
} |
|---|
| 220 |
else |
|---|
| 221 |
return "<<"~T.stringof~">>"; |
|---|
| 222 |
} |
|---|
| 223 |
} |
|---|
| 224 |
//typedef PObjectVector!(5) Five; |
|---|
| 225 |
|
|---|
| 226 |
/** |
|---|
| 227 |
*/ |
|---|
| 228 |
class PObjectList(T) : PObject |
|---|
| 229 |
{ |
|---|
| 230 |
char[] BaseName(){return typeof(this).stringof;} |
|---|
| 231 |
debug(TypeReport) pragma(msg, ">>T-"__FILE__~":"~itoa!(__LINE__)~": "~typeof(this).stringof~"!("~T.stringof~")"); |
|---|
| 232 |
|
|---|
| 233 |
T[] list; |
|---|
| 234 |
int at=0; |
|---|
| 235 |
|
|---|
| 236 |
this(){ list = null; } |
|---|
| 237 |
|
|---|
| 238 |
this(T ti) |
|---|
| 239 |
{ |
|---|
| 240 |
static if(is(T :Object)) assert(ti !is null); |
|---|
| 241 |
list = new T[5]; |
|---|
| 242 |
list[at] = ti; |
|---|
| 243 |
at++; |
|---|
| 244 |
|
|---|
| 245 |
} |
|---|
| 246 |
|
|---|
| 247 |
/// Add a T |
|---|
| 248 |
uint Add(T ti) |
|---|
| 249 |
{ |
|---|
| 250 |
static if(is(T :Object)) assert(ti !is null); |
|---|
| 251 |
if(list.length <= at) list.length = at + 5; |
|---|
| 252 |
list[at] = ti; |
|---|
| 253 |
at++; |
|---|
| 254 |
return at-1; |
|---|
| 255 |
} |
|---|
| 256 |
|
|---|
| 257 |
/// return the number of items stored |
|---|
| 258 |
uint Count(){return at;} |
|---|
| 259 |
|
|---|
| 260 |
/// |
|---|
| 261 |
T[] get() |
|---|
| 262 |
{ |
|---|
| 263 |
return list[0..at]; |
|---|
| 264 |
} |
|---|
| 265 |
|
|---|
| 266 |
bool fail() { return false; } |
|---|
| 267 |
} |
|---|
| 268 |
unittest |
|---|
| 269 |
{ |
|---|
| 270 |
writef("unittest@"__FILE__":"~itoa!(__LINE__)~\n); |
|---|
| 271 |
|
|---|
| 272 |
auto o = new PObjectList!(int); |
|---|
| 273 |
o.Add(1); |
|---|
| 274 |
o.Add(2); |
|---|
| 275 |
o.Add(3); |
|---|
| 276 |
o.Add(4); |
|---|
| 277 |
o.Add(5); |
|---|
| 278 |
o.Add(6); |
|---|
| 279 |
|
|---|
| 280 |
assert(o.get == [1,2,3,4,5,6], "PObjectList failed"); |
|---|
| 281 |
} |
|---|
| 282 |
|
|---|
| 283 |
/************************************************ |
|---|
| 284 |
List Object that build to the left |
|---|
| 285 |
*/ |
|---|
| 286 |
class PObjectListLeft(T) : PObjectList!(T) |
|---|
| 287 |
{ |
|---|
| 288 |
char[] BaseName(){return typeof(this).stringof;} |
|---|
| 289 |
debug(TypeReport) pragma(msg, ">>T-"__FILE__~":"~itoa!(__LINE__)~": "~typeof(this).stringof~"!("~T.stringof~")"); |
|---|
| 290 |
|
|---|
| 291 |
this(T t) |
|---|
| 292 |
{ |
|---|
| 293 |
list.length = 5; |
|---|
| 294 |
list[$-1] = t; |
|---|
| 295 |
at = 1; |
|---|
| 296 |
} |
|---|
| 297 |
this(){} |
|---|
| 298 |
|
|---|
| 299 |
/// Add a T |
|---|
| 300 |
uint Add(T ti) |
|---|
| 301 |
{ |
|---|
| 302 |
static if(is(T :Object)) assert(ti !is null); |
|---|
| 303 |
|
|---|
| 304 |
if(list.length <= at) |
|---|
| 305 |
{ |
|---|
| 306 |
auto t = list[$-at..$].dup; |
|---|
| 307 |
list.length = at + 5; |
|---|
| 308 |
list[$-at..$] = t; |
|---|
| 309 |
} |
|---|
| 310 |
list[$-1-at] = ti; |
|---|
| 311 |
at++; |
|---|
| 312 |
return at-1; |
|---|
| 313 |
} |
|---|
| 314 |
|
|---|
| 315 |
T[] get() |
|---|
| 316 |
{ |
|---|
| 317 |
return list[$-at..$]; |
|---|
| 318 |
} |
|---|
| 319 |
} |
|---|
| 320 |
|
|---|
| 321 |
unittest |
|---|
| 322 |
{ |
|---|
| 323 |
writef("unittest@"__FILE__":"~itoa!(__LINE__)~\n); |
|---|
| 324 |
|
|---|
| 325 |
auto o = new PObjectListLeft!(int)( 0); |
|---|
| 326 |
o.Add( 1); o.Add( 2); o.Add( 3); o.Add( 4); o.Add( 5); o.Add( 6); o.Add( 7); o.Add( 8); o.Add( 9); |
|---|
| 327 |
o.Add(10); o.Add(11); o.Add(12); o.Add(13); o.Add(14); o.Add(15); o.Add(16); o.Add(17); o.Add(18); o.Add(19); |
|---|
| 328 |
o.Add(20); o.Add(21); o.Add(22); o.Add(23); o.Add(24); o.Add(25); o.Add(26); o.Add(27); o.Add(28); o.Add(29); |
|---|
| 329 |
|
|---|
| 330 |
assert(o.get == [29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0], "PObjectListLeft failed"); |
|---|
| 331 |
} |
|---|
| 332 |
|
|---|
| 333 |
|
|---|
| 334 |
/************************************************ |
|---|
| 335 |
A sub class of PObject that is used for repeated reductions |
|---|
| 336 |
*/ |
|---|
| 337 |
class PObjectSet : PObjectList!(PObject) |
|---|
| 338 |
{ |
|---|
| 339 |
char[] BaseName(){return typeof(this).stringof;} |
|---|
| 340 |
/// Discard some PObjects |
|---|
| 341 |
void Back(uint b) |
|---|
| 342 |
{ |
|---|
| 343 |
at = b; |
|---|
| 344 |
} |
|---|
| 345 |
} |
|---|
| 346 |
|
|---|
| 347 |
|
|---|
| 348 |
|
|---|
| 349 |
/************************************************ |
|---|
| 350 |
Uniform term to prevent needing to know the |
|---|
| 351 |
exact type at compile time. |
|---|
| 352 |
*/ |
|---|
| 353 |
interface PInterfaceLeftFactor(T) { PObject InsertLeft(T,PObject); } |
|---|
| 354 |
|
|---|
| 355 |
/************************************************ |
|---|
| 356 |
PObject that uses T.Action!(str) to process a |
|---|
| 357 |
left side term with a Right side term |
|---|
| 358 |
*/ |
|---|
| 359 |
class PObjectLeftFactorT(Tp, int i, char[] str) : PObjectVector!(i), PInterfaceLeftFactor!(Tp) |
|---|
| 360 |
{ |
|---|
| 361 |
this(PObject[i] d){super(d);} |
|---|
| 362 |
|
|---|
| 363 |
PObject InsertLeft(Tp that, PObject L) |
|---|
| 364 |
{ |
|---|
| 365 |
PObject[i+1] args; |
|---|
| 366 |
|
|---|
| 367 |
args[1..$] = Get; |
|---|
| 368 |
args[0] = L; |
|---|
| 369 |
|
|---|
| 370 |
return SpecialAction!(Tp,str)(that,args); |
|---|
| 371 |
} |
|---|
| 372 |
} |
|---|
| 373 |
|
|---|
| 374 |
|
|---|
| 375 |
/************************************************ |
|---|
| 376 |
default fail object |
|---|
| 377 |
*/ |
|---|
| 378 |
class PObjectFail : PObject |
|---|
| 379 |
{ |
|---|
| 380 |
char[] BaseName(){return typeof(this).stringof;} |
|---|
| 381 |
char[][] msg; |
|---|
| 382 |
|
|---|
| 383 |
this() |
|---|
| 384 |
{ |
|---|
| 385 |
msg.length = 1; |
|---|
| 386 |
msg[0] = "Failed"; |
|---|
| 387 |
} |
|---|
| 388 |
|
|---|
| 389 |
this(char[] m) |
|---|
| 390 |
{ |
|---|
| 391 |
msg.length = 1; |
|---|
| 392 |
msg[0] = m.dup; |
|---|
| 393 |
} |
|---|
| 394 |
|
|---|
| 395 |
void Add(char[] m) |
|---|
| 396 |
{ |
|---|
| 397 |
msg ~= m.dup; |
|---|
| 398 |
} |
|---|
| 399 |
|
|---|
| 400 |
bool fail() { return true; } |
|---|
| 401 |
} |
|---|
| 402 |
|
|---|
| 403 |
/************************************************ |
|---|
| 404 |
default Pass object |
|---|
| 405 |
*/ |
|---|
| 406 |
class PObjectPass : PObject |
|---|
| 407 |
{ |
|---|
| 408 |
char[] BaseName(){return typeof(this).stringof;} |
|---|
| 409 |
this() { } |
|---|
| 410 |
bool fail() { return false; } |
|---|
| 411 |
} |
|---|
| 412 |
|
|---|
| 413 |
/************************************************ |
|---|
| 414 |
default Filler object, returns fail state as instructed |
|---|
| 415 |
*/ |
|---|
| 416 |
class PObjectFill : PObject |
|---|
| 417 |
{ |
|---|
| 418 |
char[] BaseName(){return typeof(this).stringof;} |
|---|
| 419 |
bool b; |
|---|
| 420 |
this(bool _b){b=_b;} |
|---|
| 421 |
bool fail(){return b;} |
|---|
| 422 |
} |
|---|
| 423 |
|
|---|
| 424 |
/******************************************************* |
|---|
| 425 |
interface to handle parser data |
|---|
| 426 |
*/ |
|---|
| 427 |
interface IParser |
|---|
| 428 |
{ |
|---|
| 429 |
uint pos(); |
|---|
| 430 |
void pos(uint); |
|---|
| 431 |
debug(dParse_runtime) void mark(); |
|---|
| 432 |
} |
|---|
| 433 |
|
|---|
| 434 |
|
|---|
| 435 |
/****************************************************************************** |
|---|
| 436 |
******** genaric Template code |
|---|
| 437 |
******************************************************************************/ |
|---|
| 438 |
|
|---|
| 439 |
|
|---|
| 440 |
/** generate a tuple of i U's |
|---|
| 441 |
*/ |
|---|
| 442 |
template Tupleof(uint i, U) |
|---|
| 443 |
{ |
|---|
| 444 |
static if(i == 0) |
|---|
| 445 |
alias T!() Tupleof; |
|---|
| 446 |
else |
|---|
| 447 |
static if(i == 1) |
|---|
| 448 |
alias T!(U) Tupleof; |
|---|
| 449 |
else |
|---|
| 450 |
alias T!(Tupleof!(i-1, U), U) Tupleof; |
|---|
| 451 |
} |
|---|
| 452 |
|
|---|
| 453 |
/** Tuple literal template |
|---|
| 454 |
*/ |
|---|
| 455 |
template T(A...){alias A T;} |
|---|
| 456 |
|
|---|
| 457 |
|
|---|
| 458 |
/****************************************************************************** |
|---|
| 459 |
******** CTFE functions |
|---|
| 460 |
******************************************************************************/ |
|---|
| 461 |
|
|---|
| 462 |
/****************************** |
|---|
| 463 |
drop leading whitespace |
|---|
| 464 |
*/ |
|---|
| 465 |
char[] DropWhiteF(char[] str) |
|---|
| 466 |
{ |
|---|
| 467 |
|
|---|
| 468 |
foreach(int i, char c; str) |
|---|
| 469 |
switch(c) |
|---|
| 470 |
{ |
|---|
| 471 |
case ' ', '\t', '\r', '\n': |
|---|
| 472 |
continue; |
|---|
| 473 |
default: |
|---|
| 474 |
return str[i..$]; |
|---|
| 475 |
} |
|---|
| 476 |
return ""; |
|---|
| 477 |
} |
|---|
| 478 |
struct UnittetDropWhite{ |
|---|
| 479 |
static const char[] str = DropWhiteF(" \t\n\rhello"); |
|---|
| 480 |
static assert(DropWhiteF(" \t\n\rhello") == "hello"); |
|---|
| 481 |
} |
|---|
| 482 |
|
|---|
| 483 |
|
|---|
| 484 |
/********************************** |
|---|
| 485 |
find first instance of t in str, |
|---|
| 486 |
return str up through that char |
|---|
| 487 |
*/ |
|---|
| 488 |
char[] FindChar(char t)(char[] str) |
|---|
| 489 |
{ |
|---|
| 490 |
foreach(int i, char c; str) |
|---|
| 491 |
if(c == t) |
|---|
| 492 |
return str[0..i+1]; |
|---|
| 493 |
return str; |
|---|
| 494 |
} |
|---|
| 495 |
|
|---|
| 496 |
|
|---|
| 497 |
/********************************** |
|---|
| 498 |
return an identifier |
|---|
| 499 |
*/ |
|---|
| 500 |
char[] GetID(char[] instr) |
|---|
| 501 |
{ |
|---|
| 502 |
char[] str = DropWhiteF(instr); |
|---|
| 503 |
|
|---|
| 504 |
if(str.length == 0) return ""; |
|---|
| 505 |
|
|---|
| 506 |
if( |
|---|
| 507 |
!('a' <= str[0] && str[0] <= 'z') && |
|---|
| 508 |
!('A' <= str[0] && str[0] <= 'Z') && |
|---|
| 509 |
!('_' == str[0]) |
|---|
| 510 |
) return ""; |
|---|
| 511 |
|
|---|
| 512 |
foreach(int i, char c; str) |
|---|
| 513 |
if( |
|---|
| 514 |
!('a' <= c && c <= 'z') && |
|---|
| 515 |
!('A' <= c && c <= 'Z') && |
|---|
| 516 |
!('0' <= c && c <= '9') && |
|---|
| 517 |
!('_' == c) |
|---|
| 518 |
) return str[0..i]; |
|---|
| 519 |
|
|---|
| 520 |
return str; |
|---|
| 521 |
} |
|---|
| 522 |
struct UnittetGetID{ |
|---|
| 523 |
static assert(GetID("hello world") == "hello", GetID("hello world")); |
|---|
| 524 |
} |
|---|
| 525 |
|
|---|
| 526 |
/********************************* |
|---|
| 527 |
replace all sequences of white |
|---|
| 528 |
space with single spaces |
|---|
| 529 |
*/ |
|---|
| 530 |
public char[] ReduceWhite(char[] str) |
|---|
| 531 |
{ |
|---|
| 532 |
bool pass = true; |
|---|
| 533 |
int at = 0; |
|---|
| 534 |
foreach(char c; str) |
|---|
| 535 |
switch(c) |
|---|
| 536 |
{ |
|---|
| 537 |
case ' ', '\n', '\r', '\t': |
|---|
| 538 |
if(!pass) |
|---|
| 539 |
{ |
|---|
| 540 |
pass = true; |
|---|
| 541 |
str[at++] = ' '; |
|---|
| 542 |
} |
|---|
| 543 |
break; |
|---|
| 544 |
|
|---|
| 545 |
default: |
|---|
| 546 |
pass = false; |
|---|
| 547 |
str[at++] = c; |
|---|
| 548 |
break; |
|---|
| 549 |
} |
|---|
| 550 |
return str[0..at];; |
|---|
| 551 |
} |
|---|
| 552 |
static const char[] test = ReduceWhite("hello"); |
|---|
| 553 |
|
|---|
| 554 |
/********************************* |
|---|
| 555 |
Extract # from "$(C,#,NAME)" |
|---|
| 556 |
*/ |
|---|
| 557 |
int ExtractCount(char[] str) |
|---|
| 558 |
{ |
|---|
| 559 |
int ret = int.min; |
|---|
| 560 |
|
|---|
| 561 |
if(str.length > 4 && '0' <= str[0] && str[0] <= '9') |
|---|
| 562 |
{ |
|---|
| 563 |
ret = str[0] - '0'; |
|---|
| 564 |
for(int i = 1; i<str.length; i++) |
|---|
| 565 |
{ |
|---|
| 566 |
if('0' <= str[i] && str[i] <= '9') |
|---|
| 567 |
{ |
|---|
| 568 |
ret *= 10; |
|---|
| 569 |
ret += str[i] - '0'; |
|---|
| 570 |
} |
|---|
| 571 |
else |
|---|
| 572 |
break; |
|---|
| 573 |
} |
|---|
| 574 |
} |
|---|
| 575 |
|
|---|
| 576 |
return ret; |
|---|
| 577 |
} |
|---|
| 578 |
|
|---|
| 579 |
/********************************* |
|---|
| 580 |
Extract NAME from "$(C,#,NAME)" |
|---|
| 581 |
*/ |
|---|
| 582 |
char[] ExtractAct(char[] str) |
|---|
| 583 |
{ |
|---|
| 584 |
int c, i; |
|---|
| 585 |
for(i=0; i < str.length && c < 2; i++) |
|---|
| 586 |
c += (str[i] == ','); |
|---|
| 587 |
|
|---|
| 588 |
str = str[i..$]; |
|---|
| 589 |
i=0; |
|---|
| 590 |
int d = 1; |
|---|
| 591 |
while(i < str.length && d > 0) |
|---|
| 592 |
{ |
|---|
| 593 |
d += (str[i] == '('); |
|---|
| 594 |
d -= (str[i] == ')'); |
|---|
| 595 |
i++; |
|---|
| 596 |
} |
|---|
| 597 |
|
|---|
| 598 |
return str[0..i-1]; |
|---|
| 599 |
} |
|---|
| 600 |
|
|---|
| 601 |
static assert(ExtractAct("$(T,1,$(N,1,abc,def))") == "$(N,1,abc,def)", ExtractAct("$(T,1,$(N,1,abc,def))")); |
|---|
| 602 |
|
|---|
| 603 |
/****************************************************************************** |
|---|
| 604 |
******* Special Action Code |
|---|
| 605 |
******************************************************************************/ |
|---|
| 606 |
|
|---|
| 607 |
/// Pack up stuff in a computed type of PObjectLeftFactorT Object |
|---|
| 608 |
PObject L_Action(Tp, char[] str ) (Tp, PObject[ExtractCount(str[4..$])] i) |
|---|
| 609 |
{ |
|---|
| 610 |
static const int c = ExtractCount(str[4..$]); |
|---|
| 611 |
static const char[] n = ExtractAct(str); |
|---|
| 612 |
static if(false) pragma(msg, str~" == $(L,"~c.stringof~","~n~")"); |
|---|
| 613 |
|
|---|
| 614 |
return new PObjectLeftFactorT!(Tp, c, n)(i); |
|---|
| 615 |
} |
|---|
| 616 |
|
|---|
| 617 |
/// Left Process a Leftmost term and right side terms list. |
|---|
| 618 |
PObject T_Action(Tp,char[] str) (Tp that, PObject[1+ExtractCount(str[4..$])] i) |
|---|
| 619 |
{ |
|---|
| 620 |
static const int num = ExtractCount(str[4..$]); |
|---|
| 621 |
static const char[] act = ExtractAct(str); |
|---|
| 622 |
static if(false) pragma(msg, str~" == $(T,"~num.stringof~","~act~")"); |
|---|
| 623 |
|
|---|
| 624 |
PObject[num] args = i[0..num]; |
|---|
| 625 |
auto ret = SpecialAction!(Tp,act)(that,args); |
|---|
| 626 |
|
|---|
| 627 |
auto listO = cast(PObjectList!(PObject)) i[num]; |
|---|
| 628 |
auto list = listO.get; |
|---|
| 629 |
|
|---|
| 630 |
foreach(obj; list) |
|---|
| 631 |
{ |
|---|
| 632 |
auto Tobj = cast(PInterfaceLeftFactor!(Tp))obj; |
|---|
| 633 |
ret = Tobj.InsertLeft(that,ret); |
|---|
| 634 |
} |
|---|
| 635 |
return ret; |
|---|
| 636 |
} |
|---|
| 637 |
|
|---|
| 638 |
|
|---|
| 639 |
template GetSize(Tp, char[] str) |
|---|
| 640 |
{ |
|---|
| 641 |
alias Tp.Action!(str) Fn; |
|---|
| 642 |
static if(is(typeof(Fn) Args == function)) |
|---|
| 643 |
const int size = Args[0].length; |
|---|
| 644 |
else |
|---|
| 645 |
static assert(false); |
|---|
| 646 |
} |
|---|
| 647 |
|
|---|
| 648 |
template GetArgs(Tp, char[] str) |
|---|
| 649 |
{ |
|---|
| 650 |
alias Tp.Action!(str) Fn; |
|---|
| 651 |
static if(is(typeof(Fn) Args == function)) |
|---|
| 652 |
{} |
|---|
| 653 |
else |
|---|
| 654 |
static assert(false); |
|---|
| 655 |
} |
|---|
| 656 |
|
|---|
| 657 |
/// Next actions |
|---|
| 658 |
|
|---|
| 659 |
template N_parts(char[] str) |
|---|
| 660 |
{ |
|---|
| 661 |
const int num = ExtractCount(str[4..$]); |
|---|
| 662 |
const char[] firstAct = ExtractAct(str[4..$]); |
|---|
| 663 |
const char[] s1 = str[FindChar!(',')(str[4..$]).length + 4..$]; |
|---|
| 664 |
const char[] secondAct = s1[FindChar!(',')(s1).length..$-1]; |
|---|
| 665 |
} |
|---|
| 666 |
|
|---|
| 667 |
//alias N_parts!("$(N,3,dummy,dummy)") NP; |
|---|
| 668 |
|
|---|
| 669 |
PObject N_Action(Tp,char[] str) (Tp that, GetArgs!(Tp,N_parts!(str).firstAct).Args i) |
|---|
| 670 |
{ |
|---|
| 671 |
alias N_parts!(str) P; |
|---|
| 672 |
static if(false) pragma(msg, str~" == $(N,"~P.num.stringof~","~P.act~")"); |
|---|
| 673 |
|
|---|
| 674 |
// pragma(msg,">"~str); pragma(msg,">"~P.firstAct); pragma(msg,">"~P.secondAct); |
|---|
| 675 |
|
|---|
| 676 |
PObject[] ret; |
|---|
| 677 |
ret[0] = SpecialAction!(Tp,P.firstAct)(that,i); |
|---|
| 678 |
ret[0] = SpecialAction!(Tp,P.secondAct)(that,ret); |
|---|
| 679 |
|
|---|
| 680 |
return ret[0]; |
|---|
| 681 |
} |
|---|
| 682 |
|
|---|
| 683 |
|
|---|
| 684 |
|
|---|
| 685 |
|
|---|
| 686 |
template SpecialAction(Tp,char[] str) |
|---|
| 687 |
{ |
|---|
| 688 |
// pragma(msg,str); |
|---|
| 689 |
static if(str.length > 3 && str[0..2] == "$(") |
|---|
| 690 |
{ |
|---|
| 691 |
static if(false) pragma(msg, str); |
|---|
| 692 |
static if(str[2] == 'T') |
|---|
| 693 |
alias T_Action!(Tp,str) SpecialAction; |
|---|
| 694 |
else static if(str[2] == 'L') |
|---|
| 695 |
alias L_Action!(Tp,str) SpecialAction; |
|---|
| 696 |
else static if(str[2] == 'N') |
|---|
| 697 |
{ |
|---|
| 698 |
alias N_Action!(Tp,str) SpecialAction; |
|---|
| 699 |
} |
|---|
| 700 |
else |
|---|
| 701 |
static assert (false, "unknown special action: "~str); |
|---|
| 702 |
} |
|---|
| 703 |
else |
|---|
| 704 |
{ |
|---|
| 705 |
|
|---|
| 706 |
PObject SpecialAction(Tp that, GetArgs!(Tp,str).Args i) |
|---|
| 707 |
{ |
|---|
| 708 |
return that.Action!(str)(i); |
|---|
| 709 |
} |
|---|
| 710 |
//static assert (false, "unknown special action: "~str); |
|---|
| 711 |
} |
|---|
| 712 |
} |
|---|
| 713 |
|
|---|
| 714 |
/****************************************************************************** |
|---|
| 715 |
******* Parser |
|---|
| 716 |
******************************************************************************/ |
|---|
| 717 |
|
|---|
| 718 |
|
|---|
| 719 |
/************************************************ |
|---|
| 720 |
Parse an Identifier name (ID ::= "[A-Za-z][A-Za-z0-9]*") |
|---|
| 721 |
*/ |
|---|
| 722 |
template Parse_ID (char[] str) |
|---|
| 723 |
{ |
|---|
| 724 |
private const char[] without = DropWhiteF(str); |
|---|
| 725 |
private const char[] text = GetID(without); |
|---|
| 726 |
const bool Match = (text.length != 0); |
|---|
| 727 |
static if(Match) |
|---|
| 728 |
{ |
|---|
| 729 |
const char[] Text = text; |
|---|
| 730 |
const char[] Remaining = without[Text.length .. $]; |
|---|
| 731 |
const char[] Used = str[0 .. $-Remaining.length]; |
|---|
| 732 |
} |
|---|
| 733 |
} |
|---|
| 734 |
struct UnittetParse_ID |
|---|
| 735 |
{ |
|---|
| 736 |
// Tests |
|---|
| 737 |
alias Parse_ID!(" \thello world") Parse_ID_test1; |
|---|
| 738 |
static assert(Parse_ID_test1.without =="hello world","Parse_ID failed: "~Parse_ID_test1.without); |
|---|
| 739 |
static assert(Parse_ID_test1.Remaining == " world", "Parse_ID failed: "~Parse_ID_test1.Remaining); |
|---|
| 740 |
static assert(Parse_ID_test1.Text == "hello", "Parse_ID failed: "~Parse_ID_test1.Text); |
|---|
| 741 |
static assert(Parse_ID_test1.Match, "Parse_ID failed: "); |
|---|
| 742 |
static assert(Parse_ID_test1.Used == " \thello", "Parse_ID failed: \""~Parse_ID_test1.Used~\"); |
|---|
| 743 |
|
|---|
| 744 |
alias Parse_ID!(" \t!hello world") Parse_ID_test2; |
|---|
| 745 |
static assert(!Parse_ID_test2.Match,"Parse_ID failed: "); |
|---|
| 746 |
} |
|---|
| 747 |
|
|---|
| 748 |
|
|---|
| 749 |
char[] MatchPairs(char I, char O)(char[] str) |
|---|
| 750 |
{ |
|---|
| 751 |
int i = 1; |
|---|
| 752 |
foreach(int j, char c; str) |
|---|
| 753 |
{ |
|---|
| 754 |
if (c == I) i++; |
|---|
| 755 |
if (c == O) i--; |
|---|
| 756 |
if (i == 0) return str[0..j+1]; |
|---|
| 757 |
} |
|---|
| 758 |
return ""; |
|---|
| 759 |
} |
|---|
| 760 |
|
|---|
| 761 |
|
|---|
| 762 |
/************************************************ |
|---|
| 763 |
Parse a Sepcial action name name |
|---|
| 764 |
'$([^)]*)' | |
|---|
| 765 |
'$[A-Z][A-Za-z0-9_]*' | |
|---|
| 766 |
'$' |
|---|
| 767 |
*/ |
|---|
| 768 |
template Parse_SpecialAct (char[] str) |
|---|
| 769 |
{ |
|---|
| 770 |
private const char[] without = DropWhiteF(str); |
|---|
| 771 |
//pragma(msg,">>"__FILE__":"~__LINE__.stringof[0..$-1]~": '"~without~\'); |
|---|
| 772 |
|
|---|
| 773 |
//pragma(msg,__LINE__.stringof~":"~without); |
|---|
| 774 |
|
|---|
| 775 |
static if(without.length < 4 || without[0..2] != "$(") |
|---|
| 776 |
{ |
|---|
| 777 |
pragma(msg,">>"__FILE__":"~__LINE__.stringof[0..$-1]~": unknown Special '"~without~"' from '"~str~\'); |
|---|
| 778 |
const bool Match = false; |
|---|
| 779 |
} |
|---|
| 780 |
else |
|---|
| 781 |
{ |
|---|
| 782 |
const char[] t = MatchPairs!('(',')')(without[2..$]); |
|---|
| 783 |
|
|---|
| 784 |
//pragma(msg,__LINE__.stringof~":"~t); |
|---|
| 785 |
const bool Match = (t != ""); |
|---|
| 786 |
static if(Match) |
|---|
| 787 |
{ |
|---|
| 788 |
const char[] Text = without[0..2+t.length]; |
|---|
| 789 |
const char[] Remaining = without[Text.length .. $]; |
|---|
| 790 |
const char[] Used = str[0 .. $-Remaining.length]; |
|---|
| 791 |
} |
|---|
| 792 |
} |
|---|
| 793 |
//pragma(msg,__LINE__.stringof~":"~Match.stringof); |
|---|
| 794 |
} |
|---|
| 795 |
|
|---|
| 796 |
struct UnittetParse_Parse_SpecialAct{ |
|---|
| 797 |
// Tests |
|---|
| 798 |
static assert(Parse_SpecialAct!("$(Ltree) ").Match); |
|---|
| 799 |
|
|---|
| 800 |
//static assert(!Parse_SpecialAct!("$a ").Match); |
|---|
| 801 |
//static assert(!Parse_SpecialAct!("a ").Match); |
|---|
| 802 |
} |
|---|
| 803 |
|
|---|
| 804 |
|
|---|
| 805 |
|
|---|
| 806 |
/************************************************ |
|---|
| 807 |
The types of rules |
|---|
| 808 |
*/ |
|---|
| 809 |
enum ItemType |
|---|
| 810 |
{ |
|---|
| 811 |
single, |
|---|
| 812 |
star, |
|---|
| 813 |
plus, |
|---|
| 814 |
optional |
|---|
| 815 |
} |
|---|
| 816 |
|
|---|
| 817 |
/************************************************ |
|---|
| 818 |
Parse a rule part (Item ::= ID "[?+*]?") |
|---|
| 819 |
*/ |
|---|
| 820 |
struct Parse_Item(char[] str) |
|---|
| 821 |
{ |
|---|
| 822 |
private alias Parse_ID!(str) Item; |
|---|
| 823 |
static if(Item.Match) |
|---|
| 824 |
{ |
|---|
| 825 |
/// test set if the rule matched |
|---|
| 826 |
static const bool Match = true; |
|---|
| 827 |
static const char[] Text = Item.Text; |
|---|
| 828 |
|
|---|
| 829 |
private static const char[] tmp = DropWhiteF(Item.Remaining); |
|---|
| 830 |
static if(tmp.length > 0) |
|---|
| 831 |
{ |
|---|
| 832 |
static if(tmp[0] == '*') |
|---|
| 833 |
{ |
|---|
| 834 |
static const char[] Remaining = tmp[1..$]; |
|---|
| 835 |
static const ItemType Type = ItemType.star; |
|---|
| 836 |
} |
|---|
| 837 |
else |
|---|
| 838 |
static if(tmp[0] == '+') |
|---|
| 839 |
{ |
|---|
| 840 |
static const char[] Remaining = tmp[1..$]; |
|---|
| 841 |
static const ItemType Type = ItemType.plus; |
|---|
| 842 |
} |
|---|
| 843 |
else |
|---|
| 844 |
static if(tmp[0] == '?') |
|---|
| 845 |
{ |
|---|
| 846 |
static const char[] Remaining = tmp[1..$]; |
|---|
| 847 |
static const ItemType Type = ItemType.optional; |
|---|
| 848 |
} |
|---|
| 849 |
else |
|---|
| 850 |
{ |
|---|
| 851 |
static const char[] Remaining = Item.Remaining; |
|---|
| 852 |
static const ItemType Type = ItemType.single; |
|---|
| 853 |
} |
|---|
| 854 |
} |
|---|
| 855 |
else |
|---|
| 856 |
{ |
|---|
| 857 |
static const char[] Remaining = Item.Remaining; |
|---|
| 858 |
static const ItemType Type = ItemType.single; |
|---|
| 859 |
} |
|---|
| 860 |
static const char[] Used = str[0 .. $-Remaining.length]; |
|---|
| 861 |
} |
|---|
| 862 |
else |
|---|
| 863 |
static const bool Match = false; |
|---|
| 864 |
} |
|---|
| 865 |
struct UnittestParse_Item |
|---|
| 866 |
{ |
|---|
| 867 |
// Tests |
|---|
| 868 |
alias Parse_Item!(" hello world") Parse_Item_test1; |
|---|
| 869 |
static assert(Parse_Item_test1.Match); |
|---|
| 870 |
static assert(Parse_Item_test1.Text == "hello"); |
|---|
| 871 |
static assert(Parse_Item_test1.Used == " hello", Parse_Item_test1.Used); |
|---|
| 872 |
static assert(Parse_Item_test1.Remaining == " world"); |
|---|
| 873 |
static assert(Parse_Item_test1.Type == ItemType.single); |
|---|
| 874 |
|
|---|
| 875 |
alias Parse_Item!(" hello +world") Parse_Item_test2; |
|---|
| 876 |
static assert(Parse_Item_test2.Match); |
|---|
| 877 |
static assert(Parse_Item_test2.Text == "hello"); |
|---|
| 878 |
static assert(Parse_Item_test2.Used == " hello +", Parse_Item_test1.Used); |
|---|
| 879 |
static assert(Parse_Item_test2.Remaining == "world"); |
|---|
| 880 |
static assert(Parse_Item_test2.Type == ItemType.plus); |
|---|
| 881 |
|
|---|
| 882 |
alias Parse_Item!(" hello* +world") Parse_Item_test3; |
|---|
| 883 |
static assert(Parse_Item_test3.Match); |
|---|
| 884 |
static assert(Parse_Item_test3.Text == "hello"); |
|---|
| 885 |
static assert(Parse_Item_test3.Used == " hello*", Parse_Item_test1.Used); |
|---|
| 886 |
static assert(Parse_Item_test3.Remaining == " +world"); |
|---|
| 887 |
static assert(Parse_Item_test3.Type == ItemType.star); |
|---|
| 888 |
|
|---|
| 889 |
alias Parse_Item!(" hello?*+world") Parse_Item_test4; |
|---|
| 890 |
static assert(Parse_Item_test4.Match); |
|---|
| 891 |
static assert(Parse_Item_test4.Text == "hello"); |
|---|
| 892 |
static assert(Parse_Item_test4.Used == " hello?", Parse_Item_test1.Used); |
|---|
| 893 |
static assert(Parse_Item_test4.Remaining == "*+world"); |
|---|
| 894 |
static assert(Parse_Item_test4.Type == ItemType.optional); |
|---|
| 895 |
|
|---|
| 896 |
alias Parse_Item!(" \t?*+world") Parse_Item_test5; |
|---|
| 897 |
static assert(!Parse_Item_test5.Match); |
|---|
| 898 |
} |
|---|
| 899 |
|
|---|
| 900 |
|
|---|
| 901 |
/************************************************ |
|---|
| 902 |
Parse a Sequence of rule parts (Sequence ::= ID Sequence | ID) |
|---|
| 903 |
*/ |
|---|
| 904 |
struct Parse_Sequence (char[] str) |
|---|
| 905 |
{ |
|---|
| 906 |
private alias Parse_Item!(str) first; |
|---|
| 907 |
static if(first.Match) |
|---|
| 908 |
{ |
|---|
| 909 |
static const bool Match = true; |
|---|
| 910 |
alias Parse_Sequence!(first.Remaining) rest; |
|---|
| 911 |
static if(rest.Match) |
|---|
| 912 |
{ |
|---|
| 913 |
static alias T!(first, rest.Clauses) Clauses; |
|---|
| 914 |
|
|---|
| 915 |
static const char[] Used = str[0 .. first.Used.length + rest.Used.length]; |
|---|
| 916 |
} |
|---|
| 917 |
else |
|---|
| 918 |
{ |
|---|
| 919 |
alias T!(first) Clauses; |
|---|
| 920 |
static const char[] Used = first.Used; |
|---|
| 921 |
} |
|---|
| 922 |
static const char[] Remaining = str[Used.length .. $]; |
|---|
| 923 |
} |
|---|
| 924 |
else |
|---|
| 925 |
static const bool Match = false; |
|---|
| 926 |
} |
|---|
| 927 |
struct UnittestParse_Sequence |
|---|
| 928 |
{ |
|---|
| 929 |
// Tests |
|---|
| 930 |
alias Parse_Sequence!("Hello world+this?is* good; by") Parse_Sequence_test1; |
|---|
| 931 |
static assert(Parse_Sequence_test1.Match); |
|---|
| 932 |
static assert(Parse_Sequence_test1.Used == "Hello world+this?is* good", Parse_Sequence_test1.Used); |
|---|
| 933 |
static assert(Parse_Sequence_test1.Remaining == "; by"); |
|---|
| 934 |
static assert(Parse_Sequence_test1.Clauses[0].Text == "Hello"); |
|---|
| 935 |
|
|---|
| 936 |
alias Parse_Sequence!("+this?is* good; by") Parse_Sequence_test2; |
|---|
| 937 |
static assert(!Parse_Sequence_test2.Match); |
|---|
| 938 |
} |
|---|
| 939 |
|
|---|
| 940 |
|
|---|
| 941 |
|
|---|
| 942 |
/************************************************ |
|---|
| 943 |
Parse a Reduction Case (Case ::= ID "/" Sequence) |
|---|
| 944 |
*/ |
|---|
| 945 |
struct Parse_Case(char[] str) |
|---|
| 946 |
{ |
|---|
| 947 |
static private alias Parse_ID!(str) act_1; |
|---|
| 948 |
static if(act_1.Match) |
|---|
| 949 |
{ |
|---|
| 950 |
alias act_1 act; |
|---|
| 951 |
const bool Special = false; |
|---|
| 952 |
//pragma(msg,__LINE__.stringof~":"~str); |
|---|
| 953 |
} |
|---|
| 954 |
else |
|---|
| 955 |
{ |
|---|
| 956 |
alias Parse_SpecialAct!(str) act; |
|---|
| 957 |
const bool Special = act.Match; |
|---|
| 958 |
//pragma(msg,__LINE__.stringof~":"~Special.stringof~":"~str); |
|---|
| 959 |
} |
|---|
| 960 |
|
|---|
| 961 |
static if(act.Match) |
|---|
| 962 |
{ |
|---|
| 963 |
static const char[] n1 = DropWhiteF(act.Remaining); |
|---|
| 964 |
static if(n1.length > 1 && n1[0] == '/') |
|---|
| 965 |
{ |
|---|
| 966 |
static private alias Parse_Sequence!(n1[1..$]) n2; |
|---|
| 967 |
|
|---|
| 968 |
static const bool Match = true; |
|---|
| 969 |
static const char[] Action = act.Text; |
|---|
| 970 |
static const char[] Seq = n2.Used; |
|---|
| 971 |
static alias n2.Clauses Clauses; |
|---|
| 972 |
} |
|---|
| 973 |
else |
|---|
| 974 |
{ |
|---|
| 975 |
//pragma(msg,__LINE__.stringof~":"~n1); |
|---|
| 976 |
static const bool Match = false; |
|---|
| 977 |
} |
|---|
| 978 |
} |
|---|
| 979 |
else |
|---|
| 980 |
{ |
|---|
| 981 |
//pragma(msg,__LINE__.stringof~":"~str); |
|---|
| 982 |
static const bool Match = false; |
|---|
| 983 |
} |
|---|
| 984 |
|
|---|
| 985 |
} |
|---|
| 986 |
|
|---|
| 987 |
|
|---|
| 988 |
struct UnittestParse_Case |
|---|
| 989 |
{ |
|---|
| 990 |
// Tests |
|---|
| 991 |
alias Parse_Case!("Act/foo?bar+baz*sig ") Parse_Case_test1; |
|---|
| 992 |
static assert(Parse_Case_test1.Match); |
|---|
| 993 |
static assert(Parse_Case_test1.Action == "Act"); |
|---|
| 994 |
static assert(Parse_Case_test1.Seq == "foo?bar+baz*sig"); |
|---|
| 995 |
static assert(Parse_Case_test1.Clauses[0].Text == "foo"); |
|---|
| 996 |
|
|---|
| 997 |
alias Parse_Case!("Act / foo? bar+ baz* sig|") Parse_Case_test2; |
|---|
| 998 |
static assert(Parse_Case_test2.Match); |
|---|
| 999 |
static assert(Parse_Case_test2.Action == "Act"); |
|---|
| 1000 |
static assert(Parse_Case_test2.Seq == " foo? bar+ baz* sig"); |
|---|
| 1001 |
static assert(Parse_Case_test2.Clauses[0].Text == "foo"); |
|---|
| 1002 |
|
|---|
| 1003 |
alias Parse_Case!("Act foo? / bar+ baz* sig|") Parse_Case_test3; |
|---|
| 1004 |
static assert(!Parse_Case_test3.Match); |
|---|
| 1005 |
} |
|---|
| 1006 |
|
|---|
| 1007 |
|
|---|
| 1008 |
|
|---|
| 1009 |
/************************************************ |
|---|
| 1010 |
Parse Set of Reduction cases (Cases ::= Case "|" Alt | Case) |
|---|
| 1011 |
*/ |
|---|
| 1012 |
struct Parse_Cases(char[] str) |
|---|
| 1013 |
{ |
|---|
| 1014 |
const char[] t_p = FindChar!('|')(str); |
|---|
| 1015 |
const char[] t_s = FindChar!(';')(str); |
|---|
| 1016 |
static if(t_p[$-1] == '|') |
|---|
| 1017 |
{ |
|---|
| 1018 |
//pragma(msg,__LINE__.stringof~":"~str); |
|---|
| 1019 |
const char[] t = t_p[0..$-1]; |
|---|
| 1020 |
} |
|---|
| 1021 |
else static if(t_s[$-1] == ';') |
|---|
| 1022 |
{ |
|---|
| 1023 |
//pragma(msg,__LINE__.stringof~":"~str); |
|---|
| 1024 |
const char[] t = t_s[0..$-1]; |
|---|
| 1025 |
} |
|---|
| 1026 |
else |
|---|
| 1027 |
{ |
|---|
| 1028 |
pragma(msg, "Debugging? '"~str~\'); |
|---|
| 1029 |
const char[] t = str; |
|---|
| 1030 |
} |
|---|
| 1031 |
|
|---|
| 1032 |
|
|---|
| 1033 |
private alias Parse_Case!(t) c1; |
|---|
| 1034 |
static if(c1.Match) |
|---|
| 1035 |
{ |
|---|
| 1036 |
static const bool Match = true; |
|---|
| 1037 |
private static const char[] n1 = DropWhiteF(str[t.length..$]); |
|---|
| 1038 |
static if(n1.length > 1 && n1[0] == '|') |
|---|
| 1039 |
{ |
|---|
| 1040 |
private alias Parse_Cases!(n1[1..$]) c2; |
|---|
| 1041 |
static if(c2.Match) |
|---|
| 1042 |
{ |
|---|
| 1043 |
alias T!(c1, c2.Disjuncts) Disjuncts; |
|---|
| 1044 |
static const char[] Remaining = c2.Remaining; |
|---|
| 1045 |
} |
|---|
| 1046 |
else |
|---|
| 1047 |
{ |
|---|
| 1048 |
alias T!(c1) Disjuncts; |
|---|
| 1049 |
static const char[] Remaining = str[t.length..$]; |
|---|
| 1050 |
} |
|---|
| 1051 |
} |
|---|
| 1052 |
else |
|---|
| 1053 |
{ |
|---|
| 1054 |
static alias T!(c1) Disjuncts; |
|---|
| 1055 |
static const char[] Remaining = str[t.length..$]; |
|---|
| 1056 |
} |
|---|
| 1057 |
static const char[] Used = str[0 .. $-Remaining.length]; |
|---|
| 1058 |
} |
|---|
| 1059 |
else |
|---|
| 1060 |
{ |
|---|
| 1061 |
//pragma(msg,__LINE__.stringof~":"~str); |
|---|
| 1062 |
static const bool Match = false; |
|---|
| 1063 |
} |
|---|
| 1064 |
} |
|---|
| 1065 |
|
|---|
| 1066 |
|
|---|
| 1067 |
struct UnittestParse_Cases |
|---|
| 1068 |
{ |
|---|
| 1069 |
// Tests |
|---|
| 1070 |
alias Parse_Cases!("Act/foo| For/Bar ") Parse_Cases_test1; |
|---|
| 1071 |
static assert(Parse_Cases_test1.Match); |
|---|
| 1072 |
static assert(Parse_Cases_test1.Remaining == ""); |
|---|
| 1073 |
static assert(Parse_Cases_test1.Used == "Act/foo| For/Bar "); |
|---|
| 1074 |
static assert(Parse_Cases_test1.Disjuncts[0].Action == "Act"); |
|---|
| 1075 |
static assert(Parse_Cases_test1.Disjuncts[1].Action == "For"); |
|---|
| 1076 |
|
|---|
| 1077 |
alias Parse_Cases!("Act/foo| For Bar ") Parse_Cases_test2; |
|---|
| 1078 |
static assert(Parse_Cases_test2.Match); |
|---|
| 1079 |
static assert(Parse_Cases_test2.Remaining == "| For Bar "); |
|---|
| 1080 |
static assert(Parse_Cases_test2.Used == "Act/foo"); |
|---|
| 1081 |
static assert(Parse_Cases_test2.Disjuncts[0].Action == "Act"); |
|---|
| 1082 |
} |
|---|
| 1083 |
|
|---|
| 1084 |
|
|---|
| 1085 |
|
|---|
| 1086 |
/************************************************ |
|---|
| 1087 |
Parse a Rule (Rule ::= ID ":" Cases ";") |
|---|
| 1088 |
*/ |
|---|
| 1089 |
struct Parse_Rule (char[] str) |
|---|
| 1090 |
{ |
|---|
| 1091 |
// parse off the rule name |
|---|
| 1092 |
static private alias Parse_ID!(str) name; |
|---|
| 1093 |
static if(name.Match) |
|---|
| 1094 |
{ |
|---|
| 1095 |
// parse off to the ':' |
|---|
| 1096 |
static private const char[] n1 = DropWhiteF(name.Remaining); |
|---|
| 1097 |
static if(n1.length > 1 && n1[0] == ':') |
|---|
| 1098 |
{ |
|---|
| 1099 |
// pares the cases |
|---|
| 1100 |
static private alias Parse_Cases!(n1[1..$]) cases; |
|---|
| 1101 |
static if(cases.Match) |
|---|
| 1102 |
{ |
|---|
| 1103 |
static private const char[] n2 = DropWhiteF(cases.Remaining); |
|---|
| 1104 |
static if(n2.length >= 1 && n2[0] == ';') |
|---|
| 1105 |
{ |
|---|
| 1106 |
static const bool Match = true; |
|---|
| 1107 |
static const char[] Name = name.Text; |
|---|
| 1108 |
static const char[] Remaining = n2[1..$]; |
|---|
| 1109 |
static const char[] Used = str[0 .. $-Remaining.length]; |
|---|
| 1110 |
static alias cases.Disjuncts Disjuncts; |
|---|
| 1111 |
} |
|---|
| 1112 |
else |
|---|
| 1113 |
{ |
|---|
| 1114 |
//pragma(msg,__LINE__.stringof~":"~n2~\n\n); |
|---|
| 1115 |
static const bool Match = false; |
|---|
| 1116 |
} |
|---|
| 1117 |
} |
|---|
| 1118 |
else |
|---|
| 1119 |
{ |
|---|
| 1120 |
//pragma(msg,__LINE__.stringof~":"~str); |
|---|
| 1121 |
static const bool Match = false; |
|---|
| 1122 |
} |
|---|
| 1123 |
} |
|---|
| 1124 |
else |
|---|
| 1125 |
{ |
|---|
| 1126 |
//pragma(msg,__LINE__.stringof~":"~str); |
|---|
| 1127 |
static const bool Match = false; |
|---|
| 1128 |
} |
|---|
| 1129 |
} |
|---|
| 1130 |
else |
|---|
| 1131 |
{ |
|---|
| 1132 |
//pragma(msg,__LINE__.stringof~":"~str); |
|---|
| 1133 |
static const bool Match = false; |
|---|
| 1134 |
} |
|---|
| 1135 |
} |
|---|
| 1136 |
struct UnittestParse_Rule |
|---|
| 1137 |
{ |
|---|
| 1138 |
//tests |
|---|
| 1139 |
alias Parse_Rule!("Foo:bar/baz | pig/owl*horse ; ") Parse_Rule_test1; |
|---|
| 1140 |
static assert(Parse_Rule_test1.Match); |
|---|
| 1141 |
static assert(Parse_Rule_test1.Used == "Foo:bar/baz | pig/owl*horse ;"); |
|---|
| 1142 |
static assert(Parse_Rule_test1.Remaining == " "); |
|---|
| 1143 |
static assert(Parse_Rule_test1.Name == "Foo"); |
|---|
| 1144 |
static assert(Parse_Rule_test1.Disjuncts[0].Action == "bar"); |
|---|
| 1145 |
static assert(Parse_Rule_test1.Disjuncts[0].Clauses[0].Text == "baz"); |
|---|
| 1146 |
static assert(Parse_Rule_test1.Disjuncts[1].Action == "pig"); |
|---|
| 1147 |
} |
|---|
| 1148 |
|
|---|
| 1149 |
char[] strOf(int v) |
|---|
| 1150 |
{ |
|---|
| 1151 |
if(v==0) return "0"; |
|---|
| 1152 |
|
|---|
| 1153 |
char[] ret; |
|---|
| 1154 |
|
|---|
| 1155 |
while(v) |
|---|
| 1156 |
{ |
|---|
| 1157 |
ret = cast(char)('0' + (v%10)) ~ ret; |
|---|
| 1158 |
v/=10; |
|---|
| 1159 |
} |
|---|
| 1160 |
return ret; |
|---|
| 1161 |
} |
|---|
| 1162 |
|
|---|
| 1163 |
char[] GramParts(char[] str) |
|---|
| 1164 |
{ |
|---|
| 1165 |
char[] ret, match; |
|---|
| 1166 |
|
|---|
| 1167 |
int i; |
|---|
| 1168 |
|
|---|
| 1169 |
while(str.length > 0) |
|---|
| 1170 |
{ |
|---|
| 1171 |
auto tmp = FindChar!(';')(str); |
|---|
| 1172 |
if(ReduceWhite(tmp) != "") |
|---|
| 1173 |
{ |
|---|
| 1174 |
ret ~= "Parse_Rule!(\""~ReduceWhite(tmp)~"\"), "; |
|---|
| 1175 |
match ~= "Reductions["~strOf(i)~"].Match && "; |
|---|
| 1176 |
} |
|---|
| 1177 |
str = str[tmp.length..$]; |
|---|
| 1178 |
} |
|---|
| 1179 |
|
|---|
| 1180 |
return |
|---|
| 1181 |
"alias T!("~ret[0..$-2]~") Reductions;\n" |
|---|
| 1182 |
"const bool Match = "~match[0..$-4]~";"; |
|---|
| 1183 |
} |
|---|
| 1184 |
|
|---|
| 1185 |
|
|---|
| 1186 |
/************************************************ |
|---|
| 1187 |
Parse a Grammar (Grammar ::= Rule Grammar | Rule) |
|---|
| 1188 |
*/ |
|---|
| 1189 |
template Parse_Grammar(char[] str) |
|---|
| 1190 |
{ |
|---|
| 1191 |
debug(dparse_verbose) pragma(msg,GramParts(str)); |
|---|
| 1192 |
mixin(GramParts(str)); |
|---|
| 1193 |
} |
|---|
| 1194 |
|
|---|
| 1195 |
struct UnittestParse_Grammar |
|---|
| 1196 |
{ |
|---|
| 1197 |
alias Parse_Grammar!("Foo:bar/baz | pig/owl*horse ; ") Parse_Grammar_test1; |
|---|
| 1198 |
|
|---|
| 1199 |
static assert(Parse_Grammar_test1.Match); |
|---|
| 1200 |
static assert(Parse_Grammar_test1.Reductions[0].Name == "Foo"); |
|---|
| 1201 |
static assert(Parse_Grammar_test1.Reductions[0].Disjuncts[0].Action == "bar"); |
|---|
| 1202 |
static assert(Parse_Grammar_test1.Reductions[0].Disjuncts[0].Clauses[0].Text == "baz"); |
|---|
| 1203 |
static assert(Parse_Grammar_test1.Reductions[0].Disjuncts[1].Action == "pig"); |
|---|
| 1204 |
static assert(Parse_Grammar_test1.Reductions[0].Disjuncts[1].Clauses[0].Text == "owl"); |
|---|
| 1205 |
} |
|---|
| 1206 |
|
|---|
| 1207 |
template MakeMixin(char[] starter, char[] str) |
|---|
| 1208 |
{ |
|---|
| 1209 |
const char[] MakeMixin = |
|---|
| 1210 |
MakeMixin_fn(str,starter); |
|---|
| 1211 |
debug(dparse_verbose) pragma(msg,MakeMixin); |
|---|
| 1212 |
} |
|---|
| 1213 |
|
|---|
| 1214 |
char[] MakeMixin_fn(char[] str,char[] starter) |
|---|
| 1215 |
{ |
|---|
| 1216 |
char[] ret; |
|---|
| 1217 |
|
|---|
| 1218 |
while(str.length > 0) |
|---|
| 1219 |
{ |
|---|
| 1220 |
auto tmp = FindChar!(';')(str); |
|---|
| 1221 |
if(ReduceWhite(tmp) != "") |
|---|
| 1222 |
{ |
|---|
| 1223 |
ret ~= |
|---|
| 1224 |
("PObject Terminal(char[] name : \""~GetID(FindChar!(':')(tmp)[0..$-1])~"\")(IParser p)" |
|---|
| 1225 |
"{" |
|---|
| 1226 |
"return Rule!(typeof(this), Parse_Rule!(\""~ReduceWhite(tmp)~"\"))(this,p);" |
|---|
| 1227 |
"}\n"); |
|---|
| 1228 |
} |
|---|
| 1229 |
|
|---|
| 1230 |
str = str[tmp.length..$]; |
|---|
| 1231 |
} |
|---|
| 1232 |
|
|---|
| 1233 |
return ret ~"alias Terminal!(\""~starter~"\") Parser;\n"; |
|---|
| 1234 |
} |
|---|
| 1235 |
|
|---|
| 1236 |
|
|---|
| 1237 |
|
|---|
| 1238 |
enum Places |
|---|
| 1239 |
{ |
|---|
| 1240 |
Back, |
|---|
| 1241 |
Revert, |
|---|
| 1242 |
Start |
|---|
| 1243 |
} |
|---|
| 1244 |
|
|---|
| 1245 |
template CaseLable(int i) |
|---|
| 1246 |
{ |
|---|
| 1247 |
const int Back = i*(Places.max+1) + Places.Back; |
|---|
| 1248 |
const int Revert = i*(Places.max+1) + Places.Revert; |
|---|
| 1249 |
const int Start = i*(Places.max+1) + Places.Start; |
|---|
| 1250 |
// debug(dparse_verbose) pragma(msg, ">> CaseLabel!("~itoa!(i)~") = { Back : "~itoa!(Back)~", Revert : "~itoa!(Revert)~", Start : "~itoa!(Start)~"}"); |
|---|
| 1251 |
} |
|---|
| 1252 |
|
|---|
| 1253 |
/**** |
|---|
| 1254 |
PARSER : RULE PARSER | ; |
|---|
| 1255 |
RULE : ID.name ":" CASE ALT ";"; |
|---|
| 1256 |
ALT : "|" CASE ALT | ; |
|---|
| 1257 |
CASE : ID.action "/" SEQUENCE; |
|---|
| 1258 |
SEQUENCE : ID SEQUENCE | ; |
|---|
| 1259 |
*/ |
|---|
| 1260 |
debug(dParse_runtime) int counter = 0; |
|---|
| 1261 |
|
|---|
| 1262 |
debug(dParse_runtime) int tager = 0; |
|---|
| 1263 |
|
|---|
| 1264 |
private struct Frame{uint pos; uint rule; uint count; debug(dParse_runtime) int tag;} |
|---|
| 1265 |
|
|---|
| 1266 |
PObject Rule(ParserBase,rule)(ParserBase parserBase, IParser p) |
|---|
| 1267 |
{ |
|---|
| 1268 |
debug(dParse_runtime) |
|---|
| 1269 |
{ |
|---|
| 1270 |
int ind = counter++; |
|---|
| 1271 |
writef("Try\t(%d)%s...\n", ind, rule.Name); |
|---|
| 1272 |
} |
|---|
| 1273 |
debug(dParse_runtime) scope(success) writef("Done\t(%d)%s\n", ind, rule.Name); |
|---|
| 1274 |
debug(dParse_runtime) scope(failure) writef("FAILED\t(%d)%s\n", ind, rule.Name); |
|---|
| 1275 |
|
|---|
| 1276 |
static const char[] nameIs = rule.Name; |
|---|
| 1277 |
//debug(dParse_light) |
|---|
| 1278 |
//pragma(msg, "<used>"~nameIs~"</used>"); |
|---|
| 1279 |
|
|---|
| 1280 |
Stack!(Frame) backups; |
|---|
| 1281 |
Frame store; |
|---|
| 1282 |
|
|---|
| 1283 |
debug(dparse_verbose) pragma(msg,"Build: \""~rule.Name~"\"" ); |
|---|
| 1284 |
// record start location |
|---|
| 1285 |
uint start = p.pos; |
|---|
| 1286 |
// try case |
|---|
| 1287 |
|
|---|
| 1288 |
caseLoop: foreach(ci,casev;rule.Disjuncts) |
|---|
| 1289 |
{ |
|---|
| 1290 |
debug(dParse_runtime) writef("*\t(%d)%s:%s...\n", ind, rule.Name,casev.Action); |
|---|
| 1291 |
|
|---|
| 1292 |
// dump all checkpoints |
|---|
| 1293 |
backups.Empty(); |
|---|
| 1294 |
|
|---|
| 1295 |
// return to start location |
|---|
| 1296 |
static if(ci != 0) |
|---|
| 1297 |
{ |
|---|
| 1298 |
debug(dParse_runtime) writef("backing\t (%d):%d\n", ind ,start); |
|---|
| 1299 |
p.pos = start; |
|---|
| 1300 |
} |
|---|
| 1301 |
|
|---|
| 1302 |
// allocate storage for returns |
|---|
| 1303 |
const int count = casev.Clauses.length; |
|---|
| 1304 |
PObject[count] temps; |
|---|
| 1305 |
static assert(count == temps.length); |
|---|
| 1306 |
|
|---|
| 1307 |
// debug(dparse_verbose) pragma(msg, "ICE from "~rule.name); |
|---|
| 1308 |
debug(dparse_verbose) pragma(msg, "\tfor \""~rule.Name~"\" doing case #"~itoa!(ci)~" action = \""~casev.Action~"\", length = "~itoa!(count)); |
|---|
| 1309 |
|
|---|
| 1310 |
static const int FirstCase = -1; |
|---|
| 1311 |
|
|---|
| 1312 |
int action = FirstCase; |
|---|
| 1313 |
int back = 0; |
|---|
| 1314 |
|
|---|
| 1315 |
mixin("failBack_"~itoa!(ci)~":;"); // insert uniqe label here |
|---|
| 1316 |
|
|---|
| 1317 |
switch(action) |
|---|
| 1318 |
{ |
|---|
| 1319 |
case FirstCase: |
|---|
| 1320 |
|
|---|
| 1321 |
foreach(index, cl; casev.Clauses) |
|---|
| 1322 |
{ |
|---|
| 1323 |
debug(dParse_runtime) p.mark(); |
|---|
| 1324 |
|
|---|
| 1325 |
static if(/*ci == 0 &&*/ index == 0 && nameIs == cl.Text) |
|---|
| 1326 |
pragma(msg, "Directly recursive rule: "~nameIs) |
|---|
| 1327 |
// static stuff |
|---|
| 1328 |
|
|---|
| 1329 |
debug(dParse_runtime) writef("Attemping clause %s (%d)...\n", cl.Text, index); |
|---|
| 1330 |
|
|---|
| 1331 |
debug(dparse_verbose) pragma(msg, "\t\tgenerating clause: \""~cl.Text~"\" ("~itoa!(index)~")"); |
|---|
| 1332 |
|
|---|
| 1333 |
static if(cl.Type != ItemType.single) |
|---|
| 1334 |
{ |
|---|
| 1335 |
// on [*+?] add a set |
|---|
| 1336 |
temps[index] = new PObjectSet(); |
|---|
| 1337 |
} |
|---|
| 1338 |
|
|---|
| 1339 |
static if(cl.Type == ItemType.single || cl.Type == ItemType.plus) |
|---|
| 1340 |
{ |
|---|
| 1341 |
// for cases where empty match is not allowed |
|---|
| 1342 |
// get one |
|---|
| 1343 |
debug(dparse_verbose) pragma(msg, "\t\t\t recurse in from \""~rule.Name~"\" on \""~cl.Text~\"\n); |
|---|
| 1344 |
//alias DotAction!(nameIs, cl.Text) _; |
|---|
| 1345 |
auto tmpStore1 = parserBase.Terminal!(cl.Text)(p); |
|---|
| 1346 |
assert(tmpStore1 !is null); |
|---|
| 1347 |
debug(dParse_runtime) writef("clause %s (%d) returned a %s fail=%s\n", cl.Text, index, tmpStore1.BaseName, tmpStore1.fail); |
|---|
| 1348 |
debug(dparse_verbose) pragma(msg, "\n\t\t\t recurse out from \""~rule.Name~"\" on \""~cl.Text~\"); |
|---|
| 1349 |
|
|---|
| 1350 |
// test it |
|---|
| 1351 |
if(tmpStore1.fail()) |
|---|
| 1352 |
{ |
|---|
| 1353 |
if(backups.Count != 0) // checkpoints remain |
|---|
| 1354 |
{ |
|---|
| 1355 |
store = backups.Pop(); // get checkpoint |
|---|
| 1356 |
debug(dParse_runtime) writef(">>>>>Poped %d\n", store.tag); |
|---|
| 1357 |
p.pos = store.pos; // move input steram back |
|---|
| 1358 |
action = store.rule; // set point to return to |
|---|
| 1359 |
back = store.count; // set backput amount |
|---|
| 1360 |
|
|---|
| 1361 |
// goto uniqe label from here |
|---|
| 1362 |
mixin("goto failBack_"~itoa!(ci)~";"); |
|---|
| 1363 |
} |
|---|
| 1364 |
else |
|---|
| 1365 |
{ |
|---|
| 1366 |
// try another |
|---|
| 1367 |
continue caseLoop; |
|---|
| 1368 |
} |
|---|
| 1369 |
} |
|---|
| 1370 |
|
|---|
| 1371 |
// on good: store |
|---|
| 1372 |
static if(cl.Type != ItemType.single) |
|---|
| 1373 |
{ |
|---|
| 1374 |
(cast(PObjectSet)temps[index]).Add(tmpStore1); |
|---|
| 1375 |
} |
|---|
| 1376 |
else |
|---|
| 1377 |
{ |
|---|
| 1378 |
temps[index] = tmpStore1; |
|---|
| 1379 |
} |
|---|
| 1380 |
} |
|---|
| 1381 |
|
|---|
| 1382 |
case CaseLable!(index).Back:; |
|---|
| 1383 |
|
|---|
| 1384 |
static if(cl.Type != ItemType.single) |
|---|
| 1385 |
{ |
|---|
| 1386 |
debug(dParse_runtime) |
|---|
| 1387 |
{ |
|---|
| 1388 |
writef("ReAttemping clause %s (%d)...\n", cl.Text, index); |
|---|
| 1389 |
store.tag = tager++; |
|---|
| 1390 |
writef("=======saved %d @ ", store.tag); |
|---|
| 1391 |
p.mark(); |
|---|
| 1392 |
} |
|---|
| 1393 |
store.pos = p.pos; |
|---|
| 1394 |
store.rule = CaseLable!(index).Revert; |
|---|
| 1395 |
store.count = (cast(PObjectSet)temps[index]).Count(); |
|---|
| 1396 |
|
|---|
| 1397 |
debug(dParse_runtime) writef("<<<<<< pushed %d\n", store.tag); |
|---|
| 1398 |
backups.Push(store); |
|---|
| 1399 |
|
|---|
| 1400 |
debug(dparse_verbose) pragma(msg, "\t\t\trecurse in from \""~rule.Name~"\" on \""~cl.Text~\"\n); |
|---|
| 1401 |
//alias DotAction!(nameIs, cl.Text) __; |
|---|
| 1402 |
auto tmpStore2 = parserBase.Terminal!(cl.Text)(p); |
|---|
| 1403 |
assert(tmpStore2 ! is null); |
|---|
| 1404 |
debug(dParse_runtime) writef("clause %s (%d) returned a %s fail=%s\n", cl.Text, index, tmpStore2.BaseName, tmpStore2.fail); |
|---|
| 1405 |
debug(dparse_verbose) pragma(msg, "\n\t\t\trecurse out from \""~rule.Name~"\" on \""~cl.Text~\"); |
|---|
| 1406 |
|
|---|
| 1407 |
if(!tmpStore2.fail()) |
|---|
| 1408 |
{ |
|---|
| 1409 |
(cast(PObjectSet)temps[index]).Add(tmpStore2); |
|---|
| 1410 |
|
|---|
| 1411 |
static if(cl.Type == ItemType.star || cl.Type == ItemType.plus) |
|---|
| 1412 |
{ |
|---|
| 1413 |
goto case CaseLable!(index).Back; |
|---|
| 1414 |
} |
|---|
| 1415 |
} |
|---|
| 1416 |
|
|---|
| 1417 |
// label clause |
|---|
| 1418 |
goto case CaseLable!(index).Start; |
|---|
| 1419 |
case CaseLable!(index).Revert:; |
|---|
| 1420 |
|
|---|
| 1421 |
(cast(PObjectSet)temps[index]).Back(back); // back out parsed data |
|---|
| 1422 |
case CaseLable!(index).Start:; |
|---|
| 1423 |
} |
|---|
| 1424 |
} |
|---|
| 1425 |
} |
|---|
| 1426 |
debug(dparse_verbose) pragma(msg,"\tdoing Action \""~casev.Action~\"); |
|---|
| 1427 |
debug(dParse_runtime) writef("doing Action \""~casev.Action~\"\n); |
|---|
| 1428 |
auto ret = SpecialAction!(ParserBase,casev.Action)(parserBase,temps); |
|---|
| 1429 |
debug(dParse_runtime) writef("Action \""~casev.Action~"\" done\n"); |
|---|
| 1430 |
debug(dParse_runtime) writef("\treturn (%d)Act '%s' fail=%s\n", ind, casev.Action, ret.fail); |
|---|
| 1431 |
return ret; |
|---|
| 1432 |
} |
|---|
| 1433 |
|
|---|
| 1434 |
debug(dParse_runtime) writef("Return (%d)failed\n", ind); |
|---|
| 1435 |
return new PObjectFail("Failed while looking for "~nameIs~\n);// debug(dparse_verbose) pragma(msg, ">>"__FILE__":"~itoa!(__LINE__)~": is this right?"); |
|---|
| 1436 |
debug(dparse_verbose) pragma(msg, "Done: "~rule.Name); |
|---|
| 1437 |
} |
|---|
| 1438 |
|
|---|
| 1439 |
template DotAction(char[] from, char[] to) |
|---|
| 1440 |
{ |
|---|
| 1441 |
pragma(msg, from ~ " -> " ~ to); |
|---|
| 1442 |
} |
|---|
| 1443 |
|
|---|
| 1444 |
/************************************************ |
|---|
| 1445 |
A basic stack struct used internally to the parser implementation |
|---|
| 1446 |
*/ |
|---|
| 1447 |
struct Stack(T) |
|---|
| 1448 |
{ |
|---|
| 1449 |
debug(TypeReport) pragma(msg, ">>T-"__FILE__~":"~itoa!(__LINE__)~": "~typeof(*this).stringof); |
|---|
| 1450 |
private T[] data = null; |
|---|
| 1451 |
private int at = 0; |
|---|
| 1452 |
|
|---|
| 1453 |
/** |
|---|
| 1454 |
Push an item onto the stack |
|---|
| 1455 |
*/ |
|---|
| 1456 |
void Push(T din) |
|---|
| 1457 |
{ |
|---|
| 1458 |
if(data.length <= at) data.length = at + 10; |
|---|
| 1459 |
data[at] = din; |
|---|
| 1460 |
at++; |
|---|
| 1461 |
} |
|---|
| 1462 |
|
|---|
| 1463 |
/** |
|---|
| 1464 |
Pop an item off the stack |
|---|
| 1465 |
|
|---|
| 1466 |
Throw an "Error" exception on underflow |
|---|
| 1467 |
*/ |
|---|
| 1468 |
T Pop() |
|---|
| 1469 |
{ |
|---|
| 1470 |
if(at) |
|---|
| 1471 |
{ |
|---|
| 1472 |
at--; |
|---|
| 1473 |
return data[at]; |
|---|
| 1474 |
} |
|---|
| 1475 |
else |
|---|
| 1476 |
throw new Error("Stack Underflow"); |
|---|
| 1477 |
} |
|---|
| 1478 |
|
|---|
| 1479 |
/// return the number of items on the stack |
|---|
| 1480 |
uint Count(){return at;} |
|---|
| 1481 |
|
|---|
| 1482 |
/// Clear the stack |
|---|
| 1483 |
void Empty(){ at = 0; } |
|---|
| 1484 |
|
|---|
| 1485 |
/// Dump everything |
|---|
| 1486 |
void Dist(){delete data;} |
|---|
| 1487 |
|
|---|
| 1488 |
unittest // unittest Stack!(T) |
|---|
| 1489 |
{ |
|---|
| 1490 |
writef("unittest@"__FILE__":"~itoa!(__LINE__)~"!("~T.stringof~")\n"); |
|---|
| 1491 |
|
|---|
| 1492 |
Stack!(int) st; |
|---|
| 1493 |
int i; |
|---|
| 1494 |
for(i = 0; i < 20; i++) |
|---|
| 1495 |
{ |
|---|
| 1496 |
assert(st.Count == i); |
|---|
| 1497 |
st.Push(i); |
|---|
| 1498 |
} |
|---|
| 1499 |
|
|---|
| 1500 |
i--; |
|---|
| 1501 |
|
|---|
| 1502 |
for(; i >= 0; i--) |
|---|
| 1503 |
{ |
|---|
| 1504 |
int j = st.Pop; |
|---|
| 1505 |
assert(j == i); |
|---|
| 1506 |
assert(st.Count == i); |
|---|
| 1507 |
} |
|---|
| 1508 |
|
|---|
| 1509 |
assert(st.Count == 0); |
|---|
| 1510 |
} |
|---|
| 1511 |
} |
|---|
| 1512 |
|
|---|
| 1513 |
debug(dparse_unittest) |
|---|
| 1514 |
{ |
|---|
| 1515 |
// Unittest code |
|---|
| 1516 |
unittest |
|---|
| 1517 |
{ |
|---|
| 1518 |
writef("unittest@"__FILE__":"~itoa!(__LINE__)~\n); |
|---|
| 1519 |
|
|---|
| 1520 |
P p; |
|---|
| 1521 |
data d = new data; |
|---|
| 1522 |
char[][] pass = |
|---|
| 1523 |
[ |
|---|
| 1524 |
"BB"[], //root > next baz > (baz) baz) |
|---|
| 1525 |
"HOB", //root > next baz > (owl* horse owl) baz > (() horse owl) baz |
|---|
| 1526 |
"OHOB", //root > next baz > (owl* horse owl) baz > ((owl) horse owl) baz |
|---|
| 1527 |
"OOHOB", //root > next baz > (owl* horse owl) baz > ((owl owl) horse owl) baz |
|---|
| 1528 |
"BHO", //root > baz horse owl |
|---|
| 1529 |
"CHB", //root > cat horse+ owl* baz > cat (horse) () baz |
|---|
| 1530 |
"CHOB", //root > cat horse+ owl* baz > cat (horse) (owl) baz |
|---|
| 1531 |
"CHHOB", //root > cat horse+ owl* baz > cat (horse horse) (owl) baz |
|---|
| 1532 |
"CCC", //root > cat* cat cat > (cat) cat cat |
|---|
| 1533 |
"KQKQKQKQQKQK", // root > twin* car > (twin twin twin) (car) > ((king qween) (king qween) (king qween)) (king qween qween king) |
|---|
| 1534 |
], |
|---|
| 1535 |
fail = |
|---|
| 1536 |
[ |
|---|
| 1537 |
"BHHO"[], |
|---|
| 1538 |
"COB", |
|---|
| 1539 |
"CB", |
|---|
| 1540 |
"CHHOOB", |
|---|
| 1541 |
]; |
|---|
| 1542 |
|
|---|
| 1543 |
foreach(char[] passTest; pass) |
|---|
| 1544 |
{ |
|---|
| 1545 |
d.dat = passTest; |
|---|
| 1546 |
d.i=0; |
|---|
| 1547 |
assert(!p.Parser(d).fail, \"~passTest~"\" failed to parse"); |
|---|
| 1548 |
} |
|---|
| 1549 |
|
|---|
| 1550 |
foreach(char[] failTest; fail) |
|---|
| 1551 |
{ |
|---|
| 1552 |
d.dat = failTest; |
|---|
| 1553 |
d.i=0; |
|---|
| 1554 |
assert(p.Parser(d).fail, \"~failTest~"\" failed to fail to parse"); |
|---|
| 1555 |
} |
|---|
| 1556 |
} |
|---|
| 1557 |
|
|---|
| 1558 |
struct P |
|---|
| 1559 |
{ |
|---|
| 1560 |
PObject Terminal(char[] str : "baz")(IParser i) {data d = cast(data)i; if(d.i >= d.dat.length || d.dat[d.i] != 'B') return new PObjectFail; d.i++; return new PObjectBox!(char)('B');} |
|---|
| 1561 |
PObject Terminal(char[] str : "owl")(IParser i) {data d = cast(data)i; if(d.i >= d.dat.length || d.dat[d.i] != 'O') return new PObjectFail; d.i++; return new PObjectBox!(char)('O');} |
|---|
| 1562 |
PObject Terminal(char[] str : "horse")(IParser i) {data d = cast(data)i; if(d.i >= d.dat.length || d.dat[d.i] != 'H') return new PObjectFail; d.i++; return new PObjectBox!(char)('H');} |
|---|
| 1563 |
PObject Terminal(char[] str : "cat")(IParser i) {data d = cast(data)i; if(d.i >= d.dat.length || d.dat[d.i] != 'C') return new PObjectFail; d.i++; return new PObjectBox!(char)('C');} |
|---|
| 1564 |
PObject Terminal(char[] str : "king")(IParser i) {data d = cast(data)i; if(d.i >= d.dat.length || d.dat[d.i] != 'K') return new PObjectFail; d.i++; return new PObjectBox!(char)('K');} |
|---|
| 1565 |
PObject Terminal(char[] str : "qween")(IParser i) {data d = cast(data)i; if(d.i >= d.dat.length || d.dat[d.i] != 'Q') return new PObjectFail; d.i++; return new PObjectBox!(char)('Q');} |
|---|
| 1566 |
|
|---|
| 1567 |
PObject Action(char[] str : "bar") (PObject[1] i){return new PObjectVector!(1)(i);} |
|---|
| 1568 |
PObject Action(char[] str : "beer")(PObject[2] i){return new PObjectVector!(2)(i);} |
|---|
| 1569 |
PObject Action(char[] str : "pig") (PObject[3] i){return new PObjectVector!(3)(i);} |
|---|
| 1570 |
PObject Action(char[] str : "keg") (PObject[4] i){return new PObjectVector!(4)(i);} |
|---|
| 1571 |
|
|---|
| 1572 |
const char[] gram = " |
|---|
| 1573 |
next : |
|---|
| 1574 |
bar / baz | |
|---|
| 1575 |
pig / owl * horse owl ; |
|---|
| 1576 |
root : |
|---|
| 1577 |
beer / next baz | |
|---|
| 1578 |
pig / baz horse owl | |
|---|
| 1579 |
keg / cat horse+ owl? baz | |
|---|
| 1580 |
pig / cat* cat cat | |
|---|
| 1581 |
beer / twin * car; |
|---|
| 1582 |
twin : |
|---|
| 1583 |
beer / king qween* ; |
|---|
| 1584 |
car : |
|---|
| 1585 |
keg / king qween qween king; |
|---|
| 1586 |
"; |
|---|
| 1587 |
|
|---|
| 1588 |
const char[] gram2 = import("dmp.g"); |
|---|
| 1589 |
|
|---|
| 1590 |
PObject Terminal(char[] str)(IParser i){return null;} |
|---|
| 1591 |
PObject Action(char[] str)(PObject[] i){return null;} |
|---|
| 1592 |
|
|---|
| 1593 |
mixin(MakeMixin!("root",ReduceWhite(gram))); |
|---|
| 1594 |
// mixin(MakeMixin!("Module",ReduceWhite(gram2))); |
|---|
| 1595 |
} |
|---|
| 1596 |
|
|---|
| 1597 |
class data : IParser |
|---|
| 1598 |
{ |
|---|
| 1599 |
char[] dat; |
|---|
| 1600 |
uint i =0; |
|---|
| 1601 |
uint pos() {return i;} |
|---|
| 1602 |
void pos(uint j){ i = j;} |
|---|
| 1603 |
} |
|---|
| 1604 |
void main(){} |
|---|
| 1605 |
} |
|---|