Add full preprocessor support

This commit is contained in:
drmortalwombat 2021-09-24 20:08:42 +02:00
parent fd10525780
commit 6b6122bc07
4 changed files with 365 additions and 26 deletions

View File

@ -13,19 +13,24 @@ The resulting compiler is a frankenstein constructed from a converted javascript
The performance of interpreted code is clearly not as good as native machine code but the penalty for 16bit code is around 40-50% and less than 10% for floating point. Code that can use 8bit my suffer up to a factor of 10 to 20. The performance of interpreted code is clearly not as good as native machine code but the penalty for 16bit code is around 40-50% and less than 10% for floating point. Code that can use 8bit my suffer up to a factor of 10 to 20.
The goal is to implement the actual C standard and not some subset for performance reasons. So the compiler must support:
* Floating point
* Recursion
* Multi dimensional arrays
* Pointer to structs
## Limits and Errors ## Limits and Errors
The first release of the compiler is severely limited considering it is only two weeks old, so there is quite a lot missing or broken. I hope to cross out most of the problems in the coming weeks. After four weeks, the compiler has now matured significantly. There are still several open areas.
### Language ### Language
* No union type
* No long integer * No long integer
* No struct function return * No struct function return
* Missing const checks for structs and enums * Missing const checks for structs and enums
* No static variables in functions
* Missing warnings for all kind of abuses * Missing warnings for all kind of abuses
* no #if in preprocessor
### Linker ### Linker
@ -34,7 +39,6 @@ The first release of the compiler is severely limited considering it is only two
### Standard Libraries ### Standard Libraries
* Limited formatting in printf
* No file functions * No file functions
### Runtime ### Runtime
@ -46,12 +50,11 @@ The first release of the compiler is severely limited considering it is only two
### Optimizing ### Optimizing
* All global variables are considered volatile * All global variables are considered volatile
* No loop opmtimization * Simple loop opmtimization
* Poor bookeeping of callee saved registers * Poor bookeeping of callee saved registers
* Missing livetime reduction of intermediates * Partial block domination analysis
* No block domination analysis
* No register use for arguments * No register use for arguments
* Auto variables places on fixed stack for known call sequence * Auto variables placed on fixed stack for known call sequence
### Intermediate code generation ### Intermediate code generation
@ -60,8 +63,23 @@ The first release of the compiler is severely limited considering it is only two
### Native code generation ### Native code generation
* Calling non native functions missing
* More byte operation optimisation required * More byte operation optimisation required
* Simple loop detection and optimisation not complete
## Compiler arguments
The compiler is command line driven, and creates an executable .prg file.
oscar64 {-i=includePath} [-o=output.prg] [-cr=runtime.c] [-e] [-n] [-dSYMBOL[=value]] {source.c}
* -i : additional include paths
* -o : optional output file name
* -cr : alternative runtime library, replaces the crt.c
* -e : execute the result in the integrated emulator
* -n : create pure native code for all functions
* -d : define a symbol (e.g. NOFLOAT to avoid float code in printf)
A list of source files can be provided.
## Implementation Details ## Implementation Details
@ -69,6 +87,9 @@ The compiler does a full program compile, the linker step is part of the compila
#pragma compile("stdio.c") #pragma compile("stdio.c")
The character map for string and char constants can be changed with a pragma to match a custon character set or PETSCII.
#pragma charmap(char, code [,count])
The byte code interpreter is compiled by the compiler itself and placed in the source file "crt.c". Functions implementing byte codes are marked with a pragma: The byte code interpreter is compiled by the compiler itself and placed in the source file "crt.c". Functions implementing byte codes are marked with a pragma:
@ -123,4 +144,3 @@ Routines can be marked to be compiled to 6502 machine code with the native pragm

View File

@ -49,6 +49,7 @@ enum ErrorID
EERR_ASM_INVALID_INSTRUCTION, EERR_ASM_INVALID_INSTRUCTION,
EERR_ASM_INVALID_MODE, EERR_ASM_INVALID_MODE,
EERR_PRAGMA_PARAMETER, EERR_PRAGMA_PARAMETER,
ERRR_PREPROCESSOR,
EERR_INVALID_PREPROCESSOR, EERR_INVALID_PREPROCESSOR,
}; };

View File

@ -252,9 +252,11 @@ Scanner::Scanner(Errors* errors, Preprocessor* preprocessor)
{ {
mOffset = 0; mOffset = 0;
mLine = mPreprocessor->mLine; mLine = mPreprocessor->mLine;
mPrepCondition = 0; mPrepCondFalse = 0;
mPrepPending = 0; mPrepCondDepth = 0;
mPrepCondExit = 0;
mAssemblerMode = false; mAssemblerMode = false;
mPreprocessorMode = false;
mMacroExpansion = nullptr; mMacroExpansion = nullptr;
mDefines = new MacroDict(); mDefines = new MacroDict();
@ -358,13 +360,71 @@ void Scanner::NextToken(void)
NextRawToken(); NextRawToken();
if (mToken == TK_PREP_ENDIF) if (mToken == TK_PREP_ENDIF)
{ {
if (mPrepCondition > 0) if (mPrepCondFalse > 0)
mPrepCondition--; mPrepCondFalse--;
else if (mPrepPending > 0) else if (mPrepCondExit)
mPrepPending--; mPrepCondExit = false;
else if (mPrepCondDepth > 0)
mPrepCondDepth--;
else else
mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Unexpected #endif"); mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Unexpected #endif");
} }
else if (mToken == TK_PREP_ELSE)
{
if (mPrepCondExit)
{
}
else if (mPrepCondFalse == 1)
{
mPrepCondFalse = 0;
mPrepCondDepth++;
}
else if (mPrepCondFalse > 1)
{
}
else if (mPrepCondDepth > 0)
{
mPrepCondExit = true;
}
else
mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Unexpected #else");
}
else if (mToken == TK_PREP_ELIF)
{
if (mPrepCondExit)
{
}
else if (mPrepCondFalse == 1)
{
mPreprocessorMode = true;
mPrepCondFalse = 0;
NextToken();
int v = PrepParseConditional();
if (v)
{
mPrepCondFalse = 0;
mPrepCondDepth++;
}
else
mPrepCondFalse++;
mPreprocessorMode = false;
if (mToken != TK_EOL)
mErrors->Error(mLocation, ERRR_PREPROCESSOR, "End of line expected");
}
else if (mPrepCondFalse > 1)
{
}
else if (mPrepCondDepth > 0)
{
mPrepCondExit = true;
}
else
mErrors->Error(mLocation, EERR_INVALID_PREPROCESSOR, "Unexpected #else");
}
else if (mToken == TK_EOF) else if (mToken == TK_EOF)
{ {
if (!mPreprocessor->CloseSource()) if (!mPreprocessor->CloseSource())
@ -372,10 +432,10 @@ void Scanner::NextToken(void)
mToken = TK_NONE; mToken = TK_NONE;
mOffset = 0; mOffset = 0;
} }
else if (mPrepCondition > 0) else if (mPrepCondFalse > 0 || mPrepCondExit)
{ {
if (mToken == TK_PREP_IFDEF || mToken == TK_PREP_IFNDEF) if (mToken == TK_PREP_IFDEF || mToken == TK_PREP_IFNDEF || mToken == TK_PREP_IF)
mPrepCondition++; mPrepCondFalse++;
} }
else if (mToken == TK_PREP_INCLUDE) else if (mToken == TK_PREP_INCLUDE)
{ {
@ -439,9 +499,9 @@ void Scanner::NextToken(void)
{ {
Macro * def = mDefines->Lookup(mTokenIdent); Macro * def = mDefines->Lookup(mTokenIdent);
if (def) if (def)
mPrepPending++; mPrepCondDepth++;
else else
mPrepCondition++; mPrepCondFalse++;
} }
} }
else if (mToken == TK_PREP_IFNDEF) else if (mToken == TK_PREP_IFNDEF)
@ -451,11 +511,24 @@ void Scanner::NextToken(void)
{ {
Macro * def = mDefines->Lookup(mTokenIdent); Macro * def = mDefines->Lookup(mTokenIdent);
if (!def) if (!def)
mPrepPending++; mPrepCondDepth++;
else else
mPrepCondition++; mPrepCondFalse++;
} }
} }
else if (mToken == TK_PREP_IF)
{
mPreprocessorMode = true;
NextToken();
int v = PrepParseConditional();
if (v)
mPrepCondDepth++;
else
mPrepCondFalse++;
mPreprocessorMode = false;
if (mToken != TK_EOL)
mErrors->Error(mLocation, ERRR_PREPROCESSOR, "End of line expected");
}
else if (mToken == TK_IDENT) else if (mToken == TK_IDENT)
{ {
Macro* def = nullptr; Macro* def = nullptr;
@ -551,7 +624,7 @@ void Scanner::NextRawToken(void)
while (IsWhitespace(mTokenChar)) while (IsWhitespace(mTokenChar))
{ {
if (mAssemblerMode && mTokenChar == '\n') if ((mAssemblerMode || mPreprocessorMode) && mTokenChar == '\n')
{ {
mToken = TK_EOL; mToken = TK_EOL;
NextChar(); NextChar();
@ -1321,3 +1394,234 @@ void Scanner::ParseNumberToken(void)
} }
} }
} }
int64 Scanner::PrepParseSimple(void)
{
int64 v = 0;
switch (mToken)
{
case TK_INTEGER:
v = mTokenInteger;
NextToken();
break;
case TK_SUB:
NextToken();
v = -PrepParseSimple();
break;
case TK_LOGICAL_NOT:
NextToken();
v = !PrepParseSimple();
break;
case TK_BINARY_NOT:
NextToken();
v = ~PrepParseSimple();
break;
case TK_OPEN_PARENTHESIS:
NextToken();
v = PrepParseConditional();
if (mToken == TK_CLOSE_PARENTHESIS)
NextToken();
else
mErrors->Error(mLocation, ERRR_PREPROCESSOR, "')' expected");
break;
default:
mErrors->Error(mLocation, ERRR_PREPROCESSOR, "Invalid preprocessor token", TokenName(mToken));
if (mToken != TK_EOL)
NextToken();
}
return v;
}
int64 Scanner::PrepParseMul(void)
{
int64 v = PrepParseSimple();
int64 u;
for (;;)
{
switch (mToken)
{
case TK_MUL:
NextToken();
v *= PrepParseSimple();
break;
case TK_DIV:
NextToken();
u = PrepParseSimple();
if (u == 0)
mErrors->Error(mLocation, ERRR_PREPROCESSOR, "Division by zero");
else
v /= u;
break;
case TK_MOD:
u = PrepParseSimple();
if (u == 0)
mErrors->Error(mLocation, ERRR_PREPROCESSOR, "Division by zero");
else
v %= u;
break;
default:
return v;
}
}
}
int64 Scanner::PrepParseAdd(void)
{
int64 v = PrepParseMul();
for (;;)
{
switch (mToken)
{
case TK_ADD:
NextToken();
v += PrepParseMul();
break;
case TK_SUB:
NextToken();
v -= PrepParseMul();
break;
default:
return v;
}
}
}
int64 Scanner::PrepParseShift(void)
{
int64 v = PrepParseAdd();
for (;;)
{
switch (mToken)
{
case TK_LEFT_SHIFT:
NextToken();
v <<= PrepParseAdd();
break;
case TK_RIGHT_SHIFT:
NextToken();
v >>= PrepParseAdd();
break;
default:
return v;
}
}
}
int64 Scanner::PrepParseRel(void)
{
int64 v = PrepParseShift();
for (;;)
{
switch (mToken)
{
case TK_LESS_THAN:
NextToken();
v = v < PrepParseShift();
break;
case TK_GREATER_THAN:
NextToken();
v = v > PrepParseShift();
break;
case TK_LESS_EQUAL:
NextToken();
v = v <= PrepParseShift();
break;
case TK_GREATER_EQUAL:
NextToken();
v = v >= PrepParseShift();
break;
case TK_EQUAL:
NextToken();
v = v == PrepParseShift();
break;
case TK_NOT_EQUAL:
NextToken();
v = v != PrepParseShift();
break;
default:
return v;
}
}
}
int64 Scanner::PrepParseBinaryAnd(void)
{
int64 v = PrepParseRel();
while (mToken == TK_BINARY_AND)
{
NextToken();
v &= PrepParseRel();
}
return v;
}
int64 Scanner::PrepParseBinaryXor(void)
{
int64 v = PrepParseBinaryAnd();
while (mToken == TK_BINARY_XOR)
{
NextToken();
v ^= PrepParseBinaryAnd();
}
return v;
}
int64 Scanner::PrepParseBinaryOr(void)
{
int64 v = PrepParseBinaryXor();
while (mToken == TK_BINARY_OR)
{
NextToken();
v |= PrepParseBinaryXor();
}
return v;
}
int64 Scanner::PrepParseLogicalAnd(void)
{
int64 v = PrepParseBinaryOr();
while (mToken == TK_LOGICAL_AND)
{
NextToken();
if (!PrepParseBinaryOr())
v = 0;
}
return v;
}
int64 Scanner::PrepParseLogicalOr(void)
{
int64 v = PrepParseLogicalAnd();
while (mToken == TK_LOGICAL_OR)
{
NextToken();
if (PrepParseLogicalAnd())
v = 1;
}
return v;
}
int64 Scanner::PrepParseConditional(void)
{
int64 v = PrepParseLogicalOr();
if (mToken == TK_QUESTIONMARK)
{
NextToken();
int64 vt = PrepParseConditional();
if (mToken == TK_COLON)
NextToken();
else
mErrors->Error(mLocation, ERRR_PREPROCESSOR, "':' expected");
int64 vf = PrepParseConditional();
if (v)
v = vt;
else
v = vf;
}
return v;
}

