// Copyright (C) 1999-2003 Core Technologies. // // This file is part of tpasm. // // tpasm is free software; you can redistribute it and/or modify // it under the terms of the tpasm LICENSE AGREEMENT. // // tpasm is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // tpasm LICENSE AGREEMENT for more details. // // You should have received a copy of the tpasm LICENSE AGREEMENT // along with tpasm; see the file "LICENSE.TXT". // Parse a line in various ways #include "include.h" struct OPERATORTOKEN // token list definition { char *token; int precedence; // keeps relative precedence of operators bool binaryOperator; // set true if operator can be used as a binary operator int tokenNum; }; enum { ET_NUM, // immediate number ET_STRING, // immediate string ET_LOCAL_LABEL, // local label ET_LABEL, // label ET_OPERATOR // expression operator }; struct EXPRESSION_ELEMENT { int elementType; char elementString[MAXSTRING]; // parsed string union { int numData; // if element is numeric, it is converted to binary and placed here OPERATORTOKEN *opToken; // if element is an operator, a pointer to its token is placed here }; }; enum { OP_STRLEN, OP_HIGH, OP_LOW, OP_MSW, OP_LSW, OP_SHIFTLEFT, OP_SHIFTRIGHT, OP_LESSOREQUAL, OP_GREATEROREQUAL, OP_EQUAL, OP_NOTEQUAL, OP_LOGICALAND, OP_LOGICALOR, OP_MULTIPLY, OP_DIVIDE, OP_MOD, OP_ADD, OP_SUBTRACT, OP_LESSTHAN, OP_GREATERTHAN, OP_AND, OP_XOR, OP_OR, OP_LOGICALNOT, OP_NOT, OP_LEFTPAREN, OP_RIGHTPAREN, }; // NOTE: the tokens below need to be placed in order from // longest to shortest to guarantee that the parser will extract // the longest valid operator which is available // NOTE: the precedence value tells which operators take precedence // over others. All unary operators are assumed by the code to have // the highest precedence. OPERATORTOKEN opTokenList[] = { {".strlen.",0,false,OP_STRLEN}, {"high",0,false,OP_HIGH}, {"low",0,false,OP_LOW}, {"msw",0,false,OP_MSW}, {"lsw",0,false,OP_LSW}, {"<<",8,true,OP_SHIFTLEFT}, {">>",8,true,OP_SHIFTRIGHT}, {"<=",7,true,OP_LESSOREQUAL}, {">=",7,true,OP_GREATEROREQUAL}, {"==",6,true,OP_EQUAL}, {"!=",6,true,OP_NOTEQUAL}, {"&&",5,true,OP_LOGICALAND}, {"||",4,true,OP_LOGICALOR}, {"*",10,true,OP_MULTIPLY}, {"/",10,true,OP_DIVIDE}, {"%",10,true,OP_MOD}, {"+",9,true,OP_ADD}, {"-",9,true,OP_SUBTRACT}, // - can be both binary and unary operator, but since it _can_ be used as binary, set binaryOperator true {"<",7,true,OP_LESSTHAN}, {">",7,true,OP_GREATERTHAN}, {"&",4,true,OP_AND}, {"^",2,true,OP_XOR}, {"|",1,true,OP_OR}, {"!",0,false,OP_LOGICALNOT}, {"~",0,false,OP_NOT}, {"(",0,false,OP_LEFTPAREN}, {")",0,false,OP_RIGHTPAREN}, {"",0} }; static inline bool IsWhiteChar(char theChar) // return true if theChar is white-space, false if not { return(theChar==' '||theChar=='\t'); } static inline bool IsCommaSeparator(char theChar) // see if theChar is a list separator { return(theChar==','); } static bool MatchBurriedToken(char *theString,int *theIndex,OPERATORTOKEN *theList,OPERATORTOKEN **theToken) // try to match characters of theString starting at theIndex against the list of tokens, // if a match is made, move theIndex to the end of the match in theString, // and return the matched token in theToken // if nothing is matched, return false { int i,j; bool found; char *stringA, *stringB; found=false; i=0; while(!found&&(theList[i].token[0]!='\0')) { stringA=&theString[*theIndex]; stringB=&theList[i].token[0]; j=0; while((*stringA)&&(tolower(*stringA)==*stringB)) { stringA++; stringB++; j++; } if(!(*stringB)) // got to end of token? { (*theIndex)+=j; found=true; *theToken=&theList[i]; } i++; } return(found); } bool ParseWhiteSpace(char *theLine,int *lineIndex) // push lineIndex past any white space in theLine // If there was no white space, return false { if(IsWhiteChar(theLine[*lineIndex])) { while(IsWhiteChar(theLine[++(*lineIndex)])) ; return(true); } return(false); } static inline bool IsCommentStart(char *theLine,int *lineIndex) // see if a comment is starting at the given location, or if the line is ending { return(theLine[*lineIndex]=='\0'||theLine[*lineIndex]==';'||(theLine[*lineIndex]=='/'&&theLine[(*lineIndex)+1]=='/')); // comment starting, or end of line? } bool ParseComment(char *theLine,int *lineIndex) // Return true if theLine is empty, or contains a comment { ParseWhiteSpace(theLine,lineIndex); // move past any white space return(IsCommentStart(theLine,lineIndex)); } bool IsStartLabelChar(char theChar) // return true if theChar is a character that can be used at the start of a label { return((theChar>='A'&&theChar<='Z')||(theChar>='a'&&theChar<='z')||(theChar=='_')); } bool IsLabelChar(char theChar) // return true if theChar is a character that can be used anywhwere in a label { return(IsStartLabelChar(theChar)||(theChar>='0'&&theChar<='9')); } bool ParseLabel(char *theLine,int *lineIndex,char *theLabel,bool *isLocal) // Attempt to parse a label from theLine at lineIndex, skipping // white space if desired // leave lineIndex pushed past the last thing successfully parsed // If the label is recognized as local (because it begins with a '.' or an '@'), // this will adjust it for the current scope. { int outputIndex; int localIndex; localIndex=*lineIndex; // keep index here for a while outputIndex=0; *isLocal=false; if(theLine[localIndex]=='.'||theLine[localIndex]=='@') // see if local label { sprintf(theLabel,"%s@%d@",scope,scopeValue); outputIndex=strlen(theLabel); // move to the end localIndex++; *isLocal=true; } if(IsStartLabelChar(theLine[localIndex])) // verify that the first character looks like a label { do { theLabel[outputIndex++]=theLine[localIndex++]; } while(IsLabelChar(theLine[localIndex])); theLabel[outputIndex]='\0'; // terminate the label we just collected *lineIndex=localIndex; // update output index return(true); // return the label } return(false); // no label found } static bool ParseExpressionOperator(char *theLine,int *lineIndex,EXPRESSION_ELEMENT *theElement) // attempt to parse an operator from theLine at lineIndex // return the operator found with lineIndex updated, or false if none located { int initialIndex; initialIndex=*lineIndex; if(MatchBurriedToken(theLine,lineIndex,opTokenList,&theElement->opToken)) { // Either the last character of the token, or the first character of what follows // it must not be a label character for this to match. // This is because one could define a label HIGH_COUNT // which could match the operator "high" (which is undesirable) if(!IsLabelChar(theLine[(*lineIndex)-1])||!IsLabelChar(theLine[*lineIndex])) { // if we got a token, copy it into the element's string strncpy(&theElement->elementString[0],&theLine[initialIndex],(*lineIndex)-initialIndex); theElement->elementString[(*lineIndex)-initialIndex]='\0'; theElement->elementType=ET_OPERATOR; return(true); } else { *lineIndex=initialIndex; // put this back as if no match located } } return(false); } static bool GetRadixDigitValue(char theDigit,int radix,int *theValue) // Return theValue as a binary representation of the value of theDigit given in // radix // NOTE: if theDigit is not valid for radix, return false { if(theDigit>='a') { theDigit-='a'-'A'; // pull back to upper case } if((theDigit>='0'&&theDigit<='9')||(theDigit>='A'&&theDigit<='Z')) { theDigit-='0'; // make binary if(theDigit>9) { theDigit-='A'-'0'-10; } if(theDigitelementString[outputIndex++]=theLine[inputIndex++]; // copy the prefix } if(ParseRadixNumber(theLine,&inputIndex,theElement->elementString,&outputIndex,&theElement->numData,radix)) { theElement->elementString[outputIndex]='\0'; // terminate the output string theElement->elementType=ET_NUM; *lineIndex=inputIndex; return(true); } return(false); } static bool ParseQuotedNumber(char *theLine,int *lineIndex,int radix,EXPRESSION_ELEMENT *theElement) // a quoted number with a given radix is arriving, so parse it into theElement // if there is a problem, return false { int outputIndex, inputIndex; inputIndex=*lineIndex; outputIndex=0; theElement->elementString[outputIndex++]=theLine[inputIndex++]; // copy the prefix theElement->elementString[outputIndex++]=theLine[inputIndex++]; // copy the quote if(ParseRadixNumber(theLine,&inputIndex,theElement->elementString,&outputIndex,&theElement->numData,radix)) { if(theLine[inputIndex]=='\'') { theElement->elementString[outputIndex++]='\''; theElement->elementString[outputIndex]='\0'; // terminate the output string theElement->elementType=ET_NUM; *lineIndex=inputIndex+1; return(true); } } return(false); } static bool ParseDecimalOrPostfixNumber(char *theLine,int *lineIndex,EXPRESSION_ELEMENT *theElement) // The next thing on the line should be interpreted as a decimal number, // or a number with a postfix radix specifier. // parse it into theElement // if there is a problem (does not look like a proper decimal or postfix number), return false { int outputIndex, inputIndex; bool done; int i; int radix; inputIndex=*lineIndex; outputIndex=0; // first run to the end of the string of digits and letters, attempting to locate // a radix specifier i=*lineIndex; done=false; while(!done) { if((theLine[i]>='0'&&theLine[i]<='9')|| // decimal digit ((theLine[i]>='A'&&theLine[i]<='F')||(theLine[i]>='a'&&theLine[i]<='f'))|| // hex digit, or binary or decimal specifier (theLine[i]=='H'||theLine[i]=='h'||theLine[i]=='O'||theLine[i]=='o')) // hex specifier, or octal specifier { i++; // step over it } else { done=true; // something which is none of the above } } if(theLine[i-1]>='0'&&theLine[i-1]<='9') // raw decimal number { if(ParseRadixNumber(theLine,&inputIndex,theElement->elementString,&outputIndex,&theElement->numData,10)) // fetch in the decimal number { if(inputIndex==i) // verify we have read all the digits (nothing funky in between) { theElement->elementString[outputIndex]='\0'; // terminate the output string theElement->elementType=ET_NUM; *lineIndex=inputIndex; // push to the end of the number return(true); } } } else { switch(theLine[i-1]) // look at the specifier { case 'b': case 'B': radix=2; break; case 'o': case 'O': radix=8; break; case 'd': case 'D': radix=10; break; case 'h': case 'H': radix=16; break; default: return(false); // something other than a digit or specifier at the end of the string break; } if(ParseRadixNumber(theLine,&inputIndex,theElement->elementString,&outputIndex,&theElement->numData,radix)) // fetch in the number { if(inputIndex==(i-1)) // verify we have read up until the radix specifier (nothing funky in between) { theElement->elementString[outputIndex++]=theLine[i-1]; // drop in the specifier theElement->elementString[outputIndex]='\0'; // terminate the output string theElement->elementType=ET_NUM; *lineIndex=inputIndex+1; // push past the specifier return(true); } } } return(false); } bool ParseQuotedString(char *theLine,int *lineIndex,char startQuote,char endQuote,char *theString,int *stringLength) // Parse a quoted string (if none can be found, return false) // NOTE: if a quoted string contains \'s, they will quote the next character, and possibly interpret it // \a, \b, \f, \n, \r, \t, \v. \x## and \0## are interpreted // NOTE: the outer quotes are not returned in theString // NOTE: since the string could contain NULs, the length is returned { int outputIndex; int localIndex; int digitValue, value; ParseWhiteSpace(theLine,lineIndex); // move past any white space outputIndex=0; if(theLine[*lineIndex]==startQuote) { localIndex=(*lineIndex)+1; // skip over the start quote while((theLine[localIndex]!=endQuote)&&theLine[localIndex]) { if(theLine[localIndex]!='\\') { theString[outputIndex++]=theLine[localIndex++]; } else { localIndex++; // skip over the '\' if(theLine[localIndex]) { switch(theLine[localIndex]) { case 'a': // alert theString[outputIndex++]='\a'; localIndex++; break; case 'b': // backspace theString[outputIndex++]='\b'; localIndex++; break; case 'f': // form feed theString[outputIndex++]='\f'; localIndex++; break; case 'n': // newline theString[outputIndex++]='\n'; localIndex++; break; case 'r': // return theString[outputIndex++]='\r'; localIndex++; break; case 't': // tab theString[outputIndex++]='\t'; localIndex++; break; case 'v': // vertical tab theString[outputIndex++]='\v'; localIndex++; break; case 'x': // hex number localIndex++; // skip the introduction value=0; while(theLine[localIndex]&&GetRadixDigitValue(theLine[localIndex],16,&digitValue)) { localIndex++; value=value*16+digitValue; } theString[outputIndex++]=value&0xFF; break; case '0': // octal number localIndex++; // skip the introduction value=0; while(theLine[localIndex]&&GetRadixDigitValue(theLine[localIndex],8,&digitValue)) { localIndex++; value=value*8+digitValue; } theString[outputIndex++]=value&0xFF; break; default: theString[outputIndex++]=theLine[localIndex++]; break; } } } } if(theLine[localIndex]==endQuote) // make sure it ended { theString[outputIndex]='\0'; // terminate the string *stringLength=outputIndex; // return the length (excluding the termination we added) *lineIndex=localIndex+1; // skip over the terminating quote return(true); } } return(false); // none found } bool ParseQuotedStringVerbatim(char *theLine,int *lineIndex,char startQuote,char endQuote,char *theString) // Parse a quoted string (if none can be found, return false) // NOTE: if a quoted string contains \'s, they will quote the next character, but will be left in // the output, along with an unmodified next character // NOTE: the outer quotes are not returned in theString { int outputIndex; int localIndex; ParseWhiteSpace(theLine,lineIndex); // move past any white space outputIndex=0; if(theLine[*lineIndex]==startQuote) { localIndex=(*lineIndex)+1; // skip over the start quote while((theLine[localIndex]!=endQuote)&&theLine[localIndex]) { theString[outputIndex++]=theLine[localIndex++]; if(theLine[localIndex-1]=='\\') { if(theLine[localIndex]) { theString[outputIndex++]=theLine[localIndex++]; } } } if(theLine[localIndex]==endQuote) // make sure it ended { theString[outputIndex]='\0'; // terminate the string *lineIndex=localIndex+1; // skip over the terminating quote return(true); } } return(false); // none found } static bool ParseASCIIConstant(char *theLine,int *lineIndex,EXPRESSION_ELEMENT *theElement) // parse ascii data into a number as an ascii constant // if there is a problem, return false { int inputIndex; int i; int stringLength; inputIndex=*lineIndex; if(ParseQuotedString(theLine,&inputIndex,'\'','\'',&theElement->elementString[0],&stringLength)) { theElement->numData=0; i=0; while(inumData<<=8; theElement->numData|=theElement->elementString[i]; i++; } theElement->elementType=ET_NUM; *lineIndex=inputIndex; return(true); } return(false); } static bool ParseExpressionNumber(char *theLine,int *lineIndex,EXPRESSION_ELEMENT *theElement) // See if a number can be parsed from theLine at lineIndex // The following forms are supported: // // ### decimal // 'cccc' for ascii // // C style // 0b### binary // 0o### octal // 0d### decimal // 0x### hex // // Microchip style // A'###' ascii // B'###' binary // O'###' octal // D'###' decimal // H'###' hex // // Intel style // ###b binary // ###B binary // ###o octal // ###O octal // ###d decimal // ###D decimal // ###h hex (first digit must be 0-9) // ###H hex (first digit must be 0-9) // // Motorola style // %### binary // .### decimal // $### hex // // return the number found with lineIndex updated, or false if none located { int localIndex; switch(theLine[*lineIndex]) { case '0': // leading 0? switch(theLine[(*lineIndex)+1]) { case 'b': // could be 0b (0 in binary), OR 0b1101 (binary 1101) OR 0b3cFh (0b3cf in hex) if(ParseDecimalOrPostfixNumber(theLine,lineIndex,theElement)) { return(true); } else { return(ParsePrefixedNumber(theLine,lineIndex,2,2,theElement)); // try it as binary } break; case 'o': // could be 0o (0 in octal), OR 0o777 (octal 777) if(ParseDecimalOrPostfixNumber(theLine,lineIndex,theElement)) { return(true); } else { return(ParsePrefixedNumber(theLine,lineIndex,8,2,theElement)); } break; case 'd': // could be 0d (0 in decimal), OR 0d1234 (decimal 1234) OR 0d45h (0d45 in hex) if(ParseDecimalOrPostfixNumber(theLine,lineIndex,theElement)) { return(true); } else { return(ParsePrefixedNumber(theLine,lineIndex,10,2,theElement)); // try it as base 10 } break; case 'x': return(ParsePrefixedNumber(theLine,lineIndex,16,2,theElement)); break; default: return(ParseDecimalOrPostfixNumber(theLine,lineIndex,theElement)); break; } break; case 'A': if(theLine[(*lineIndex)+1]=='\'') { localIndex=(*lineIndex)+1; // skip over the 'A' if(ParseASCIIConstant(theLine,&localIndex,theElement)) // see if we actually got a constant { *lineIndex=localIndex; return(true); } return(false); } break; case 'B': if(theLine[(*lineIndex)+1]=='\'') { return(ParseQuotedNumber(theLine,lineIndex,2,theElement)); } break; case 'O': if(theLine[(*lineIndex)+1]=='\'') { return(ParseQuotedNumber(theLine,lineIndex,8,theElement)); } break; case 'D': if(theLine[(*lineIndex)+1]=='\'') { return(ParseQuotedNumber(theLine,lineIndex,10,theElement)); } break; case 'H': if(theLine[(*lineIndex)+1]=='\'') { return(ParseQuotedNumber(theLine,lineIndex,16,theElement)); } break; case '$': if((theLine[(*lineIndex)+1]>='0'&&theLine[(*lineIndex)+1]<='9')|| (theLine[(*lineIndex)+1]>='A'&&theLine[(*lineIndex)+1]<='F')|| (theLine[(*lineIndex)+1]>='a'&&theLine[(*lineIndex)+1]<='f')) { return(ParsePrefixedNumber(theLine,lineIndex,16,1,theElement)); } break; case '.': if((theLine[(*lineIndex)+1]>='0'&&theLine[(*lineIndex)+1]<='9')) { return(ParsePrefixedNumber(theLine,lineIndex,10,1,theElement)); } break; case '%': if((theLine[(*lineIndex)+1]>='0'&&theLine[(*lineIndex)+1]<='1')) { return(ParsePrefixedNumber(theLine,lineIndex,2,1,theElement)); } break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return(ParseDecimalOrPostfixNumber(theLine,lineIndex,theElement)); break; case '\'': return(ParseASCIIConstant(theLine,lineIndex,theElement)); break; } return(false); } static bool ParseExpressionConstant(char *theLine,int *lineIndex,EXPRESSION_ELEMENT *theElement) // See if the thing at lineIndex represents a constant we know about { switch(theLine[*lineIndex]) { case '$': // this means the current PC (one $ is current, $$ is current ignoring any relative origin) theElement->elementString[0]=theLine[*lineIndex]; theElement->elementString[1]='\0'; // terminate the output string (*lineIndex)++; theElement->elementType=ET_NUM; if(theLine[*lineIndex]=='$') { theElement->elementString[1]=theLine[*lineIndex]; // add this to theElement parsed theElement->elementString[2]='\0'; // terminate the output string (*lineIndex)++; if(currentSegment) { theElement->numData=currentSegment->currentPC; } else { theElement->numData=0; // outside of segments, this is 0 } } else { if(currentSegment) { theElement->numData=currentSegment->currentPC+currentSegment->codeGenOffset; } else { theElement->numData=0; // outside of segments, this is 0 } } return(true); break; } return(false); } static bool ParseExpressionLabel(char *theLine,int *lineIndex,EXPRESSION_ELEMENT *theElement) // Attempt to parse an expression element which is a label from the // given line { bool isLocal; if(ParseLabel(theLine,lineIndex,&theElement->elementString[0],&isLocal)) { if(isLocal) { theElement->elementType=ET_LOCAL_LABEL; } else { theElement->elementType=ET_LABEL; } return(true); } return(false); } static bool ParseExpressionElementOperator(char *theLine,int *lineIndex,EXPRESSION_ELEMENT *theElement) // Parse an element of an expression which must be an operator // If no operator can be located (or a comment is found), then return false // NOTE: this is useful to avoid certain ambiguities when parsing expressions: // namely, the expression 1%1 will not be parsed correctly without it { ParseWhiteSpace(theLine,lineIndex); // move past any white space if(!ParseComment(theLine,lineIndex)) // if comment is hit, then done -- no more elements { if(ParseExpressionOperator(theLine,lineIndex,theElement)) { return(true); } } return(false); // none found } static bool ParseExpressionElement(char *theLine,int *lineIndex,EXPRESSION_ELEMENT *theElement) // Parse an element of an expression (a label, number, or operator) // If no element can be located (or a comment is found), then return false { ParseWhiteSpace(theLine,lineIndex); // move past any white space if(!ParseComment(theLine,lineIndex)) // if comment is hit, then done -- no more elements { if(ParseExpressionNumber(theLine,lineIndex,theElement)) // try to get a numeric value { return(true); } if(ParseExpressionOperator(theLine,lineIndex,theElement)) { return(true); } if(ParseExpressionConstant(theLine,lineIndex,theElement)) // try to get a constant we know about { return(true); } if(ParseExpressionLabel(theLine,lineIndex,theElement)) // try to get a label (convert local label if possible) { return(true); } } return(false); // none found } // prototype recursive function static bool ParentheticExpression(char *theLine,int *lineIndex,EXPRESSION_ELEMENT *theElement,bool *haveElement,int *theValue,bool *unresolved,int precedence); static char stringBuffer[MAXSTRING]; static bool Factor(char *theLine,int *lineIndex,EXPRESSION_ELEMENT *theElement,bool *haveElement,int *theValue,bool *unresolved) // handle factors in expressions { bool fail; LABEL_RECORD *oldLabel; fail=false; if(*haveElement) { if(theElement->elementType==ET_OPERATOR) // handle unary operators { switch(theElement->opToken->tokenNum) { case OP_SUBTRACT: *haveElement=ParseExpressionElement(theLine,lineIndex,theElement); if(Factor(theLine,lineIndex,theElement,haveElement,theValue,unresolved)) // get the next factor { if(!(*unresolved)) { *theValue=-(*theValue); // negate it } } else { fail=true; } break; case OP_STRLEN: if(ParseQuotedString(theLine,lineIndex,'"','"',&stringBuffer[0],theValue)) { *haveElement=ParseExpressionElementOperator(theLine,lineIndex,theElement); } else { AssemblyComplaint(NULL,true,"Expected string, but did not find one\n"); fail=true; } break; case OP_HIGH: *haveElement=ParseExpressionElement(theLine,lineIndex,theElement); if(Factor(theLine,lineIndex,theElement,haveElement,theValue,unresolved)) // get the next factor { if(!(*unresolved)) { *theValue=((*theValue)>>8)&0xFF; // get high byte of word } } else { fail=true; } break; case OP_LOW: *haveElement=ParseExpressionElement(theLine,lineIndex,theElement); if(Factor(theLine,lineIndex,theElement,haveElement,theValue,unresolved)) // get the next factor { if(!(*unresolved)) { *theValue=(*theValue)&0xFF; // get low byte of word } } else { fail=true; } break; case OP_MSW: *haveElement=ParseExpressionElement(theLine,lineIndex,theElement); if(Factor(theLine,lineIndex,theElement,haveElement,theValue,unresolved)) // get the next factor { if(!(*unresolved)) { *theValue=((*theValue)>>16)&0xFFFF; // get high word } } else { fail=true; } break; case OP_LSW: *haveElement=ParseExpressionElement(theLine,lineIndex,theElement); if(Factor(theLine,lineIndex,theElement,haveElement,theValue,unresolved)) // get the next factor { if(!(*unresolved)) { *theValue=(*theValue)&0xFFFF; // get low word } } else { fail=true; } break; case OP_LOGICALNOT: *haveElement=ParseExpressionElement(theLine,lineIndex,theElement); if(Factor(theLine,lineIndex,theElement,haveElement,theValue,unresolved)) // get the next factor { if(!(*unresolved)) { *theValue=!(*theValue); // get logical not } } else { fail=true; } break; case OP_NOT: *haveElement=ParseExpressionElement(theLine,lineIndex,theElement); if(Factor(theLine,lineIndex,theElement,haveElement,theValue,unresolved)) // get the next factor { if(!(*unresolved)) { *theValue=~(*theValue); // get not } } else { fail=true; } break; case OP_LEFTPAREN: *haveElement=ParseExpressionElement(theLine,lineIndex,theElement); if(ParentheticExpression(theLine,lineIndex,theElement,haveElement,theValue,unresolved,0)) // get expression within the parens { if(*haveElement&&(theElement->elementType==ET_OPERATOR)&&theElement->opToken->tokenNum==OP_RIGHTPAREN) { *haveElement=ParseExpressionElementOperator(theLine,lineIndex,theElement); } else { AssemblyComplaint(NULL,true,"Missing ')' in expression\n"); fail=true; } } else { fail=true; } break; default: AssemblyComplaint(NULL,true,"Unexpected '%s' in expression\n",theElement->elementString); fail=true; break; } } else { if(theElement->elementType==ET_NUM) // handle numbers { *theValue=theElement->numData; } else // must be a label, or local label { if((oldLabel=LocateLabel(&theElement->elementString[0]))&&oldLabel->resolved) { *theValue=oldLabel->theValue; } else { *theValue=0; *unresolved=true; numUnresolvedLabels++; AssemblyComplaint(NULL,true,"Failed to resolve '%s'\n",theElement->elementString); ReportDiagnostic("Unresolved label: '%s'\n",theElement->elementString); } } *haveElement=ParseExpressionElementOperator(theLine,lineIndex,theElement); } } else { AssemblyComplaint(NULL,true,"Missing factor in expression\n"); fail=true; } return(!fail); } static bool ProcessBinaryOperator(char *theLine,int *lineIndex,EXPRESSION_ELEMENT *theElement,bool *haveElement,int *theValue,bool *unresolved) // A binary operator has been encountered during expression parsing // Get the next term for the operator, operate on it, and return the result { OPERATORTOKEN *theToken; int tmpValue; bool fail; fail=false; theToken=theElement->opToken; // remember this as we may write over it *haveElement=ParseExpressionElement(theLine,lineIndex,theElement); if(ParentheticExpression(theLine,lineIndex,theElement,haveElement,&tmpValue,unresolved,theToken->precedence)) { if(!(*unresolved)) { switch(theToken->tokenNum) { case OP_MULTIPLY: *theValue*=tmpValue; break; case OP_DIVIDE: if(tmpValue!=0) { *theValue/=tmpValue; } else { AssemblyComplaint(NULL,true,"Attempt to divide by 0\n"); fail=true; } break; case OP_MOD: if(tmpValue!=0) { *theValue%=tmpValue; } else { AssemblyComplaint(NULL,true,"Attempt to mod by 0\n"); fail=true; } break; case OP_ADD: *theValue+=tmpValue; break; case OP_SUBTRACT: *theValue-=tmpValue; break; case OP_SHIFTLEFT: *theValue<<=tmpValue; if(tmpValue<0) { AssemblyComplaint(NULL,false,"Shift by negative value\n"); } break; case OP_SHIFTRIGHT: *theValue>>=tmpValue; if(tmpValue<0) { AssemblyComplaint(NULL,false,"Shift by negative value\n"); } break; case OP_GREATEROREQUAL: *theValue=((*theValue)>=tmpValue); break; case OP_LESSOREQUAL: *theValue=((*theValue)<=tmpValue); break; case OP_GREATERTHAN: *theValue=((*theValue)>tmpValue); break; case OP_LESSTHAN: *theValue=((*theValue)elementType==ET_OPERATOR) { if(theElement->opToken->tokenNum==OP_RIGHTPAREN) { done=true; } else { if(theElement->opToken->binaryOperator) { if(theElement->opToken->precedence>precedence) { fail=!ProcessBinaryOperator(theLine,lineIndex,theElement,haveElement,theValue,unresolved); } else { done=true; // operator is of lower precedence, so save it until next time } } else { AssemblyComplaint(NULL,true,"Unexpected operator '%s' in expression\n",theElement->elementString); fail=true; } } } else { AssemblyComplaint(NULL,true,"Expected operator, got '%s'\n",theElement->elementString); fail=true; } } } else { fail=true; } return(!fail); } bool ParseExpression(char *theLine,int *lineIndex,int *theValue,bool *unresolved) // Parse an expression from theLine, return the result in theValue // NOTE: if unresolved is returned true, the expression could not be evaluated // because 1 or more labels it contained were not defined. In this case, // value will be returned as 0. // Leave lineIndex pushed past the last thing successfully parsed. // NOTE: this is responsible for updating numUnresolvedLabels every time // it encounters a label it cannot resolve // NOTE: this prints its own error messages // NOTE: because this is fairly high level, it should only be called // when you _expect_ the next thing on the line to be an expression. // In other words, this function should not be called to test to see // if the next thing is an expression, since it may output spurious // error/warning messages. { bool haveElement; EXPRESSION_ELEMENT theElement; bool fail; fail=false; *unresolved=false; if((haveElement=ParseExpressionElement(theLine,lineIndex,&theElement))) // get the first element of the expression (if there is one) { if(ParentheticExpression(theLine,lineIndex,&theElement,&haveElement,theValue,unresolved,0)) { if(haveElement) { AssemblyComplaint(NULL,true,"Unexpected '%s' in expression\n",theElement.elementString); fail=true; } } else { fail=true; } } else { fail=true; } if(*unresolved) { *theValue=0; } return(!fail); } bool ParseNumber(char *theLine,int *lineIndex,int *theValue) // Do not parse an expression, just a numeric constant // This may be called to test to see if the next thing on theLine is a number { EXPRESSION_ELEMENT theElement; bool fail; fail=false; ParseWhiteSpace(theLine,lineIndex); // move past any white space if(ParseExpressionNumber(theLine,lineIndex,&theElement)) // try to get a numeric value { *theValue=theElement.numData; return(true); } return(false); } bool ParseParentheticString(char *theLine,int *lineIndex,char *theString) // Parse out a string which is enclosed in parenthesis // theLine must begin with an open paren, and will be // placed into theString until a matching close paren is seen // if a comment, this will stop parsing, and // return false // NOTE: this understands that there may be quoted material between the // parenthesis, and abides by the quotes // NOTE: the parenthesis that enclose the string are not included in // the parsed output { bool fail; int initialIndex, workingIndex; int parenDepth; int stringLength; fail=false; ParseWhiteSpace(theLine,lineIndex); // move past any white space parenDepth=0; if(theLine[*lineIndex]=='(') // open paren? { parenDepth++; initialIndex=workingIndex=(*lineIndex)+1; while(parenDepth&&!ParseComment(theLine,&workingIndex)) { switch(theLine[workingIndex]) { case '(': parenDepth++; break; case ')': parenDepth--; break; case '\'': fail=!ParseQuotedString(theLine,&workingIndex,'\'','\'',theString,&stringLength); break; case '"': fail=!ParseQuotedString(theLine,&workingIndex,'"','"',theString,&stringLength); break; } workingIndex++; } if(!parenDepth) // finished? { *lineIndex=workingIndex; workingIndex--; // step back off the last parenthesis strncpy(theString,&theLine[initialIndex],workingIndex-initialIndex); theString[workingIndex-initialIndex]='\0'; } else { fail=true; } } else { fail=true; } return(!fail); } bool ParseLabelDefinition(char *theLine,int *lineIndex,char *theLabel,bool *isLocal) // Attempt to parse a label from theLine at lineIndex, skipping // white space if desired // leave lineIndex pushed past the last thing successfully parsed // If the label is recognized as local, this will adjust it for the // current scope. { if(ParseLabel(theLine,lineIndex,theLabel,isLocal)) { if(theLine[*lineIndex]==':') // if there is a colon after the label, we move past it { (*lineIndex)++; } return(true); // return the label } return(false); // no label found } bool ParseOpcode(char *theLine,int *lineIndex,char *theOpcode) // Parse an opcode from the line // leave lineIndex pushed past the last thing successfully parsed // return false if no opcode could be located { int outputIndex; int localIndex; if(!ParseComment(theLine,lineIndex)) // move past any white space, make sure we're not at the end { localIndex=*lineIndex; // keep index here for a while outputIndex=0; while(!IsWhiteChar(theLine[localIndex])&&!IsCommentStart(theLine,&localIndex)) // opcodes are everything non-white and non-comment { theOpcode[outputIndex++]=theLine[localIndex++]; } theOpcode[outputIndex]='\0'; *lineIndex=localIndex; return(true); } return(false); // no opcode found } bool ParseCommaSeparator(char *theLine,int *lineIndex) // Expect a comma, step over one if found { ParseWhiteSpace(theLine,lineIndex); // skip leading whitespace if(IsCommaSeparator(theLine[*lineIndex])) // does this look like a separator? { (*lineIndex)++; // step over it return(true); } return(false); } bool ParseFirstListElement(char *theLine,int *lineIndex,char *theElement) // Parse the first element from a comma separated list // NOTE: a list element is anything which is non-white at the start, // non-white at the end, and which is not a comma, or a comment character // NOTE: this will pay attention to quotes in the list element, and WILL // allow comment characters or comma's to be embedded in elements // as long as they are quoted // false is returned if no element could be located // NOTE: if the list separator is encountered before any list // element characters are seen, an empty element is returned // NOTE: the list separator which marks the end of the element is NOT used up by // this routine, and is left around for calls to ParseNextListElement { bool done; char theChar; int outputIndex; outputIndex=0; if(!ParseComment(theLine,lineIndex)) { done=false; while(!done&&!IsCommentStart(theLine,lineIndex)) { theChar=theLine[*lineIndex]; if(!IsCommaSeparator(theChar)) { switch(theChar) { case '"': // start of a double quoted string case '\'': // start of a single quoted string theElement[outputIndex++]=theChar; if(ParseQuotedStringVerbatim(theLine,lineIndex,theChar,theChar,&theElement[outputIndex])) { outputIndex+=strlen(&theElement[outputIndex]); // push forward theElement[outputIndex++]=theChar; } else { // no final matching quote, so just get it all (*lineIndex)++; while(theLine[*lineIndex]) { theElement[outputIndex++]=theLine[(*lineIndex)++]; } } break; case '\\': // quoting the next single character? (*lineIndex)++; if(theLine[*lineIndex]) // if not end of line, then pull into the element { theElement[outputIndex++]=theLine[*lineIndex]; (*lineIndex)++; } break; default: theElement[outputIndex++]=theChar; (*lineIndex)++; break; } } else { done=true; } } // remove white space from the end while(outputIndex&&IsWhiteChar(theElement[outputIndex-1])) { outputIndex--; } theElement[outputIndex]='\0'; return(true); } return(false); } bool ParseNextListElement(char *theLine,int *lineIndex,char *theElement) // Expect a comma, and a list element { if(!ParseComment(theLine,lineIndex)) // move past any white space, make sure we're not at the end { if(ParseCommaSeparator(theLine,lineIndex)) // make sure that a separator is the next thing we see { if(!ParseComment(theLine,lineIndex)) // see if at the end { return(ParseFirstListElement(theLine,lineIndex,theElement)); } else { theElement[0]='\0'; return(true); // empty element at end of list } } } return(false); } bool ParseFirstLabelElement(char *theLine,int *lineIndex,char *theElement) // Parse the first label element from a comma separated list // NOTE: a label element is anything is IsLabelChar, // NOTE: if the list separator is encountered before any label // characters are seen, an empty element is returned // NOTE: the list separator which marks the end of the element is NOT used up by // this routine, and is left around for calls to ParseNextLabelElement { char theChar; int outputIndex; outputIndex=0; if(!ParseComment(theLine,lineIndex)) { while(IsLabelChar((theChar=theLine[*lineIndex]))) { (*lineIndex)++; theElement[outputIndex++]=theChar; } theElement[outputIndex]='\0'; return(outputIndex!=0); } return(false); } bool ParseNextLabelElement(char *theLine,int *lineIndex,char *theElement) // Expect a comma, and a label element { int currentIndex; if(!ParseComment(theLine,lineIndex)) // move past any white space, make sure we're not at the end { if(IsCommaSeparator(theLine[*lineIndex])) // make sure that a separator is the next thing we see { currentIndex=(*lineIndex)+1; // hold but do not step over the separator (just in case there is one at the end, do not move past it) if(ParseFirstLabelElement(theLine,¤tIndex,theElement)) { (*lineIndex)=currentIndex; return(true); } } } return(false); } // -------------------------------------------------------------------------------------------- static inline int StringMatch(char *lineString,char *labelString) // return the last non-zero matching index of lineString against labelString { int index; index=0; while(lineString[index]&&labelString[index]&&(lineString[index]==labelString[index])) { index++; } return(index); } static inline void AddCharToOutput(char *outputLine,int *outputIndex,char theChar,bool *overflow) // add a character to outputLine at outputIndex // Check the index and keep it from overflowing { if(*outputIndex(MAXSTRING-*outputIndex)) { length=MAXSTRING-*outputIndex; *overflow=true; } strncpy(&outputLine[*outputIndex],theString,length); (*outputIndex)+=length; } static inline void TerminateOutput(char *outputLine,int outputIndex,bool *overflow) // drop a terminator at the end of the line { if(outputIndexfirstLine; currentSubText=substitutionText->firstLine; while(currentLabel) { matchIndex=StringMatch(&inputLine[*inputIndex],¤tLabel->theLine[0]); if(!currentLabel->theLine[matchIndex]&&!IsLabelChar(inputLine[(*inputIndex)+matchIndex])) // found match? { if(currentSubText) { AddStringToOutput(outputLine,outputIndex,¤tSubText->theLine[0],overflow); // blast in the substitution text } (*inputIndex)+=matchIndex; return(true); } else { currentLabel=currentLabel->next; if(currentSubText) { currentSubText=currentSubText->next; } } } return(false); } bool ProcessTextSubsitutions(char *outputLine,char *inputLine,TEXT_BLOCK *labels,TEXT_BLOCK *substitutionText,bool *overflow) // read inputLine, perform substitutions on it, create substituted text in // outputLine which is at least MAXSTRING bytes long // If there is a hard error, report it, and return false // NOTE: the substitutions which are made here are very rudimentary: // text which is not contained in quotes is searched to find matches within the // label character space to the labels which are specified in "labels" // if a match occurs, the label is replaced with the characters which are // specified in the same relative position of substitutionText. // The results are placed into output buffer. // NOTE: if there is no matching substitution text for a given label, // the label is removed from the input // NOTE: this is very inefficient { int inputIndex, outputIndex; char theChar; bool wasLabelChar; char parsedString[MAXSTRING]; inputIndex=0; outputIndex=0; wasLabelChar=false; *overflow=false; while((theChar=inputLine[inputIndex])) { switch(theChar) { case '"': // start of a double quoted string case '\'': // start of a single quoted string AddCharToOutput(outputLine,&outputIndex,theChar,overflow); if(ParseQuotedStringVerbatim(inputLine,&inputIndex,theChar,theChar,&parsedString[0])) { AddStringToOutput(outputLine,&outputIndex,&parsedString[0],overflow); AddCharToOutput(outputLine,&outputIndex,theChar,overflow); wasLabelChar=false; } else { // failed to parse a string, so just copy the rest of the line and leave AddStringToOutput(outputLine,&outputIndex,&inputLine[inputIndex+1],overflow); TerminateOutput(outputLine,outputIndex,overflow); return(true); } break; default: if(!IsLabelChar(theChar)) { AddCharToOutput(outputLine,&outputIndex,theChar,overflow); inputIndex++; wasLabelChar=false; } else { if(!wasLabelChar) { if(!AttemptSubstitution(outputLine,&outputIndex,inputLine,&inputIndex,labels,substitutionText,overflow)) { AddCharToOutput(outputLine,&outputIndex,theChar,overflow); inputIndex++; wasLabelChar=true; } } else { AddCharToOutput(outputLine,&outputIndex,theChar,overflow); inputIndex++; } } break; } } TerminateOutput(outputLine,outputIndex,overflow); // substitution complete return(true); }