You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

810 lines
24 KiB

//////////////////////////////////////////////////////////////
// //
// Propeller Spin/PASM Compiler //
// (c)2012-2016 Parallax Inc. DBA Parallax Semiconductor. //
// Adapted from Chip Gracey's x86 asm code by Roy Eltham //
// See end of file for terms of use. //
// //
//////////////////////////////////////////////////////////////
//
// Elementizer.cpp
//
#include <string.h>
#include "PropellerCompilerInternal.h"
#include "Elementizer.h"
#include "SymbolEngine.h"
#include "ErrorStrings.h"
#include "Utilities.h"
// private
// set elementizer data from the currently set symbol entry
void Elementizer::SetFromSymbolEntry()
{
if (m_pSymbolEntry)
{
m_type = m_pSymbolEntry->m_data.type;
m_value = m_pSymbolEntry->m_data.value;
m_value_2 = m_pSymbolEntry->m_data.value_2;
if (m_pSymbolEntry->m_data.dual)
{
m_dual = true;
m_asm = m_pSymbolEntry->m_data.operator_type_or_asm;
}
else
{
m_dual = false;
m_opType = m_pSymbolEntry->m_data.operator_type_or_asm;
// fixup for AND and OR to have asm also
if (m_type == type_binary && m_opType == op_log_and)
{
m_asm = 0x18 + 0x40;
}
if (m_type == type_binary && m_opType == op_log_or)
{
m_asm = 0x1A + 0x40;
}
}
}
else
{
m_type = 0;
m_value = 0;
m_value_2 = 0;
m_asm = -1;
m_opType = -1;
}
}
// public
// reset to start of source
void Elementizer::Reset()
{
m_sourceOffset = 0;
m_sourceFlags = 0;
}
// get the next element in source, returns true no error, bEof will be set to true if eof is hit
bool Elementizer::GetNext(bool& bEof)
{
// update back data
m_backOffsets[m_backIndex&0x03] = m_sourceOffset;
m_backFlags[m_backIndex&0x03] = m_sourceFlags;
m_backIndex++;
// default to type_undefined
m_type = 0;
m_value = 0;
m_value_2 = 0;
m_asm = -1;
m_opType = -1;
m_pSymbolEntry = 0;
// no error, and not end of file
int error = error_none;
bEof = false;
bool bDocComment = false;
int constantBase = 0;
// setup source and symbol pointers
char* pSource = m_pCompilerData->source;
int sourceStart = m_sourceOffset;
m_currentSymbol[0] = 0;
int symbolOffset = 0;
bool bConstantOverflow = false;
for (;;)
{
char currentChar = pSource[m_sourceOffset++];
// parse
if (constantBase > 0)
{
// this handles reading in a constant of base 2, 4, 10, or 16
// the constantBase value is set based on prefix characters handled below
if (currentChar == '_')
{
// skip over _'s
continue;
}
char digitValue;
if (!CheckDigit(currentChar, digitValue, (char)constantBase))
{
char notUsed;
char nextChar = pSource[m_sourceOffset];
bool bNextCharDigit = CheckDigit(nextChar, notUsed, (char)constantBase);
if ((constantBase == 10 &&
(currentChar == '.' && bNextCharDigit)) ||
currentChar == 'e' || currentChar == 'E')
{
// handle float
bConstantOverflow = false;
m_sourceOffset = sourceStart;
if (GetFloat(pSource, m_sourceOffset, m_value))
{
m_sourceOffset--; // back up to point at last digit
m_type = type_con_float;
}
else
{
error = error_fpcmbw;
}
}
else
{
// done with this constant
m_sourceOffset--; // back up to point at last digit
m_type = type_con;
}
constantBase = 0;
break;
}
else
{
// multiply accumulate the constant
unsigned int oldValue = m_value;
m_value *= constantBase;
// check for overflow
if (((unsigned int)m_value / constantBase) != oldValue)
{
bConstantOverflow = true;
}
m_value += digitValue;
}
continue;
}
else if (m_sourceFlags != 0)
{
// old string? (continue parsing a string)
// for strings, m_sourceFlags will start out 0, and then cycle between 1 and 2 for
// each character of the string, when it is 1, a type_comma is returned, when it is
// 2 the next character is returned
// return a comma element between each character of the string
if (m_sourceFlags == 1)
{
m_sourceFlags++;
m_sourceOffset--;
m_type = type_comma;
break;
}
// reset flag
m_sourceFlags = 0;
// check for errors
if (currentChar == '\"')
{
error = error_es;
break;
}
else if (currentChar == 0)
{
m_sourceOffset--; // back up from eof
error = error_eatq;
break;
}
else if (currentChar == 13)
{
error = error_eatq;
break;
}
// return the character
m_value = currentChar;
// check the next character, if it's not a " then setup so the next
// call returns a type_comma, if it is a ", then we are done with this string
// and we leave the offset pointing after the "
currentChar = pSource[m_sourceOffset++];
if (currentChar != '\"')
{
m_sourceOffset--;
m_sourceFlags++;
}
// return the character constant
m_type = type_con;
break;
}
else if (currentChar == '\"')
{
// new string (start parsing a string)
// we got here because m_sourceFlags was 0 and the character is a "
// get first character of string
currentChar = pSource[m_sourceOffset++];
// check for errors
if (currentChar == '\"')
{
error = error_es;
break;
}
else if (currentChar == 0)
{
m_sourceOffset--; // back up from eof
error = error_eatq;
break;
}
else if (currentChar == 13)
{
error = error_eatq;
break;
}
// return the character in value
m_value = currentChar & 0x000000FF;
// check the next character, it's it's not a " then setup so the next
// call returns a type_comma, if it is a " then it means it's a one character
// string and we leave the offset pointing after the "
currentChar = pSource[m_sourceOffset++];
if (currentChar != '\"')
{
m_sourceOffset--; // back up, so this character will be read after the type_comma
m_sourceFlags = 1; // cause the next call to return a type_comma
}
// return the character constant
m_type = type_con;
break;
}
else if (currentChar == 0)
{
// eof
m_type = type_end;
bEof = true;
m_sourceOffset--;
sourceStart = m_sourceOffset;
break;
}
else if (currentChar == 13)
{
// eol
m_type = type_end;
break;
}
else if (currentChar <= ' ')
{
// space or tab?
sourceStart = m_sourceOffset;
continue;
}
else if (currentChar == '\'')
{
// comment
// read until end of line or file, handle doc comment
if (pSource[m_sourceOffset] == '\'')
{
m_sourceOffset++; // skip over second '
bDocComment = true;
g_pCompilerData->doc_flag = true;
}
for (;;)
{
currentChar = pSource[m_sourceOffset++];
if (currentChar == 0)
{
m_sourceOffset--; // back up from eof
m_type = type_end;
bEof = true;
break;
}
if (bDocComment)
{
DocPrint(currentChar);
}
if (currentChar == 13)
{
m_type = type_end;
break;
}
}
break;
}
else if (currentChar == '{')
{
// brace comment
// read the whole comment, handling doc comments as needed
int braceCommentLevel = 1;
if (pSource[m_sourceOffset] == '{')
{
m_sourceOffset++; // skip over second {
bDocComment = true;
g_pCompilerData->doc_flag = true;
if (pSource[m_sourceOffset] == 13)
{
m_sourceOffset++; // skip over end if present
}
}
for (;;)
{
currentChar = pSource[m_sourceOffset++];
if (currentChar == 0)
{
if (bDocComment)
{
error = error_erbb;
}
else
{
error = error_erb;
}
m_sourceOffset--; // back up from eof
sourceStart = m_sourceOffset;
break;
}
else if (!bDocComment && currentChar == '{')
{
braceCommentLevel++;
}
else if (currentChar == '}')
{
if (bDocComment && pSource[m_sourceOffset] == '}')
{
m_sourceOffset++; // skip over second }
break;
}
else if (!bDocComment)
{
braceCommentLevel--;
if (braceCommentLevel < 1)
{
break;
}
}
}
else if (bDocComment)
{
DocPrint(currentChar);
}
}
if (error == error_none)
{
sourceStart = m_sourceOffset;
continue;
}
else
{
break;
}
}
else if (currentChar == '}')
{
// unmatched brace comment end
error = error_bmbpbb;
break;
}
else if (currentChar == '%')
{
// binary
currentChar = pSource[m_sourceOffset++];
char temp;
if (currentChar == '%')
{
// double binary
currentChar = pSource[m_sourceOffset++];
if (!CheckDigit(currentChar, temp, 4))
{
error = error_idbn;
break;
}
constantBase = 4;
}
else
{
if (!CheckDigit(currentChar, temp, 2))
{
error = error_idbn;
break;
}
constantBase = 2;
}
m_sourceOffset--; // back up to first digit
// constantBase is now set, so loop back around to read in the constant
continue;
}
else if (currentChar == '$')
{
// hex
currentChar = pSource[m_sourceOffset++];
char temp;
if (!CheckDigit(currentChar, temp, 16))
{
m_sourceOffset--;
m_type = type_asm_org;
break;
}
constantBase = 16;
m_sourceOffset--; // back up to first digit
// constantBase is now set, so loop back around to read in the constant
continue;
}
else if (currentChar >= '0' && currentChar <= '9')
{
// dec
constantBase = 10;
m_sourceOffset--; // back up to first digit
// constantBase is now set, so loop back around to read in the constant
continue;
}
else
{
// symbol
currentChar = Uppercase(currentChar);
if (CheckWordChar(currentChar))
{
// do word symbol
while(CheckWordChar(currentChar) && symbolOffset <= symbol_limit)
{
m_currentSymbol[symbolOffset++] = currentChar;
currentChar = Uppercase(pSource[m_sourceOffset++]);
}
if (symbolOffset > symbol_limit)
{
error = error_sexc;
}
else
{
// back up so we point at last char of symbol
m_sourceOffset--;
// terminate symbol
m_currentSymbol[symbolOffset] = 0;
m_pSymbolEntry = m_pSymbolEngine->FindSymbol(m_currentSymbol);
}
}
else
{
// try non-word symbol (one or two char operators)
m_currentSymbol[symbolOffset++] = currentChar;
currentChar = pSource[m_sourceOffset++];
bool bDoOneChar = false;
bool bDoTwoChar = false;
// if the next char is not whitespace or eol
if (currentChar > ' ')
{
// three char symbol
// assign second char into symbol
m_currentSymbol[symbolOffset++] = currentChar;
// read third char into symbol
m_currentSymbol[symbolOffset++] = pSource[m_sourceOffset++];
// terminate symbol
m_currentSymbol[symbolOffset] = 0;
m_pSymbolEntry = m_pSymbolEngine->FindSymbol(m_currentSymbol);
if (m_pSymbolEntry == 0)
{
bDoTwoChar = true;
symbolOffset--;
}
}
if (bDoTwoChar)
{
// two char symbol
// back up so we point at last char of symbol
m_sourceOffset--;
// terminate symbol
m_currentSymbol[symbolOffset] = 0;
m_pSymbolEntry = m_pSymbolEngine->FindSymbol(m_currentSymbol);
if (m_pSymbolEntry == 0)
{
bDoOneChar = true;
symbolOffset--;
}
}
if (bDoOneChar || currentChar <= ' ')
{
// one char symbol
// back up so we point at last char of symbol
m_sourceOffset--;
// terminate symbol
m_currentSymbol[symbolOffset] = 0;
m_pSymbolEntry = m_pSymbolEngine->FindSymbol(m_currentSymbol);
if (m_pSymbolEntry == 0)
{
error = error_uc;
}
}
}
break;
}
}
if (bConstantOverflow)
{
error = error_ce32b;
}
// update pointers
m_pCompilerData->source_start = sourceStart;
m_pCompilerData->source_finish = m_sourceOffset;
// if we got a symbol, then set the type, value, etc.
if (m_type == 0 && m_pSymbolEntry)
{
SetFromSymbolEntry();
}
if (error != error_none)
{
m_pCompilerData->error = true;
m_pCompilerData->error_msg = g_pErrorStrings[error];
return false;
}
return true;
}
// if the next element is type, then return true, else false, retains value
bool Elementizer::GetElement(int type)
{
int value = m_value; // save current value
bool bEof = false;
GetNext(bEof);
if (GetType() != type)
{
m_pCompilerData->error = true;
int errorNum = 0;
switch (type)
{
case type_left: errorNum = error_eleft; break;
case type_right: errorNum = error_eright; break;
case type_rightb: errorNum = error_erightb; break;
case type_comma: errorNum = error_ecomma; break;
case type_pound: errorNum = error_epound; break;
case type_colon: errorNum = error_ecolon; break;
case type_dot: errorNum = error_edot; break;
case type_sub: errorNum = error_easn; break;
case type_end: errorNum = error_eeol; break;
}
m_pCompilerData->error_msg = g_pErrorStrings[errorNum];
return false;
}
m_value = value; // restore saved value
return true;
}
// check if next element is of the given type, if so return true, if not, backup and return false
bool Elementizer::CheckElement(int type)
{
bool bEof = false;
GetNext(bEof);
if (GetType() == type)
{
return true;
}
Backup();
return false;
}
// scan for the next block element of type, returns true if no error, , bEof will be set to true if eof is hit
bool Elementizer::GetNextBlock(int type, bool& bEof)
{
bool bFound = false;
while(bFound == false)
{
if (GetNext(bEof) == false || bEof == true)
{
break;
}
if (GetType() == type_block && GetValue() == type)
{
if (GetColumn() != 1)
{
m_pCompilerData->error = true;
m_pCompilerData->error_msg = g_pErrorStrings[error_bdmbifc];
return false;
}
bFound = true;
}
}
// if we found the block or we hit eof, then we got no error so return true
return (bFound || bEof);
}
// returns column of most recent Element gotten
int Elementizer::GetColumn()
{
char* pSource = m_pCompilerData->source;
int sourceStart = m_pCompilerData->source_start;
if (sourceStart == 0)
{
// we are at the start of the source, so return 1
return 1;
}
// back up until we hit a CR character
while(pSource[sourceStart] != 13 && sourceStart > 0)
{
sourceStart--;
}
// advance forward one, (off of the CR)
sourceStart++;
if (sourceStart == m_pCompilerData->source_start)
{
// we are at the start of the line, so return 1
return 1;
}
// adjust source pointer to start of line
pSource += sourceStart;
// adjust sourceStart such that it is how many characters we backed up
sourceStart = m_pCompilerData->source_start - sourceStart;
// count the characters we backed up over, accounting for tabs (tabs are 8 chars)
int column = 0;
for (int i = 0; i < sourceStart; i++)
{
if (pSource[i] == 9)
{
column |= 7;
}
column++;
}
return column + 1;
}
int Elementizer::GetCurrentLineNumber(int &offsetToStartOfLine, int& offsetToEndOfLine)
{
int lineCount = 1;
char* pSource = m_pCompilerData->source;
int scanEnd = m_pCompilerData->source_start;
offsetToStartOfLine = -1;
while (scanEnd > 0)
{
if (pSource[--scanEnd] == 13)
{
if (offsetToStartOfLine == -1)
{
offsetToStartOfLine = scanEnd+1;
}
lineCount++;
}
}
if (offsetToStartOfLine == -1)
{
offsetToStartOfLine = 0;
}
scanEnd = m_pCompilerData->source_start;
while (pSource[scanEnd] != 0)
{
if (pSource[scanEnd] == 13 || pSource[scanEnd] == 0)
{
break;
}
scanEnd++;
}
offsetToEndOfLine = scanEnd;
return lineCount;
}
// backup to the previous element
void Elementizer::Backup()
{
m_backIndex--;
m_sourceOffset = m_backOffsets[m_backIndex&0x03];
m_sourceFlags = m_backFlags[m_backIndex&0x03];
}
void Elementizer::ObjConToCon()
{
m_type -= (type_objcon - type_con);
}
void Elementizer::DatResToLong()
{
if (m_type == type_dat_long_res)
{
m_type = type_dat_long;
}
}
bool Elementizer::SubToNeg()
{
if (m_type == type_binary && m_opType == op_sub)
{
m_type = type_unary;
m_opType = op_neg;
m_value = 0;
m_value_2 = 0;
return true;
}
return false;
}
bool Elementizer::NegConToCon()
{
if (m_type == type_binary && m_opType == op_sub)
{
int savedValue = m_value;
bool bEof = false;
if (!GetNext(bEof))
{
return false;
}
if (m_type == type_con)
{
m_value = -m_value;
}
else if (m_type == type_con_float)
{
m_value |= 0x80000000;
}
else
{
Backup();
m_type = type_binary;
m_asm = -1;
m_opType = op_sub;
m_value = savedValue;
}
}
return true;
}
bool Elementizer::FindSymbol(const char* symbol)
{
m_pSymbolEntry = m_pSymbolEngine->FindSymbol(symbol);
SetFromSymbolEntry();
return true;
}
void Elementizer::BackupSymbol()
{
strcpy(m_pCompilerData->symbolBackup, m_currentSymbol);
}
///////////////////////////////////////////////////////////////////////////////////////////
// TERMS OF USE: MIT License //
///////////////////////////////////////////////////////////////////////////////////////////
// Permission is hereby granted, free of charge, to any person obtaining a copy of this //
// software and associated documentation files (the "Software"), to deal in the Software //
// without restriction, including without limitation the rights to use, copy, modify, //
// merge, publish, distribute, sublicense, and/or sell copies of the Software, and to //
// permit persons to whom the Software is furnished to do so, subject to the following //
// conditions: //
// //
// The above copyright notice and this permission notice shall be included in all copies //
// or substantial portions of the Software. //
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, //
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A //
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT //
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION //
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE //
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //
///////////////////////////////////////////////////////////////////////////////////////////