Add full preprocessor support
This commit is contained in:
parent
fd10525780
commit
6b6122bc07
42
README.md
42
README.md
|
@ -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 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
|
||||
|
||||
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
|
||||
|
||||
* No union type
|
||||
* No long integer
|
||||
* No struct function return
|
||||
* Missing const checks for structs and enums
|
||||
* No static variables in functions
|
||||
* Missing warnings for all kind of abuses
|
||||
* no #if in preprocessor
|
||||
|
||||
### Linker
|
||||
|
||||
|
@ -34,7 +39,6 @@ The first release of the compiler is severely limited considering it is only two
|
|||
|
||||
### Standard Libraries
|
||||
|
||||
* Limited formatting in printf
|
||||
* No file functions
|
||||
|
||||
### Runtime
|
||||
|
@ -46,12 +50,11 @@ The first release of the compiler is severely limited considering it is only two
|
|||
### Optimizing
|
||||
|
||||
* All global variables are considered volatile
|
||||
* No loop opmtimization
|
||||
* Simple loop opmtimization
|
||||
* Poor bookeeping of callee saved registers
|
||||
* Missing livetime reduction of intermediates
|
||||
* No block domination analysis
|
||||
* Partial block domination analysis
|
||||
* 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
|
||||
|
||||
|
@ -60,8 +63,23 @@ The first release of the compiler is severely limited considering it is only two
|
|||
|
||||
### Native code generation
|
||||
|
||||
* Calling non native functions missing
|
||||
* 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
|
||||
|
||||
|
@ -69,6 +87,9 @@ The compiler does a full program compile, the linker step is part of the compila
|
|||
|
||||
#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:
|
||||
|
||||
|
@ -123,4 +144,3 @@ Routines can be marked to be compiled to 6502 machine code with the native pragm
|
|||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ enum ErrorID
|
|||
EERR_ASM_INVALID_INSTRUCTION,
|
||||
EERR_ASM_INVALID_MODE,
|
||||
EERR_PRAGMA_PARAMETER,
|
||||
ERRR_PREPROCESSOR,
|
||||
|
||||
EERR_INVALID_PREPROCESSOR,
|
||||
};
|
||||
|
|
|
@ -252,9 +252,11 @@ Scanner::Scanner(Errors* errors, Preprocessor* preprocessor)
|
|||
{
|
||||
mOffset = 0;
|
||||
mLine = mPreprocessor->mLine;
|
||||
mPrepCondition = 0;
|
||||
mPrepPending = 0;
|
||||
mPrepCondFalse = 0;
|
||||
mPrepCondDepth = 0;
|
||||
mPrepCondExit = 0;
|
||||
mAssemblerMode = false;
|
||||
mPreprocessorMode = false;
|
||||
mMacroExpansion = nullptr;
|
||||
|
||||
mDefines = new MacroDict();
|
||||
|
@ -358,13 +360,71 @@ void Scanner::NextToken(void)
|
|||
NextRawToken();
|
||||
if (mToken == TK_PREP_ENDIF)
|
||||
{
|
||||
if (mPrepCondition > 0)
|
||||
mPrepCondition--;
|
||||
else if (mPrepPending > 0)
|
||||
mPrepPending--;
|
||||
if (mPrepCondFalse > 0)
|
||||
mPrepCondFalse--;
|
||||
else if (mPrepCondExit)
|
||||
mPrepCondExit = false;
|
||||
else if (mPrepCondDepth > 0)
|
||||
mPrepCondDepth--;
|
||||
else
|
||||
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)
|
||||
{
|
||||
if (!mPreprocessor->CloseSource())
|
||||
|
@ -372,10 +432,10 @@ void Scanner::NextToken(void)
|
|||
mToken = TK_NONE;
|
||||
mOffset = 0;
|
||||
}
|
||||
else if (mPrepCondition > 0)
|
||||
else if (mPrepCondFalse > 0 || mPrepCondExit)
|
||||
{
|
||||
if (mToken == TK_PREP_IFDEF || mToken == TK_PREP_IFNDEF)
|
||||
mPrepCondition++;
|
||||
if (mToken == TK_PREP_IFDEF || mToken == TK_PREP_IFNDEF || mToken == TK_PREP_IF)
|
||||
mPrepCondFalse++;
|
||||
}
|
||||
else if (mToken == TK_PREP_INCLUDE)
|
||||
{
|
||||
|
@ -439,9 +499,9 @@ void Scanner::NextToken(void)
|
|||
{
|
||||
Macro * def = mDefines->Lookup(mTokenIdent);
|
||||
if (def)
|
||||
mPrepPending++;
|
||||
mPrepCondDepth++;
|
||||
else
|
||||
mPrepCondition++;
|
||||
mPrepCondFalse++;
|
||||
}
|
||||
}
|
||||
else if (mToken == TK_PREP_IFNDEF)
|
||||
|
@ -451,11 +511,24 @@ void Scanner::NextToken(void)
|
|||
{
|
||||
Macro * def = mDefines->Lookup(mTokenIdent);
|
||||
if (!def)
|
||||
mPrepPending++;
|
||||
mPrepCondDepth++;
|
||||
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)
|
||||
{
|
||||
Macro* def = nullptr;
|
||||
|
@ -551,7 +624,7 @@ void Scanner::NextRawToken(void)
|
|||
|
||||
while (IsWhitespace(mTokenChar))
|
||||
{
|
||||
if (mAssemblerMode && mTokenChar == '\n')
|
||||
if ((mAssemblerMode || mPreprocessorMode) && mTokenChar == '\n')
|
||||
{
|
||||
mToken = TK_EOL;
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -175,7 +175,8 @@ public:
|
|||
Errors* mErrors;
|
||||
Preprocessor * mPreprocessor;
|
||||
|
||||
int mPrepCondition, mPrepPending;
|
||||
int mPrepCondFalse, mPrepCondDepth;
|
||||
bool mPrepCondExit;
|
||||
|
||||
int mOffset;
|
||||
const char * mLine;
|
||||
|
@ -194,6 +195,7 @@ public:
|
|||
void SetAssemblerMode(bool mode);
|
||||
|
||||
bool mAssemblerMode;
|
||||
bool mPreprocessorMode;
|
||||
|
||||
void AddMacro(const Ident* ident, const char* value);
|
||||
protected:
|
||||
|
@ -215,4 +217,16 @@ protected:
|
|||
bool NextChar(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);
|
||||
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue