diff --git a/src/lu/fisch/structorizer/generators/ArmGenerator.java b/src/lu/fisch/structorizer/generators/ArmGenerator.java
index 565704ba..e7179d07 100644
--- a/src/lu/fisch/structorizer/generators/ArmGenerator.java
+++ b/src/lu/fisch/structorizer/generators/ArmGenerator.java
@@ -41,10 +41,12 @@
* A. Simonetta 2021-04-23 Input and output and some parsing flaws fixed
* Kay Gürtzig 2021-04-24/26 Some corrections to the fixes of A. Simonetta
* Kay Gürtzig 2021-04-30 Problem with too many variables fixed.
-* Kay Gürtzig 2021-05-02 Mechanisms to support EXIT instructions, subroutines, CALLs added
+* Kay Gürtzig 2021-05-02 Mechanisms added to support EXIT instructions, subroutines, CALLs
* Kay Gürtzig 2021-05-11 Appended an endless loop to the end of a program
* Kay Gürtzig 2021-10-05 Condition handling for Alternative, While, and Repeat unified and delegated
* Kay Gürtzig 2021-10-06 Arm Instruction detection revised.
+* Kay Gürtzig 2021-10-11 Risk of NullPointerException in getVariables() averted, some
+* code revisions in the variable and statement detection.
*
******************************************************************************************************
*
@@ -85,29 +87,38 @@ public class ArmGenerator extends Generator {
private static final String numberPattern = "-?[0-9]+";
private static final String hexNumberPattern = "(0|0x|0x([0-9]|[a-fA-F])+)";
private static final String assignmentOperators = "(<-|:=)";
+ private static final String relationOperators = "(==|!=|<|>|<=|>=|=)";
private static final String supportedOperationsPattern = "(-|\\+|\\*|and|or|&|\\||&&|\\|\\|)";
private static final String registerVariableNumberHex = String.format("(%s|%s|%s|%s)", registerPattern, variablePattern, numberPattern, hexNumberPattern);
private static final String negativeNumberPattern = "-[0-9]+";
private static final Pattern assignment = Pattern.compile(String.format("(%s|%s) *%s *%s", registerPattern, variablePattern, assignmentOperators, registerVariableNumberHex));
private static final Pattern expression = Pattern.compile(String.format("(%s|%s) *%s *%s *%s *%s", registerPattern, variablePattern, assignmentOperators, registerVariableNumberHex, supportedOperationsPattern, registerVariableNumberHex));
- private static final Pattern memoryAccess = Pattern.compile(String.format("(%s|%s) *%s *(memoria|memory)\\[(%s|%s)( *\\+ *%s)?]", registerPattern, variablePattern, assignmentOperators, registerPattern, variablePattern, registerVariableNumberHex));
- private static final Pattern memoryStore = Pattern.compile(String.format("(memoria|memory)\\[(%s|%s|%s)( *\\+ *%s)?] *%s *(%s|%s)", registerPattern, variablePattern, numberPattern, registerVariableNumberHex, assignmentOperators, registerPattern, variablePattern));
- private static final Pattern arrayExpression = Pattern.compile(String.format("(%s|%s) *%s *(%s|%s)\\[(%s|%s|%s)]", registerPattern, variablePattern, assignmentOperators, registerPattern, variablePattern, registerPattern, variablePattern, numberPattern));
- private static final Pattern arrayAssignment = Pattern.compile(String.format("(%s|%s)\\[(%s|%s|%s)( *\\+ *%s)?] *%s *(%s|%s)", registerPattern, variablePattern, registerPattern, variablePattern, numberPattern, registerVariableNumberHex, assignmentOperators, registerPattern, variablePattern));
- private static final Pattern arrayInitialization = Pattern.compile(String.format("(word|hword|byte|octa|quad) *(%s|%s|%s) *%s *\\{(%s|%s)(, *(%s|%s))*}", registerPattern, variablePattern, numberPattern, assignmentOperators, numberPattern, hexNumberPattern, numberPattern, hexNumberPattern));
+ private static final Pattern memoryAccess = Pattern.compile(String.format("(%s|%s) *%s *(memoria|memory)\\[(%s|%s)( *\\+ *%s)?\\]", registerPattern, variablePattern, assignmentOperators, registerPattern, variablePattern, registerVariableNumberHex));
+ private static final Pattern memoryStore = Pattern.compile(String.format("(memoria|memory)\\[(%s|%s|%s)( *\\+ *%s)?\\] *%s *(%s|%s)", registerPattern, variablePattern, numberPattern, registerVariableNumberHex, assignmentOperators, registerPattern, variablePattern));
+ private static final Pattern arrayExpression = Pattern.compile(String.format("(%s|%s) *%s *(%s|%s)\\[(%s|%s|%s)\\]", registerPattern, variablePattern, assignmentOperators, registerPattern, variablePattern, registerPattern, variablePattern, numberPattern));
+ private static final Pattern arrayAssignment = Pattern.compile(String.format("(%s|%s)\\[(%s|%s|%s)( *\\+ *%s)?\\] *%s *(%s|%s)", registerPattern, variablePattern, registerPattern, variablePattern, numberPattern, registerVariableNumberHex, assignmentOperators, registerPattern, variablePattern));
+ // START KGU#968 2021-10-11: Issue #967 it can hardly make sense to have a number on the left-hand side
+ private static final Pattern arrayInitialization = Pattern.compile(String.format("(word|hword|byte|octa|quad) *(%s|%s|%s) *%s *\\{(%s|%s)(, *(%s|%s))*\\}", registerPattern, variablePattern, numberPattern, assignmentOperators, numberPattern, hexNumberPattern, numberPattern, hexNumberPattern));
+ //private static final Pattern arrayInitialization = Pattern.compile(String.format("(word|hword|byte|octa|quad) +(%s|%s) *%s *\\{(%s|%s)(, *(%s|%s))*\\}", registerPattern, variablePattern, assignmentOperators, numberPattern, hexNumberPattern, numberPattern, hexNumberPattern));
+ // END KGU#968 2021-10-11
private static final Pattern address = Pattern.compile(String.format("%s *%s *(indirizzo|address)\\((%s|%s)\\)", registerPattern, assignmentOperators, registerPattern, variablePattern));
+ // FIXME KGU#968: Why isn't an empty string allowed? Why are only identifier characters supported as content?
private static final Pattern stringInitialization = Pattern.compile(String.format("(%s|%s) *%s *\"[\\w]{2,}\"", registerPattern, variablePattern, assignmentOperators));
- private static final Pattern charInitialization = Pattern.compile(String.format("(%s|%s) *%s *\"[\\w]\"", registerPattern, variablePattern, assignmentOperators));
+ // START KGU#968 2021-10-11: Issue #967 Single quotes shall also be supported (preferrably even!)
+ //private static final Pattern charInitialization = Pattern.compile(String.format("(%s|%s) *%s *\"[\\w]\"", registerPattern, variablePattern, assignmentOperators));
+ private static final Pattern charInitialization = Pattern.compile(String.format("(%s|%s) *%s *(\"[\\w]\"|'[\\w]')", registerPattern, variablePattern, assignmentOperators));
+ // END KGU#968 2021-10-11
private static final Pattern booleanAssignmentPattern = Pattern.compile(String.format("(%s|%s) *%s *(true|false)", registerPattern, variablePattern, assignmentOperators));
// START KGU#968 2021-05-02: More general variable syntax - might this cause trouble?
//private final Pattern conditionPattern = Pattern.compile("(while)?\\((R([0-9]|1[0-5])|[a-zA-Z]+)(==|!=|<|>|<=|>=|=)(R([0-9]|1[0-5])|[0-9]+|[a-zA-Z]+|0x([0-9]|[a-fA-F])+|'([a-zA-Z]|[0-9])')((and|AND|or|OR|&&|\\|\\|)(R([0-9]|1[0-5])|[a-zA-Z]+)(==|!=|<|>|<=|>=|=)(R([0-9]|1[0-5])|[0-9]+|[a-zA-Z]+|0x([0-9]|[a-fA-F])+|'([a-zA-Z]|[0-9])'))*\\)");
+ private static final String comparisonPattern = String.format("(%s|%s)%s(%s|[0-9]+|%s|0x[0-9a-fA-F]+|'[a-zA-Z0-9]')",
+ registerPattern1, variablePattern,
+ relationOperators,
+ registerPattern1, variablePattern);
private static final Pattern conditionPattern = Pattern.compile(
- String.format("\\((%s|%s)(==|!=|<|>|<=|>=|=)(%s|[0-9]+|%s|0x([0-9]|[a-fA-F])+|'([a-zA-Z]|[0-9])')((&&|\\|\\|)(%s|%s)(==|!=|<|>|<=|>=|=)(%s|[0-9]+|%s|0x([0-9]|[a-fA-F])+|'([a-zA-Z]|[0-9])'))*\\)",
- registerPattern1, variablePattern,
- registerPattern1, variablePattern,
- registerPattern1, variablePattern,
- registerPattern1, variablePattern));
+ String.format("\\(%s((&&|\\|\\|)%s)*\\)",
+ comparisonPattern, comparisonPattern));
// END KGU#968 2021-05-02
// START KGU#968 2021-10-05: Special support for [negated] registers or variables as conditions
private static final Pattern atomicCondPattern = Pattern.compile(
@@ -216,7 +227,15 @@ public String toString() {
// END KGU#968 2021-05-02
/**
- * Stores the difference between GNU and Keil compilers
+ * Stores the difference between GNU and Keil compilers
+ * First index:
+ * [0] - Gnu phrases
+ * [1] - KEIL phrases
+ * Second index:
+ * [0] - label declaration postfix
+ * [1] - direct operand prefix in MOV instructions
+ * [2] - data area header
+ * [3] - text area header
*/
private final String[][] difference = {
{":", "#", ".data", ".text"},
@@ -285,6 +304,7 @@ public String generateCode(Root _root, String _indent, boolean _public) {
if (optionGnu instanceof Boolean) {
gnuEnabled = (Boolean) optionGnu;
}
+ // END KGU#968 2021-04-15
// START KGU#968 2021-04-24: Enh. #967 - prepare correct keyword comparison
String inputKeyword = CodeParser.getKeywordOrDefault("input", "input");
String outputKeyword = CodeParser.getKeywordOrDefault("output", "output");
@@ -296,7 +316,6 @@ public String generateCode(Root _root, String _indent, boolean _public) {
this.isResultSet = varNames.contains("result", false);
this.isFunctionNameSet = varNames.contains(procName);
// END KGU#968 2021-04-24
- // END KGU#968 2021-04-15
// START KGU#705 2021-04-14: Enh. #738 (Direct code changes compromise codeMap)
//if (topLevel && gnuEnabled) {
// code.add(difference[0][2]);
@@ -321,7 +340,16 @@ public String generateCode(Root _root, String _indent, boolean _public) {
// END KGU#705 2021-04-14
for (Map.Entry entry : mVariables.entrySet()) {
- mVariables.put(entry.getKey(), "");
+ // START KGU#968 2021-10-11 Reserve all register names used as variables
+ //mVariables.put(entry.getKey(), "");
+ String reg = entry.getKey();
+ if (this.varNames.contains(reg, false)) {
+ mVariables.put(reg, USER_REGISTER_TAG);
+ }
+ else {
+ mVariables.put(reg, "");
+ }
+ // END KGU#968 2021-10-11
}
// START KGU#968 2021-05-02: EXITs, subroutines
// Support for loop EXITs - map the ARM loop labels
@@ -340,7 +368,8 @@ public String generateCode(Root _root, String _indent, boolean _public) {
//}
}
if (_root.isSubroutine()) {
- addCode(procName + colon, "", false);
+ addCode(procName + colon, "", false);
+ // FIXME: Adhere to the GNU call standard
// Push all registers (FIXME: Could we reduce the register set to the actual needs?)
addCode("STMFD SP!, {R0-R12}", getIndent(), false);
// Now get the arguments from the stack
@@ -364,6 +393,7 @@ public String generateCode(Root _root, String _indent, boolean _public) {
// Add a return mechanism if the code does not end with return anyway
if (_root.isSubroutine() && !this.alwaysReturns && i >= 0
&& !code.get(i).trim().equals("MOVS PC, LR")) {
+ // FIXME: Adhere to the GNU call standard
// Provide the result value if this is a function
String regResult = "";
if (this.isFunctionNameSet) {
@@ -1335,6 +1365,7 @@ private String[] getCondition(String condition, boolean inverse) {
* (should be an {@link Alternative}, {@link While}, or {@link Repeat} object)
* and returns either {@code null} (in case the transformation failed) or a
* multi-line instruction sequence as translation.
+ *
* @param _ele - The element the code for which is to be generated
* @param prefix - an element-class-specific prefix for the error message
* @param keys - a pair of keys for creating the jump labels
@@ -1569,7 +1600,7 @@ else if (secondOperator.matches(registerPattern)) {
* This method translates basic operations between variables and/or registers
* EXAMPLE: R0 <- R1 + 1
*
- * @param line the string that contains the instruction to translate
+ * @param line - the instruction to translate as string
*/
private void generateExpr(String line, boolean isDisabled) {
String code = "%s %s, %s, %s";
@@ -1634,11 +1665,12 @@ else if (isPowerOfTwo(value - 1)) {
}
/**
- * This method translates an array's address assignment to a register using indirizzo or address as keywords
+ * This method translates an array's address assignment to a register using
+ * indirizzo or address as keywords
* EXAMPLE: R0 <- address(R1)
*
- * @param line the string that contains the instruction to translate
- * @param isDisabled whether this element or one of its ancestors is disabled
+ * @param line - the instruction to translate as string
+ * @param isDisabled - whether this element or one of its ancestors is disabled
*/
private void generateAddressAssignment(String line, boolean isDisabled) {
line = line.replace(" ", "");
@@ -1653,10 +1685,12 @@ private void generateAddressAssignment(String line, boolean isDisabled) {
/**
* This method translates an alternative way of using arrays (this time with memory access) using memoria or memory as keywords
- * EXAMPLE: R0 <- memory[R1] or memory[R0] <- R1
+ * EXAMPLES:
+ * R0 <- memory[R1]
+ * memory[R0] <- R1
*
- * @param line the string that contains the instruction to translate
- * @param isDisabled whether this element or one of its ancestors is disabled
+ * @param line - the instruction to translate as string
+ * @param isDisabled - whether this element or one of its ancestors is disabled
*/
private void generateMemoryAssignment(String line, boolean isDisabled) {
String code = "%s %s, [%s]";
@@ -1709,7 +1743,7 @@ private void generateMemoryAssignment(String line, boolean isDisabled) {
private void generateString(String line, boolean isDisabled) {
String[] split = line.split("<- ?|:= ?");
- // FIXME: The string literal might contain escaped quotes!
+ // FIXME: The string literal might contain escaped quotes in future!
split[1] = split[1].replace("\"", "");
String c = "word %s<-{%s}";
StringBuilder array = new StringBuilder();
@@ -2016,7 +2050,10 @@ else if (!item.toString().matches(variablePattern)) {
String variable = item.substring(0, item.length() - 1);
// if it's a register we add it as not available and, if it's already assigned to a variable, we warn the user
if (variable.matches(registerPattern)) {
- if (mVariables.get(variable).equals("")) {
+ // START KGU#968 2021-10-11: Bugfix #967 case matters here! (Caused NullPointerExceptions)
+ variable = variable.toUpperCase();
+ // END KGU#968 2021-10-11
+ if ("".equals(mVariables.get(variable))) {
mVariables.put(variable, USER_REGISTER_TAG);
} else if (!mVariables.get(variable).equals(USER_REGISTER_TAG)) {
appendComment(String.format("Register: %s is already assigned to variable: %s. Be careful!\n", variable, mVariables.get(variable)), getIndent());
@@ -2024,7 +2061,7 @@ else if (!item.toString().matches(variablePattern)) {
}
// if it's not a register and it's not empty and it's not in the reservedWords list we add it to the arraylist
- if (!variable.matches(registerPattern) && !variable.equals("") && !Arrays.asList(reservedWords).contains(variable) && !variable.matches(hexNumberPattern)) {
+ else if (!variable.equals("") && !Arrays.asList(reservedWords).contains(variable) && !variable.matches(hexNumberPattern)) {
stringPositions.add(new Tuple<>(variable, i - 2));
}
item = new StringBuilder(split[i]);
@@ -2057,11 +2094,11 @@ private String getAvailableRegister() {
}
/**
- * This method returns the register assigned to variable
+ * This method returns the register assigned to the given variable
*
- * @param variable - the string that contains the variable name
- * @return the register assigned to the variable or an empty string if the
- * set of variables is exhausted
+ * @param variable - the variable name
+ * @return name of the register assigned to the variable, or an empty string
+ * if the set of variables is exhausted
*/
private String getRegister(String variable) {
String register = "";
diff --git a/src/lu/fisch/structorizer/gui/changelog.txt b/src/lu/fisch/structorizer/gui/changelog.txt
index de150106..f0521769 100644
--- a/src/lu/fisch/structorizer/gui/changelog.txt
+++ b/src/lu/fisch/structorizer/gui/changelog.txt
@@ -20,8 +20,10 @@ Known issues:
- Shell export neither copes with nested array/record initialisers and
component access nor with cleanly handling usual and associative arrays as
parameters or results.
+- ARM export is still experimental and relies on a specific and very restricted
+ syntax for the element contents in order to produce meaningful results.
-Current development version 3.32-02 (2021-10-11)
+Current development version 3.32-02 (2021-10-12)
- 01: Bugfix #987: Duplicate subroutine comment export by Pascal generator <2>
- 01: Bugfix #988: Syntax error in Structorizer.bat and Arranger.bat fixed <2>
- 01: Bugfix #989: Expressions in EXIT elements (e.g. return) were forgotten
@@ -35,11 +37,11 @@ Current development version 3.32-02 (2021-10-11)
were not correctly exported to Pascal, Oberon, C, and other languages <2>
- 01: Bugfix #995: Unjustified Analyser warnings about dubious initialization
and missing line numbers in case of multi-line instruction warnings <2>
+- 02: Enh. #967: Generator for ARM code (prototype) introduced
- 02: Bugfix #988: Structorizer.exe (in Windows zip package) config updated <2>
- 02: Bugfix #997: Inconsistent handling of blank sequences in FOR control
phrases (e.g. string literals could get compromised) <2>
-- 02: Enh. #967: Generator for ARM code (prototype) introduced
Version 3.32 (2021-09-19) requiring Java 11 or newer
- 01: Bugfix #851/2: SPECIAL-NAMES sections caused COBOL parser abort <2>