Skip to content

Commit

Permalink
Feature: added support for #ifdef, #ifndef and #else to C parser.
Browse files Browse the repository at this point in the history
WIP: nested #ifdef's not working correctly yet.
  • Loading branch information
uxmal committed Aug 28, 2023
1 parent e1bfb75 commit 24d2d0d
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 1 deletion.
65 changes: 64 additions & 1 deletion src/Core/Hll/C/CDirectiveLexer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,23 +39,32 @@ public class CDirectiveLexer
private readonly Dictionary<string, Directive> directives = new()
{
{ "define", Directive.Define },
{ "ifdef", Directive.Ifdef },
{ "ifndef", Directive.Ifndef },
{ "else", Directive.Else },
{ "endif", Directive.Endif },
{ "line", Directive.Line },
{ "pragma", Directive.Pragma },
{ "__pragma", Directive.Pragma }
{ "__pragma", Directive.Pragma },
{ "undef", Directive.Undef },
};

private readonly ParserState parserState;
private readonly Dictionary<string, List<CToken>> macros;
private readonly CLexer lexer;
private readonly Stack<bool> ifdefs;
private IEnumerator<CToken>? expandedTokens;
private State state;
private bool ignoreTokens;

public CDirectiveLexer(ParserState state, CLexer lexer)
{
this.parserState = state;
this.lexer = lexer;
this.macros = new();
this.state = State.StartLine;
this.ifdefs = new Stack<bool>();
this.ignoreTokens = false;
}

public int LineNumber => lexer.LineNumber;
Expand All @@ -70,9 +79,14 @@ private enum State
private enum Directive
{
Define,
Else,
Endif,
Ifdef,
Ifndef,
Line,
Pragma,
__Pragma,
Undef,
}

public CToken Read()
Expand All @@ -98,6 +112,11 @@ public CToken Read()
break;
default:
state = State.InsideLine;
if (ignoreTokens)
{
token = ReadToken();
break;
}
return token;
}
break;
Expand All @@ -118,6 +137,11 @@ public CToken Read()
token = ReadToken();
break;
}
if (ignoreTokens)
{
token = ReadToken();
break;
}
return token;
case CTokenType.__Pragma:
Expect(CTokenType.LParen);
Expand All @@ -126,6 +150,11 @@ public CToken Read()
state = State.InsideLine;
break;
default:
if (ignoreTokens)
{
token = ReadToken();
break;
}
return token;
}
break;
Expand Down Expand Up @@ -156,8 +185,37 @@ public CToken Read()
token = ReadDefine();
state = State.StartLine;
break;
case Directive.Ifdef:
var ifdefVar = (string) Expect(CTokenType.Id)!;
ifdefs.Push(ignoreTokens);
ignoreTokens = !IsDefined(ifdefVar);
token = ReadToken(); //$TODO: read to end of line
state = State.StartLine;
break;
case Directive.Ifndef:
ifdefVar = (string) Expect(CTokenType.Id)!;
ifdefs.Push(ignoreTokens);
ignoreTokens = IsDefined(ifdefVar);
token = ReadToken(); //$TODO: read to end of line
state = State.StartLine;
break;
case Directive.Endif:
if (ifdefs.Count == 0)
throw new FormatException($"Unbalanced #if/#endif");
ignoreTokens = ifdefs.Pop();
token = ReadToken(); //$TODO: read to end of line
state = State.StartLine;
break;
}
break;
case CTokenType.Else:
if (ifdefs.Count == 0)
throw new FormatException($"Unbalanced #if/#else");
state = State.StartLine;
ignoreTokens = !ignoreTokens;
token = ReadToken(); //$TODO: read to end of line
state = State.StartLine;
break;
default:
throw new FormatException($"Unexpected token {token.Type} on line {lexer.LineNumber}.");
}
Expand Down Expand Up @@ -331,5 +389,10 @@ public virtual CToken ReadPragma(string pragma)
throw new FormatException(string.Format("Expected '{0}' but got '{1}' on line {2}.", expected, actualToken.Type, lexer.LineNumber));
return actualToken.Value;
}

private bool IsDefined(string preprocessorVar)
{
return macros.ContainsKey(preprocessorVar);
}
}
}
58 changes: 58 additions & 0 deletions src/UnitTests/Core/Hll/C/CDirectiveLexerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,5 +206,63 @@ public void CDirectiveLexer_msvc_pragma_pack_push()
Assert.AreEqual(CTokenType.EOF, lexer.Read().Type);
Assert.AreEqual(8, state.Alignment);
}

[Test]
public void CDirectiveLexer_ifdef()
{
LexMsvc(@"
#define DOIT
#ifdef DOIT
foo
#endif
bar
");
Assert.AreEqual("foo", lexer.Read().Value);
Assert.AreEqual("bar", lexer.Read().Value);
Assert.AreEqual(CTokenType.EOF, lexer.Read().Type);
}

[Test]
public void CDirectiveLexer_ifdef_not_defined()
{
LexMsvc(@"
#ifdef DOIT
foo
#endif
bar
");
Assert.AreEqual("bar", lexer.Read().Value);
Assert.AreEqual(CTokenType.EOF, lexer.Read().Type);
}

[Test]
public void CDirectiveLexer_ifndef()
{
LexMsvc(@"
#ifndef DOIT
foo
#endif
bar
");
Assert.AreEqual("foo", lexer.Read().Value);
Assert.AreEqual("bar", lexer.Read().Value);
Assert.AreEqual(CTokenType.EOF, lexer.Read().Type);
}

[Test]
public void CDirectiveLexer_if_else()
{
LexMsvc(@"
#ifdef DOIT
doit
#else
dontdoit
#endif
bar
");
Assert.AreEqual("dontdoit", lexer.Read().Value);
Assert.AreEqual("bar", lexer.Read().Value);
Assert.AreEqual(CTokenType.EOF, lexer.Read().Type);
}
}
}

0 comments on commit 24d2d0d

Please sign in to comment.