mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-11-26 12:49:21 +00:00
Implemented #elif. #169
This commit is contained in:
parent
4352874305
commit
d8138615ac
@ -108,7 +108,11 @@ public class CPreprocessor implements TokenSource {
|
|||||||
return true;
|
return true;
|
||||||
} else if(inputToken.getType() == KickCLexer.IFELSE) {
|
} else if(inputToken.getType() == KickCLexer.IFELSE) {
|
||||||
// #else means we must skip until #endif
|
// #else means we must skip until #endif
|
||||||
ifelse(cTokenSource);
|
skipToEndIf(cTokenSource);
|
||||||
|
return true;
|
||||||
|
} else if(inputToken.getType() == KickCLexer.ELIF) {
|
||||||
|
// #elif means we must skip until #endif
|
||||||
|
skipToEndIf(cTokenSource);
|
||||||
return true;
|
return true;
|
||||||
} else if(inputToken.getType() == KickCLexer.ENDIF) {
|
} else if(inputToken.getType() == KickCLexer.ENDIF) {
|
||||||
// Skip #endif - they have already been handled by #if / #else
|
// Skip #endif - they have already been handled by #if / #else
|
||||||
@ -119,6 +123,25 @@ public class CPreprocessor implements TokenSource {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define a macro.
|
||||||
|
*
|
||||||
|
* @param cTokenSource The token source used to get the macro name and body.
|
||||||
|
*/
|
||||||
|
private void define(CTokenSource cTokenSource) {
|
||||||
|
// #define a new macro - find the name
|
||||||
|
skipWhitespace(cTokenSource);
|
||||||
|
String macroName = nextToken(cTokenSource, KickCLexer.NAME).getText();
|
||||||
|
// Examine whether the macro has parameters
|
||||||
|
skipWhitespace(cTokenSource);
|
||||||
|
if(cTokenSource.peekToken().getType() == KickCLexer.PAR_BEGIN) {
|
||||||
|
// Macro has parameters - find parameter name list
|
||||||
|
throw new CompileError("Macros with parameters not supported!");
|
||||||
|
}
|
||||||
|
final ArrayList<Token> macroBody = readBody(cTokenSource);
|
||||||
|
defines.put(macroName, macroBody);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encountered an IDENTIFIER. Attempt to expand as a macro.
|
* Encountered an IDENTIFIER. Attempt to expand as a macro.
|
||||||
*
|
*
|
||||||
@ -182,7 +205,21 @@ public class CPreprocessor implements TokenSource {
|
|||||||
String macroName = nextToken(cTokenSource, KickCLexer.NAME).getText();
|
String macroName = nextToken(cTokenSource, KickCLexer.NAME).getText();
|
||||||
final boolean defined = this.defines.containsKey(macroName);
|
final boolean defined = this.defines.containsKey(macroName);
|
||||||
if(!defined) {
|
if(!defined) {
|
||||||
iffalse(cTokenSource);
|
skipIfBody(cTokenSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* #ifdef checks if a macro is _NOT_ defined.
|
||||||
|
*
|
||||||
|
* @param cTokenSource The token source used to get the macro name
|
||||||
|
*/
|
||||||
|
private void ifndef(CTokenSource cTokenSource) {
|
||||||
|
skipWhitespace(cTokenSource);
|
||||||
|
String macroName = nextToken(cTokenSource, KickCLexer.NAME).getText();
|
||||||
|
final boolean defined = this.defines.containsKey(macroName);
|
||||||
|
if(defined) {
|
||||||
|
skipIfBody(cTokenSource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,6 +229,19 @@ public class CPreprocessor implements TokenSource {
|
|||||||
* @param cTokenSource The token source used to get the condition
|
* @param cTokenSource The token source used to get the condition
|
||||||
*/
|
*/
|
||||||
private void ifif(CTokenSource cTokenSource) {
|
private void ifif(CTokenSource cTokenSource) {
|
||||||
|
Long conditionValue = readAndEvaluateCondition(cTokenSource);
|
||||||
|
if(conditionValue == null || conditionValue == 0L) {
|
||||||
|
skipIfBody(cTokenSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a condition expression ( for #if / #elif ) from a token source and evaluate it. Return the result.
|
||||||
|
*
|
||||||
|
* @param cTokenSource The token source to read from
|
||||||
|
* @return The value of the evaluation of the constant condition expression
|
||||||
|
*/
|
||||||
|
private Long readAndEvaluateCondition(CTokenSource cTokenSource) {
|
||||||
// Read the condition body
|
// Read the condition body
|
||||||
ArrayList<Token> conditionTokens = readBody(cTokenSource);
|
ArrayList<Token> conditionTokens = readBody(cTokenSource);
|
||||||
// Evaluate any uses of the defined operator (to prevent expansion of the named macro)
|
// Evaluate any uses of the defined operator (to prevent expansion of the named macro)
|
||||||
@ -201,10 +251,7 @@ public class CPreprocessor implements TokenSource {
|
|||||||
// Parse the expression
|
// Parse the expression
|
||||||
KickCParser.ExprContext conditionExpr = ExprParser.parseExpression(subPreprocessor);
|
KickCParser.ExprContext conditionExpr = ExprParser.parseExpression(subPreprocessor);
|
||||||
// Evaluate the expression
|
// Evaluate the expression
|
||||||
Long conditionValue = evaluateExpression(conditionExpr);
|
return evaluateExpression(conditionExpr);
|
||||||
if(conditionValue == null || conditionValue == 0L) {
|
|
||||||
iffalse(cTokenSource);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -240,7 +287,7 @@ public class CPreprocessor implements TokenSource {
|
|||||||
if(result instanceof ConstantInteger) {
|
if(result instanceof ConstantInteger) {
|
||||||
return ((ConstantInteger) result).getInteger();
|
return ((ConstantInteger) result).getInteger();
|
||||||
} else if(result instanceof ConstantBool) {
|
} else if(result instanceof ConstantBool) {
|
||||||
return ((ConstantBool) result).getBool()?1L:0L;
|
return ((ConstantBool) result).getBool() ? 1L : 0L;
|
||||||
} else
|
} else
|
||||||
return 0L;
|
return 0L;
|
||||||
}
|
}
|
||||||
@ -254,7 +301,7 @@ public class CPreprocessor implements TokenSource {
|
|||||||
if(result instanceof ConstantInteger) {
|
if(result instanceof ConstantInteger) {
|
||||||
return ((ConstantInteger) result).getInteger();
|
return ((ConstantInteger) result).getInteger();
|
||||||
} else if(result instanceof ConstantBool) {
|
} else if(result instanceof ConstantBool) {
|
||||||
return ((ConstantBool) result).getBool()?1L:0L;
|
return ((ConstantBool) result).getBool() ? 1L : 0L;
|
||||||
} else
|
} else
|
||||||
return 0L;
|
return 0L;
|
||||||
}
|
}
|
||||||
@ -269,8 +316,9 @@ public class CPreprocessor implements TokenSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find and evaluate the special defined X operator which evaluates to 1 if a named macro if defined and 0 if it is undefined.
|
* Find and evaluate the special "defined X" operator which evaluates to 1 if a named macro if defined and 0 if it is undefined.
|
||||||
* Works by replacing the defined X tokens with the resulting 0/1 value in the passed token list.
|
* Works by replacing the "defined X" tokens with the resulting 0/1 value in the passed token list.
|
||||||
|
*
|
||||||
* @param conditionTokens The token list
|
* @param conditionTokens The token list
|
||||||
*/
|
*/
|
||||||
private void evaluateDefinedOperator(ArrayList<Token> conditionTokens) {
|
private void evaluateDefinedOperator(ArrayList<Token> conditionTokens) {
|
||||||
@ -278,18 +326,18 @@ public class CPreprocessor implements TokenSource {
|
|||||||
ListIterator<Token> tokenIt = conditionTokens.listIterator();
|
ListIterator<Token> tokenIt = conditionTokens.listIterator();
|
||||||
while(tokenIt.hasNext()) {
|
while(tokenIt.hasNext()) {
|
||||||
Token token = tokenIt.next();
|
Token token = tokenIt.next();
|
||||||
if(token.getType()== KickCLexer.DEFINED) {
|
if(token.getType() == KickCLexer.DEFINED) {
|
||||||
// Remove the token
|
// Remove the token
|
||||||
tokenIt.remove();
|
tokenIt.remove();
|
||||||
// Read the macro name to examine - and skip any parenthesis
|
// Read the macro name to examine - and skip any parenthesis
|
||||||
token = getNextSkipWhitespace(tokenIt);
|
token = getNextSkipWhitespace(tokenIt);
|
||||||
boolean hasPar = false;
|
boolean hasPar = false;
|
||||||
if(token.getType()==KickCLexer.PAR_BEGIN) {
|
if(token.getType() == KickCLexer.PAR_BEGIN) {
|
||||||
tokenIt.remove();
|
tokenIt.remove();
|
||||||
token = getNextSkipWhitespace(tokenIt);
|
token = getNextSkipWhitespace(tokenIt);
|
||||||
hasPar = true;
|
hasPar = true;
|
||||||
}
|
}
|
||||||
if(token.getType()!=KickCLexer.NAME) {
|
if(token.getType() != KickCLexer.NAME) {
|
||||||
throw new CompileError("Unexpected token. Was expecting NAME!");
|
throw new CompileError("Unexpected token. Was expecting NAME!");
|
||||||
}
|
}
|
||||||
tokenIt.remove();
|
tokenIt.remove();
|
||||||
@ -298,7 +346,7 @@ public class CPreprocessor implements TokenSource {
|
|||||||
if(hasPar) {
|
if(hasPar) {
|
||||||
// Skip closing parenthesis
|
// Skip closing parenthesis
|
||||||
token = getNextSkipWhitespace(tokenIt);
|
token = getNextSkipWhitespace(tokenIt);
|
||||||
if(token.getType()!=KickCLexer.PAR_END) {
|
if(token.getType() != KickCLexer.PAR_END) {
|
||||||
throw new CompileError("Unexpected token. Was expecting ')'!");
|
throw new CompileError("Unexpected token. Was expecting ')'!");
|
||||||
}
|
}
|
||||||
tokenIt.remove();
|
tokenIt.remove();
|
||||||
@ -306,7 +354,7 @@ public class CPreprocessor implements TokenSource {
|
|||||||
final boolean defined = defines.containsKey(macroName);
|
final boolean defined = defines.containsKey(macroName);
|
||||||
CommonToken definedToken = new CommonToken(macroNameToken);
|
CommonToken definedToken = new CommonToken(macroNameToken);
|
||||||
definedToken.setType(KickCLexer.NUMBER);
|
definedToken.setType(KickCLexer.NUMBER);
|
||||||
definedToken.setText(defined?"1":"0");
|
definedToken.setText(defined ? "1" : "0");
|
||||||
tokenIt.add(definedToken);
|
tokenIt.add(definedToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -314,43 +362,38 @@ public class CPreprocessor implements TokenSource {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the next token from an iterator skipping whitespace (unless it has a newline)
|
* Get the next token from an iterator skipping whitespace (unless it has a newline)
|
||||||
|
*
|
||||||
* @param tokenIt The iterator
|
* @param tokenIt The iterator
|
||||||
* @return The next non-whitespace token
|
* @return The next non-whitespace token
|
||||||
*/
|
*/
|
||||||
private Token getNextSkipWhitespace(ListIterator<Token> tokenIt) {
|
private Token getNextSkipWhitespace(ListIterator<Token> tokenIt) {
|
||||||
Token token = tokenIt.next();
|
Token token = tokenIt.next();
|
||||||
while(token.getChannel()==CParser.CHANNEL_WHITESPACE && !token.getText().contains("\n"))
|
while(token.getChannel() == CParser.CHANNEL_WHITESPACE && !token.getText().contains("\n"))
|
||||||
token = tokenIt.next();
|
token = tokenIt.next();
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* #ifdef checks if a macro is _NOT_ defined.
|
* Skip tokens based in an #if that is false - look for a matching #elif, #else or the #endif
|
||||||
*
|
|
||||||
* @param cTokenSource The token source used to get the macro name
|
|
||||||
*/
|
|
||||||
private void ifndef(CTokenSource cTokenSource) {
|
|
||||||
skipWhitespace(cTokenSource);
|
|
||||||
String macroName = nextToken(cTokenSource, KickCLexer.NAME).getText();
|
|
||||||
final boolean defined = this.defines.containsKey(macroName);
|
|
||||||
if(defined) {
|
|
||||||
iffalse(cTokenSource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Skip tokens based in an #if that is false
|
|
||||||
*
|
*
|
||||||
* @param cTokenSource The token source
|
* @param cTokenSource The token source
|
||||||
*/
|
*/
|
||||||
private void iffalse(CTokenSource cTokenSource) {
|
private void skipIfBody(CTokenSource cTokenSource) {
|
||||||
// Skip tokens until finding a matching #endif - respect nesting
|
// Skip tokens until finding a matching #endif or a matching #else - respect nesting
|
||||||
int nesting = 1;
|
int nesting = 1;
|
||||||
while(true) {
|
while(true) {
|
||||||
final Token token = cTokenSource.nextToken();
|
final Token token = cTokenSource.nextToken();
|
||||||
final int tokenType = token.getType();
|
final int tokenType = token.getType();
|
||||||
if(tokenType == KickCLexer.IFDEF || tokenType == KickCLexer.IFNDEF || tokenType == KickCLexer.IFIF) {
|
if(tokenType == KickCLexer.IFDEF || tokenType == KickCLexer.IFNDEF || tokenType == KickCLexer.IFIF) {
|
||||||
++nesting;
|
++nesting;
|
||||||
|
} else if(tokenType == KickCLexer.ELIF) {
|
||||||
|
if(nesting == 1) {
|
||||||
|
// We are at the outer #if - #elif means we must evaluate the condition and maybe generate output from here!
|
||||||
|
final Long conditionValue = readAndEvaluateCondition(cTokenSource);
|
||||||
|
if(conditionValue != null && conditionValue != 0L)
|
||||||
|
// #elif condition !=0 - generate output from here!
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else if(tokenType == KickCLexer.IFELSE) {
|
} else if(tokenType == KickCLexer.IFELSE) {
|
||||||
if(nesting == 1) {
|
if(nesting == 1) {
|
||||||
// We are at the outer #if - #else means we must generate output from here!
|
// We are at the outer #if - #else means we must generate output from here!
|
||||||
@ -366,11 +409,11 @@ public class CPreprocessor implements TokenSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* #else skips until a matching #endif
|
* Skip until a matching #endif
|
||||||
*
|
*
|
||||||
* @param cTokenSource The token source
|
* @param cTokenSource The token source
|
||||||
*/
|
*/
|
||||||
private void ifelse(CTokenSource cTokenSource) {
|
private void skipToEndIf(CTokenSource cTokenSource) {
|
||||||
int nesting = 1;
|
int nesting = 1;
|
||||||
while(true) {
|
while(true) {
|
||||||
final Token token = cTokenSource.nextToken();
|
final Token token = cTokenSource.nextToken();
|
||||||
@ -386,24 +429,6 @@ public class CPreprocessor implements TokenSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a macro.
|
|
||||||
*
|
|
||||||
* @param cTokenSource The token source used to get the macro name and body.
|
|
||||||
*/
|
|
||||||
private void define(CTokenSource cTokenSource) {
|
|
||||||
// #define a new macro - find the name
|
|
||||||
skipWhitespace(cTokenSource);
|
|
||||||
String macroName = nextToken(cTokenSource, KickCLexer.NAME).getText();
|
|
||||||
// Examine whether the macro has parameters
|
|
||||||
skipWhitespace(cTokenSource);
|
|
||||||
if(cTokenSource.peekToken().getType() == KickCLexer.PAR_BEGIN) {
|
|
||||||
// Macro has parameters - find parameter name list
|
|
||||||
throw new CompileError("Macros with parameters not supported!");
|
|
||||||
}
|
|
||||||
final ArrayList<Token> macroBody = readBody(cTokenSource);
|
|
||||||
defines.put(macroName, macroBody);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a preprocessor body (eg. a macro body) until encountering a newline
|
* Read a preprocessor body (eg. a macro body) until encountering a newline
|
||||||
|
@ -159,6 +159,8 @@ public class TestPreprocessor {
|
|||||||
assertEquals("name:x;name:y;", parse("#define A X\n#if defined A\nx;\n#endif\ny;"));
|
assertEquals("name:x;name:y;", parse("#define A X\n#if defined A\nx;\n#endif\ny;"));
|
||||||
// Output not generated by #if using defined operator
|
// Output not generated by #if using defined operator
|
||||||
assertEquals("name:y;", parse("#if defined A\nx;\n#endif\ny;"));
|
assertEquals("name:y;", parse("#if defined A\nx;\n#endif\ny;"));
|
||||||
|
// Output generated by #elif
|
||||||
|
assertEquals("name:y;name:w;", parse("#if 0\nx;\n#elif 1\ny;\n#elif 1\nz;\n#else\nq;\n#endif\nw;"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -181,12 +183,12 @@ public class TestPreprocessor {
|
|||||||
CParser cParser = new CParser(null);
|
CParser cParser = new CParser(null);
|
||||||
cParser.addSource(charStream);
|
cParser.addSource(charStream);
|
||||||
KickCParser.StmtSeqContext stmtSeqContext = cParser.getParser().stmtSeq();
|
KickCParser.StmtSeqContext stmtSeqContext = cParser.getParser().stmtSeq();
|
||||||
MacrosPrinter printVisitor = new MacrosPrinter();
|
ProgramPrinter printVisitor = new ProgramPrinter();
|
||||||
printVisitor.visit(stmtSeqContext);
|
printVisitor.visit(stmtSeqContext);
|
||||||
return printVisitor.getOut().toString();
|
return printVisitor.getOut().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class MacrosPrinter extends KickCParserBaseVisitor<Object> {
|
private static class ProgramPrinter extends KickCParserBaseVisitor<Object> {
|
||||||
|
|
||||||
StringBuilder out = new StringBuilder();
|
StringBuilder out = new StringBuilder();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user