2016-12-28 18:57:39 +01:00
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 ;
2017-01-08 20:03:49 +01:00
//final static int CONV_TO_INT = 3; //Use handleIntConversion instead
final static int CONV_TO_STR = 4 ;
final static int CONV_TO_CHARARRAY = 5 ;
2017-01-07 19:56:03 +01:00
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 " } ;
2016-12-28 18:57:39 +01:00
static String tabs = " \ t " ;
//Be sure to clear these variables between programs.
static ArrayList < String > instructions = new ArrayList < String > ( ) ;
static int instructionNumber = 0 ;
2017-01-07 19:56:03 +01:00
static int [ ] buffers = { 0 , 0 , 0 , 0 } ;
2016-12-28 18:57:39 +01:00
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 ) {
2017-01-07 19:56:03 +01:00
System . out . println ( " Parsing instruction: " + printNonAscii ( content ) ) ;
2016-12-28 18:57:39 +01:00
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 ) ) ;
2017-01-07 19:56:03 +01:00
instructionNumber + + ;
2016-12-28 18:57:39 +01:00
return parse ( content . substring ( 0 , matchResult ) , WHOLE_INSTRUCTION ) + " \ n " + tabs +
parse ( content . substring ( matchResult + 1 ) , WHOLE_INSTRUCTION ) ;
}
2017-01-07 19:56:03 +01:00
2016-12-28 18:57:39 +01:00
//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.
2017-01-07 19:56:03 +01:00
//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 } ) ) ) {
2016-12-28 18:57:39 +01:00
if ( content . charAt ( content . length ( ) - 1 ) = = ')' ) {
content = content . substring ( 0 , content . length ( ) - 1 ) ;
}
2017-01-11 16:54:40 +01:00
Integer [ ] args = parseArgs ( content . substring ( 2 ) ) ;
2016-12-28 18:57:39 +01:00
if ( args . length ! = 1 ) {
2017-01-11 16:54:40 +01:00
error ( " Fill method doesn't have 2 arguments! ( " + args . length + " ) " ) ;
2017-01-07 19:56:03 +01:00
}
2017-01-11 16:54:40 +01:00
args [ 0 ] + = 2 ;
2017-01-07 19:56:03 +01:00
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 ) + " , "
2017-01-11 16:54:40 +01:00
+ getChar ( content . substring ( args [ 0 ] + 1 + 2 ) ) + " ); " ;
2017-01-07 19:56:03 +01:00
} 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! " ) ;
2016-12-28 18:57:39 +01:00
}
2017-01-07 19:56:03 +01:00
2016-12-28 18:57:39 +01:00
2017-01-01 10:53:07 +01:00
}
2016-12-28 18:57:39 +01:00
2017-01-07 19:56:03 +01:00
//unary plus = no purpose
if ( content . startsWith ( new String ( new char [ ] { 0x89 } ) ) ) {
return parse ( content . substring ( 1 ) , option ) ;
2016-12-28 18:57:39 +01:00
}
//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 } ) ) ;
2017-01-01 10:53:07 +01:00
String variable = content . substring ( assignmentPosition + 1 , toPosition ) ;
2016-12-28 18:57:39 +01:00
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;
2017-01-11 16:54:40 +01:00
result + = parse ( content . substring ( 2 , toPosition ) , WHOLE_INSTRUCTION ) + " " ;
2016-12-28 18:57:39 +01:00
//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) {
2017-01-01 10:53:07 +01:00
String step = content . substring ( stepPosition + 2 ) ;
2016-12-28 18:57:39 +01:00
System . out . println ( " Step = " + step ) ;
2017-01-01 10:53:07 +01:00
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] " + " ; "
2017-01-11 16:54:40 +01:00
+ parse ( variable + ( char ) 0x89 + step + ( char ) 0x0E + variable ) + " ) { " ;
2016-12-28 18:57:39 +01:00
//+ "\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) {"
2017-01-01 10:53:07 +01:00
result + = " (* " + parse ( variable + ( char ) 0x10 + content . substring ( toPosition + 2 ) , CONV_TO_BCD ) + " )[1]; "
2017-01-11 16:54:40 +01:00
+ parse ( variable + ( char ) 0x89 + " 1 " + ( char ) 0x0E + variable ) + " ) { " ;
2016-12-28 18:57:39 +01:00
}
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 ] ) )
2017-01-08 20:03:49 +01:00
+ " ); Print( "
+ parseStr ( content . substring ( args [ 1 ] + 1 ) , CONV_TO_CHARARRAY ) + " ); ML_display_vram(); " ;
2016-12-28 18:57:39 +01:00
}
//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(); " ;
}
2017-01-07 19:56:03 +01:00
//ClrMat
if ( content . startsWith ( new String ( new char [ ] { 0xF9 , 0x1E } ) ) ) {
if ( content . length ( ) ! = 3 ) {
error ( " Invalid argument for ClrMat! " ) ;
}
2017-01-11 16:54:40 +01:00
return " B2C_clrMat( " + getChar ( content . charAt ( 2 ) ) + " ); " ;
2017-01-07 19:56:03 +01:00
}
2016-12-28 18:57:39 +01:00
//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.
2017-01-08 20:03:49 +01:00
2016-12-28 18:57:39 +01:00
//Check for assignment
matchResult = checkMatch ( content , new String ( new char [ ] { 0x0E } ) ) ;
if ( matchResult > = 0 ) {
2017-01-11 16:54:40 +01:00
/ * if ( option ! = WHOLE_INSTRUCTION ) {
2017-01-08 20:03:49 +01:00
error ( " Assignment isn't whole instruction?! " ) ;
2017-01-11 16:54:40 +01:00
} * /
2017-01-08 20:03:49 +01:00
2016-12-28 18:57:39 +01:00
//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
2017-01-01 10:53:07 +01:00
String result = " B2C_setDimMat( " + ( content . charAt ( matchResult + 5 ) - 'A' ) + " , " ;
2016-12-28 18:57:39 +01:00
if ( content . startsWith ( new String ( new char [ ] { 0x7F , 0x51 } ) ) ) {
2017-01-08 20:03:49 +01:00
result + = handleIntConversion ( content . substring ( 0 , matchResult ) + " [1] " ) + " , " ;
result + = handleIntConversion ( content . substring ( 0 , matchResult ) + " [2] " ) + " ); " ;
2016-12-28 18:57:39 +01:00
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 ( ) ;
2017-01-11 16:54:40 +01:00
String result = " for (i = " + content . charAt ( matchResult + 1 ) + " ; i <= " + content . charAt ( matchResult + 3 ) + " ; i++) { \ n " + tabs + " B2C_setAlphaVar(i, " + assignment + " ); \ n " ;
2016-12-28 18:57:39 +01:00
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! " ) ;
}
2017-01-11 16:54:40 +01:00
String result = " B2C_setMatCase( " + getChar ( content . charAt ( matchResult + 3 ) ) + " , " +
2016-12-28 18:57:39 +01:00
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
2017-01-01 10:53:07 +01:00
} else if ( content . substring ( matchResult + 1 ) . matches ( " [A-Z \\ xCD \\ xCE] " ) ) {
2017-01-11 16:54:40 +01:00
String result = " B2C_setAlphaVar( " + getChar ( content . charAt ( matchResult + 1 ) )
+ " , " + parse ( content . substring ( 0 , matchResult ) , CONV_TO_BCD ) + " ) " ;
if ( option = = WHOLE_INSTRUCTION ) {
result + = " ; " ;
}
2017-01-08 20:03:49 +01:00
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 ) ) + " ); " ;
2016-12-28 18:57:39 +01:00
return result ;
2017-01-08 20:03:49 +01:00
2016-12-28 18:57:39 +01:00
} else {
error ( " Unknown assignment! " ) ;
}
}
2017-01-08 20:03:49 +01:00
//Check for strings
if ( startsWithStringFunction ( content ) ) {
if ( option = = WHOLE_INSTRUCTION ) {
error ( " B2C does not support standalone strings! " ) ;
} else {
return parseStr ( content , option ) ;
}
}
2016-12-28 18:57:39 +01:00
//at this point it is a mathematical operation
//stock the level of the parentheses
Integer [ ] parenthesesPos = parseBrackets ( content ) ;
2017-01-07 19:56:03 +01:00
//System.out.println(Arrays.toString(parenthesesPos));
2016-12-28 18:57:39 +01:00
//Mat
if ( content . startsWith ( new String ( new char [ ] { 0x7F , 0x40 } ) ) ) {
System . out . println ( " Parsing a matrix " ) ;
2017-01-07 19:56:03 +01:00
//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 ) {
2017-01-11 16:54:40 +01:00
str = " B2C_setMat( " + getChar ( ( char ) 0xC0 ) + " , " + str + " ); " ;
2017-01-07 19:56:03 +01:00
}
return str ;
}
2016-12-28 18:57:39 +01:00
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 {
2017-01-11 16:54:40 +01:00
return supportAns ( " & " + getMat ( content . charAt ( 2 ) ) + " .data[ " + getMat ( content . charAt ( 2 ) ) + " .height*( "
2017-01-01 10:53:07 +01:00
/ * + 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 ) ; * /
2017-01-07 19:56:03 +01:00
+ 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 ) ;
2016-12-28 18:57:39 +01:00
}
}
}
2017-01-07 19:56:03 +01:00
//Dim
2016-12-28 18:57:39 +01:00
if ( content . startsWith ( new String ( new char [ ] { 0x7F , 0x46 } ) ) ) {
2017-01-07 19:56:03 +01:00
//If it is Dim Mat
if ( content . substring ( 2 ) . startsWith ( new String ( new char [ ] { 0x7F , 0x40 } ) ) ) {
2017-01-11 16:54:40 +01:00
String result = " B2C_getDimMat( " + getChar ( content . charAt ( 4 ) ) + " ) " ;
2017-01-07 19:56:03 +01:00
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! " ) ;
}
2016-12-28 18:57:39 +01:00
}
//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 ) ;
2017-01-07 19:56:03 +01:00
if ( check . length = = 0 ) {
String result = getList ( content . substring ( 2 ) ) ;
if ( option = = WHOLE_INSTRUCTION )
return " B2C_setList(LIST_ANS, " + result + " ); " ;
return result ;
}
2016-12-28 18:57:39 +01:00
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 ) ;
}
2017-01-07 19:56:03 +01:00
return supportAns ( " list[ " + getList ( content . substring ( 2 , check [ 0 ] ) ) + " ].data[ "
2017-01-08 20:03:49 +01:00
+ handleIntConversion ( content . substring ( check [ 0 ] + 1 , content . length ( ) ) ) + " ] " , option ) ;
2016-12-28 18:57:39 +01:00
}
}
//searches for an operator
2017-01-01 10:53:07 +01:00
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 ) {
2017-01-07 19:56:03 +01:00
if ( j - Operators . operators [ h ] [ i ] . getCasioChar ( ) . length ( ) > = parenthesesPos [ k ] & & j - Operators . operators [ h ] [ i ] . getCasioChar ( ) . length ( ) < = parenthesesPos [ k + 1 ] ) {
2017-01-01 10:53:07 +01:00
isInParentheses = true ;
}
2016-12-28 18:57:39 +01:00
}
2017-01-01 10:53:07 +01:00
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
2017-01-07 19:56:03 +01:00
if ( ! isInParentheses & & ( j ! = Operators . operators [ h ] [ i ] . getCasioChar ( ) . length ( ) | | Operators . operators [ h ] [ i ] . getNbOperands ( ) = = 1 ) ) {
2017-01-01 10:53:07 +01:00
String str = " " ;
2017-01-07 19:56:03 +01:00
//System.out.println("Found the above operator");
2017-01-01 10:53:07 +01:00
str + = Operators . operators [ h ] [ i ] . getAsciiFunction ( ) + " ( " ;
2017-01-07 19:56:03 +01:00
//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 ( ) + " , " ;
2017-01-01 10:53:07 +01:00
}
2017-01-07 19:56:03 +01:00
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 ) ;
2017-01-01 10:53:07 +01:00
} else {
2017-01-07 19:56:03 +01:00
//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 ) ;
}
2017-01-01 10:53:07 +01:00
}
2017-01-07 19:56:03 +01:00
str + = " ) " ;
return supportAns ( str , option ) ;
} / * else {
System . out . println ( " Operator was in parenthesis, ignoring it " ) ;
} * /
2017-01-01 10:53:07 +01:00
2016-12-28 18:57:39 +01:00
}
2017-01-01 10:53:07 +01:00
/ * if ( isMultibytePrefix ( content . charAt ( j ) ) ) {
j + + ;
} * /
2016-12-28 18:57:39 +01:00
}
}
}
2017-01-07 19:56:03 +01:00
2016-12-28 18:57:39 +01:00
//replace variables with their position in the var[] array; only do this if the string only contains the variable
2017-01-07 19:56:03 +01:00
if ( content . matches ( " [A-Z \\ xCD \\ xCE \\ xC0] " ) ) {
return handleAlphaVar ( content , option ) ;
2016-12-28 18:57:39 +01:00
}
//Test if it is a number (note that it can be something like 2X, implicit multiplication)
2017-01-07 19:56:03 +01:00
if ( content . matches ( " ^[ \\ d \\ x99 \\ x87 \\ .](.+)? " ) ) {
2016-12-28 18:57:39 +01:00
int testForImplicitMultiplication = - 1 ;
for ( char i = '0' ; i < = '9' ; i + + ) {
if ( testForImplicitMultiplication < content . lastIndexOf ( i ) ) {
testForImplicitMultiplication = content . lastIndexOf ( i ) ;
}
}
if ( testForImplicitMultiplication ! = content . length ( ) - 1 ) {
2017-01-07 19:56:03 +01:00
return parse ( content . substring ( 0 , testForImplicitMultiplication + 1 ) + ( char ) 0xA9 + content . substring ( testForImplicitMultiplication + 1 , content . length ( ) ) ) ;
2016-12-28 18:57:39 +01:00
}
//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 ;
}
2017-01-07 19:56:03 +01:00
2016-12-28 18:57:39 +01:00
//At this point it is a calculation, check for calculations functions
2017-01-07 19:56:03 +01:00
//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 ) ;
}
2017-01-11 16:54:40 +01:00
Integer [ ] args = parseArgs ( content . substring ( 2 ) ) ;
2017-01-07 19:56:03 +01:00
if ( args . length ! = 1 ) {
error ( " RanInt# method doesn't have 2 arguments! " ) ;
}
2017-01-11 16:54:40 +01:00
args [ 0 ] + = 2 ;
2017-01-07 19:56:03 +01:00
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 = " " ;
2016-12-28 18:57:39 +01:00
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 ) ;
2017-01-07 19:56:03 +01:00
return result ; * /
/ * } else {
2016-12-28 18:57:39 +01:00
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
2017-01-07 19:56:03 +01:00
error ( " function not recognized! " + printNonAscii ( content ) ) ;
return " ===COMPILATION ERROR=== " ;
2016-12-28 18:57:39 +01:00
}
2017-01-07 19:56:03 +01:00
/ * *
* This function is called to parse hardcoded lists ( written like { 1 , 2 , 3 } ) .
2016-12-28 18:57:39 +01:00
* 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 ) + " ) " ;
}
2017-01-07 19:56:03 +01:00
/ * *
* 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
2016-12-28 18:57:39 +01:00
* 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 + + ) {
2017-01-08 20:03:49 +01:00
if ( isMultibytePrefix ( content . charAt ( i ) ) ) {
2016-12-28 18:57:39 +01:00
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 ;
}
2017-01-07 19:56:03 +01:00
/ * *
* This method parses a string . It is designed to parse things like :
2016-12-28 18:57:39 +01:00
* 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 .
*
* /
2017-01-08 20:03:49 +01:00
//TODO use buffers as str and place free() after
public static String parseStr ( String content , int option ) {
2017-01-07 19:56:03 +01:00
System . out . println ( " Parsing string: " + printNonAscii ( content ) ) ;
2017-01-08 20:03:49 +01:00
Integer [ ] parenthesesPos = parseBrackets ( content ) ;
2017-01-07 19:56:03 +01:00
if ( startsWithStringFunction ( content ) ) {
2017-01-08 20:03:49 +01:00
//Note that standalone strings aren't supported, there is no Str Ans; therefore, no ';'!
2017-01-07 19:56:03 +01:00
//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 ) {
2017-01-08 20:03:49 +01:00
//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 ) + " ) " ;
}
2017-01-07 19:56:03 +01:00
return str ;
}
}
}
//Plain strings
if ( content . startsWith ( " \" " ) ) {
if ( content . charAt ( content . length ( ) - 1 ) ! = '"' ) {
content + = '"' ;
}
2017-01-08 20:03:49 +01:00
content = " (unsigned char*) " + replaceNonAscii ( content ) ;
if ( option = = CONV_TO_CHARARRAY ) {
return content ;
}
return " B2C_charArrayToStr( " + content + " ) " ;
2017-01-07 19:56:03 +01:00
}
2017-01-08 20:03:49 +01:00
//No returns from here; always assign the result to this string!
//This is to handle the conversion to char array/str.
String result = " " ;
2017-01-07 19:56:03 +01:00
//Str function
if ( content . startsWith ( new String ( new char [ ] { 0xF9 , 0x3F } ) ) ) {
2017-01-08 20:03:49 +01:00
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 + " ) " ) ;
2017-01-07 19:56:03 +01:00
}
2017-01-08 20:03:49 +01:00
//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 ] ) ) + " ) " ;
}
}
2016-12-28 18:57:39 +01:00
}
2017-01-08 20:03:49 +01:00
//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 ) ;
2016-12-28 18:57:39 +01:00
}
2017-01-08 20:03:49 +01:00
return " B2C_convToStr( " + result + " ) " ;
2016-12-28 18:57:39 +01:00
}
//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 ;
}
2017-01-07 19:56:03 +01:00
/ * *
* This method parses comma - separated arguments . It is used to parse any function
2016-12-28 18:57:39 +01:00
* 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 ) {
2017-01-08 20:03:49 +01:00
System . out . println ( " Parsing arguments: " + printNonAscii ( content ) ) ;
2016-12-28 18:57:39 +01:00
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 ) ;
}
2017-01-08 20:03:49 +01:00
if ( startsWithParenthesis ( content . substring ( i ) ) & & ! positionIsString ) {
2016-12-28 18:57:39 +01:00
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] " .
2017-01-07 19:56:03 +01:00
* When adding an opcode containing parenthesis , make sure to include it in this function .
2016-12-28 18:57:39 +01:00
* /
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 + + ) {
2017-01-08 20:03:49 +01:00
if ( ! currentPositionIsString & & startsWithParenthesis ( content . substring ( i ) ) ) {
2016-12-28 18:57:39 +01:00
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 ( ) ] ) ;
}
2017-01-08 20:03:49 +01:00
/ * *
* 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 ;
}
2016-12-28 18:57:39 +01:00
/ * 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 | |
2017-01-08 20:03:49 +01:00
prefix = = ( char ) 0xF9 | |
2016-12-28 18:57:39 +01:00
prefix = = ( char ) 0xE5 | |
prefix = = ( char ) 0xE6 | |
prefix = = ( char ) 0xE7 )
return true ;
return false ;
}
2017-01-07 19:56:03 +01:00
/ * *
* Returns true if the given string starts with a string function .
2017-01-08 20:03:49 +01:00
* A string function is defined as a function that returns a string .
2017-01-07 19:56:03 +01:00
* 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 ;
}
2016-12-28 18:57:39 +01:00
public static String supportAns ( String content , int option ) {
if ( option = = WHOLE_INSTRUCTION )
2017-01-11 16:54:40 +01:00
return " B2C_setAlphaVar( " + getChar ( ( char ) 0xC0 ) + " , " + content + " ); " ;
2016-12-28 18:57:39 +01:00
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
2017-01-08 20:03:49 +01:00
if ( Constants . isNumber ( content ) ) {
return Constants . getNumberNotation ( content ) ;
2016-12-28 18:57:39 +01:00
}
return " B2C_convToUInt( " + parse ( content ) + " ) " ;
}
public static String handleAlphaVar ( String content , int option ) {
2017-01-01 10:53:07 +01:00
//Handle var such that var[char-'A'] gives the correct variable
2016-12-28 18:57:39 +01:00
if ( content . matches ( " [A-Z \\ xCD \\ xCE \\ xC0] " ) ) {
2017-01-07 19:56:03 +01:00
String result = getAlphaVar ( content ) ;
2016-12-28 18:57:39 +01:00
return supportAns ( result , option ) ;
}
return parse ( content , option ) ;
}
2017-01-08 20:03:49 +01:00
/ * *
* 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 ;
}
2016-12-28 18:57:39 +01:00
public static void incrementTabs ( ) {
tabs + = " \ t " ;
}
public static void decrementTabs ( ) {
tabs = tabs . substring ( 0 , tabs . length ( ) - 1 ) ;
}
2017-01-01 10:53:07 +01:00
public static String getAlphaVar ( char var ) { return getAlphaVar ( " " + var ) ; }
public static String getAlphaVar ( String var ) {
2017-01-07 19:56:03 +01:00
String result = " VAR_ " ;
2017-01-01 10:53:07 +01:00
if ( var . length ( ) ! = 1 ) {
error ( " Unknown var! " ) ;
} else if ( var . charAt ( 0 ) > = 'A' & & var . charAt ( 0 ) < = 'Z' ) {
2017-01-11 16:54:40 +01:00
result + = var . charAt ( 0 ) ;
2017-01-01 10:53:07 +01:00
} else if ( var . charAt ( 0 ) = = 0xCD ) {
2017-01-07 19:56:03 +01:00
result + = " RADIUS " ;
2017-01-01 10:53:07 +01:00
} else if ( var . charAt ( 0 ) = = 0xCE ) {
2017-01-07 19:56:03 +01:00
result + = " THETA " ;
2017-01-01 10:53:07 +01:00
} else if ( var . charAt ( 0 ) = = 0xC0 ) {
2017-01-07 19:56:03 +01:00
result + = " ANS " ;
} else {
error ( " Unknown var! " ) ;
2017-01-01 10:53:07 +01:00
}
2017-01-07 19:56:03 +01:00
return result ;
2017-01-01 10:53:07 +01:00
}
2017-01-07 19:56:03 +01:00
public static String getMat ( char mat ) {
String result = " MAT_ " ;
if ( mat > = 'A' & & mat < = 'Z' ) {
2017-01-11 16:54:40 +01:00
result + = mat ;
2017-01-07 19:56:03 +01:00
} else if ( mat = = 0xC0 ) {
result + = " ANS " ;
} else {
error ( " Unknown mat " + mat + " ! " ) ;
}
2017-01-01 10:53:07 +01:00
return result ;
2017-01-07 19:56:03 +01:00
}
2017-01-11 16:54:40 +01:00
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 ;
}
2017-01-07 19:56:03 +01:00
public static String getList ( String list ) {
if ( list . length ( ) = = 1 & & list . charAt ( 0 ) = = 0xC0 ) {
2017-01-11 16:54:40 +01:00
return " ANS " ;
2017-01-07 19:56:03 +01:00
} else {
2017-01-08 20:03:49 +01:00
return handleIntConversion ( list ) ;
2017-01-07 19:56:03 +01:00
}
}
public static String getStr ( String str ) {
if ( str . length ( ) > 2 ) {
2017-01-08 20:03:49 +01:00
error ( " Invalid str " + printNonAscii ( str ) + " ! " ) ;
2017-01-07 19:56:03 +01:00
}
try {
int strno = Integer . valueOf ( str ) ;
if ( strno < 1 | | strno > 20 ) {
error ( " Invalid str number " + strno + " ! " ) ;
}
} catch ( NumberFormatException e ) {
error ( " Invalid str " + str + " ! " ) ;
}
2017-01-08 20:03:49 +01:00
return " STR_ " + Integer . valueOf ( str ) ;
2017-01-01 10:53:07 +01:00
}
2017-01-07 19:56:03 +01:00
public static String addBCDBuffer ( ) { return addBuffer ( BCD_BUFFER ) ; }
2017-01-08 20:03:49 +01:00
//public static String addStrBuffer() {return addBuffer(STR_BUFFER);}
2017-01-07 19:56:03 +01:00
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 + + ) {
2017-01-11 16:54:40 +01:00
if ( content . charAt ( i ) > = 32 & & content . charAt ( i ) < 127 & & ( i = = 0 | | i > 0 & & ! isMultibytePrefix ( content . charAt ( i - 1 ) ) ) ) {
2017-01-07 19:56:03 +01:00
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 ;
}
2016-12-28 18:57:39 +01:00
public static void error ( String error ) {
2017-01-07 19:56:03 +01:00
System . out . println ( " \ nERROR: " + error + " \ n " ) ;
2016-12-28 18:57:39 +01:00
System . exit ( 0 ) ;
}
}