diff --git a/B2C/src/B2CFunctions.c b/B2C/src/B2CFunctions.c index 81e51df..ca6a5b7 100644 --- a/B2C/src/B2CFunctions.c +++ b/B2C/src/B2CFunctions.c @@ -478,6 +478,9 @@ void B2C_exit(int exitCode) { case MEMORY_ERROR: locate(4,3); Print((unsigned char*)"Erreur m\xE6""\x0A""moire"); break; + case ARG_ERROR: + locate(4,3); Print((unsigned char*)"Erreur argument"); + break; } locate(4,5); Print((unsigned char*)"Appuyer:[EXIT]"); @@ -534,106 +537,153 @@ BCDvar *B2C_calcExp(unsigned char* exp) { void B2C_setAlphaVar(BCDvar *alphaVar, BCDvar *value) { memcpy(*alphaVar, *value, sizeof(BCDvar)-3); } -void B2C_setStr(Str *value, int isString, int strNum) { - free(strings[strNum].data); - strings[strNum].length = value->length; - strings[strNum].data = malloc((value->length+2)*2); - strings[strNum].data[(value->length+1)*2] = 0x00; - memcpy(strings[strNum].data + 2, value->data + 2, value->length * 2); +void B2C_setStr(Str *str, Str *value, int isString) { + free(str->data); + str->length = value->length; + str->data = malloc(value->length*2); + memcpy(str->data, value->data, value->length*2); free_str(value); } unsigned char* B2C_strToCharArray(Str *str, int isString) { int j = 0; //Initialize the buffer - memset(stringBuffer, 0x00, 256); + memset(stringBuffer, 0x00, 512); //Goes through the string, starting at 2 - for (i = 2; i <= (str->length+1) * 2; i++) { + for (i = 0; i < str->length; i++) { //Skip null bytes - if (str->data[i]) { - stringBuffer[j] = str->data[i]; + if ((str->data[i])&0xFF00) { + stringBuffer[j] = (((str->data[i])&0xFF00)>>8); j++; } + //There is normally no need to skip null bytes here + //if ((str->data[i])&0x00FF) { + stringBuffer[j] = ((str->data[i])&0x00FF); + j++; + //} } free_str(str); return stringBuffer; } Str *B2C_charArrayToStr(unsigned char* charArray) { - int strPos = 2; + int strPos = 0; + unsigned short currentChar; Str *result = malloc(sizeof(Str)); - result->data = calloc(strlen((char*)charArray)+1, 2); - result->length = 0; + result->data = calloc(strlen((char*)charArray), 2); for (i = 0; charArray[i]; i++) { - if (!(strPos%2) && - charArray[i] != 0xE5 && - charArray[i] != 0xE6 && - charArray[i] != 0xE7 && - charArray[i] != 0xF7 && - charArray[i] != 0xF9 && - charArray[i] != 0x7F) { - strPos++; + currentChar = 0; + if (charArray[i] == 0xE5 || + charArray[i] == 0xE6 || + charArray[i] == 0xE7 || + charArray[i] == 0xF7 || + charArray[i] == 0xF9 || + charArray[i] == 0x7F) { + currentChar = charArray[i] << 8; + i++; } - result->data[strPos] = charArray[i]; + result->data[strPos] = currentChar + charArray[i]; strPos++; } - result->length = (strPos-2)/2; + result->length = strPos; return result; } -BCDvar *B2C_strCmp(Str *str1, Str *str2, int isString1, int isString2) { - unsigned char str_1[256] = {0}, str_2[256] = {0}; - int j = 0, isString; - //Can't use strToCharArray because the buffer can't be used twice - for (i = 2; i <= (str1->length+1) * 2; i++) { - //Skip null bytes - if (str1->data[i]) { - str_1[j] = str1->data[i]; - j++; - } - } - for (i = 2; i <= (str2->length+1) * 2; i++) { - //Skip null bytes - if (str2->data[i]) { - str_2[j] = str2->data[i]; - j++; - } - } - isString = isString1; - //Can't use strcmp() because it must return -1, 0 or 1 - //so just implement strcmp while adapting free_str to free both str1 and str2 - for (i = 0; str_1[i] || str_2[i]; i++) { - if (str_1[i] < str_2[i]) { - free_str(str1); - isString = isString2; - free_str(str2); - return &__1_; - } else if (str_1[i] > str_2[i]) { - free_str(str1); - isString = isString2; - free_str(str2); - return &_1_; + +BCDvar *B2C_strCmp(BCDvar *buffer, Str *str1, int isString1, Str *str2, int isString2) { + int j, isString = isString1; + for (i = 0; i < str1->length && i < str1->length; i++) { + //Case 1, most common: both characters are single bytes + if (!(str1->data[i]&0xFF00) && (str2->data[i]&0xFF00)) { + if (str1->data[i] < str2->data[i]) { + goto str1_lt_str2; + } else if (str1->data[i] > str2->data[i]) { + goto str1_gt_str2; + } + } + //Case 2: str1[i] is single byte, str2[i] is multi byte + //They can't be equal because the single byte at str1[i] can't match the multi byte prefix + else if (!(str1->data[i]&0xFF00)) { + if (str1->data[i] < (str2->data[i]>>8)) { + goto str1_lt_str2; + } else { + goto str1_gt_str2; + } + } + //Case 3: str1[i] is multi byte, str2[i] is single byte + else if (!(str2->data[i]&0xFF00)) { + if ((str1->data[i]>>8) < str2->data[i]) { + goto str1_lt_str2; + } else { + goto str1_gt_str2; + } + } + //Case 4: both are multi bytes + else { + if (str1->data[i] < str2->data[i]) { + goto str1_lt_str2; + } else if (str1->data[i] > str2->data[i]) { + goto str1_gt_str2; + } } } + + //goto equalStrings; + //equalStrings: + buffer = &_0_; + goto freestrs; + + str1_gt_str2: + buffer = &_1_; + goto freestrs; + + str1_lt_str2: + buffer = &__1_; + //goto freestrs; + + freestrs: free_str(str1); isString = isString2; free_str(str2); - return &_0_; + return buffer; } + +BCDvar *B2C_strSrc(BCDvar *buffer, Str *str1, int isString1, Str *str2, int isString2, int n) { + int isString = isString1; + n--; + exitIfNeg(n); + for (i = n; i < str1->length-str2->length+1; i++) { + if (!memcmp(str1->data+i, str2->data+i, str2->length*2)) { + intToBCD(i+1, buffer); + goto freestrs; + } + } + + //str_not_found: + buffer = &_0_; + + freestrs: + free_str(str1); + isString = isString2; + free_str(str2); + return buffer; +} + Str *B2C_strInv(Str *str, int isString) { Str *result = malloc(sizeof(Str)); - result->data = malloc(2*(str->length+1)); + result->data = malloc(2*str->length); result->length = str->length; - for (i = 2*str->length; i >= 2; i -= 2) { - memcpy(result->data + 2*str->length-i, str->data + i, 2); + for (i = 0; i < str->length; i++) { + result->data[str->length-1-i] = str->data[i]; } free_str(str); return result; } -Str *B2C_strJoin(Str *str1, Str *str2, int isString1, int isString2) { + +Str *B2C_strJoin(Str *str1, int isString1, Str *str2, int isString2) { int isString = isString1; Str *result = malloc(sizeof(Str)); - result->data = malloc(2*(str1->length + str2->length + 1)); + result->data = malloc(2*(str1->length + str2->length)); result->length = str1->length + str2->length; - memcpy(result->data + 2, str1->data + 2, str1->length * 2); - memcpy(result->data + 2 + str1->length * 2, str2->data + 2, str2->length * 2); + memcpy(result->data, str1->data, str1->length * 2); + memcpy(result->data+str1->length, str2->data, str2->length * 2); free_str(str1); isString = isString2; free_str(str2); @@ -643,37 +693,47 @@ BCDvar *B2C_strLen(BCDvar *buffer, Str *str, int isString) { intToBCD(str->length, buffer); return buffer; } + +Str *B2C_strMid2args(Str *str, int isString, int start) { + //+1 because strings are 1-indexed in casio + return B2C_strMid(str, isString, start, str->length+1-start); +} + Str *B2C_strMid(Str *str, int isString, int start, int offset) { Str *result = malloc(sizeof(Str)); - if (!offset) { + start--; //In casio, strings are 1-indexed! + exitIfNeg(start); + exitIfNeg(offset); + if (offset+start > str->length) { offset = str->length-start; } - result->data = malloc((offset+2) * 2); - //Set null byte at the end - result->data[(offset+1) * 2] = 0x00; + if (start > str->length) { + start = str->length; + } + result->data = malloc(offset * 2); result->length = offset; //Copy the substring - memcpy(result->data + 2, str->data + start*2, 2*offset); + memcpy(result->data, str->data+start, 2*offset); free_str(str); return result; } Str *B2C_charAt(Str *str, int isString, int charPos) { + //Unused at the moment - not tested Str *result = malloc(sizeof(Str)); - result->data = malloc(3*2); - result->data[2*2] = 0x00; + charPos--; //In casio, strings are 1-indexed! + exitIfNeg(charPos); + result->data = malloc(2); result->length = 1; - memcpy(result->data+2, str->data+charPos*2, 2); + result->data[0] = str->data[charPos]; free_str(str); return result; } Str *B2C_strUpr(Str *str, int isString) { Str *result = malloc(sizeof(Str)); - unsigned short currentChar; - result->data = malloc((str->length+2) * 2); - result->data[str->length+1] = 0x00; - memcpy(result->data+2, str->data+2, str->length * 2); + result->data = malloc(str->length * 2); + memcpy(result->data, str->data, str->length * 2); result->length = str->length; - for (i = 3; i <= result->length*2; i+=2) { + for (i = 0; i < result->length; i++) { if (result->data[i] >= 'a' && result->data[i] <= 'z') { result->data[i] -= 32; } @@ -683,12 +743,10 @@ Str *B2C_strUpr(Str *str, int isString) { } Str *B2C_strLwr(Str *str, int isString) { //(almost) copy of B2C_strUpr - any changes made here must be reflected in strUpr Str *result = malloc(sizeof(Str)); - unsigned short currentChar; - result->data = malloc((str->length+2) * 2); - result->data[str->length+1] = 0x00; - memcpy(result->data+2, str->data+2, str->length * 2); + result->data = malloc(str->length * 2); + memcpy(result->data, str->data, str->length * 2); result->length = str->length; - for (i = 3; i <= result->length*2; i+=2) { + for (i = 0; i < result->length; i++) { if (result->data[i] >= 'A' && result->data[i] <= 'Z') { result->data[i] += 32; } @@ -698,34 +756,58 @@ Str *B2C_strLwr(Str *str, int isString) { //(almost) copy of B2C_strUpr - any ch } Str *B2C_strRight(Str *str, int isString, int offset) { Str *result = malloc(sizeof(Str)); - - result->data = malloc((offset+2)*2); - result->data[(offset+1)*2] = 0x00; + exitIfNeg(offset); + result->data = malloc(offset*2); + if (offset > str->length) offset=str->length; result->length = offset; - memcpy(result->data+2, str->data + (str->length - offset + 1)*2, offset * 2); + memcpy(result->data, str->data+(str->length-offset), offset*2); free_str(str); return result; } Str *B2C_strLeft(Str *str, int isString, int offset) { Str *result = malloc(sizeof(Str)); - result->data = malloc((offset+2)*2); - result->data[(offset+1)*2] = 0x00; + exitIfNeg(offset); + result->data = malloc(offset*2); + if (offset > str->length) offset=str->length; result->length = offset; - memcpy(result->data+2, str->data + 2, offset * 2); + memcpy(result->data, str->data, offset*2); + free_str(str); + return result; +} +Str *B2C_strShift(Str *str, int isString, int offset) { + unsigned short* spaces = malloc(2*abs(offset)); + Str *result = malloc(sizeof(Str)); + for (i = 0; i < abs(offset); i++) { + spaces[i] = (unsigned short)' '; + } + result->length = str->length; + result->data = malloc(str->length*2); + if (offset > str->length) { + offset = str->length; + } else if (-offset > str->length) { + offset = -str->length; + } + if (offset > 0) { + memcpy(result->data, str->data+offset, (str->length-offset)*2); + memcpy(result->data+(str->length-offset), spaces, offset*2); + } else { + memcpy(result->data, spaces, -offset*2); + memcpy(result->data-offset, str->data, (str->length+offset)*2); + } free_str(str); return result; } Str *B2C_strRotate(Str *str, int isString, int offset) { Str *result = malloc(sizeof(Str)); result->length = str->length; - result->data = malloc((str->length+1)*2); + result->data = malloc(str->length*2); offset %= str->length; if (offset > 0) { - memcpy(result->data+2, str->data+2+2*offset, (str->length-offset)*2); - memcpy(result->data+2+2*(str->length-offset), str->data+2, offset*2); + memcpy(result->data, str->data+offset, (str->length-offset)*2); + memcpy(result->data+(str->length-offset), str->data, offset*2); } else { - memcpy(result->data+2, str->data+2+2*(str->length+offset), -offset*2); - memcpy(result->data+2+2*-offset, str->data+2, (str->length+offset)*2); + memcpy(result->data, str->data+(str->length+offset), -offset*2); + memcpy(result->data-offset, str->data, (str->length+offset)*2); } free_str(str); return result; diff --git a/B2C/src/b2c/B2C.java b/B2C/src/b2c/B2C.java index 74644c7..82e8b6f 100644 --- a/B2C/src/b2c/B2C.java +++ b/B2C/src/b2c/B2C.java @@ -19,8 +19,8 @@ public class B2C { final static boolean debugMode = true; static String path = "C:\\Users\\Catherine\\Documents\\CASIO\\fx-9860G SDK\\TestB2C\\"; - static String mainProgramName = "DEMNR"; - static String pathToG1M = "C:\\Users\\Catherine\\Desktop\\goldroad.g2m"; + static String mainProgramName = "TESTSTR"; + static String pathToG1M = "C:\\Users\\Catherine\\Desktop\\teststr.g1m"; static boolean isRealTimeGame = true; static boolean assureOS1Compatibility = true; static boolean usesAcOnTimer = true; @@ -108,7 +108,7 @@ public class B2C { "unsigned int key;\n" + "int i;\n"+ "BCDvar var[29] = {0}; //A-Z, r, theta, Ans\n"+ - "Str strings[20];\n" + + "Str str[20];\n" + "Mat mat[27]; //Important thing: matrixes are (height, width) not (width, height)\n" + "List list[26];\n" + "char dummyOpCode[2] = {5, 8};\n" + @@ -116,7 +116,7 @@ public class B2C { "BCDvar alphaVarBuffer;\n" + "BCDvar expressionBuffer;\n" + "BCDvar getkeyBuffer;\n" + - "unsigned char stringBuffer[256] = {0};\n"; + "unsigned char stringBuffer[512] = {0};\n"; /*"const BCDvar ZERO = {0};\n"; for (int i = 1; i <= 9; i++) { main_c += "const BCDvar " + Parser.consts.get(i) + " = {0x10, 0x0" + i + "};\n"; @@ -124,7 +124,7 @@ public class B2C { main_c+="\nint AddIn_main(int isAppli, unsigned short OptionNum) {\n" + "\t//Initialize strings\n" + "\tfor (i = 0; i < 20; i++) {\n" + - "\t\tstrings[i].length = 0;\n" + + "\t\tstr[i].length = 0;\n" + "\t}\n" + "\tsrand((unsigned int)" + new Random().nextInt() + ");\n" + "\t#ifdef USES_INTERRUPTION_TIMER\n" + @@ -204,9 +204,16 @@ public class B2C { for (int i = 0; i <= 1; i++) { IO.writeToFile(new File(path+externalLibs[i]), IO.readFromRelativeFile(externalLibs[i]), true); } - /*for (char c = 'A'; c <= 'Z'; c++) { - Header.addDefine(c+" "+(c-'A')); - }*/ + //Add constants for easier reading and debugging + for (char c = 'A'; c <= 'Z'; c++) { + Header.addDefine("VAR_"+c+" (&var["+(c-'A')+"])"); + } + for (char c = 'A'; c <= 'Z'; c++) { + Header.addDefine("MAT_"+c+" (&mat["+(c-'A')+"])"); + } + for (int i = 1; i <= 20; i++) { + Header.addDefine("STR_"+i+" (&str["+(i-1)+"])"); + } Header.addDefine("FALSE 0"); Header.addDefine("TRUE 1"); Header.addDefine("A_GREATER_THAN_B 1"); @@ -214,6 +221,7 @@ public class B2C { Header.addDefine("A_LESS_THAN_B -1"); Header.addDefine("NO_ERROR 1"); Header.addDefine("MEMORY_ERROR 4"); + Header.addDefine("ARG_ERROR 8"); Header.addDefine("INTERRUPTION_TIMER 2"); if (usesAcOnTimer) { Header.addDefine("USES_INTERRUPTION_TIMER"); @@ -227,13 +235,14 @@ public class B2C { Header.addDefine("LIST_START 0x10"); Header.addDefine("MAT_START 0x10"); - Header.addDefine("ANS 28"); - Header.addDefine("THETA 27"); - Header.addDefine("RADIUS 26"); + Header.addDefine("VAR_ANS (&var[28])"); + Header.addDefine("VAR_THETA (&var[27])"); + Header.addDefine("VAR_RADIUS (&var[26])"); Header.addDefine("SETUP_LISTFILE 0x2E"); Header.addDefine("free_str(x) if(!isString){free(x->data); free(x);}"); + Header.addDefine("exitIfNeg(x) if((x)<0){B2C_exit(ARG_ERROR);}"); Header.addDefine("getDigit(BCDvar, i) (((i)%2) ? (*(BCDvar))[((i)+1)/2+1]>>4 : (*(BCDvar))[((i)+1)/2+1]&0x0F)"); Header.addDefine("getExp(BCDvar) (((*(BCDvar))[0]>>4) * 100 + ((*(BCDvar))[0]&0x0F) * 10 + ((*(BCDvar))[1]>>4))"); Header.create(); diff --git a/B2C/src/b2c/Constants.java b/B2C/src/b2c/Constants.java index 3a71ab2..4f429db 100644 --- a/B2C/src/b2c/Constants.java +++ b/B2C/src/b2c/Constants.java @@ -12,15 +12,7 @@ public class Constants { * special things like 1e5, 1/3, etc. */ public static void add(String constant) { - constant = constant.replaceAll("\\x99|\\x87", "-"); - //System.out.println(constant); - //Interpret "." as "0.", example: -.5 = -0.5 - constant = constant.replaceAll("(?) //Functions like Int or Frac have their arguments after but return a value so they can be used in a calculation. //Those are listed in the alphabetical order of their opcodes. @@ -319,8 +313,8 @@ public class Parser { Integer[] args = parseArgs(content); return "locate(" + handleIntConversion(content.substring(2, args[0])) + ", " + handleIntConversion(content.substring(args[0]+1, args[1])) - + "); Print((unsigned char*)" - + parseStr(content.substring(args[1]+1)) + "); ML_display_vram();"; + + "); Print(" + + parseStr(content.substring(args[1]+1), CONV_TO_CHARARRAY) + "); ML_display_vram();"; } //ClrText @@ -342,16 +336,16 @@ public class Parser { //End of starting functions. At this point the instruction is likely a mathematical operation, or a string, //or a variable assignment. Note that it can have functions inside, like the factorial or nCr function. - - if (content.startsWith("\"") || content.startsWith(new String(new char[]{0xF9, 0x3F}))) { //it is a standalone string - return parseStr(content); - } - + //Check for assignment matchResult = checkMatch(content, new String(new char[]{0x0E})); if (matchResult >= 0) { + if (option != WHOLE_INSTRUCTION) { + error("Assignment isn't whole instruction?!"); + } + //Check for the Dim assignment case; in this case it's not an assignment but a method calling if (content.substring(matchResult+1).startsWith(new String(new char[]{0x7F, 0x46}))) { //The assignment is followed by a Dim. Now check if it's to a List or a Mat @@ -360,8 +354,8 @@ public class Parser { if (content.substring(matchResult+3).startsWith(new String(new char[]{0x7F, 0x40}))) { //followed by Mat String result = "B2C_setDimMat(" + (content.charAt(matchResult+5)-'A') + ", "; if (content.startsWith(new String(new char[]{0x7F, 0x51}))) { - result += handleIntConversion(parse(content.substring(0, matchResult) + "[1]")) + ", "; - result += handleIntConversion(parse(content.substring(0, matchResult) + "[2]")) + ");"; + result += handleIntConversion(content.substring(0, matchResult) + "[1]") + ", "; + result += handleIntConversion(content.substring(0, matchResult) + "[2]") + ");"; return result; } else if (content.startsWith("{")) { Integer[] commaPos = parseArgs(content.substring(1, matchResult)); @@ -443,17 +437,31 @@ public class Parser { //Check for variable assignment } else if (content.substring(matchResult+1).matches("[A-Z\\xCD\\xCE]")) { String result = "B2C_setAlphaVar(" + getAlphaVar(content.charAt(matchResult+1)) - + ", " + parse(content.substring(0, matchResult), CONV_TO_BCD) + ")"; - if (option == WHOLE_INSTRUCTION) { - result += ";"; - } + + ", " + parse(content.substring(0, matchResult), CONV_TO_BCD) + ");"; return result; + + //Check for Str assignment + } else if (content.substring(matchResult+1).startsWith(new String(new char[]{0xF9, 0x3F}))) { + System.out.println("Parsing Str assignment, matchresult = " + matchResult); + String result = "B2C_setStr(" + getStr(content.substring(matchResult+3)) + + ", " + parse(content.substring(0, matchResult)) + ", " + handleString(content.substring(0, matchResult)) + ");"; + return result; + } else { error("Unknown assignment!"); } } + //Check for strings + if (startsWithStringFunction(content)) { + if (option == WHOLE_INSTRUCTION) { + error("B2C does not support standalone strings!"); + } else { + return parseStr(content, option); + } + } + //at this point it is a mathematical operation //stock the level of the parentheses Integer[] parenthesesPos = parseBrackets(content); @@ -526,7 +534,7 @@ public class Parser { content = content.substring(0, content.length()-1); } return supportAns("list[" + getList(content.substring(2, check[0])) + "].data[" - + handleIntConversion(parse(content.substring(check[0]+1, content.length()))) + "]", option); + + handleIntConversion(content.substring(check[0]+1, content.length())) + "]", option); } } @@ -768,11 +776,7 @@ public class Parser { boolean positionIsString = false; for (int i = 0; i < content.length(); i++) { - if (content.charAt(i) == (char)0x7F || - content.charAt(i) == (char)0xF7 || - content.charAt(i) == (char)0xE5 || - content.charAt(i) == (char)0xE6 || - content.charAt(i) == (char)0xE7) { + if (isMultibytePrefix(content.charAt(i))) { i += 2; if (i >= content.length()) { break; @@ -845,13 +849,17 @@ public class Parser { * */ - public static String parseStr(String content) { + //TODO use buffers as str and place free() after + public static String parseStr(String content, int option) { System.out.println("Parsing string: "+printNonAscii(content)); + + Integer[] parenthesesPos = parseBrackets(content); if (startsWithStringFunction(content)) { + //Note that standalone strings aren't supported, there is no Str Ans; therefore, no ';'! + //Parse operators //It is easy because the only operator is the '+' - Integer[] parenthesesPos = parseBrackets(content); for (int i = content.length()-1; i >= 0; i--) { if (content.substring(0, i).endsWith(new String(new char[]{0x89}))) { //test if the operator is not within parentheses @@ -864,40 +872,147 @@ public class Parser { System.out.println("Parsing concatenation operator (+)"); if (!isInParentheses) { - String str = "B2C_strcat("; - str += parse(content.substring(0, i-1)) + ", " + parse(content.substring(i)); - str += ")"; + //Parse as StrJoin(a,b) + String str = parse(new String(new char[]{0xF9,0x30}) + content.substring(0, i-1) + "," + + content.substring(i) + ")", option); + if (option == CONV_TO_CHARARRAY) { + return "B2C_strToCharArray(" + str + ", " + handleString(str) + ")"; + } return str; } } } - //Note that standalone strings aren't supported, there is no Str Ans; therefore, no ';'! - //Plain strings if (content.startsWith("\"")) { if (content.charAt(content.length()-1) != '"') { content += '"'; } - content = replaceNonAscii(content); - return content; + content = "(unsigned char*)" + replaceNonAscii(content); + if (option == CONV_TO_CHARARRAY) { + return content; + } + return "B2C_charArrayToStr(" + content + ")"; } - //All subsequent str functions have parenthesis, so check if there is the right number - if (parenthesesPos.length != 2) { - error("Str function does not have one level of parenthesis!"); - } + //No returns from here; always assign the result to this string! + //This is to handle the conversion to char array/str. + String result = ""; //Str function if (content.startsWith(new String(new char[]{0xF9, 0x3F}))) { - return getStr(content.substring(2)); + result = getStr(content.substring(2)); + + //All subsequent str functions have parenthesis, so check if there is the right number + } else if (parenthesesPos.length != 2) { + error("Str function does not have one level of parenthesis! ("+parenthesesPos.length+")"); } - //StrRotate - if (content.startsWith(new String(new char[]{0xF9, 0x3D}))) { - return "B2C_strRotate(" + addStrBuffer() + parse(content.substring(2, parenthesesPos[0])) + ")"; + + //These string functions all have similar structures (arguments = str, int) + String[] strFunctions1 = {"strLeft", "strRight", "strShift", "strRotate"}; + String[] strOpcodes1 = {new String(new char[]{0xF9,0x34}), + new String(new char[]{0xF9,0x35}), + new String(new char[]{0xF9,0x3C}), + new String(new char[]{0xF9,0x3D}), + }; + for (int i = 0; i < strFunctions1.length; i++) { + if (content.startsWith(strOpcodes1[i])) { + Integer[] argPos = parseArgs(content.substring(2, parenthesesPos[1])); + if (argPos.length > 1) { + error(strFunctions1[i]+" does not contain 2 arguments!"); + } + if (argPos.length == 0) { + result = "B2C_"+strFunctions1[i]+"(" + parse(content.substring(2, parenthesesPos[1])) + ", " + handleString(content.substring(2, parenthesesPos[1])) + + ", 1)"; + } else { + result = "B2C_"+strFunctions1[i]+"(" + parse(content.substring(2, argPos[0]+2)) + ", " + handleString(content.substring(2, argPos[0]+2)) + + ", " + handleIntConversion(content.substring(argPos[0]+2+1, parenthesesPos[1])) + ")"; + } + } } + + //These string functions all have similar structures (argument = str) + String[] strFunctions2 = {"strInv", "strUpr", "strLwr"}; + String[] strOpcodes2 = {new String(new char[]{0xF9,0x3B}), + new String(new char[]{0xF9,0x39}), + new String(new char[]{0xF9,0x3A}), + }; + + for (int i = 0; i < strFunctions2.length; i++) { + if (content.startsWith(strOpcodes2[i])) { + result = "B2C_"+strFunctions2[i]+"(" + parse(content.substring(2, parenthesesPos[1])) + ", " + handleString(content.substring(2, parenthesesPos[1])) + ")"; + } + } + + //StrMid + if (content.startsWith(new String(new char[]{0xF9, 0x36}))) { + Integer[] argPos = parseArgs(content.substring(2, parenthesesPos[1])); + if (argPos.length == 1) { + //StrMid with only 2 arguments = StrLeft + result = "B2C_strMid2args(" + parse(content.substring(2, argPos[0]+2)) + ", " + handleString(content.substring(2, argPos[0]+2)) + + ", " + handleIntConversion(content.substring(argPos[0]+2+1, parenthesesPos[1])) + ")"; + } else if (argPos.length != 2) { + error("strMid does not contain 3 arguments!"); + } else { + result = "B2C_strMid(" + parse(content.substring(2, argPos[0]+2)) + ", " + handleString(content.substring(2, argPos[0]+2)) + ", " + + handleIntConversion(content.substring(argPos[0]+2+1, argPos[1]+2)) + ", " + handleIntConversion(content.substring(argPos[1]+2+1, parenthesesPos[1])) + ")"; + } + + } + + + //StrJoin + if (content.startsWith(new String(new char[]{0xF9, 0x30}))) { + Integer[] argPos = parseArgs(content.substring(2, parenthesesPos[1])); + if (argPos.length != 1) { + error("StrJoin function does not contain 2 arguments!"); + } + result = "B2C_strJoin(" + parse(content.substring(2, argPos[0]+2)) + ", " +handleString(content.substring(2, argPos[0]+2)) + ", " + + parse(content.substring(argPos[0]+2+1, parenthesesPos[1])) + ", " +handleString(content.substring(argPos[0]+2+1, parenthesesPos[1])) + ")"; + } + + + if (option == CONV_TO_CHARARRAY) { + return "B2C_strToCharArray(" + result + ", " + handleString(content) + ")"; + } + return result; } - return "B2C_convToStr(" + handleAlphaVar(content, NO_OPTION) + ")"; + + String result = ""; + //StrLen + if (content.startsWith(new String(new char[]{0xF9, 0x31}))) { + result = "B2C_strLen(" + addBCDBuffer() + ", " + parse(content.substring(2, parenthesesPos[1])) + ", " + handleString(content.substring(2, parenthesesPos[1])) + ")"; + } + + //Both these functions have 2 strings as arguments and return a BCDvar + //StrCmp + else if (content.startsWith(new String(new char[]{0xF9,0x32}))) { + Integer[] argPos = parseArgs(content.substring(2, parenthesesPos[1])); + if (argPos.length != 1) { + error("StrCmp function does not contain 2 arguments!"); + } + result = "B2C_strCmp(" + addBCDBuffer() + ", " + parse(content.substring(2, argPos[0]+2)) +", " + handleString(content.substring(2, argPos[0]+2)) + ", " + + parse(content.substring(argPos[0]+2+1, parenthesesPos[1])) + ", "+ handleString(content.substring(argPos[0]+2+1, parenthesesPos[1])) + ")"; + } + + //StrSrc + else if (content.startsWith(new String(new char[]{0xF9,0x33}))) { + Integer[] argPos = parseArgs(content.substring(2, parenthesesPos[1])); + if (argPos.length > 2 || argPos.length < 1) { + error("StrSrc function does not contain 2 or 3 arguments!"); + } else if (argPos.length == 1) { + result = "B2C_strSrc(" + addBCDBuffer() + ", " + parse(content.substring(2, argPos[0]+2)) +", " + handleString(content.substring(2, argPos[0]+2)) + ", " + + parse(content.substring(argPos[0]+2+1, parenthesesPos[1])) + ", " +handleString(content.substring(argPos[0]+2+1, parenthesesPos[1])) + ", 1)"; + } else { + result = "B2C_strSrc(" + addBCDBuffer() + ", " + parse(content.substring(2, argPos[0]+2)) +", " + handleString(content.substring(2, argPos[0]+2)) + ", " + + parse(content.substring(argPos[0]+2+1, argPos[1]+2)) +", " + handleString(content.substring(argPos[0]+2+1, argPos[1]+2)) + + ", " + handleIntConversion(content.substring(argPos[1]+2+1, parenthesesPos[1])) + ")"; + } + } else { + result = handleAlphaVar(content, NO_OPTION); + } + + return "B2C_convToStr(" + result + ")"; } //Replace non-ASCII characters with \xXX, using string concatenation. @@ -933,7 +1048,7 @@ public class Parser { * */ public static Integer[] parseArgs(String content) { - System.out.println("Parsing arguments: "+content); + System.out.println("Parsing arguments: "+printNonAscii(content)); ArrayList args = new ArrayList(); int argsBuffer = 0; boolean positionIsString = false; @@ -942,7 +1057,7 @@ public class Parser { if (content.charAt(i) == ',' && argsBuffer == 0 && !positionIsString) { args.add(i); } - if ((content.charAt(i) == '(' || content.charAt(i) == '[' || content.charAt(i) == '{') && !positionIsString) { + if (startsWithParenthesis(content.substring(i)) && !positionIsString) { argsBuffer++; } else if ((content.charAt(i) == ')' || content.charAt(i) == ']' || content.charAt(i) == '}') && !positionIsString) { argsBuffer--; @@ -971,20 +1086,7 @@ public class Parser { int bracketsLevel = 0; boolean currentPositionIsString = false; for (int i = 0; i < content.length(); i++) { - if (!currentPositionIsString && (content.charAt(i) == '(' || content.charAt(i) == '[' || content.charAt(i) == '{' || - //Check for opcodes that contains parenthesis - content.startsWith(new String(new char[]{0x7F, 0x87}), i) || //RanInt#( - content.startsWith(new String(new char[]{0x7F, 0x47}), i) || //Fill( - content.startsWith(new String(new char[]{0xF9, 0x30}), i) || //StrJoin( - content.startsWith(new String(new char[]{0xF9, 0x34}), i) || //StrLeft( - content.startsWith(new String(new char[]{0xF9, 0x35}), i) || //StrRight( - content.startsWith(new String(new char[]{0xF9, 0x36}), i) || //StrMid( - content.startsWith(new String(new char[]{0xF9, 0x39}), i) || //StrUpr( - content.startsWith(new String(new char[]{0xF9, 0x3A}), i) || //StrLwr( - content.startsWith(new String(new char[]{0xF9, 0x3B}), i) || //StrInv( - content.startsWith(new String(new char[]{0xF9, 0x3C}), i) || //StrShift( - content.startsWith(new String(new char[]{0xF9, 0x3D}), i) //StrRotate( - )) { + if (!currentPositionIsString && startsWithParenthesis(content.substring(i))) { bracketsLevel++; if (bracketsLevel == 1) { bracketsPos.add(i); @@ -1013,6 +1115,35 @@ public class Parser { return bracketsPos.toArray(new Integer[bracketsPos.size()]); } + /** + * Returns true if the given string starts with a parenthesis (or a bracket), + * but also with an opcode containing a parenthesis (RanInt#(, StrRotate(, etc). + */ + public static boolean startsWithParenthesis(String content) { + if ((content.charAt(0) == '(' || content.charAt(0) == '[' || content.charAt(0) == '{' || + //Check for opcodes that contains parenthesis + content.startsWith(new String(new char[]{0x7F, 0x87})) || //RanInt#( + content.startsWith(new String(new char[]{0x7F, 0x47})) || //Fill( + content.startsWith(new String(new char[]{0xF9, 0x30})) || //StrJoin( + content.startsWith(new String(new char[]{0xF9, 0x31})) || //StrLen( + content.startsWith(new String(new char[]{0xF9, 0x32})) || //StrCmp( + content.startsWith(new String(new char[]{0xF9, 0x33})) || //StrSrc( + content.startsWith(new String(new char[]{0xF9, 0x34})) || //StrLeft( + content.startsWith(new String(new char[]{0xF9, 0x35})) || //StrRight( + content.startsWith(new String(new char[]{0xF9, 0x36})) || //StrMid( + content.startsWith(new String(new char[]{0xF9, 0x37})) || //Exp->Str( + content.startsWith(new String(new char[]{0xF9, 0x38})) || //Exp( + content.startsWith(new String(new char[]{0xF9, 0x39})) || //StrUpr( + content.startsWith(new String(new char[]{0xF9, 0x3A})) || //StrLwr( + content.startsWith(new String(new char[]{0xF9, 0x3B})) || //StrInv( + content.startsWith(new String(new char[]{0xF9, 0x3C})) || //StrShift( + content.startsWith(new String(new char[]{0xF9, 0x3D})) //StrRotate( + )) { + return true; + } + return false; + } + /* This method replaces some basic functions (listed below). * Reason for this method is to avoid replacing functions in strings, which can't be done with replaceAll(). * The replacements are the following: @@ -1065,6 +1196,7 @@ public class Parser { public static boolean isMultibytePrefix(char prefix) { if (prefix == (char)0xF7 || prefix == (char)0x7F || + prefix == (char)0xF9 || prefix == (char)0xE5 || prefix == (char)0xE6 || prefix == (char)0xE7) @@ -1073,6 +1205,7 @@ public class Parser { } /** * Returns true if the given string starts with a string function. + * A string function is defined as a function that returns a string. * That means that Str, StrLwr, StrShift, '"', ... return true, * but not StrLen, StrCmp or StrSrc as they return BCDvars! * @@ -1106,8 +1239,8 @@ public class Parser { public static String handleIntConversion(String content) { //TODO: optimise some cases like "A+2" where you could convert A then add 2 //instead of calculating "A+2" in BCD - if (content.matches("\\d+")) { - return content; + if (Constants.isNumber(content)) { + return Constants.getNumberNotation(content); } return "B2C_convToUInt(" + parse(content) + ")"; } @@ -1121,6 +1254,30 @@ public class Parser { return parse(content, option); } + /** + * Handle the isString param, required for any function taking a string as argument. + * If isString(content) returns true, this function returns "TRUE". Else it returns "FALSE". + */ + + public static String handleString(String content) { + if (isString(content)) { + return "TRUE"; + } + return "FALSE"; + } + + /** + * Returns true iff the content consists only of the Str function. + * "Str 1 + StrRotate(Str 2)" will return false. + * "Str 20" will return true. + */ + public static boolean isString(String content) { + if (content.startsWith(new String(new char[]{0xF9, 0x3F})) && content.length() <= 4) { + return true; + } + return false; + } + public static void incrementTabs() { tabs += "\t"; } @@ -1162,12 +1319,12 @@ public class Parser { if (list.length() == 1 && list.charAt(0) == 0xC0) { return "LIST_ANS"; } else { - return handleIntConversion(parse(list)); + return handleIntConversion(list); } } public static String getStr(String str) { if (str.length() > 2) { - error("Invalid str " + str + "!"); + error("Invalid str " + printNonAscii(str) + "!"); } try { int strno = Integer.valueOf(str); @@ -1177,12 +1334,12 @@ public class Parser { } catch (NumberFormatException e) { error("Invalid str " + str + "!"); } - return "&str["+Integer.valueOf(str)+"]"; + return "STR_"+Integer.valueOf(str); } public static String addBCDBuffer() {return addBuffer(BCD_BUFFER);} - public static String addStrBuffer() {return addBuffer(STR_BUFFER);} + //public static String addStrBuffer() {return addBuffer(STR_BUFFER);} public static String addMatBuffer() {return addBuffer(MAT_BUFFER);} public static String addListBuffer() {return addBuffer(LIST_BUFFER);}