1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-11-26 12:49:21 +00:00

Fixed problem with macros with empty parameter lists. Closes #688

This commit is contained in:
jespergravgaard 2021-08-02 07:52:50 +02:00
parent 9bcda2cb67
commit dbd8a3cbac
8 changed files with 185 additions and 5 deletions

View File

@ -38,7 +38,7 @@ public class CPreprocessor implements TokenSource {
static class Macro {
/** The name of the define. */
final String name;
/** The parameters. Empty if there are no parameters. */
/** The parameters. Null if there are no parameters. */
final List<String> parameters;
/** The body. */
final List<Token> body;
@ -48,6 +48,25 @@ public class CPreprocessor implements TokenSource {
this.parameters = parameters;
this.body = body;
}
/** Does the macro have any parameters */
boolean hasParameters() { return parameters!=null; }
/** Does the macro have a specific parameter */
boolean hasParameter(String param) {
return parameters!=null && parameters.contains(param);
}
/**
* Get the index of a specific parameter in the parameter list
* @param param The parameter name to look for
* @return The index of the parameter in the list. -1 if the passed name is not a parameter.
*/
private int getParameterIndex(String param) {
if(parameters==null)
return -1;
return parameters.indexOf(param);
}
}
public CPreprocessor(CParser cParser, TokenSource input, Map<String, Macro> defines) {
@ -275,8 +294,9 @@ public class CPreprocessor implements TokenSource {
skipWhitespace(cTokenSource);
String macroName = nextToken(cTokenSource, KickCLexer.NAME).getText();
// Examine whether the macro has parameters
List<String> macroParameters = new ArrayList<>();
List<String> macroParameters = null;
if(cTokenSource.peekToken().getType() == KickCLexer.PAR_BEGIN) {
macroParameters = new ArrayList<>();
// Read past the '('
cTokenSource.nextToken();
// Macro has parameters - find parameter name list
@ -327,7 +347,7 @@ public class CPreprocessor implements TokenSource {
if(macro != null) {
// Handle parameters
List<List<Token>> paramValues = new ArrayList<>();
if(!macro.parameters.isEmpty()) {
if(macro.hasParameters()) {
// Parse parameter value list
{
// Skip '('
@ -383,9 +403,9 @@ public class CPreprocessor implements TokenSource {
List<Token> expandedBody = new ArrayList<>();
final List<Token> macroBody = macro.body;
for(Token macroBodyToken : macroBody) {
if(macroBodyToken.getType() == KickCLexer.NAME && macro.parameters.contains(macroBodyToken.getText())) {
if(macroBodyToken.getType() == KickCLexer.NAME && macro.hasParameter(macroBodyToken.getText())) {
// body token is a parameter name - replace with expanded parameter value
final int paramIndex = macro.parameters.indexOf(macroBodyToken.getText());
final int paramIndex = macro.getParameterIndex(macroBodyToken.getText());
final List<Token> expandedParamValue = paramValues.get(paramIndex);
for(Token expandedParamValueToken : expandedParamValue) {
addTokenToExpandedBody(expandedParamValueToken, macroNameToken, expandedBody);

View File

@ -182,6 +182,8 @@ public class TestPreprocessor {
*/
@Test
public void testDefineParams() {
// A simple define with an empty parameter
assertEquals("+(name:a,num:1);", parse("#define A() a+1\nA();"));
// A simple define with one parameter
assertEquals("+(name:b,num:1);", parse("#define A(a) a+1\nA(b);"));
// A simple define with one parameter used twice

View File

@ -929,6 +929,11 @@ public class TestProgramsFast extends TestPrograms {
compileAndCompare("cstyle-decl-function.c");
}
@Test
public void testPreprocessor16() throws IOException {
compileAndCompare("preprocessor-16.c");
}
@Test
public void testPreprocessor15() throws IOException {
assertError("preprocessor-15.c", "Error parsing file: extraneous input 'X' ");

View File

@ -0,0 +1,8 @@
// Demonstrates a problem with the preprocessor where a macro with an empty parameter list does not accept an empty list of parameters
#define CLEAR() a=0
void main() {
char a=1;
CLEAR();
}

View File

@ -0,0 +1,14 @@
// Demonstrates a problem with the preprocessor where a macro with an empty parameter list does not accept an empty list of parameters
// Commodore 64 PRG executable file
.file [name="preprocessor-16.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segment Basic
:BasicUpstart(main)
.segment Code
main: {
// }
rts
}

View File

@ -0,0 +1,8 @@
void main()
main: scope:[main] from
[0] phi()
to:main::@return
main::@return: scope:[main] from main
[1] return
to:@return

View File

@ -0,0 +1,121 @@
CONTROL FLOW GRAPH SSA
void main()
main: scope:[main] from __start
to:main::@return
main::@return: scope:[main] from main
return
to:@return
void __start()
__start: scope:[__start] from
call main
to:__start::@1
__start::@1: scope:[__start] from __start
to:__start::@return
__start::@return: scope:[__start] from __start::@1
return
to:@return
SYMBOL TABLE SSA
void __start()
void main()
Removing unused procedure __start
Removing unused procedure block __start
Removing unused procedure block __start::@1
Removing unused procedure block __start::@return
Successful SSA optimization PassNEliminateEmptyStart
Adding NOP phi() at start of main
CALL GRAPH
Created 0 initial phi equivalence classes
Coalesced down to 0 phi equivalence classes
Adding NOP phi() at start of main
FINAL CONTROL FLOW GRAPH
void main()
main: scope:[main] from
[0] phi()
to:main::@return
main::@return: scope:[main] from main
[1] return
to:@return
VARIABLE REGISTER WEIGHTS
void main()
Initial phi equivalence classes
Complete equivalence classes
REGISTER UPLIFT POTENTIAL REGISTERS
REGISTER UPLIFT SCOPES
Uplift Scope [main]
Uplift Scope []
Uplifting [main] best 36 combination
Uplifting [] best 36 combination
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
// Demonstrates a problem with the preprocessor where a macro with an empty parameter list does not accept an empty list of parameters
// Upstart
// Commodore 64 PRG executable file
.file [name="preprocessor-16.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segment Basic
:BasicUpstart(main)
// Global Constants & labels
.segment Code
// main
main: {
jmp __breturn
// main::@return
__breturn:
// [1] return
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp __breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction __breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
void main()
FINAL ASSEMBLER
Score: 6
// File Comments
// Demonstrates a problem with the preprocessor where a macro with an empty parameter list does not accept an empty list of parameters
// Upstart
// Commodore 64 PRG executable file
.file [name="preprocessor-16.prg", type="prg", segments="Program"]
.segmentdef Program [segments="Basic, Code, Data"]
.segmentdef Basic [start=$0801]
.segmentdef Code [start=$80d]
.segmentdef Data [startAfter="Code"]
.segment Basic
:BasicUpstart(main)
// Global Constants & labels
.segment Code
// main
main: {
// main::@return
// }
// [1] return
rts
}
// File Data

View File

@ -0,0 +1,2 @@
void main()