View File

@ -175,7 +175,8 @@ public:
Errors* mErrors; Errors* mErrors;
Preprocessor * mPreprocessor; Preprocessor * mPreprocessor;
int mPrepCondition, mPrepPending; int mPrepCondFalse, mPrepCondDepth;
bool mPrepCondExit;
int mOffset; int mOffset;
const char * mLine; const char * mLine;
@ -194,6 +195,7 @@ public:
void SetAssemblerMode(bool mode); void SetAssemblerMode(bool mode);
bool mAssemblerMode; bool mAssemblerMode;
bool mPreprocessorMode;
void AddMacro(const Ident* ident, const char* value); void AddMacro(const Ident* ident, const char* value);
protected: protected:
@ -215,4 +217,16 @@ protected:
bool NextChar(void); bool NextChar(void);
void ParseNumberToken(void); void ParseNumberToken(void);
int64 PrepParseSimple(void);
int64 PrepParseMul(void);
int64 PrepParseAdd(void);
int64 PrepParseShift(void);
int64 PrepParseRel(void);
int64 PrepParseBinaryAnd(void);
int64 PrepParseBinaryXor(void);
int64 PrepParseBinaryOr(void);
int64 PrepParseLogicalAnd(void);
int64 PrepParseLogicalOr(void);
int64 PrepParseConditional(void);
}; };