1400 lines
51 KiB
Java
1400 lines
51 KiB
Java
package b2c;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
|
|
public class Parser {
|
|
|
|
final static int NO_OPTION = 0;
|
|
final static int WHOLE_INSTRUCTION = 1;
|
|
final static int CONV_TO_BCD = 2;
|
|
//final static int CONV_TO_INT = 3; //Use handleIntConversion instead
|
|
final static int CONV_TO_STR = 4;
|
|
final static int CONV_TO_CHARARRAY = 5;
|
|
|
|
final static int BCD_BUFFER = 0;
|
|
final static int STR_BUFFER = 1;
|
|
final static int LIST_BUFFER = 2;
|
|
final static int MAT_BUFFER = 3;
|
|
final static String[] bufferVars = {"bcdBuffer", "strBuffer", "listBuffer", "matBuffer"};
|
|
final static String[] bufferTypes = {"BCDvar", "Str", "List", "Mat"};
|
|
static String tabs = "\t";
|
|
|
|
//Be sure to clear these variables between programs.
|
|
static ArrayList<String> instructions = new ArrayList<String>();
|
|
static int instructionNumber = 0;
|
|
static int[] buffers = {0,0,0,0};
|
|
|
|
|
|
public static String parse(String content) {
|
|
return parse(content, NO_OPTION);
|
|
}
|
|
|
|
/* This is the main method for the parsing.
|
|
* It recursively parses each instruction it receives.
|
|
* Note that instructions must not have the \r at the end, or any \r at all.
|
|
* It obviously assumes that the instruction is valid Basic Casio code.
|
|
*
|
|
* When adding a method returning a value, always declare it like this:
|
|
*
|
|
* if (content.startsWith(method) {
|
|
* return supportAns(isWholeInstruction, <parsing result>);
|
|
* }
|
|
*
|
|
* This is to provide support for the Ans variable.
|
|
*/
|
|
|
|
public static String parse(String content, int option) {
|
|
|
|
System.out.println("Parsing instruction: " + printNonAscii(content));
|
|
|
|
if (content.equals("")) {
|
|
return "";
|
|
}
|
|
|
|
int matchResult = -1;
|
|
|
|
//comment
|
|
if (content.startsWith("'")) {
|
|
return "//"+content.substring(1);
|
|
}
|
|
|
|
//instruction colon ':' that counts as a \r
|
|
matchResult = checkMatch(content, ":");
|
|
if (matchResult >= 0) {
|
|
instructions.add(instructionNumber, content.substring(matchResult+1));
|
|
instructionNumber++;
|
|
return parse(content.substring(0, matchResult), WHOLE_INSTRUCTION) + "\n" + tabs +
|
|
parse(content.substring(matchResult+1), WHOLE_INSTRUCTION);
|
|
}
|
|
|
|
//The easy functions: those that always have their arguments after, AND that don't return anything. (exception of =>)
|
|
//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.
|
|
//So Fill (0x7F47) is above Lbl (0xE2) which is above If (0xF700). (again, exception of => which is after IfEnd)
|
|
|
|
//Fill(
|
|
if (content.startsWith(new String(new char[]{0x7F,0x47}))) {
|
|
if (content.charAt(content.length()-1) == ')') {
|
|
content = content.substring(0, content.length()-1);
|
|
}
|
|
Integer[] args = parseArgs(content.substring(2));
|
|
if (args.length != 1) {
|
|
error("Fill method doesn't have 2 arguments! ("+args.length+")");
|
|
}
|
|
args[0] += 2;
|
|
if (content.substring(args[0]+1).startsWith(new String(new char[]{0x7F,0x40}))) {
|
|
return "B2C_fillMat(" + parse(content.substring(2, args[0]), CONV_TO_BCD) + ", "
|
|
+ getChar(content.substring(args[0]+1+2)) + ");";
|
|
} else if (content.substring(args[0]+1).startsWith(new String(new char[]{0x7F,0x51}))) {
|
|
return "B2C_fillList(" + parse(content.substring(2, args[0]), CONV_TO_BCD) + ", "
|
|
+ parse(content.substring(args[0]+1), CONV_TO_BCD) + ");";
|
|
} else {
|
|
error("2nd argument of Fill isn't a Mat or a List!");
|
|
}
|
|
|
|
|
|
}
|
|
|
|
//unary plus = no purpose
|
|
if (content.startsWith(new String(new char[]{0x89}))) {
|
|
return parse(content.substring(1), option);
|
|
}
|
|
|
|
//Lbl
|
|
if (content.startsWith(new String(new char[]{0xE2}))) {
|
|
content = content.substring(1); //remove the Lbl to exploit the variable
|
|
if (content.equals(new String(new char[]{0xCD}))) {
|
|
content = "radius";
|
|
} else if (content.equals(new String(new char[]{0xCE}))) {
|
|
content = "theta";
|
|
}
|
|
return "Lbl_"+content+":;"; //the ';' is needed because you can place Lbls just before IfEnd/WhileEnd/Next
|
|
}
|
|
|
|
//Goto
|
|
if (content.startsWith(new String(new char[]{0xEC}))) {
|
|
content = content.substring(1); //remove the Goto to exploit the variable
|
|
if (content.equals(new String(new char[]{0xCD}))) {
|
|
content = "radius";
|
|
} else if (content.equals(new String(new char[]{0xCE}))) {
|
|
content = "theta";
|
|
}
|
|
if (content.length() > 2) {
|
|
error("Instruction begins by Goto but includes something else!");
|
|
}
|
|
String[] gotosAreBad = {
|
|
"Gotos are the root of all evil.",
|
|
"I know you're writing in basic but still... gotos?",
|
|
"Get that goto out of there, we're not in assembly.",
|
|
"I sincerely hope you're not using that goto for a loop. Else you need to learn about do/while.",
|
|
"This is justified if and only if you are using this goto for a Menu.",
|
|
"You're lucky gotos work in C the exact way they do in basic.",
|
|
"Gotos are bad and you should feel bad.",
|
|
"The use of gotos in your program makes it read like a \"Chose your own adventure\" book.",
|
|
"http://xkcd.com/292/",
|
|
"If you're using that to break out of nested loops... I'll allow it.",
|
|
"Like this converted code wasn't unreadable enough.",
|
|
};
|
|
String result = "goto Lbl_"+content+";";
|
|
if (Math.random() > 0.9) {
|
|
result += " //" + Arrays.asList(gotosAreBad).get((int)(Math.random()*gotosAreBad.length));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//Prog
|
|
if (content.startsWith(new String(new char[]{0xED}))) {
|
|
String result = content.substring(1);
|
|
|
|
if (result.charAt(0) == '"') {
|
|
result = result.substring(1);
|
|
}
|
|
if (result.charAt(result.length()-1) == '"') {
|
|
result = result.substring(0, result.length()-1);
|
|
}
|
|
|
|
return "prog_"+parseProgName(result)+"();";
|
|
}
|
|
|
|
//If
|
|
if (content.startsWith(new String(new char[]{0xF7,0x00}))) {
|
|
incrementTabs();
|
|
return "if ((*" + parse(content.substring(2), CONV_TO_BCD) + ")[1]) {";
|
|
}
|
|
|
|
//Then
|
|
if (content.startsWith(new String(new char[]{0xF7,0x01}))) {
|
|
return parse(content.substring(2), WHOLE_INSTRUCTION);
|
|
}
|
|
|
|
//Else
|
|
if (content.startsWith(new String(new char[]{0xF7,0x02}))) {
|
|
decrementTabs();
|
|
String result = "\n" + tabs + "} else {\n";
|
|
incrementTabs();
|
|
return result + tabs + parse(content.substring(2), WHOLE_INSTRUCTION);
|
|
|
|
}
|
|
|
|
//IfEnd ; it is always a single instruction
|
|
if (content.startsWith(new String(new char[]{0xF7,0x03}))) {
|
|
decrementTabs();
|
|
if (content.length() > 2) {
|
|
error("Instruction begins by IfEnd but includes something else!");
|
|
}
|
|
return "\n" + tabs + "}";
|
|
}
|
|
|
|
//inline if (double arrow '=>')
|
|
matchResult = checkMatch(content, new String(new char[]{0x13}));
|
|
if (matchResult >= 0) {
|
|
incrementTabs();
|
|
String result = "if ((*" + parse(content.substring(0, matchResult), CONV_TO_BCD)
|
|
+ ")[1]) {\n" + tabs + parse(content.substring(matchResult+1), WHOLE_INSTRUCTION) + "\n";
|
|
decrementTabs();
|
|
return result + "\n" + tabs + "}";
|
|
}
|
|
|
|
//For, To, Step
|
|
if (content.startsWith(new String(new char[]{0xF7,0x04}))) {
|
|
//Stocks the position of the "To", no need to check for strings because there are no strings in a for
|
|
int toPosition = content.indexOf(new String(new char[]{0xF7, 0x05}));
|
|
//Stocks the position of the ->
|
|
int assignmentPosition = content.indexOf((char)0x0E);
|
|
//Checks for "Step"
|
|
int stepPosition = content.indexOf(new String(new char[]{0xF7, 0x06}));
|
|
String variable = content.substring(assignmentPosition+1, toPosition);
|
|
System.out.println("Parsing a For instruction. Position of To is "+toPosition+
|
|
", position of -> is "+assignmentPosition+", position of Step is "+stepPosition+
|
|
", variable is: "+variable);
|
|
|
|
//Check for empty for, which is replaced by Sleep()
|
|
if (instructions.get(instructionNumber+1).equals(new String(new char[]{0xF7, 0x07}))) {
|
|
System.out.println("Parsing empty for");
|
|
String result = "Sleep(";
|
|
if (stepPosition >= 0) {
|
|
result += "Sleep(" + handleIntConversion(content.substring(toPosition+2, stepPosition)) + "/"+
|
|
handleIntConversion(content.substring(stepPosition+2));
|
|
} else {
|
|
result += handleIntConversion(content.substring(toPosition+2));
|
|
}
|
|
instructionNumber++;
|
|
return result + ");";
|
|
}
|
|
incrementTabs();
|
|
String result = "for (";
|
|
//variable = beginning;
|
|
result += parse(content.substring(2, toPosition), WHOLE_INSTRUCTION) + " ";
|
|
|
|
//TODO: parse the step as an integer to know if it is <0 or >0
|
|
//also put the break condition in the for
|
|
if (stepPosition >= 0) {
|
|
//step < 0 && var >= limit || step > 0 && var <= limit; variable = variable + step) {
|
|
String step = content.substring(stepPosition+2);
|
|
System.out.println("Step = " + step);
|
|
String limit = parse(content.substring(toPosition+2, stepPosition), CONV_TO_BCD);
|
|
result += "(*" + parse(step+"<0"+(char)0x7F+(char)0xB0+variable+(char)0x12+limit+(char)0x7F+(char)0xB1+step+">0"+(char)0x7F+(char)0xB0+variable+(char)0x10+limit, CONV_TO_BCD)+")[1]" + "; "
|
|
+ parse(variable+(char)0x89+step+(char)0x0E+variable) + ") {";
|
|
//+ "\n" + tabs + "if ((*B2C_calcExp((unsigned char*)\""+step+"<0\\x7F\"\"\\xB0\"\""+variable+"<"+limit+"\\x7F\"\"\\xB1\"\""+step+">0\\x7F\"\"\\xB0\"\""+variable+">"+limit+"\"))[1]) break;";
|
|
} else {
|
|
//variable <= limit; variable = variable + 1) {"
|
|
result += "(*" + parse(variable + (char)0x10 + content.substring(toPosition+2), CONV_TO_BCD) + ")[1]; "
|
|
+ parse(variable + (char)0x89 + "1" + (char)0x0E + variable) + ") {";
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
//Next ; like IfEnd
|
|
if (content.startsWith(new String(new char[]{0xF7,0x07}))) {
|
|
if (content.length() > 2) {
|
|
error("Instruction begins by Next but includes something else!");
|
|
}
|
|
decrementTabs();
|
|
return "\n"+tabs+"}";
|
|
}
|
|
|
|
//While
|
|
if (content.startsWith(new String(new char[]{0xF7,0x08}))) {
|
|
incrementTabs();
|
|
return "while ((*" + parse(content.substring(2), CONV_TO_BCD) + ")[1]) {";
|
|
}
|
|
|
|
//WhileEnd
|
|
if (content.startsWith(new String(new char[]{0xF7,0x09}))) {
|
|
if (content.length() > 2) {
|
|
error("Instruction begins by WhileEnd but includes something else!");
|
|
}
|
|
decrementTabs();
|
|
return "\n" + tabs + "}";
|
|
}
|
|
|
|
//Do
|
|
if (content.startsWith(new String(new char[]{0xF7,0x0A}))) {
|
|
if (content.length() > 2) {
|
|
error("Instruction begins by Do but includes something else!");
|
|
}
|
|
incrementTabs();
|
|
return "do {";
|
|
}
|
|
|
|
//LpWhile
|
|
if (content.startsWith(new String(new char[]{0xF7,0x0B}))) {
|
|
decrementTabs();
|
|
return "\n" + tabs + "} while ((*" + parse(content.substring(2)) + ")[1]);";
|
|
}
|
|
|
|
//Return
|
|
if (content.startsWith(new String(new char[]{0xF7,0x0C}))) {
|
|
if (content.length() > 2) {
|
|
error("Instruction begins by Return but includes something else!");
|
|
}
|
|
return "return;";
|
|
}
|
|
|
|
//Break
|
|
if (content.startsWith(new String(new char[]{0xF7,0x0D}))) {
|
|
if (content.length() > 2) {
|
|
error("Instruction begins by Break but includes something else!");
|
|
}
|
|
return "break;";
|
|
}
|
|
|
|
//Stop
|
|
if (content.startsWith(new String(new char[]{0xF7,0x0E}))) {
|
|
if (content.length() > 2) {
|
|
error("Instruction begins by Stop but includes something else!");
|
|
}
|
|
return "B2C_stop();";
|
|
}
|
|
|
|
//Locate
|
|
if (content.startsWith(new String(new char[]{0xF7,0x10}))) {
|
|
Integer[] args = parseArgs(content);
|
|
return "locate(" + handleIntConversion(content.substring(2, args[0])) + ", "
|
|
+ handleIntConversion(content.substring(args[0]+1, args[1]))
|
|
+ "); Print("
|
|
+ parseStr(content.substring(args[1]+1), CONV_TO_CHARARRAY) + "); ML_display_vram();";
|
|
}
|
|
|
|
//ClrText
|
|
if (content.startsWith(new String(new char[]{0xF7,0x18}))) {
|
|
if (content.length() > 2) {
|
|
error("Instruction begins by ClrText but includes something else!");
|
|
}
|
|
return "ML_clear_vram();";
|
|
}
|
|
|
|
//ClrMat
|
|
if (content.startsWith(new String(new char[]{0xF9,0x1E}))) {
|
|
if (content.length() != 3) {
|
|
error("Invalid argument for ClrMat!");
|
|
}
|
|
return "B2C_clrMat(" + getChar(content.charAt(2)) + ");";
|
|
}
|
|
|
|
|
|
//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.
|
|
|
|
//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
|
|
System.out.println("Parsing a Dim assignment.");
|
|
|
|
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(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));
|
|
if (commaPos.length != 1) {
|
|
error("List must consist of 2 numbers!");
|
|
}
|
|
result += content.substring(1, commaPos[0]+1) + ", ";
|
|
if (content.charAt(matchResult-1) != '}') {
|
|
content = content.substring(0, matchResult) + '}' + content.substring(matchResult);
|
|
matchResult++;
|
|
}
|
|
result += content.substring(commaPos[0]+2, matchResult-1) + ");";
|
|
return result;
|
|
} else {
|
|
error("Unknown mat assignment");
|
|
}
|
|
|
|
} else if (content.substring(matchResult+3).startsWith(new String(new char[]{0x7F, 0x51}))) { //followed by List
|
|
return "B2C_setDimList(" + handleIntConversion(content.substring(matchResult+5)) + ", "
|
|
+ handleIntConversion(content.substring(0, matchResult)) + ");";
|
|
} else {
|
|
error("Dim instruction is not followed by List or Mat!");
|
|
}
|
|
|
|
//Check for '~' operator
|
|
} else if (content.substring(matchResult+1).matches("[A-Z]~[A-Z]")) {
|
|
String assignment = parse(content.substring(0, matchResult), CONV_TO_BCD);
|
|
incrementTabs();
|
|
String result = "for (i = "+content.charAt(matchResult+1)+"; i <= "+content.charAt(matchResult+3)+"; i++) {\n"+tabs+"B2C_setAlphaVar(i, "+assignment+");\n";
|
|
decrementTabs();
|
|
return result + tabs + "}";
|
|
|
|
//Check for list assignment
|
|
} else if (content.substring(matchResult+1).startsWith(new String(new char[]{0x7F, 0x51}))) {
|
|
Integer[] check = parseBrackets(content.substring(matchResult+1));
|
|
|
|
if (check.length > 0) {
|
|
if (check.length == 2 && matchResult+1+check[1] == content.length()-1) {
|
|
content = content.substring(0, content.length()-1);
|
|
}
|
|
String result = "B2C_setListRow(" + handleIntConversion(
|
|
content.substring(matchResult+3, matchResult+1+check[0])) +
|
|
", " + handleIntConversion(
|
|
content.substring(matchResult+2+check[0], content.length())) +
|
|
", " + parse(content.substring(0, matchResult), CONV_TO_BCD) + ");";
|
|
return result;
|
|
}
|
|
|
|
//Check for Mat assignment
|
|
} else if (content.substring(matchResult+1).startsWith(new String(new char[]{0x7F, 0x40}))) {
|
|
//Account for possible unmatched bracket
|
|
if (content.charAt(content.length()-1) != ']') {
|
|
content += ']';
|
|
}
|
|
Integer[] check = parseArgs(content.substring(matchResult+5, content.length()-1));
|
|
|
|
if (check.length != 1) {
|
|
error("Mat instruction does not have one comma!");
|
|
}
|
|
String result = "B2C_setMatCase(" + getChar(content.charAt(matchResult+3)) + ", " +
|
|
handleIntConversion(content.substring(matchResult+5, matchResult+5+check[0])) + ", " +
|
|
handleIntConversion(content.substring(matchResult+5+check[0]+1, content.length()-1)) + ", " +
|
|
parse(content.substring(0, matchResult), CONV_TO_BCD) + ");";
|
|
|
|
return result;
|
|
|
|
/*if (check.length > 0) {
|
|
if (check.length == 2 && matchResult+1+check[1] == content.length()-1) {
|
|
content = content.substring(0, content.length()-1);
|
|
}
|
|
String result = "B2C_setMat(" + handleIntConversion(
|
|
content.substring(matchResult+3, matchResult+1+check[0])) +
|
|
", " + handleIntConversion(
|
|
content.substring(matchResult+2+check[0], content.length())) +
|
|
", " + parse(content.substring(0, matchResult), CONV_TO_BCD) + ");";
|
|
return result;
|
|
}*/
|
|
|
|
//Check for variable assignment
|
|
} else if (content.substring(matchResult+1).matches("[A-Z\\xCD\\xCE]")) {
|
|
String result = "B2C_setAlphaVar(" + getChar(content.charAt(matchResult+1))
|
|
+ ", " + parse(content.substring(0, matchResult), CONV_TO_BCD) + ")";
|
|
if (option == WHOLE_INSTRUCTION) {
|
|
result += ";";
|
|
}
|
|
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);
|
|
//System.out.println(Arrays.toString(parenthesesPos));
|
|
|
|
//Mat
|
|
if (content.startsWith(new String(new char[]{0x7F,0x40}))) {
|
|
System.out.println("Parsing a matrix");
|
|
|
|
//Before parsing, we must check if the Mat instruction does not include dimensions
|
|
if (content.length() == 3) {
|
|
String str = getMat(content.charAt(2));
|
|
if (option == WHOLE_INSTRUCTION) {
|
|
str = "B2C_setMat(" + getChar((char)0xC0) + ", " + str + ");";
|
|
}
|
|
return str;
|
|
}
|
|
|
|
Integer[] check = parseBrackets(content);
|
|
Integer[] arg = parseArgs(content.substring(check[0]+1, check[1]));
|
|
if (check[1] == content.length()-1) {
|
|
if (arg.length != 1) {
|
|
error("matrix coordinates are fewer or more than two!");
|
|
} else {
|
|
return supportAns("&" + getMat(content.charAt(2)) + ".data["+getMat(content.charAt(2)) + ".height*("
|
|
/*+ handleIntConversion(parse(content.substring(check[0]+1, check[0]+1+arg[0]))) + ")+("
|
|
+ handleIntConversion(parse(content.substring(check[0]+2+arg[0],content.length()-1))) + ")]", option);*/
|
|
+ handleIntConversion(content.substring(check[0]+1, check[0]+1+arg[0])) + "-1)+("
|
|
+ handleIntConversion(content.substring(check[0]+2+arg[0],content.length()-1)) + "-1)]", option);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//Dim
|
|
if (content.startsWith(new String(new char[]{0x7F,0x46}))) {
|
|
|
|
//If it is Dim Mat
|
|
if (content.substring(2).startsWith(new String(new char[]{0x7F, 0x40}))) {
|
|
String result = "B2C_getDimMat(" + getChar(content.charAt(4)) + ")";
|
|
if (option == WHOLE_INSTRUCTION)
|
|
return "B2C_setList(LIST_ANS, " + result + ");";
|
|
return result;
|
|
|
|
//If it is Dim List
|
|
} else if (content.substring(2).startsWith(new String(new char[]{0x7F, 0x40}))) {
|
|
return supportAns("&list["+getList(content.substring(2)) + "].nbElements", option);
|
|
} else {
|
|
error("Dim instruction is not followed by List or Mat!");
|
|
}
|
|
|
|
}
|
|
|
|
//List
|
|
if (content.startsWith(new String(new char[]{0x7F,0x51}))) {
|
|
System.out.println("Parsing a list");
|
|
//Before parsing, we must check if the entire instruction is a List instruction
|
|
Integer[] check = parseBrackets(content);
|
|
|
|
if (check.length == 0) {
|
|
String result = getList(content.substring(2));
|
|
if (option == WHOLE_INSTRUCTION)
|
|
return "B2C_setList(LIST_ANS, " + result + ");";
|
|
return result;
|
|
}
|
|
|
|
if (check.length == 2 && check[1] == content.length()-1 || option == WHOLE_INSTRUCTION && check.length > 0) {
|
|
if (check.length == 2 && check[1] == content.length()-1) {
|
|
content = content.substring(0, content.length()-1);
|
|
}
|
|
return supportAns("list[" + getList(content.substring(2, check[0])) + "].data["
|
|
+ handleIntConversion(content.substring(check[0]+1, content.length())) + "]", option);
|
|
}
|
|
}
|
|
|
|
//searches for an operator
|
|
for (int h = Operators.maxPrecedence; h >= 0; h--) {
|
|
for (int j = content.length()-1; j >= 0; j--) {
|
|
for (int i = 0; i < Operators.operators[h].length; i++) {
|
|
//System.out.println("Checking for operator " + Operators.operators[h][i].getAsciiFunction() + "(i="+i+", j="+j+", h="+h+")");
|
|
if (content.substring(0, j).endsWith(Operators.operators[h][i].getCasioChar())) {
|
|
//test if the operator is not within parentheses
|
|
boolean isInParentheses = false;
|
|
for (int k = 0; k < parenthesesPos.length; k+=2) {
|
|
if (j-Operators.operators[h][i].getCasioChar().length() >= parenthesesPos[k] && j-Operators.operators[h][i].getCasioChar().length() <= parenthesesPos[k+1]) {
|
|
isInParentheses = true;
|
|
}
|
|
}
|
|
System.out.println("Parsing operator: " + Operators.operators[h][i].getAsciiFunction());
|
|
//If the operator is at the beginning of the string, it isn't a binary operator
|
|
if (!isInParentheses && (j != Operators.operators[h][i].getCasioChar().length() || Operators.operators[h][i].getNbOperands() == 1)) {
|
|
String str = "";
|
|
//System.out.println("Found the above operator");
|
|
str += Operators.operators[h][i].getAsciiFunction() + "(";
|
|
|
|
//Check for special of unary plus operator (which does nothing)
|
|
/*if (str.equals("B2C_unaryPlus(")) {
|
|
return parse(content.substring(1), option);
|
|
}*/
|
|
|
|
//If it is a mathematical operation
|
|
if (Operators.operators[h][i].isMathematicalFunction()) {
|
|
str += addBCDBuffer() + ", ";
|
|
}
|
|
|
|
if (Operators.operators[h][i].getNbOperands() != 1) {
|
|
str += parse(content.substring(0, j-Operators.operators[h][i].getCasioChar().length()), CONV_TO_BCD) + ", " + parse(content.substring(j), CONV_TO_BCD);
|
|
} else {
|
|
//If it is an unary operator, check if we can convert them as a constant
|
|
if (content.matches("[\\d\\x99\\x87\\.\\x86]+")) {
|
|
//System.out.println("convert to const");
|
|
Constants.add(content);
|
|
return supportAns("&"+Constants.getVarNotation(content), option);
|
|
} else {
|
|
str += parse(content.substring(j), CONV_TO_BCD);
|
|
}
|
|
}
|
|
str += ")";
|
|
return supportAns(str, option);
|
|
}/* else {
|
|
System.out.println("Operator was in parenthesis, ignoring it");
|
|
}*/
|
|
|
|
}
|
|
/*if (isMultibytePrefix(content.charAt(j))) {
|
|
j++;
|
|
}*/
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//replace variables with their position in the var[] array; only do this if the string only contains the variable
|
|
if (content.matches("[A-Z\\xCD\\xCE\\xC0]")) {
|
|
return handleAlphaVar(content, option);
|
|
}
|
|
|
|
//Test if it is a number (note that it can be something like 2X, implicit multiplication)
|
|
if (content.matches("^[\\d\\x99\\x87\\.](.+)?")) {
|
|
int testForImplicitMultiplication = -1;
|
|
for (char i = '0'; i <= '9'; i++) {
|
|
if (testForImplicitMultiplication < content.lastIndexOf(i)) {
|
|
testForImplicitMultiplication = content.lastIndexOf(i);
|
|
}
|
|
}
|
|
if (testForImplicitMultiplication != content.length()-1) {
|
|
return parse(content.substring(0, testForImplicitMultiplication+1) + (char)0xA9 + content.substring(testForImplicitMultiplication+1, content.length()));
|
|
}
|
|
//At this point it is a number, add it to constants
|
|
Constants.add(content);
|
|
String result = "";
|
|
if (option == WHOLE_INSTRUCTION || option == CONV_TO_BCD) {
|
|
/*if (content.matches("\\d")) {
|
|
result += consts[Integer.valueOf(content)];
|
|
} else if (content.equals("10")) {
|
|
result += consts[10];
|
|
} else {
|
|
result = "B2C_convToBCD(\"" + content + "\")";
|
|
}*/
|
|
result += "&"+Constants.getVarNotation(content);
|
|
} else {
|
|
result = content;
|
|
}
|
|
if (option == WHOLE_INSTRUCTION) {
|
|
return supportAns(result, option);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
//At this point it is a calculation, check for calculations functions
|
|
//Any function that returns a BCD value must be here! (GetKey, RanInt...)
|
|
//Don't forget to add the buffer!
|
|
|
|
//RanInt#(
|
|
if (content.startsWith(new String(new char[]{0x7F,0x87}))) {
|
|
if (content.charAt(content.length()-1) == ')') {
|
|
content = content.substring(0, content.length()-1);
|
|
}
|
|
Integer[] args = parseArgs(content.substring(2));
|
|
if (args.length != 1) {
|
|
error("RanInt# method doesn't have 2 arguments!");
|
|
}
|
|
args[0] += 2;
|
|
return supportAns("B2C_ranInt(" + addBCDBuffer() + ", " + parse(content.substring(2, args[0]), CONV_TO_BCD) + ","
|
|
+ parse(content.substring(args[0]+1), CONV_TO_BCD) + ")", option);
|
|
|
|
}
|
|
|
|
//Getkey
|
|
if (content.startsWith(new String(new char[]{0x7F,0x8F}))) {
|
|
if (content.length() > 2) {
|
|
error("Instruction begins by GetKey but includes something else!");
|
|
}
|
|
|
|
return supportAns("B2C_getkey(" + addBCDBuffer() + ")", option);
|
|
}
|
|
|
|
//Not
|
|
if (content.startsWith(new String(new char[]{0x7F,0xB3}))) {
|
|
|
|
return supportAns("B2C_not(" + parse(content.substring(1, CONV_TO_BCD) + ")"), option);
|
|
}
|
|
|
|
//Int
|
|
if (content.startsWith(new String(new char[]{0xA6}))) {
|
|
if (content.length() == 1) {
|
|
error("Int instruction is standalone!");
|
|
}
|
|
return supportAns("B2C_int(" + addBCDBuffer() + ", " + parse(content.substring(1), CONV_TO_BCD) + ")", option);
|
|
}
|
|
|
|
//Ran#
|
|
if (content.startsWith(new String(new char[]{0xC1}))) {
|
|
if (content.length() != 1) {
|
|
error("Ran# instruction includes something else!");
|
|
}
|
|
return supportAns("B2C_rand(" + addBCDBuffer() + ")", option);
|
|
}
|
|
|
|
//this only occurs if the entire string is within parentheses, such as "(2+3)"
|
|
if (parenthesesPos.length == 2 && parenthesesPos[0] == 0 && parenthesesPos[1] == content.length()-1) {
|
|
return "(" + parse(content.substring(1, content.length()-1)) + ")";
|
|
}
|
|
|
|
|
|
/*String result = "";
|
|
if (content.matches("\\d+")) {
|
|
//Parse numbers as a global variable (for example, 36 is replaced by a const BCDvar _36 which value is calculated at the beginning).
|
|
if (!Constants.consts.contains(Integer.parseInt(content))) {
|
|
Constants.add(content);
|
|
}
|
|
result += "&" + Constants.getVarNotation(content);
|
|
return result;
|
|
//Check if it is a lone variable
|
|
} else if (content.matches("[A-Z\\xCD\\xCE\\xC0]")) {
|
|
result += handleAlphaVar(content, option);
|
|
return result;*/
|
|
/*} else {
|
|
result += "B2C_calcExp((unsigned char*)" + parseStr("\"" + content + "\"") + ")";
|
|
return result;
|
|
}*/
|
|
//return supportAns(result, option);
|
|
|
|
//At this point in the code, the method must have already returned
|
|
//if it has detected at least one instruction it understands
|
|
|
|
error("function not recognized! " + printNonAscii(content));
|
|
return "===COMPILATION ERROR===";
|
|
}
|
|
|
|
/**
|
|
* This function is called to parse hardcoded lists (written like {1,2,3}).
|
|
* At the moment the only functions calling this method are:
|
|
* - Assignment operation on Dim Mat ({1,2}->Dim Mat M)
|
|
* - Assignment operation on List ({1,2}->List 3)
|
|
* - Multi/Super drawstat (Graph(X,Y)=({1,2},{3,4});
|
|
*/
|
|
|
|
public static String parseList(String content) {
|
|
String result = "B2C_newList(";
|
|
|
|
//Check if the list is hardcoded or not
|
|
if (content.startsWith(new String(new char[]{0x7F, 0x51}))) {
|
|
return parse(content);
|
|
}
|
|
if (content.charAt(0) != '{') {
|
|
error("Trying to parse hardcoded list but the list doesn't begin with a '{'!");
|
|
return "";
|
|
}
|
|
|
|
|
|
if (content.charAt(content.length()-1) == '}') {
|
|
content = content.substring(0, content.length()-1);
|
|
}
|
|
|
|
System.out.println("Parsing a hardcoded list: "+content);
|
|
//remove the leading '{' for easier argument parsing
|
|
content = content.substring(1);
|
|
|
|
ArrayList<Integer> args = new ArrayList<Integer>();
|
|
args.addAll(Arrays.asList(parseArgs(content.substring(0))));
|
|
args.add(content.length());
|
|
|
|
System.out.println(args.toString());
|
|
result += args.size() + ", ";
|
|
result += parse(content.substring(0, args.get(0)), CONV_TO_BCD) + ", ";
|
|
for (int i = 0; i < args.size()-1; i++) {
|
|
result += parse(content.substring(args.get(i)+1, args.get(i+1)), CONV_TO_BCD) + ", ";
|
|
}
|
|
|
|
//remove the last comma
|
|
return result.substring(0, result.length()-2) + ")";
|
|
}
|
|
|
|
/**
|
|
* This method checks for the presence of the string match in the string content.
|
|
* This can't be done with traditional methods because it must check if the match
|
|
* string is in the content string AND not in a string.
|
|
*
|
|
* Returns -1 if the value doesn't exist, or the beginning of the first occurence of the match.
|
|
*
|
|
* Examples:
|
|
*
|
|
* checkMatch("Locate 1,1,\"=> First option\"", "=>") will return -1,
|
|
* because there is a '=>' but inside a string.
|
|
*
|
|
* checkMatch("A>B => Locate 1,1,C", "=>") will return 4.
|
|
*/
|
|
|
|
public static int checkMatch(String content, String match) {
|
|
|
|
boolean positionIsString = false;
|
|
for (int i = 0; i < content.length(); i++) {
|
|
if (isMultibytePrefix(content.charAt(i))) {
|
|
i += 2;
|
|
if (i >= content.length()) {
|
|
break;
|
|
}
|
|
}
|
|
if (content.substring(i).startsWith(match) && !positionIsString) {
|
|
return i;
|
|
}
|
|
if (content.charAt(i) == '"') {
|
|
positionIsString = !positionIsString;
|
|
} else if (content.charAt(i) == '\\') {
|
|
i++;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
return -1;
|
|
}
|
|
|
|
public static String parseProgName(String content) {
|
|
//The use of replaceAll is possible because you can't have multi byte characters in program names
|
|
String[] specialChars = {
|
|
"\\{", "\\}",
|
|
"\\[", "\\]",
|
|
"\\.",
|
|
"\"",
|
|
" ",
|
|
"\\xA9|\\*",
|
|
"\\xB9|\\/",
|
|
"\\x99|-",
|
|
"\\x89|\\+",
|
|
"\\xCD",
|
|
"\\xCE",
|
|
"'",
|
|
"~",
|
|
"\0"
|
|
};
|
|
String[] replacements = {
|
|
"lcurlybracket",
|
|
"rcurlybracket",
|
|
"lbracket",
|
|
"rbracket",
|
|
"dot",
|
|
"quote",
|
|
"space",
|
|
"mult",
|
|
"div",
|
|
"sub",
|
|
"add",
|
|
"radius",
|
|
"theta",
|
|
"apos",
|
|
"tilde",
|
|
""
|
|
};
|
|
for (int i = 0; i < specialChars.length; i++) {
|
|
content = content.replaceAll(specialChars[i], replacements[i]);
|
|
}
|
|
content = content.replaceAll("[^ -~]", "_"); //replace non-ASCII characters
|
|
return content;
|
|
}
|
|
|
|
/**
|
|
* This method parses a string. It is designed to parse things like:
|
|
* Str 1 + "test" + Str 2
|
|
*
|
|
* The main parse() method only calls this method in case of an argument that is always a string,
|
|
* for the functions Locate, Text and standalone strings.
|
|
*
|
|
*/
|
|
|
|
//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 '+'
|
|
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
|
|
boolean isInParentheses = false;
|
|
for (int k = 0; k < parenthesesPos.length; k+=2) {
|
|
if (i-1 >= parenthesesPos[k] && i-1 <= parenthesesPos[k+1]) {
|
|
isInParentheses = true;
|
|
}
|
|
}
|
|
System.out.println("Parsing concatenation operator (+)");
|
|
|
|
if (!isInParentheses) {
|
|
//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;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Plain strings
|
|
if (content.startsWith("\"")) {
|
|
if (content.charAt(content.length()-1) != '"') {
|
|
content += '"';
|
|
}
|
|
content = "(unsigned char*)" + replaceNonAscii(content);
|
|
if (option == CONV_TO_CHARARRAY) {
|
|
return content;
|
|
}
|
|
return "B2C_charArrayToStr(" + content + ")";
|
|
}
|
|
|
|
//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}))) {
|
|
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+")");
|
|
}
|
|
|
|
//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;
|
|
}
|
|
|
|
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.
|
|
public static String replaceNonAscii(String content) {
|
|
for (int i = 0; i < content.length(); i++) {
|
|
if (content.charAt(i) < ' ' || content.charAt(i) > '~') {
|
|
String result = content.substring(0, i);
|
|
result += "\\x";
|
|
String str = Integer.toHexString(content.charAt(i));
|
|
if (str.length() > 2) {
|
|
error("Unicode character u" + str + "!");
|
|
} else {
|
|
result += str;
|
|
}
|
|
|
|
//if (i < content.length()-1)
|
|
result += "\"\"";
|
|
result += content.substring(i+1);
|
|
content = result;
|
|
|
|
}
|
|
}
|
|
return content;
|
|
}
|
|
|
|
/**
|
|
* This method parses comma-separated arguments. It is used to parse any function
|
|
* with those kind of arguments. It is needed because the arguments themselves may have
|
|
* commas (example: Locate 1,Mat M[A,B],Str 1).
|
|
*
|
|
* Note that it doesn't return the arguments themselves, but the position of the commas, that
|
|
* must be exploited by the parser.
|
|
*
|
|
*/
|
|
public static Integer[] parseArgs(String content) {
|
|
System.out.println("Parsing arguments: "+printNonAscii(content));
|
|
ArrayList<Integer> args = new ArrayList<Integer>();
|
|
int argsBuffer = 0;
|
|
boolean positionIsString = false;
|
|
for (int i = 0; i < content.length(); i++) {
|
|
|
|
if (content.charAt(i) == ',' && argsBuffer == 0 && !positionIsString) {
|
|
args.add(i);
|
|
}
|
|
if (startsWithParenthesis(content.substring(i)) && !positionIsString) {
|
|
argsBuffer++;
|
|
} else if ((content.charAt(i) == ')' || content.charAt(i) == ']' || content.charAt(i) == '}') && !positionIsString) {
|
|
argsBuffer--;
|
|
} else if (content.charAt(i) == '"') {
|
|
positionIsString = !positionIsString;
|
|
} else if (content.charAt(i) == '\\') {
|
|
i++;
|
|
}
|
|
if (isMultibytePrefix(content.charAt(i))) {
|
|
i++;
|
|
}
|
|
|
|
}
|
|
//System.out.println("Result:"+args.toString());
|
|
return args.toArray(new Integer[args.size()]);
|
|
}
|
|
|
|
/**
|
|
* This function returns the index of each first-level opening and closing brackets/parentheses.
|
|
* Example: the string "3*(4*(5+6))+(4*5)" will return {2, 10, 12, 16}.
|
|
* It accounts for unmatched brackets at the end, so "2->Mat M[1,3" will return the same as "2->Mat M[1,3]".
|
|
* When adding an opcode containing parenthesis, make sure to include it in this function.
|
|
*/
|
|
public static Integer[] parseBrackets(String content) {
|
|
ArrayList<Integer> bracketsPos = new ArrayList<Integer>();
|
|
int bracketsLevel = 0;
|
|
boolean currentPositionIsString = false;
|
|
for (int i = 0; i < content.length(); i++) {
|
|
if (!currentPositionIsString && startsWithParenthesis(content.substring(i))) {
|
|
bracketsLevel++;
|
|
if (bracketsLevel == 1) {
|
|
bracketsPos.add(i);
|
|
}
|
|
} else if ((content.charAt(i) == ')' || content.charAt(i) == ']' || content.charAt(i) == '}') && !currentPositionIsString) {
|
|
bracketsLevel--;
|
|
if (bracketsLevel == 0) {
|
|
bracketsPos.add(i);
|
|
} else if (bracketsLevel < 0) {
|
|
error("brackets level below 0!");
|
|
}
|
|
} else if (content.charAt(i) == '"') {
|
|
currentPositionIsString = !currentPositionIsString;
|
|
} else if (content.charAt(i) == '\\') {
|
|
i++;
|
|
} else if (isMultibytePrefix(content.charAt(i))) {
|
|
i++;
|
|
}
|
|
}
|
|
//This should take care of unmatched brackets at the end
|
|
while (bracketsLevel > 0) {
|
|
bracketsPos.add(content.length());
|
|
bracketsLevel--;
|
|
}
|
|
|
|
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:
|
|
*
|
|
* 0x99 (-) and 0x87 (-) by '-'
|
|
* 0x89 (+) by '+'
|
|
* 0xA8 (^) by '^'
|
|
* 0xA9 (*) by '*'
|
|
* 0xB9 (/) by '/'
|
|
*
|
|
* It it not used at the moment.
|
|
*/
|
|
/*public static StringBuilder autoreplace(StringBuilder result) {
|
|
boolean positionIsString = false;
|
|
for (int i = 0; i < result.length(); i++) {
|
|
|
|
if (result.charAt(i) == (char)0x99) {
|
|
result.setCharAt(i, '-');
|
|
}
|
|
if (result.charAt(i) == (char)0x87 && !positionIsString) {
|
|
result.setCharAt(i, '-');
|
|
}
|
|
if (result.charAt(i) == (char)0x89) {
|
|
result.setCharAt(i, '+');
|
|
}
|
|
if (result.charAt(i) == (char)0xA8) {
|
|
result.setCharAt(i, '^');
|
|
}
|
|
if (result.charAt(i) == (char)0xA9) {
|
|
result.setCharAt(i, '*');
|
|
}
|
|
if (result.charAt(i) == (char)0xB9) {
|
|
result.setCharAt(i, '/');
|
|
}
|
|
if (result.charAt(i) == '"') {
|
|
positionIsString = !positionIsString;
|
|
} else if (result.charAt(i) == '\\') {
|
|
i++;
|
|
}
|
|
if (isMultibytePrefix(result.charAt(i))) {
|
|
i++;
|
|
}
|
|
|
|
}
|
|
return result;
|
|
|
|
|
|
}*/
|
|
|
|
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)
|
|
return true;
|
|
return false;
|
|
}
|
|
/**
|
|
* 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!
|
|
*
|
|
* The function must be the function alone, if there is something else then
|
|
* it will return false.
|
|
*/
|
|
public static boolean startsWithStringFunction(String function) {
|
|
if (function.startsWith(new String(new char[]{0xF9, 0x30})) || //StrJoin(
|
|
function.startsWith(new String(new char[]{0xF9, 0x34})) || //StrLeft(
|
|
function.startsWith(new String(new char[]{0xF9, 0x35})) || //StrRight(
|
|
function.startsWith(new String(new char[]{0xF9, 0x36})) || //StrMid(
|
|
function.startsWith(new String(new char[]{0xF9, 0x39})) || //StrUpr(
|
|
function.startsWith(new String(new char[]{0xF9, 0x3A})) || //StrLwr(
|
|
function.startsWith(new String(new char[]{0xF9, 0x3B})) || //StrInv(
|
|
function.startsWith(new String(new char[]{0xF9, 0x3C})) || //StrShift(
|
|
function.startsWith(new String(new char[]{0xF9, 0x3D})) || //StrRotate(
|
|
function.startsWith(new String(new char[]{0xF9, 0x3F})) || //Str
|
|
function.charAt(0) == '"'
|
|
) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public static String supportAns(String content, int option) {
|
|
if (option == WHOLE_INSTRUCTION)
|
|
return "B2C_setAlphaVar("+getChar((char)0xC0)+", " + content + ");";
|
|
return content;
|
|
}
|
|
|
|
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 (Constants.isNumber(content)) {
|
|
return Constants.getNumberNotation(content);
|
|
}
|
|
return "B2C_convToUInt(" + parse(content) + ")";
|
|
}
|
|
|
|
public static String handleAlphaVar(String content, int option) {
|
|
//Handle var such that var[char-'A'] gives the correct variable
|
|
if (content.matches("[A-Z\\xCD\\xCE\\xC0]")) {
|
|
String result = getAlphaVar(content);
|
|
return supportAns(result, option);
|
|
}
|
|
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";
|
|
}
|
|
public static void decrementTabs() {
|
|
tabs = tabs.substring(0, tabs.length()-1);
|
|
}
|
|
|
|
public static String getAlphaVar(char var) {return getAlphaVar(""+var);}
|
|
public static String getAlphaVar(String var) {
|
|
String result = "VAR_";
|
|
if (var.length() != 1) {
|
|
error("Unknown var!");
|
|
} else if (var.charAt(0) >= 'A' && var.charAt(0) <= 'Z') {
|
|
result += var.charAt(0);
|
|
} else if (var.charAt(0) == 0xCD) {
|
|
result += "RADIUS";
|
|
} else if (var.charAt(0) == 0xCE) {
|
|
result += "THETA";
|
|
} else if (var.charAt(0) == 0xC0) {
|
|
result += "ANS";
|
|
} else {
|
|
error("Unknown var!");
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public static String getMat(char mat) {
|
|
String result = "MAT_";
|
|
if (mat >= 'A' && mat <= 'Z') {
|
|
result += mat;
|
|
} else if (mat == 0xC0) {
|
|
result += "ANS";
|
|
} else {
|
|
error("Unknown mat "+mat+"!");
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public static String getChar(String char_) {
|
|
if (char_.length() != 1) {
|
|
error("Unknown char " + char_ + "!");
|
|
}
|
|
return getChar(char_.charAt(0));
|
|
}
|
|
public static String getChar(char char_) {
|
|
String result = null;
|
|
if (char_ >= 'A' && char_ <= 'Z') {
|
|
result = ""+char_;
|
|
} else if (char_ == 0xCD) {
|
|
result = "RADIUS";
|
|
} else if (char_ == 0xCE) {
|
|
result = "THETA";
|
|
} else if (char_ == 0xC0) {
|
|
result = "ANS";
|
|
} else {
|
|
error("Unknown char "+char_+"!");
|
|
}
|
|
return result;
|
|
}
|
|
public static String getList(String list) {
|
|
if (list.length() == 1 && list.charAt(0) == 0xC0) {
|
|
return "ANS";
|
|
} else {
|
|
return handleIntConversion(list);
|
|
}
|
|
}
|
|
public static String getStr(String str) {
|
|
if (str.length() > 2) {
|
|
error("Invalid str " + printNonAscii(str) + "!");
|
|
}
|
|
try {
|
|
int strno = Integer.valueOf(str);
|
|
if (strno < 1 || strno > 20) {
|
|
error("Invalid str number " + strno + "!");
|
|
}
|
|
} catch (NumberFormatException e) {
|
|
error("Invalid str " + 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 addMatBuffer() {return addBuffer(MAT_BUFFER);}
|
|
public static String addListBuffer() {return addBuffer(LIST_BUFFER);}
|
|
|
|
public static String addBuffer(int buffer) {
|
|
String result = "&" + bufferVars[buffer] + buffers[buffer];
|
|
buffers[buffer]++;
|
|
return result;
|
|
}
|
|
|
|
public static String printNonAscii(String content) {
|
|
String result = "";
|
|
for (int i = 0; i < content.length(); i++) {
|
|
if (content.charAt(i) >= 32 && content.charAt(i) < 127 && (i == 0 || i > 0 && !isMultibytePrefix(content.charAt(i-1)))) {
|
|
result += content.charAt(i) + " ";
|
|
} else {
|
|
String hex = Integer.toHexString(content.charAt(i));
|
|
result += "0x" + hex + " ";
|
|
if (hex.length() > 2) {
|
|
error("Unhandled unicode character u+" + hex);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public static void error(String error) {
|
|
System.out.println("\nERROR: "+error+"\n");
|
|
System.exit(0);
|
|
}
|
|
}
|