503 lines
11 KiB
C#
503 lines
11 KiB
C#
|
public abstract class AbstractPreprocessorEnumerator : InputElementEnumerator {
|
||
|
protected bool Verbose = false;
|
||
|
protected bool emitting;
|
||
|
protected System.Collections.Hashtable table;
|
||
|
protected InputElementEnumerator iee;
|
||
|
protected AbstractPreprocessorEnumerator subordinate;
|
||
|
|
||
|
protected int linedelta;
|
||
|
protected string file;
|
||
|
|
||
|
public AbstractPreprocessorEnumerator(InputElementEnumerator iee) {
|
||
|
this.iee = iee;
|
||
|
linedelta = 0;
|
||
|
}
|
||
|
|
||
|
public override InputElement Current {
|
||
|
get {
|
||
|
return iee.Current;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override bool MoveNext() {
|
||
|
// System.Console.WriteLine("MoveNext[" + this.ToString() + "]");
|
||
|
if (subordinate != null) {
|
||
|
if (subordinate.MoveNext()) {
|
||
|
return true;
|
||
|
}
|
||
|
subordinate = null;
|
||
|
}
|
||
|
return _moveNext();
|
||
|
}
|
||
|
|
||
|
protected abstract bool _moveNext();
|
||
|
|
||
|
protected void tokenize(InputElement ie) {
|
||
|
String2CharEnumerator s2ce = new String2CharEnumerator(ie.str);
|
||
|
PreprocessorInputElementEnumerator pp = new PreprocessorInputElementEnumerator(s2ce, ie.coord.file);
|
||
|
SkipWhiteEnumerator swe = new SkipWhiteEnumerator(pp);
|
||
|
tokens = swe;
|
||
|
if (Verbose) {
|
||
|
tokens = new EchoInputEnumerator(tokens, "PPD> ");
|
||
|
}
|
||
|
}
|
||
|
protected InputElementEnumerator tokens;
|
||
|
|
||
|
protected void doLine() {
|
||
|
if (!tokens.MoveNext()) {
|
||
|
error("incomplete #line");
|
||
|
return;
|
||
|
}
|
||
|
if (tokens.Current.tag != "integer-literal") {
|
||
|
error("expecting integer");
|
||
|
return;
|
||
|
}
|
||
|
string line = tokens.Current.str;
|
||
|
if (emitting) {
|
||
|
try {
|
||
|
this.linedelta = System.Convert.ToInt32(line) - iee.Current.coord.line;
|
||
|
} catch (System.OverflowException) {
|
||
|
error("line number too big");
|
||
|
}
|
||
|
}
|
||
|
if (!tokens.MoveNext()) {
|
||
|
return;
|
||
|
}
|
||
|
if (tokens.Current.tag == "<EOF>") {
|
||
|
return;
|
||
|
}
|
||
|
if (tokens.Current.tag != "string-literal") {
|
||
|
error("expecting string");
|
||
|
}
|
||
|
file = tokens.Current.str;
|
||
|
}
|
||
|
|
||
|
protected void doWarning() {
|
||
|
if (emitting) {
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected void doError() {
|
||
|
if (emitting) {
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected void doDefine() {
|
||
|
if (!tokens.MoveNext()) {
|
||
|
error("incomplete #define");
|
||
|
return;
|
||
|
}
|
||
|
if (tokens.Current.tag != "identifier") {
|
||
|
error("expecting identifier");
|
||
|
return;
|
||
|
}
|
||
|
if (emitting) {
|
||
|
table.Add(tokens.Current.str, tokens.Current.str);
|
||
|
}
|
||
|
}
|
||
|
protected void doPragma() {
|
||
|
if (!tokens.MoveNext()) {
|
||
|
error("incomplete #pragma");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (tokens.Current.str != "warning") {
|
||
|
error("expecting warning");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!tokens.MoveNext()) {
|
||
|
error("incomplete #pragma");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
switch (tokens.Current.str)
|
||
|
{
|
||
|
case "disable":
|
||
|
case "restore":
|
||
|
break;
|
||
|
|
||
|
default :
|
||
|
error("expecting disable or restore");
|
||
|
return;
|
||
|
|
||
|
}
|
||
|
|
||
|
if (!tokens.MoveNext()) {
|
||
|
error("incomplete #pragma");
|
||
|
return;
|
||
|
}
|
||
|
// now we are at the error number
|
||
|
}
|
||
|
|
||
|
protected void doUndef() {
|
||
|
if (!tokens.MoveNext()) {
|
||
|
error("incomplete #undefine");
|
||
|
return;
|
||
|
}
|
||
|
if (tokens.Current.tag != "identifier") {
|
||
|
error("expecting identifier");
|
||
|
return;
|
||
|
}
|
||
|
if (emitting) {
|
||
|
table.Remove(tokens.Current.str);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected void error(string str) {
|
||
|
System.Console.WriteLine("line " + iee.Current.coord.line + ": Preprocessor Error: " + str);
|
||
|
}
|
||
|
|
||
|
protected bool eval() {
|
||
|
if (!tokens.MoveNext()) {
|
||
|
error("incomplete expression");
|
||
|
return true;
|
||
|
}
|
||
|
return e1();
|
||
|
}
|
||
|
|
||
|
bool e1() {
|
||
|
bool v;
|
||
|
v = e2();
|
||
|
while (tokens.Current.tag == "||") {
|
||
|
if (!tokens.MoveNext()) {
|
||
|
error("incomplete expression");
|
||
|
return true;
|
||
|
}
|
||
|
v |= e2();
|
||
|
}
|
||
|
return v;
|
||
|
}
|
||
|
|
||
|
bool e2() {
|
||
|
bool v;
|
||
|
v = e3();
|
||
|
while (tokens.Current.tag == "&&") {
|
||
|
if (!tokens.MoveNext()) {
|
||
|
error("incomplete expression");
|
||
|
return true;
|
||
|
}
|
||
|
v &= e3();
|
||
|
}
|
||
|
return v;
|
||
|
}
|
||
|
|
||
|
bool e3() {
|
||
|
bool v;
|
||
|
v = e4();
|
||
|
for (;;) {
|
||
|
switch (tokens.Current.tag) {
|
||
|
case "==":
|
||
|
if (!tokens.MoveNext()) {
|
||
|
error("incomplete expression");
|
||
|
return true;
|
||
|
}
|
||
|
v = (v == e4());
|
||
|
break;
|
||
|
case "!=":
|
||
|
if (!tokens.MoveNext()) {
|
||
|
error("incomplete expression");
|
||
|
return true;
|
||
|
}
|
||
|
v = (v != e4());
|
||
|
break;
|
||
|
default:
|
||
|
return v;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool e4() {
|
||
|
if (tokens.Current.tag == "!") {
|
||
|
if (!tokens.MoveNext()) {
|
||
|
error("incomplete expression");
|
||
|
return true;
|
||
|
}
|
||
|
return !e4();
|
||
|
}
|
||
|
return e5();
|
||
|
}
|
||
|
|
||
|
bool e5() {
|
||
|
bool v;
|
||
|
switch (tokens.Current.tag) {
|
||
|
case "true":
|
||
|
return true;
|
||
|
case "false":
|
||
|
return false;
|
||
|
case "(":
|
||
|
if (!tokens.MoveNext()) {
|
||
|
error("incomplete expression");
|
||
|
return true;
|
||
|
}
|
||
|
v = e1();
|
||
|
if (tokens.Current.tag != ")") {
|
||
|
error("incomplete expression");
|
||
|
return true;
|
||
|
}
|
||
|
if (!tokens.MoveNext()) {
|
||
|
error("incomplete expression");
|
||
|
return true;
|
||
|
}
|
||
|
return v;
|
||
|
case "identifier":
|
||
|
v = table.ContainsKey(tokens.Current.str);
|
||
|
if (!tokens.MoveNext()) {
|
||
|
error("incomplete expression");
|
||
|
return true;
|
||
|
}
|
||
|
return v;
|
||
|
}
|
||
|
error("invalid expression");
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class PreprocessorEnumerator : AbstractPreprocessorEnumerator {
|
||
|
|
||
|
public PreprocessorEnumerator(InputElementEnumerator iee) : base(iee) {
|
||
|
this.table = new System.Collections.Hashtable();
|
||
|
this.emitting = true;
|
||
|
}
|
||
|
|
||
|
public PreprocessorEnumerator(InputElementEnumerator iee, bool Verbose) : this(iee) {
|
||
|
this.Verbose = Verbose;
|
||
|
}
|
||
|
|
||
|
protected override bool _moveNext() {
|
||
|
System.Diagnostics.Trace.Assert(emitting);
|
||
|
while (iee.MoveNext()) {
|
||
|
if (iee.Current.tag != "PREPROC") {
|
||
|
iee.Current.coord.line += linedelta;
|
||
|
if (file != null) {
|
||
|
iee.Current.coord.file = file;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
tokenize(iee.Current);
|
||
|
bool status = tokens.MoveNext();
|
||
|
switch (tokens.Current.tag) {
|
||
|
default:
|
||
|
error("unknown preprocessor command: " + tokens.Current.tag);
|
||
|
break;
|
||
|
case "define":
|
||
|
doDefine();
|
||
|
break;
|
||
|
case "undef":
|
||
|
doUndef();
|
||
|
break;
|
||
|
case "line":
|
||
|
doLine();
|
||
|
break;
|
||
|
case "error":
|
||
|
doError();
|
||
|
break;
|
||
|
case "warning":
|
||
|
doWarning();
|
||
|
break;
|
||
|
case "region":
|
||
|
subordinate = new RegionEnumerator(table, iee, emitting);
|
||
|
if (subordinate.MoveNext()) {
|
||
|
return true;
|
||
|
}
|
||
|
subordinate = null;
|
||
|
continue;
|
||
|
case "endregion":
|
||
|
error("unexpected #endregion");
|
||
|
break;
|
||
|
case "pragma":
|
||
|
doPragma();
|
||
|
break;
|
||
|
case "if":
|
||
|
bool val = eval();
|
||
|
subordinate = new IfEnumerator(table, iee, emitting, val);
|
||
|
if (subordinate.MoveNext()) {
|
||
|
return true;
|
||
|
}
|
||
|
subordinate = null;
|
||
|
continue;
|
||
|
case "elif":
|
||
|
error("unexpected #elif");
|
||
|
break;
|
||
|
case "else":
|
||
|
error("unexpected #else");
|
||
|
break;
|
||
|
case "endif":
|
||
|
error("unexpected #endif");
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class RegionEnumerator : AbstractPreprocessorEnumerator {
|
||
|
|
||
|
public RegionEnumerator(System.Collections.Hashtable table, InputElementEnumerator iee, bool emitting) : base(iee) {
|
||
|
this.table = table;
|
||
|
this.emitting = emitting;
|
||
|
}
|
||
|
|
||
|
protected override bool _moveNext() {
|
||
|
while (iee.MoveNext()) {
|
||
|
if (iee.Current.tag != "PREPROC") {
|
||
|
if (emitting) {
|
||
|
iee.Current.coord.line += linedelta;
|
||
|
if (file != null) {
|
||
|
iee.Current.coord.file = file;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
tokenize(iee.Current);
|
||
|
|
||
|
bool status = tokens.MoveNext();
|
||
|
switch (tokens.Current.tag) {
|
||
|
default:
|
||
|
error("unknown preprocessor command: " + tokens.Current.tag);
|
||
|
break;
|
||
|
case "define":
|
||
|
doDefine();
|
||
|
break;
|
||
|
case "undef":
|
||
|
doUndef();
|
||
|
break;
|
||
|
case "line":
|
||
|
doLine();
|
||
|
break;
|
||
|
case "error":
|
||
|
doError();
|
||
|
break;
|
||
|
case "warning":
|
||
|
doWarning();
|
||
|
break;
|
||
|
case "region":
|
||
|
subordinate = new RegionEnumerator(table, iee, emitting);
|
||
|
if (subordinate.MoveNext()) {
|
||
|
return true;
|
||
|
}
|
||
|
subordinate = null;
|
||
|
continue;
|
||
|
case "endregion":
|
||
|
return false;
|
||
|
case "pragma":
|
||
|
doPragma();
|
||
|
break;
|
||
|
case "if":
|
||
|
bool val = eval();
|
||
|
subordinate = new IfEnumerator(table, iee, emitting, val);
|
||
|
if (subordinate.MoveNext()) {
|
||
|
return true;
|
||
|
}
|
||
|
subordinate = null;
|
||
|
continue;
|
||
|
case "elif":
|
||
|
error("unexpected #elif");
|
||
|
break;
|
||
|
case "else":
|
||
|
error("unexpected #else");
|
||
|
break;
|
||
|
case "endif":
|
||
|
error("unexpected #endif");
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
error("unfinished #region");
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class IfEnumerator : AbstractPreprocessorEnumerator {
|
||
|
|
||
|
public IfEnumerator(System.Collections.Hashtable table, InputElementEnumerator iee, bool emitting, bool conditional) : base(iee) {
|
||
|
|
||
|
this.table = table;
|
||
|
this.emitting = emitting;
|
||
|
if (emitting) {
|
||
|
this.conditional = conditional;
|
||
|
this.condHistory = conditional;
|
||
|
} else {
|
||
|
this.conditional = false;
|
||
|
this.condHistory = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private bool conditional;
|
||
|
private bool condHistory;
|
||
|
|
||
|
protected override bool _moveNext() {
|
||
|
while (iee.MoveNext()) {
|
||
|
bool val;
|
||
|
|
||
|
if (iee.Current.tag != "PREPROC") {
|
||
|
if (conditional) {
|
||
|
iee.Current.coord.line += linedelta;
|
||
|
if (file != null) {
|
||
|
iee.Current.coord.file = file;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
continue;
|
||
|
}
|
||
|
tokenize(iee.Current);
|
||
|
|
||
|
bool status = tokens.MoveNext();
|
||
|
switch (tokens.Current.tag) {
|
||
|
default:
|
||
|
error("unknown preprocessor command: " + tokens.Current.tag);
|
||
|
break;
|
||
|
case "define":
|
||
|
doDefine();
|
||
|
break;
|
||
|
case "undef":
|
||
|
doUndef();
|
||
|
break;
|
||
|
case "line":
|
||
|
doLine();
|
||
|
break;
|
||
|
case "error":
|
||
|
doError();
|
||
|
break;
|
||
|
case "warning":
|
||
|
doWarning();
|
||
|
break;
|
||
|
case "region":
|
||
|
subordinate = new RegionEnumerator(table, iee, emitting && conditional);
|
||
|
if (subordinate.MoveNext()) {
|
||
|
return true;
|
||
|
}
|
||
|
subordinate = null;
|
||
|
continue;
|
||
|
case "endregion":
|
||
|
error("unexpected #endregion");
|
||
|
break;
|
||
|
case "pragma":
|
||
|
doPragma();
|
||
|
break;
|
||
|
case "if":
|
||
|
val = eval();
|
||
|
subordinate = new IfEnumerator(table, iee, emitting && conditional, val);
|
||
|
if (subordinate.MoveNext()) {
|
||
|
return true;
|
||
|
}
|
||
|
subordinate = null;
|
||
|
continue;
|
||
|
case "elif":
|
||
|
val = eval();
|
||
|
conditional = val && !condHistory;
|
||
|
condHistory |= conditional;
|
||
|
break;
|
||
|
case "else":
|
||
|
conditional = !condHistory;
|
||
|
condHistory = true;
|
||
|
continue;
|
||
|
case "endif":
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
error("unfinished #if");
|
||
|
return false;
|
||
|
}
|
||
|
}
|