From 577e0f60117d54c8950ebb0e1a4b222c8d2ed2a2 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Sun, 18 Aug 2019 16:43:15 +0200 Subject: [PATCH] Implemented support for string and character escape codes \n \r \\ \" \'. Closes #241 --- .../camelot64/kickc/fragment/AsmFormat.java | 16 +- .../kickc/model/values/ConstantChar.java | 59 +- .../kickc/model/values/ConstantString.java | 91 ++- .../java/dk/camelot64/kickc/parser/KickC.g4 | 2 +- .../dk/camelot64/kickc/parser/KickCLexer.java | 590 ++++++++-------- .../Pass0GenerateStatementSequence.java | 24 +- .../kickc/passes/Pass1ProcedureInline.java | 5 + .../dk/camelot64/kickc/test/TestPrograms.java | 25 + src/test/kc/string-escapes-0.kc | 10 + src/test/kc/string-escapes-1.kc | 23 + src/test/kc/string-escapes-2.kc | 21 + src/test/kc/string-escapes-err-0.kc | 11 + src/test/kc/string-escapes-err-1.kc | 11 + src/test/ref/string-escapes-0.asm | 20 + src/test/ref/string-escapes-0.cfg | 23 + src/test/ref/string-escapes-0.log | 370 ++++++++++ src/test/ref/string-escapes-0.sym | 17 + src/test/ref/string-escapes-1.asm | 59 ++ src/test/ref/string-escapes-1.cfg | 38 + src/test/ref/string-escapes-1.log | 663 ++++++++++++++++++ src/test/ref/string-escapes-1.sym | 29 + src/test/ref/string-escapes-2.asm | 31 + src/test/ref/string-escapes-2.cfg | 33 + src/test/ref/string-escapes-2.log | 496 +++++++++++++ src/test/ref/string-escapes-2.sym | 23 + 25 files changed, 2377 insertions(+), 313 deletions(-) create mode 100644 src/test/kc/string-escapes-0.kc create mode 100644 src/test/kc/string-escapes-1.kc create mode 100644 src/test/kc/string-escapes-2.kc create mode 100644 src/test/kc/string-escapes-err-0.kc create mode 100644 src/test/kc/string-escapes-err-1.kc create mode 100644 src/test/ref/string-escapes-0.asm create mode 100644 src/test/ref/string-escapes-0.cfg create mode 100644 src/test/ref/string-escapes-0.log create mode 100644 src/test/ref/string-escapes-0.sym create mode 100644 src/test/ref/string-escapes-1.asm create mode 100644 src/test/ref/string-escapes-1.cfg create mode 100644 src/test/ref/string-escapes-1.log create mode 100644 src/test/ref/string-escapes-1.sym create mode 100644 src/test/ref/string-escapes-2.asm create mode 100644 src/test/ref/string-escapes-2.cfg create mode 100644 src/test/ref/string-escapes-2.log create mode 100644 src/test/ref/string-escapes-2.sym diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFormat.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFormat.java index 98d82d1a6..bb21fdb1e 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFormat.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFormat.java @@ -34,15 +34,27 @@ public class AsmFormat { } else if(value instanceof ConstantChar) { ConstantChar constantChar = (ConstantChar) value; if(!ConstantString.Encoding.SCREENCODE_MIXED.equals(constantChar.getEncoding())) { + // Current KickAsm does not support encoded literal chars // Manually convert literal chars in non-standard encodings CharToPetsciiConverter.setCurrentEncoding(constantChar.getEncoding().name); byte converted = CharToPetsciiConverter.convertOrChar(constantChar.getChar(), true); return getAsmNumber(new Long(converted)); } else { - return "'" + constantChar.getValue() + "'"; + String escapedChar = ConstantChar.asciiToCharEscape(((ConstantChar) value).getChar()); + if(escapedChar.length()>1) { + // Currently KickAss does not support escaped characters - so instead we must output the number value instead + CharToPetsciiConverter.setCurrentEncoding(constantChar.getEncoding().name); + byte converted = CharToPetsciiConverter.convertOrChar(constantChar.getChar(), true); + return getAsmNumber(new Long(converted)); + } else { + return "'" + escapedChar + "'"; + } } } else if(value instanceof ConstantString) { - return "\"" + ((ConstantString) value).getValue() + "\""; + String stringValue = ((ConstantString) value).getValue(); + String escapedString = ConstantString.asciiToStringEscape(stringValue); + boolean hasEscape = !stringValue.equals(escapedString); + return (hasEscape?"@":"")+"\"" + escapedString + "\""; } else if(value instanceof ConstantUnary) { ConstantUnary unary = (ConstantUnary) value; Operator operator = unary.getOperator(); diff --git a/src/main/java/dk/camelot64/kickc/model/values/ConstantChar.java b/src/main/java/dk/camelot64/kickc/model/values/ConstantChar.java index e722a654e..39b6b67d7 100644 --- a/src/main/java/dk/camelot64/kickc/model/values/ConstantChar.java +++ b/src/main/java/dk/camelot64/kickc/model/values/ConstantChar.java @@ -1,5 +1,7 @@ package dk.camelot64.kickc.model.values; +import dk.camelot64.kickc.model.CompileError; +import dk.camelot64.kickc.model.InternalError; import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.symbols.ProgramScope; import dk.camelot64.kickc.model.types.SymbolType; @@ -45,12 +47,63 @@ public class ConstantChar implements ConstantLiteral { @Override public String toString(Program program) { - String enc = (encoding.equals(ConstantString.Encoding.SCREENCODE_MIXED))?"":encoding.suffix; + String enc = (encoding.equals(ConstantString.Encoding.SCREENCODE_MIXED)) ? "" : encoding.suffix; if(program == null) { - return "'" + value + "'"+enc; + return "'" + value + "'" + enc; } else { - return "(" + SymbolType.BYTE.getTypeName() + ") " + "'" + value + "'"+enc; + return "(" + SymbolType.BYTE.getTypeName() + ") " + "'" + value + "'" + enc; } } + /** + * Parses a potentially escaped char + * + * @param charString Either just a single char - or an escaped char using \-notation + * @return The ASCII char + */ + public static char charEscapeToAscii(String charString) { + if(charString.length() == 1) { + return charString.charAt(0); + } else if(charString.length() == 2) { + switch(charString.charAt(1)) { + case 'n': + return '\n'; + case 'r': + return '\r'; + case 'f': + return '\f'; + case '"': + return '\"'; + case '\'': + return '\''; + default: + throw new CompileError("Illegal char escape sequence \\" + charString.charAt(1)); + } + } else { + throw new InternalError("Illegal char '" + charString + "'"); + } + } + + /** + * Converts a char to an escape sequence if needed. If not needed the char itself is returned. + * @param aChar The char + * @return The char itself - or the appropriate escape sequence + */ + public static String asciiToCharEscape(char aChar) { + switch(aChar) { + case '\n': + return "\\n"; + case '\r': + return "\\r"; + case '\f': + return "\\f"; + case '\'': + return "\\'"; + default: + return Character.toString(aChar); + } + } + + + } diff --git a/src/main/java/dk/camelot64/kickc/model/values/ConstantString.java b/src/main/java/dk/camelot64/kickc/model/values/ConstantString.java index d4ae8d361..7dfa6b131 100644 --- a/src/main/java/dk/camelot64/kickc/model/values/ConstantString.java +++ b/src/main/java/dk/camelot64/kickc/model/values/ConstantString.java @@ -1,5 +1,6 @@ package dk.camelot64.kickc.model.values; +import dk.camelot64.kickc.model.CompileError; import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.symbols.ProgramScope; import dk.camelot64.kickc.model.types.SymbolType; @@ -11,13 +12,13 @@ import java.util.Objects; */ public class ConstantString implements ConstantLiteral { + /** String encoding. */ public static enum Encoding { PETSCII_MIXED("petscii_mixed", "pm"), PETSCII_UPPER("petscii_upper", "pu"), SCREENCODE_MIXED("screencode_mixed", "sm"), - SCREENCODE_UPPER("screencode_upper", "su") - ; + SCREENCODE_UPPER("screencode_upper", "su"); public final String name; public final String suffix; @@ -73,12 +74,12 @@ public class ConstantString implements ConstantLiteral { @Override public String toString(Program program) { - String suffix = (encoding.equals(Encoding.SCREENCODE_MIXED))?"":encoding.suffix; - suffix += zeroTerminated?"":"z"; + String suffix = (encoding.equals(Encoding.SCREENCODE_MIXED)) ? "" : encoding.suffix; + suffix += zeroTerminated ? "" : "z"; if(program == null) { - return "\"" + value + "\""+suffix; + return "\"" + value + "\"" + suffix; } else { - return "(" + SymbolType.STRING.getTypeName() + ") "+"\"" + value + "\""+suffix; + return "(" + SymbolType.STRING.getTypeName() + ") " + "\"" + value + "\"" + suffix; } } @@ -96,4 +97,82 @@ public class ConstantString implements ConstantLiteral { public int hashCode() { return Objects.hash(value, encoding, zeroTerminated); } + + /** + * Find any string escape sequences and convert them to the ASCII-equivalent character + * + * @param stringValue The string to convert + * @return The string where any escape sequence has been converted to ASCII + * @throws CompileError If the string value has a syntax error (unfinished or illegal escape sequences) + */ + public static String stringEscapeToAscii(String stringValue) { + StringBuilder stringResult = new StringBuilder(); + char[] stringChars = stringValue.toCharArray(); + int i = 0; + while(i < stringChars.length) { + // State: Normal - examine whether an escape is starting + char stringChar = stringChars[i]; + if(stringChar == '\\') { + // Escape started - handle it! + i++; + if(i >= stringChars.length) throw new CompileError("Unfinished string escape sequence at end of string"); + char escapeChar = stringChars[i]; + switch(escapeChar) { + case 'n': + stringChar = '\n'; + break; + case 'r': + stringChar = '\r'; + break; + case 'f': + stringChar = '\f'; + break; + case '"': + stringChar = '"'; + break; + case '\'': + stringChar = '\''; + break; + default: + throw new CompileError("Illegal string escape sequence \\" + escapeChar); + } + } + // Output the char + stringResult.append(stringChar); + i++; + } + return stringResult.toString(); + } + + /** + * Find any ASCII character that must be escaped to represent the string in source code - and convert them to the escaped string. + * + * @param stringValue The string to convert + * @return The string where any character that must be escaped is converted to the escape sequence + */ + public static String asciiToStringEscape(String stringValue) { + StringBuilder stringResult = new StringBuilder(); + char[] stringChars = stringValue.toCharArray(); + for(char stringChar : stringChars) { + switch(stringChar) { + case '\n': + stringResult.append("\\n"); + break; + case '\r': + stringResult.append("\\r"); + break; + case '\f': + stringResult.append("\\f"); + break; + case '"': + stringResult.append("\\\""); + break; + default: + stringResult.append(stringChar); + } + } + return stringResult.toString(); + } + + } diff --git a/src/main/java/dk/camelot64/kickc/parser/KickC.g4 b/src/main/java/dk/camelot64/kickc/parser/KickC.g4 index f16a05d50..7de469667 100644 --- a/src/main/java/dk/camelot64/kickc/parser/KickC.g4 +++ b/src/main/java/dk/camelot64/kickc/parser/KickC.g4 @@ -285,7 +285,7 @@ MNEMONIC: KICKASM: '{{' .*? '}}'; SIMPLETYPE: 'byte' | 'word' | 'dword' | 'bool' | 'char' | 'short' | 'int' | 'long' | 'void' ; STRING : '"' ('\\"' | ~'"')* '"' [z]?([ps][mu]?)?[z]?; -CHAR : '\'' ('\\\'' | ~'\'' ) '\''; +CHAR : '\'' ('\\'['"rfn] | ~'\'' ) '\''; BOOLEAN : 'true' | 'false'; NUMBER : NUMFLOAT | NUMINT ; NUMFLOAT : BINFLOAT | DECFLOAT | HEXFLOAT; diff --git a/src/main/java/dk/camelot64/kickc/parser/KickCLexer.java b/src/main/java/dk/camelot64/kickc/parser/KickCLexer.java index d380c7c4b..18acffd1d 100644 --- a/src/main/java/dk/camelot64/kickc/parser/KickCLexer.java +++ b/src/main/java/dk/camelot64/kickc/parser/KickCLexer.java @@ -219,291 +219,291 @@ public class KickCLexer extends Lexer { "U\u00a9V\u00abW\u00adX\u00afY\u00b1Z\u00b3[\u00b5\\\u00b7]\u00b9^\u00bb"+ "_\u00bd`\u00bfa\u00c1b\u00c3c\u00c5d\u00c7e\u00c9f\u00cbg\u00cdh\u00cf"+ "i\u00d1j\u00d3k\u00d5l\u00d7m\u00d9n\u00dbo\u00dd\2\u00df\2\u00e1\2\u00e3"+ - "p\u00e5\2\u00e7\2\u00e9q\u00ebr\u00eds\u00eft\3\2\22\3\2$$\3\2||\4\2r"+ - "ruu\4\2ooww\3\2))\4\2uuww\7\2dfkknnuuyy\4\2DDdd\3\2\62\63\3\2\62;\5\2"+ - "\62;CHch\5\2C\\aac|\6\2\62;C\\aac|\4\2--//\6\2\13\f\17\17\"\"\u00a2\u00a2"+ - "\4\2\f\f\17\17\2\u04fc\2\3\3\2\2\2\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2\2"+ - "\2\13\3\2\2\2\2\r\3\2\2\2\2\17\3\2\2\2\2\21\3\2\2\2\2\23\3\2\2\2\2\25"+ - "\3\2\2\2\2\27\3\2\2\2\2\31\3\2\2\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2"+ - "\2\2\2!\3\2\2\2\2#\3\2\2\2\2%\3\2\2\2\2\'\3\2\2\2\2)\3\2\2\2\2+\3\2\2"+ - "\2\2-\3\2\2\2\2/\3\2\2\2\2\61\3\2\2\2\2\63\3\2\2\2\2\65\3\2\2\2\2\67\3"+ - "\2\2\2\29\3\2\2\2\2;\3\2\2\2\2=\3\2\2\2\2?\3\2\2\2\2A\3\2\2\2\2C\3\2\2"+ - "\2\2E\3\2\2\2\2G\3\2\2\2\2I\3\2\2\2\2K\3\2\2\2\2M\3\2\2\2\2O\3\2\2\2\2"+ - "Q\3\2\2\2\2S\3\2\2\2\2U\3\2\2\2\2W\3\2\2\2\2Y\3\2\2\2\2[\3\2\2\2\2]\3"+ - "\2\2\2\2_\3\2\2\2\2a\3\2\2\2\2c\3\2\2\2\2e\3\2\2\2\2g\3\2\2\2\2i\3\2\2"+ - "\2\2k\3\2\2\2\2m\3\2\2\2\2o\3\2\2\2\2q\3\2\2\2\2s\3\2\2\2\2u\3\2\2\2\2"+ - "w\3\2\2\2\2y\3\2\2\2\2{\3\2\2\2\2}\3\2\2\2\2\177\3\2\2\2\2\u0081\3\2\2"+ - "\2\2\u0083\3\2\2\2\2\u0085\3\2\2\2\2\u0087\3\2\2\2\2\u0089\3\2\2\2\2\u008b"+ - "\3\2\2\2\2\u008d\3\2\2\2\2\u008f\3\2\2\2\2\u0091\3\2\2\2\2\u0093\3\2\2"+ - "\2\2\u0095\3\2\2\2\2\u0097\3\2\2\2\2\u0099\3\2\2\2\2\u009b\3\2\2\2\2\u009d"+ - "\3\2\2\2\2\u009f\3\2\2\2\2\u00a1\3\2\2\2\2\u00a3\3\2\2\2\2\u00a5\3\2\2"+ - "\2\2\u00a7\3\2\2\2\2\u00a9\3\2\2\2\2\u00ab\3\2\2\2\2\u00ad\3\2\2\2\2\u00af"+ - "\3\2\2\2\2\u00b1\3\2\2\2\2\u00b3\3\2\2\2\2\u00b5\3\2\2\2\2\u00b7\3\2\2"+ - "\2\2\u00b9\3\2\2\2\2\u00bb\3\2\2\2\2\u00bd\3\2\2\2\2\u00bf\3\2\2\2\2\u00c1"+ - "\3\2\2\2\2\u00c3\3\2\2\2\2\u00c5\3\2\2\2\2\u00c7\3\2\2\2\2\u00c9\3\2\2"+ - "\2\2\u00cb\3\2\2\2\2\u00cd\3\2\2\2\2\u00cf\3\2\2\2\2\u00d1\3\2\2\2\2\u00d3"+ - "\3\2\2\2\2\u00d5\3\2\2\2\2\u00d7\3\2\2\2\2\u00d9\3\2\2\2\2\u00db\3\2\2"+ - "\2\2\u00e3\3\2\2\2\2\u00e9\3\2\2\2\2\u00eb\3\2\2\2\2\u00ed\3\2\2\2\2\u00ef"+ - "\3\2\2\2\3\u00f1\3\2\2\2\5\u00f8\3\2\2\2\7\u00fa\3\2\2\2\t\u0102\3\2\2"+ - "\2\13\u0104\3\2\2\2\r\u0106\3\2\2\2\17\u0108\3\2\2\2\21\u010a\3\2\2\2"+ - "\23\u010c\3\2\2\2\25\u010e\3\2\2\2\27\u0116\3\2\2\2\31\u011e\3\2\2\2\33"+ - "\u0127\3\2\2\2\35\u012a\3\2\2\2\37\u012e\3\2\2\2!\u0135\3\2\2\2#\u013d"+ - "\3\2\2\2%\u0142\3\2\2\2\'\u014b\3\2\2\2)\u0154\3\2\2\2+\u015d\3\2\2\2"+ - "-\u0167\3\2\2\2/\u016d\3\2\2\2\61\u0174\3\2\2\2\63\u017b\3\2\2\2\65\u0181"+ - "\3\2\2\2\67\u018a\3\2\2\29\u0191\3\2\2\2;\u019a\3\2\2\2=\u01a4\3\2\2\2"+ - "?\u01a7\3\2\2\2A\u01ac\3\2\2\2C\u01b2\3\2\2\2E\u01b5\3\2\2\2G\u01b9\3"+ - "\2\2\2I\u01c0\3\2\2\2K\u01c7\3\2\2\2M\u01cd\3\2\2\2O\u01d6\3\2\2\2Q\u01da"+ - "\3\2\2\2S\u01e3\3\2\2\2U\u01e8\3\2\2\2W\u01ea\3\2\2\2Y\u01ed\3\2\2\2["+ - "\u01f4\3\2\2\2]\u01fd\3\2\2\2_\u01ff\3\2\2\2a\u0201\3\2\2\2c\u0203\3\2"+ - "\2\2e\u020a\3\2\2\2g\u020f\3\2\2\2i\u0211\3\2\2\2k\u0214\3\2\2\2m\u021b"+ - "\3\2\2\2o\u0222\3\2\2\2q\u0225\3\2\2\2s\u0228\3\2\2\2u\u022a\3\2\2\2w"+ - "\u022c\3\2\2\2y\u022e\3\2\2\2{\u0230\3\2\2\2}\u0232\3\2\2\2\177\u0235"+ - "\3\2\2\2\u0081\u0238\3\2\2\2\u0083\u023a\3\2\2\2\u0085\u023c\3\2\2\2\u0087"+ - "\u023e\3\2\2\2\u0089\u0240\3\2\2\2\u008b\u0243\3\2\2\2\u008d\u0246\3\2"+ - "\2\2\u008f\u0249\3\2\2\2\u0091\u024c\3\2\2\2\u0093\u024e\3\2\2\2\u0095"+ - "\u0250\3\2\2\2\u0097\u0253\3\2\2\2\u0099\u0256\3\2\2\2\u009b\u0258\3\2"+ - "\2\2\u009d\u025b\3\2\2\2\u009f\u025e\3\2\2\2\u00a1\u0261\3\2\2\2\u00a3"+ - "\u0264\3\2\2\2\u00a5\u0267\3\2\2\2\u00a7\u026b\3\2\2\2\u00a9\u026f\3\2"+ - "\2\2\u00ab\u0272\3\2\2\2\u00ad\u0275\3\2\2\2\u00af\u0278\3\2\2\2\u00b1"+ - "\u0280\3\2\2\2\u00b3\u0289\3\2\2\2\u00b5\u028e\3\2\2\2\u00b7\u0297\3\2"+ - "\2\2\u00b9\u029d\3\2\2\2\u00bb\u02a4\3\2\2\2\u00bd\u02aa\3\2\2\2\u00bf"+ - "\u038a\3\2\2\2\u00c1\u038c\3\2\2\2\u00c3\u03bd\3\2\2\2\u00c5\u03bf\3\2"+ - "\2\2\u00c7\u03d5\3\2\2\2\u00c9\u03e6\3\2\2\2\u00cb\u03ea\3\2\2\2\u00cd"+ - "\u03ef\3\2\2\2\u00cf\u03f6\3\2\2\2\u00d1\u0407\3\2\2\2\u00d3\u0415\3\2"+ - "\2\2\u00d5\u0426\3\2\2\2\u00d7\u043a\3\2\2\2\u00d9\u043d\3\2\2\2\u00db"+ - "\u0446\3\2\2\2\u00dd\u044d\3\2\2\2\u00df\u044f\3\2\2\2\u00e1\u0451\3\2"+ - "\2\2\u00e3\u0453\3\2\2\2\u00e5\u045a\3\2\2\2\u00e7\u045c\3\2\2\2\u00e9"+ - "\u045e\3\2\2\2\u00eb\u046b\3\2\2\2\u00ed\u0471\3\2\2\2\u00ef\u047c\3\2"+ - "\2\2\u00f1\u00f2\7k\2\2\u00f2\u00f3\7o\2\2\u00f3\u00f4\7r\2\2\u00f4\u00f5"+ - "\7q\2\2\u00f5\u00f6\7t\2\2\u00f6\u00f7\7v\2\2\u00f7\4\3\2\2\2\u00f8\u00f9"+ - "\7=\2\2\u00f9\6\3\2\2\2\u00fa\u00fb\7v\2\2\u00fb\u00fc\7{\2\2\u00fc\u00fd"+ - "\7r\2\2\u00fd\u00fe\7g\2\2\u00fe\u00ff\7f\2\2\u00ff\u0100\7g\2\2\u0100"+ - "\u0101\7h\2\2\u0101\b\3\2\2\2\u0102\u0103\7.\2\2\u0103\n\3\2\2\2\u0104"+ - "\u0105\7?\2\2\u0105\f\3\2\2\2\u0106\u0107\7*\2\2\u0107\16\3\2\2\2\u0108"+ - "\u0109\7+\2\2\u0109\20\3\2\2\2\u010a\u010b\7}\2\2\u010b\22\3\2\2\2\u010c"+ - "\u010d\7\177\2\2\u010d\24\3\2\2\2\u010e\u010f\7%\2\2\u010f\u0110\7r\2"+ - "\2\u0110\u0111\7t\2\2\u0111\u0112\7c\2\2\u0112\u0113\7i\2\2\u0113\u0114"+ - "\7o\2\2\u0114\u0115\7c\2\2\u0115\26\3\2\2\2\u0116\u0117\7t\2\2\u0117\u0118"+ - "\7g\2\2\u0118\u0119\7u\2\2\u0119\u011a\7g\2\2\u011a\u011b\7t\2\2\u011b"+ - "\u011c\7x\2\2\u011c\u011d\7g\2\2\u011d\30\3\2\2\2\u011e\u011f\7%\2\2\u011f"+ - "\u0120\7t\2\2\u0120\u0121\7g\2\2\u0121\u0122\7u\2\2\u0122\u0123\7g\2\2"+ - "\u0123\u0124\7t\2\2\u0124\u0125\7x\2\2\u0125\u0126\7g\2\2\u0126\32\3\2"+ - "\2\2\u0127\u0128\7r\2\2\u0128\u0129\7e\2\2\u0129\34\3\2\2\2\u012a\u012b"+ - "\7%\2\2\u012b\u012c\7r\2\2\u012c\u012d\7e\2\2\u012d\36\3\2\2\2\u012e\u012f"+ - "\7v\2\2\u012f\u0130\7c\2\2\u0130\u0131\7t\2\2\u0131\u0132\7i\2\2\u0132"+ - "\u0133\7g\2\2\u0133\u0134\7v\2\2\u0134 \3\2\2\2\u0135\u0136\7%\2\2\u0136"+ - "\u0137\7v\2\2\u0137\u0138\7c\2\2\u0138\u0139\7t\2\2\u0139\u013a\7i\2\2"+ - "\u013a\u013b\7g\2\2\u013b\u013c\7v\2\2\u013c\"\3\2\2\2\u013d\u013e\7n"+ - "\2\2\u013e\u013f\7k\2\2\u013f\u0140\7p\2\2\u0140\u0141\7m\2\2\u0141$\3"+ - "\2\2\2\u0142\u0143\7e\2\2\u0143\u0144\7q\2\2\u0144\u0145\7f\2\2\u0145"+ - "\u0146\7g\2\2\u0146\u0147\7a\2\2\u0147\u0148\7u\2\2\u0148\u0149\7g\2\2"+ - "\u0149\u014a\7i\2\2\u014a&\3\2\2\2\u014b\u014c\7f\2\2\u014c\u014d\7c\2"+ - "\2\u014d\u014e\7v\2\2\u014e\u014f\7c\2\2\u014f\u0150\7a\2\2\u0150\u0151"+ - "\7u\2\2\u0151\u0152\7g\2\2\u0152\u0153\7i\2\2\u0153(\3\2\2\2\u0154\u0155"+ - "\7g\2\2\u0155\u0156\7p\2\2\u0156\u0157\7e\2\2\u0157\u0158\7q\2\2\u0158"+ - "\u0159\7f\2\2\u0159\u015a\7k\2\2\u015a\u015b\7p\2\2\u015b\u015c\7i\2\2"+ - "\u015c*\3\2\2\2\u015d\u015e\7%\2\2\u015e\u015f\7g\2\2\u015f\u0160\7p\2"+ - "\2\u0160\u0161\7e\2\2\u0161\u0162\7q\2\2\u0162\u0163\7f\2\2\u0163\u0164"+ - "\7k\2\2\u0164\u0165\7p\2\2\u0165\u0166\7i\2\2\u0166,\3\2\2\2\u0167\u0168"+ - "\7e\2\2\u0168\u0169\7q\2\2\u0169\u016a\7p\2\2\u016a\u016b\7u\2\2\u016b"+ - "\u016c\7v\2\2\u016c.\3\2\2\2\u016d\u016e\7g\2\2\u016e\u016f\7z\2\2\u016f"+ - "\u0170\7v\2\2\u0170\u0171\7g\2\2\u0171\u0172\7t\2\2\u0172\u0173\7p\2\2"+ - "\u0173\60\3\2\2\2\u0174\u0175\7g\2\2\u0175\u0176\7z\2\2\u0176\u0177\7"+ - "r\2\2\u0177\u0178\7q\2\2\u0178\u0179\7t\2\2\u0179\u017a\7v\2\2\u017a\62"+ - "\3\2\2\2\u017b\u017c\7c\2\2\u017c\u017d\7n\2\2\u017d\u017e\7k\2\2\u017e"+ - "\u017f\7i\2\2\u017f\u0180\7p\2\2\u0180\64\3\2\2\2\u0181\u0182\7t\2\2\u0182"+ - "\u0183\7g\2\2\u0183\u0184\7i\2\2\u0184\u0185\7k\2\2\u0185\u0186\7u\2\2"+ - "\u0186\u0187\7v\2\2\u0187\u0188\7g\2\2\u0188\u0189\7t\2\2\u0189\66\3\2"+ - "\2\2\u018a\u018b\7k\2\2\u018b\u018c\7p\2\2\u018c\u018d\7n\2\2\u018d\u018e"+ - "\7k\2\2\u018e\u018f\7p\2\2\u018f\u0190\7g\2\2\u01908\3\2\2\2\u0191\u0192"+ - "\7x\2\2\u0192\u0193\7q\2\2\u0193\u0194\7n\2\2\u0194\u0195\7c\2\2\u0195"+ - "\u0196\7v\2\2\u0196\u0197\7k\2\2\u0197\u0198\7n\2\2\u0198\u0199\7g\2\2"+ - "\u0199:\3\2\2\2\u019a\u019b\7k\2\2\u019b\u019c\7p\2\2\u019c\u019d\7v\2"+ - "\2\u019d\u019e\7g\2\2\u019e\u019f\7t\2\2\u019f\u01a0\7t\2\2\u01a0\u01a1"+ - "\7w\2\2\u01a1\u01a2\7r\2\2\u01a2\u01a3\7v\2\2\u01a3<\3\2\2\2\u01a4\u01a5"+ - "\7k\2\2\u01a5\u01a6\7h\2\2\u01a6>\3\2\2\2\u01a7\u01a8\7g\2\2\u01a8\u01a9"+ - "\7n\2\2\u01a9\u01aa\7u\2\2\u01aa\u01ab\7g\2\2\u01ab@\3\2\2\2\u01ac\u01ad"+ - "\7y\2\2\u01ad\u01ae\7j\2\2\u01ae\u01af\7k\2\2\u01af\u01b0\7n\2\2\u01b0"+ - "\u01b1\7g\2\2\u01b1B\3\2\2\2\u01b2\u01b3\7f\2\2\u01b3\u01b4\7q\2\2\u01b4"+ - "D\3\2\2\2\u01b5\u01b6\7h\2\2\u01b6\u01b7\7q\2\2\u01b7\u01b8\7t\2\2\u01b8"+ - "F\3\2\2\2\u01b9\u01ba\7u\2\2\u01ba\u01bb\7y\2\2\u01bb\u01bc\7k\2\2\u01bc"+ - "\u01bd\7v\2\2\u01bd\u01be\7e\2\2\u01be\u01bf\7j\2\2\u01bfH\3\2\2\2\u01c0"+ - "\u01c1\7t\2\2\u01c1\u01c2\7g\2\2\u01c2\u01c3\7v\2\2\u01c3\u01c4\7w\2\2"+ - "\u01c4\u01c5\7t\2\2\u01c5\u01c6\7p\2\2\u01c6J\3\2\2\2\u01c7\u01c8\7d\2"+ - "\2\u01c8\u01c9\7t\2\2\u01c9\u01ca\7g\2\2\u01ca\u01cb\7c\2\2\u01cb\u01cc"+ - "\7m\2\2\u01ccL\3\2\2\2\u01cd\u01ce\7e\2\2\u01ce\u01cf\7q\2\2\u01cf\u01d0"+ - "\7p\2\2\u01d0\u01d1\7v\2\2\u01d1\u01d2\7k\2\2\u01d2\u01d3\7p\2\2\u01d3"+ - "\u01d4\7w\2\2\u01d4\u01d5\7g\2\2\u01d5N\3\2\2\2\u01d6\u01d7\7c\2\2\u01d7"+ - "\u01d8\7u\2\2\u01d8\u01d9\7o\2\2\u01d9P\3\2\2\2\u01da\u01db\7f\2\2\u01db"+ - "\u01dc\7g\2\2\u01dc\u01dd\7h\2\2\u01dd\u01de\7c\2\2\u01de\u01df\7w\2\2"+ - "\u01df\u01e0\7n\2\2\u01e0\u01e1\7v\2\2\u01e1\u01e2\7<\2\2\u01e2R\3\2\2"+ - "\2\u01e3\u01e4\7e\2\2\u01e4\u01e5\7c\2\2\u01e5\u01e6\7u\2\2\u01e6\u01e7"+ - "\7g\2\2\u01e7T\3\2\2\2\u01e8\u01e9\7<\2\2\u01e9V\3\2\2\2\u01ea\u01eb\7"+ - "\60\2\2\u01eb\u01ec\7\60\2\2\u01ecX\3\2\2\2\u01ed\u01ee\7u\2\2\u01ee\u01ef"+ - "\7k\2\2\u01ef\u01f0\7i\2\2\u01f0\u01f1\7p\2\2\u01f1\u01f2\7g\2\2\u01f2"+ - "\u01f3\7f\2\2\u01f3Z\3\2\2\2\u01f4\u01f5\7w\2\2\u01f5\u01f6\7p\2\2\u01f6"+ - "\u01f7\7u\2\2\u01f7\u01f8\7k\2\2\u01f8\u01f9\7i\2\2\u01f9\u01fa\7p\2\2"+ - "\u01fa\u01fb\7g\2\2\u01fb\u01fc\7f\2\2\u01fc\\\3\2\2\2\u01fd\u01fe\7,"+ - "\2\2\u01fe^\3\2\2\2\u01ff\u0200\7]\2\2\u0200`\3\2\2\2\u0201\u0202\7_\2"+ - "\2\u0202b\3\2\2\2\u0203\u0204\7u\2\2\u0204\u0205\7v\2\2\u0205\u0206\7"+ - "t\2\2\u0206\u0207\7w\2\2\u0207\u0208\7e\2\2\u0208\u0209\7v\2\2\u0209d"+ - "\3\2\2\2\u020a\u020b\7g\2\2\u020b\u020c\7p\2\2\u020c\u020d\7w\2\2\u020d"+ - "\u020e\7o\2\2\u020ef\3\2\2\2\u020f\u0210\7\60\2\2\u0210h\3\2\2\2\u0211"+ - "\u0212\7/\2\2\u0212\u0213\7@\2\2\u0213j\3\2\2\2\u0214\u0215\7u\2\2\u0215"+ - "\u0216\7k\2\2\u0216\u0217\7|\2\2\u0217\u0218\7g\2\2\u0218\u0219\7q\2\2"+ - "\u0219\u021a\7h\2\2\u021al\3\2\2\2\u021b\u021c\7v\2\2\u021c\u021d\7{\2"+ - "\2\u021d\u021e\7r\2\2\u021e\u021f\7g\2\2\u021f\u0220\7k\2\2\u0220\u0221"+ - "\7f\2\2\u0221n\3\2\2\2\u0222\u0223\7/\2\2\u0223\u0224\7/\2\2\u0224p\3"+ - "\2\2\2\u0225\u0226\7-\2\2\u0226\u0227\7-\2\2\u0227r\3\2\2\2\u0228\u0229"+ - "\7-\2\2\u0229t\3\2\2\2\u022a\u022b\7/\2\2\u022bv\3\2\2\2\u022c\u022d\7"+ - "#\2\2\u022dx\3\2\2\2\u022e\u022f\7(\2\2\u022fz\3\2\2\2\u0230\u0231\7\u0080"+ - "\2\2\u0231|\3\2\2\2\u0232\u0233\7@\2\2\u0233\u0234\7@\2\2\u0234~\3\2\2"+ - "\2\u0235\u0236\7>\2\2\u0236\u0237\7>\2\2\u0237\u0080\3\2\2\2\u0238\u0239"+ - "\7\61\2\2\u0239\u0082\3\2\2\2\u023a\u023b\7\'\2\2\u023b\u0084\3\2\2\2"+ - "\u023c\u023d\7>\2\2\u023d\u0086\3\2\2\2\u023e\u023f\7@\2\2\u023f\u0088"+ - "\3\2\2\2\u0240\u0241\7?\2\2\u0241\u0242\7?\2\2\u0242\u008a\3\2\2\2\u0243"+ - "\u0244\7#\2\2\u0244\u0245\7?\2\2\u0245\u008c\3\2\2\2\u0246\u0247\7>\2"+ - "\2\u0247\u0248\7?\2\2\u0248\u008e\3\2\2\2\u0249\u024a\7@\2\2\u024a\u024b"+ - "\7?\2\2\u024b\u0090\3\2\2\2\u024c\u024d\7`\2\2\u024d\u0092\3\2\2\2\u024e"+ - "\u024f\7~\2\2\u024f\u0094\3\2\2\2\u0250\u0251\7(\2\2\u0251\u0252\7(\2"+ - "\2\u0252\u0096\3\2\2\2\u0253\u0254\7~\2\2\u0254\u0255\7~\2\2\u0255\u0098"+ - "\3\2\2\2\u0256\u0257\7A\2\2\u0257\u009a\3\2\2\2\u0258\u0259\7-\2\2\u0259"+ - "\u025a\7?\2\2\u025a\u009c\3\2\2\2\u025b\u025c\7/\2\2\u025c\u025d\7?\2"+ - "\2\u025d\u009e\3\2\2\2\u025e\u025f\7,\2\2\u025f\u0260\7?\2\2\u0260\u00a0"+ - "\3\2\2\2\u0261\u0262\7\61\2\2\u0262\u0263\7?\2\2\u0263\u00a2\3\2\2\2\u0264"+ - "\u0265\7\'\2\2\u0265\u0266\7?\2\2\u0266\u00a4\3\2\2\2\u0267\u0268\7>\2"+ - "\2\u0268\u0269\7>\2\2\u0269\u026a\7?\2\2\u026a\u00a6\3\2\2\2\u026b\u026c"+ - "\7@\2\2\u026c\u026d\7@\2\2\u026d\u026e\7?\2\2\u026e\u00a8\3\2\2\2\u026f"+ - "\u0270\7(\2\2\u0270\u0271\7?\2\2\u0271\u00aa\3\2\2\2\u0272\u0273\7~\2"+ - "\2\u0273\u0274\7?\2\2\u0274\u00ac\3\2\2\2\u0275\u0276\7`\2\2\u0276\u0277"+ - "\7?\2\2\u0277\u00ae\3\2\2\2\u0278\u0279\7m\2\2\u0279\u027a\7k\2\2\u027a"+ - "\u027b\7e\2\2\u027b\u027c\7m\2\2\u027c\u027d\7c\2\2\u027d\u027e\7u\2\2"+ - "\u027e\u027f\7o\2\2\u027f\u00b0\3\2\2\2\u0280\u0281\7t\2\2\u0281\u0282"+ - "\7g\2\2\u0282\u0283\7u\2\2\u0283\u0284\7q\2\2\u0284\u0285\7w\2\2\u0285"+ - "\u0286\7t\2\2\u0286\u0287\7e\2\2\u0287\u0288\7g\2\2\u0288\u00b2\3\2\2"+ - "\2\u0289\u028a\7w\2\2\u028a\u028b\7u\2\2\u028b\u028c\7g\2\2\u028c\u028d"+ - "\7u\2\2\u028d\u00b4\3\2\2\2\u028e\u028f\7e\2\2\u028f\u0290\7n\2\2\u0290"+ - "\u0291\7q\2\2\u0291\u0292\7d\2\2\u0292\u0293\7d\2\2\u0293\u0294\7g\2\2"+ - "\u0294\u0295\7t\2\2\u0295\u0296\7u\2\2\u0296\u00b6\3\2\2\2\u0297\u0298"+ - "\7d\2\2\u0298\u0299\7{\2\2\u0299\u029a\7v\2\2\u029a\u029b\7g\2\2\u029b"+ - "\u029c\7u\2\2\u029c\u00b8\3\2\2\2\u029d\u029e\7e\2\2\u029e\u029f\7{\2"+ - "\2\u029f\u02a0\7e\2\2\u02a0\u02a1\7n\2\2\u02a1\u02a2\7g\2\2\u02a2\u02a3"+ - "\7u\2\2\u02a3\u00ba\3\2\2\2\u02a4\u02a5\7\60\2\2\u02a5\u02a6\7d\2\2\u02a6"+ - "\u02a7\7{\2\2\u02a7\u02a8\7v\2\2\u02a8\u02a9\7g\2\2\u02a9\u00bc\3\2\2"+ - "\2\u02aa\u02ab\7%\2\2\u02ab\u00be\3\2\2\2\u02ac\u02ad\7d\2\2\u02ad\u02ae"+ - "\7t\2\2\u02ae\u038b\7m\2\2\u02af\u02b0\7q\2\2\u02b0\u02b1\7t\2\2\u02b1"+ - "\u038b\7c\2\2\u02b2\u02b3\7m\2\2\u02b3\u02b4\7k\2\2\u02b4\u038b\7n\2\2"+ - "\u02b5\u02b6\7u\2\2\u02b6\u02b7\7n\2\2\u02b7\u038b\7q\2\2\u02b8\u02b9"+ - "\7p\2\2\u02b9\u02ba\7q\2\2\u02ba\u038b\7r\2\2\u02bb\u02bc\7c\2\2\u02bc"+ - "\u02bd\7u\2\2\u02bd\u038b\7n\2\2\u02be\u02bf\7r\2\2\u02bf\u02c0\7j\2\2"+ - "\u02c0\u038b\7r\2\2\u02c1\u02c2\7c\2\2\u02c2\u02c3\7p\2\2\u02c3\u038b"+ - "\7e\2\2\u02c4\u02c5\7d\2\2\u02c5\u02c6\7r\2\2\u02c6\u038b\7n\2\2\u02c7"+ - "\u02c8\7e\2\2\u02c8\u02c9\7n\2\2\u02c9\u038b\7e\2\2\u02ca\u02cb\7l\2\2"+ - "\u02cb\u02cc\7u\2\2\u02cc\u038b\7t\2\2\u02cd\u02ce\7c\2\2\u02ce\u02cf"+ - "\7p\2\2\u02cf\u038b\7f\2\2\u02d0\u02d1\7t\2\2\u02d1\u02d2\7n\2\2\u02d2"+ - "\u038b\7c\2\2\u02d3\u02d4\7d\2\2\u02d4\u02d5\7k\2\2\u02d5\u038b\7v\2\2"+ - "\u02d6\u02d7\7t\2\2\u02d7\u02d8\7q\2\2\u02d8\u038b\7n\2\2\u02d9\u02da"+ - "\7r\2\2\u02da\u02db\7n\2\2\u02db\u038b\7c\2\2\u02dc\u02dd\7r\2\2\u02dd"+ - "\u02de\7n\2\2\u02de\u038b\7r\2\2\u02df\u02e0\7d\2\2\u02e0\u02e1\7o\2\2"+ - "\u02e1\u038b\7k\2\2\u02e2\u02e3\7u\2\2\u02e3\u02e4\7g\2\2\u02e4\u038b"+ - "\7e\2\2\u02e5\u02e6\7t\2\2\u02e6\u02e7\7v\2\2\u02e7\u038b\7k\2\2\u02e8"+ - "\u02e9\7g\2\2\u02e9\u02ea\7q\2\2\u02ea\u038b\7t\2\2\u02eb\u02ec\7u\2\2"+ - "\u02ec\u02ed\7t\2\2\u02ed\u038b\7g\2\2\u02ee\u02ef\7n\2\2\u02ef\u02f0"+ - "\7u\2\2\u02f0\u038b\7t\2\2\u02f1\u02f2\7r\2\2\u02f2\u02f3\7j\2\2\u02f3"+ - "\u038b\7c\2\2\u02f4\u02f5\7c\2\2\u02f5\u02f6\7n\2\2\u02f6\u038b\7t\2\2"+ - "\u02f7\u02f8\7l\2\2\u02f8\u02f9\7o\2\2\u02f9\u038b\7r\2\2\u02fa\u02fb"+ - "\7d\2\2\u02fb\u02fc\7x\2\2\u02fc\u038b\7e\2\2\u02fd\u02fe\7e\2\2\u02fe"+ - "\u02ff\7n\2\2\u02ff\u038b\7k\2\2\u0300\u0301\7t\2\2\u0301\u0302\7v\2\2"+ - "\u0302\u038b\7u\2\2\u0303\u0304\7c\2\2\u0304\u0305\7f\2\2\u0305\u038b"+ - "\7e\2\2\u0306\u0307\7t\2\2\u0307\u0308\7t\2\2\u0308\u038b\7c\2\2\u0309"+ - "\u030a\7d\2\2\u030a\u030b\7x\2\2\u030b\u038b\7u\2\2\u030c\u030d\7u\2\2"+ - "\u030d\u030e\7g\2\2\u030e\u038b\7k\2\2\u030f\u0310\7u\2\2\u0310\u0311"+ - "\7c\2\2\u0311\u038b\7z\2\2\u0312\u0313\7u\2\2\u0313\u0314\7v\2\2\u0314"+ - "\u038b\7{\2\2\u0315\u0316\7u\2\2\u0316\u0317\7v\2\2\u0317\u038b\7c\2\2"+ - "\u0318\u0319\7u\2\2\u0319\u031a\7v\2\2\u031a\u038b\7z\2\2\u031b\u031c"+ - "\7f\2\2\u031c\u031d\7g\2\2\u031d\u038b\7{\2\2\u031e\u031f\7v\2\2\u031f"+ - "\u0320\7z\2\2\u0320\u038b\7c\2\2\u0321\u0322\7z\2\2\u0322\u0323\7c\2\2"+ - "\u0323\u038b\7c\2\2\u0324\u0325\7d\2\2\u0325\u0326\7e\2\2\u0326\u038b"+ - "\7e\2\2\u0327\u0328\7c\2\2\u0328\u0329\7j\2\2\u0329\u038b\7z\2\2\u032a"+ - "\u032b\7v\2\2\u032b\u032c\7{\2\2\u032c\u038b\7c\2\2\u032d\u032e\7v\2\2"+ - "\u032e\u032f\7z\2\2\u032f\u038b\7u\2\2\u0330\u0331\7v\2\2\u0331\u0332"+ - "\7c\2\2\u0332\u038b\7u\2\2\u0333\u0334\7u\2\2\u0334\u0335\7j\2\2\u0335"+ - "\u038b\7{\2\2\u0336\u0337\7u\2\2\u0337\u0338\7j\2\2\u0338\u038b\7z\2\2"+ - "\u0339\u033a\7n\2\2\u033a\u033b\7f\2\2\u033b\u038b\7{\2\2\u033c\u033d"+ - "\7n\2\2\u033d\u033e\7f\2\2\u033e\u038b\7c\2\2\u033f\u0340\7n\2\2\u0340"+ - "\u0341\7f\2\2\u0341\u038b\7z\2\2\u0342\u0343\7n\2\2\u0343\u0344\7c\2\2"+ - "\u0344\u038b\7z\2\2\u0345\u0346\7v\2\2\u0346\u0347\7c\2\2\u0347\u038b"+ - "\7{\2\2\u0348\u0349\7v\2\2\u0349\u034a\7c\2\2\u034a\u038b\7z\2\2\u034b"+ - "\u034c\7d\2\2\u034c\u034d\7e\2\2\u034d\u038b\7u\2\2\u034e\u034f\7e\2\2"+ - "\u034f\u0350\7n\2\2\u0350\u038b\7x\2\2\u0351\u0352\7v\2\2\u0352\u0353"+ - "\7u\2\2\u0353\u038b\7z\2\2\u0354\u0355\7n\2\2\u0355\u0356\7c\2\2\u0356"+ - "\u038b\7u\2\2\u0357\u0358\7e\2\2\u0358\u0359\7r\2\2\u0359\u038b\7{\2\2"+ - "\u035a\u035b\7e\2\2\u035b\u035c\7o\2\2\u035c\u038b\7r\2\2\u035d\u035e"+ - "\7e\2\2\u035e\u035f\7r\2\2\u035f\u038b\7z\2\2\u0360\u0361\7f\2\2\u0361"+ - "\u0362\7e\2\2\u0362\u038b\7r\2\2\u0363\u0364\7f\2\2\u0364\u0365\7g\2\2"+ - "\u0365\u038b\7e\2\2\u0366\u0367\7k\2\2\u0367\u0368\7p\2\2\u0368\u038b"+ - "\7e\2\2\u0369\u036a\7c\2\2\u036a\u036b\7z\2\2\u036b\u038b\7u\2\2\u036c"+ - "\u036d\7d\2\2\u036d\u036e\7p\2\2\u036e\u038b\7g\2\2\u036f\u0370\7e\2\2"+ - "\u0370\u0371\7n\2\2\u0371\u038b\7f\2\2\u0372\u0373\7u\2\2\u0373\u0374"+ - "\7d\2\2\u0374\u038b\7e\2\2\u0375\u0376\7k\2\2\u0376\u0377\7u\2\2\u0377"+ - "\u038b\7e\2\2\u0378\u0379\7k\2\2\u0379\u037a\7p\2\2\u037a\u038b\7z\2\2"+ - "\u037b\u037c\7d\2\2\u037c\u037d\7g\2\2\u037d\u038b\7s\2\2\u037e\u037f"+ - "\7u\2\2\u037f\u0380\7g\2\2\u0380\u038b\7f\2\2\u0381\u0382\7f\2\2\u0382"+ - "\u0383\7g\2\2\u0383\u038b\7z\2\2\u0384\u0385\7k\2\2\u0385\u0386\7p\2\2"+ - "\u0386\u038b\7{\2\2\u0387\u0388\7t\2\2\u0388\u0389\7q\2\2\u0389\u038b"+ - "\7t\2\2\u038a\u02ac\3\2\2\2\u038a\u02af\3\2\2\2\u038a\u02b2\3\2\2\2\u038a"+ - "\u02b5\3\2\2\2\u038a\u02b8\3\2\2\2\u038a\u02bb\3\2\2\2\u038a\u02be\3\2"+ - "\2\2\u038a\u02c1\3\2\2\2\u038a\u02c4\3\2\2\2\u038a\u02c7\3\2\2\2\u038a"+ - "\u02ca\3\2\2\2\u038a\u02cd\3\2\2\2\u038a\u02d0\3\2\2\2\u038a\u02d3\3\2"+ - "\2\2\u038a\u02d6\3\2\2\2\u038a\u02d9\3\2\2\2\u038a\u02dc\3\2\2\2\u038a"+ - "\u02df\3\2\2\2\u038a\u02e2\3\2\2\2\u038a\u02e5\3\2\2\2\u038a\u02e8\3\2"+ - "\2\2\u038a\u02eb\3\2\2\2\u038a\u02ee\3\2\2\2\u038a\u02f1\3\2\2\2\u038a"+ - "\u02f4\3\2\2\2\u038a\u02f7\3\2\2\2\u038a\u02fa\3\2\2\2\u038a\u02fd\3\2"+ - "\2\2\u038a\u0300\3\2\2\2\u038a\u0303\3\2\2\2\u038a\u0306\3\2\2\2\u038a"+ - "\u0309\3\2\2\2\u038a\u030c\3\2\2\2\u038a\u030f\3\2\2\2\u038a\u0312\3\2"+ - "\2\2\u038a\u0315\3\2\2\2\u038a\u0318\3\2\2\2\u038a\u031b\3\2\2\2\u038a"+ - "\u031e\3\2\2\2\u038a\u0321\3\2\2\2\u038a\u0324\3\2\2\2\u038a\u0327\3\2"+ - "\2\2\u038a\u032a\3\2\2\2\u038a\u032d\3\2\2\2\u038a\u0330\3\2\2\2\u038a"+ - "\u0333\3\2\2\2\u038a\u0336\3\2\2\2\u038a\u0339\3\2\2\2\u038a\u033c\3\2"+ - "\2\2\u038a\u033f\3\2\2\2\u038a\u0342\3\2\2\2\u038a\u0345\3\2\2\2\u038a"+ - "\u0348\3\2\2\2\u038a\u034b\3\2\2\2\u038a\u034e\3\2\2\2\u038a\u0351\3\2"+ - "\2\2\u038a\u0354\3\2\2\2\u038a\u0357\3\2\2\2\u038a\u035a\3\2\2\2\u038a"+ - "\u035d\3\2\2\2\u038a\u0360\3\2\2\2\u038a\u0363\3\2\2\2\u038a\u0366\3\2"+ - "\2\2\u038a\u0369\3\2\2\2\u038a\u036c\3\2\2\2\u038a\u036f\3\2\2\2\u038a"+ - "\u0372\3\2\2\2\u038a\u0375\3\2\2\2\u038a\u0378\3\2\2\2\u038a\u037b\3\2"+ - "\2\2\u038a\u037e\3\2\2\2\u038a\u0381\3\2\2\2\u038a\u0384\3\2\2\2\u038a"+ - "\u0387\3\2\2\2\u038b\u00c0\3\2\2\2\u038c\u038d\7}\2\2\u038d\u038e\7}\2"+ - "\2\u038e\u0392\3\2\2\2\u038f\u0391\13\2\2\2\u0390\u038f\3\2\2\2\u0391"+ - "\u0394\3\2\2\2\u0392\u0393\3\2\2\2\u0392\u0390\3\2\2\2\u0393\u0395\3\2"+ - "\2\2\u0394\u0392\3\2\2\2\u0395\u0396\7\177\2\2\u0396\u0397\7\177\2\2\u0397"+ - "\u00c2\3\2\2\2\u0398\u0399\7d\2\2\u0399\u039a\7{\2\2\u039a\u039b\7v\2"+ - "\2\u039b\u03be\7g\2\2\u039c\u039d\7y\2\2\u039d\u039e\7q\2\2\u039e\u039f"+ - "\7t\2\2\u039f\u03be\7f\2\2\u03a0\u03a1\7f\2\2\u03a1\u03a2\7y\2\2\u03a2"+ - "\u03a3\7q\2\2\u03a3\u03a4\7t\2\2\u03a4\u03be\7f\2\2\u03a5\u03a6\7d\2\2"+ - "\u03a6\u03a7\7q\2\2\u03a7\u03a8\7q\2\2\u03a8\u03be\7n\2\2\u03a9\u03aa"+ - "\7e\2\2\u03aa\u03ab\7j\2\2\u03ab\u03ac\7c\2\2\u03ac\u03be\7t\2\2\u03ad"+ - "\u03ae\7u\2\2\u03ae\u03af\7j\2\2\u03af\u03b0\7q\2\2\u03b0\u03b1\7t\2\2"+ - "\u03b1\u03be\7v\2\2\u03b2\u03b3\7k\2\2\u03b3\u03b4\7p\2\2\u03b4\u03be"+ - "\7v\2\2\u03b5\u03b6\7n\2\2\u03b6\u03b7\7q\2\2\u03b7\u03b8\7p\2\2\u03b8"+ - "\u03be\7i\2\2\u03b9\u03ba\7x\2\2\u03ba\u03bb\7q\2\2\u03bb\u03bc\7k\2\2"+ - "\u03bc\u03be\7f\2\2\u03bd\u0398\3\2\2\2\u03bd\u039c\3\2\2\2\u03bd\u03a0"+ - "\3\2\2\2\u03bd\u03a5\3\2\2\2\u03bd\u03a9\3\2\2\2\u03bd\u03ad\3\2\2\2\u03bd"+ - "\u03b2\3\2\2\2\u03bd\u03b5\3\2\2\2\u03bd\u03b9\3\2\2\2\u03be\u00c4\3\2"+ - "\2\2\u03bf\u03c5\7$\2\2\u03c0\u03c1\7^\2\2\u03c1\u03c4\7$\2\2\u03c2\u03c4"+ - "\n\2\2\2\u03c3\u03c0\3\2\2\2\u03c3\u03c2\3\2\2\2\u03c4\u03c7\3\2\2\2\u03c5"+ - "\u03c3\3\2\2\2\u03c5\u03c6\3\2\2\2\u03c6\u03c8\3\2\2\2\u03c7\u03c5\3\2"+ - "\2\2\u03c8\u03ca\7$\2\2\u03c9\u03cb\t\3\2\2\u03ca\u03c9\3\2\2\2\u03ca"+ - "\u03cb\3\2\2\2\u03cb\u03d0\3\2\2\2\u03cc\u03ce\t\4\2\2\u03cd\u03cf\t\5"+ - "\2\2\u03ce\u03cd\3\2\2\2\u03ce\u03cf\3\2\2\2\u03cf\u03d1\3\2\2\2\u03d0"+ - "\u03cc\3\2\2\2\u03d0\u03d1\3\2\2\2\u03d1\u03d3\3\2\2\2\u03d2\u03d4\t\3"+ - "\2\2\u03d3\u03d2\3\2\2\2\u03d3\u03d4\3\2\2\2\u03d4\u00c6\3\2\2\2\u03d5"+ - "\u03d9\7)\2\2\u03d6\u03d7\7^\2\2\u03d7\u03da\7)\2\2\u03d8\u03da\n\6\2"+ - "\2\u03d9\u03d6\3\2\2\2\u03d9\u03d8\3\2\2\2\u03da\u03db\3\2\2\2\u03db\u03dc"+ - "\7)\2\2\u03dc\u00c8\3\2\2\2\u03dd\u03de\7v\2\2\u03de\u03df\7t\2\2\u03df"+ - "\u03e0\7w\2\2\u03e0\u03e7\7g\2\2\u03e1\u03e2\7h\2\2\u03e2\u03e3\7c\2\2"+ - "\u03e3\u03e4\7n\2\2\u03e4\u03e5\7u\2\2\u03e5\u03e7\7g\2\2\u03e6\u03dd"+ - "\3\2\2\2\u03e6\u03e1\3\2\2\2\u03e7\u00ca\3\2\2\2\u03e8\u03eb\5\u00cdg"+ - "\2\u03e9\u03eb\5\u00d5k\2\u03ea\u03e8\3\2\2\2\u03ea\u03e9\3\2\2\2\u03eb"+ + "p\u00e5\2\u00e7\2\u00e9q\u00ebr\u00eds\u00eft\3\2\23\3\2$$\3\2||\4\2r"+ + "ruu\4\2ooww\7\2$$))hhpptt\3\2))\4\2uuww\7\2dfkknnuuyy\4\2DDdd\3\2\62\63"+ + "\3\2\62;\5\2\62;CHch\5\2C\\aac|\6\2\62;C\\aac|\4\2--//\6\2\13\f\17\17"+ + "\"\"\u00a2\u00a2\4\2\f\f\17\17\2\u04fc\2\3\3\2\2\2\2\5\3\2\2\2\2\7\3\2"+ + "\2\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2\2\2\2\17\3\2\2\2\2\21\3\2\2\2\2"+ + "\23\3\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2\2\31\3\2\2\2\2\33\3\2\2\2\2\35\3"+ + "\2\2\2\2\37\3\2\2\2\2!\3\2\2\2\2#\3\2\2\2\2%\3\2\2\2\2\'\3\2\2\2\2)\3"+ + "\2\2\2\2+\3\2\2\2\2-\3\2\2\2\2/\3\2\2\2\2\61\3\2\2\2\2\63\3\2\2\2\2\65"+ + "\3\2\2\2\2\67\3\2\2\2\29\3\2\2\2\2;\3\2\2\2\2=\3\2\2\2\2?\3\2\2\2\2A\3"+ + "\2\2\2\2C\3\2\2\2\2E\3\2\2\2\2G\3\2\2\2\2I\3\2\2\2\2K\3\2\2\2\2M\3\2\2"+ + "\2\2O\3\2\2\2\2Q\3\2\2\2\2S\3\2\2\2\2U\3\2\2\2\2W\3\2\2\2\2Y\3\2\2\2\2"+ + "[\3\2\2\2\2]\3\2\2\2\2_\3\2\2\2\2a\3\2\2\2\2c\3\2\2\2\2e\3\2\2\2\2g\3"+ + "\2\2\2\2i\3\2\2\2\2k\3\2\2\2\2m\3\2\2\2\2o\3\2\2\2\2q\3\2\2\2\2s\3\2\2"+ + "\2\2u\3\2\2\2\2w\3\2\2\2\2y\3\2\2\2\2{\3\2\2\2\2}\3\2\2\2\2\177\3\2\2"+ + "\2\2\u0081\3\2\2\2\2\u0083\3\2\2\2\2\u0085\3\2\2\2\2\u0087\3\2\2\2\2\u0089"+ + "\3\2\2\2\2\u008b\3\2\2\2\2\u008d\3\2\2\2\2\u008f\3\2\2\2\2\u0091\3\2\2"+ + "\2\2\u0093\3\2\2\2\2\u0095\3\2\2\2\2\u0097\3\2\2\2\2\u0099\3\2\2\2\2\u009b"+ + "\3\2\2\2\2\u009d\3\2\2\2\2\u009f\3\2\2\2\2\u00a1\3\2\2\2\2\u00a3\3\2\2"+ + "\2\2\u00a5\3\2\2\2\2\u00a7\3\2\2\2\2\u00a9\3\2\2\2\2\u00ab\3\2\2\2\2\u00ad"+ + "\3\2\2\2\2\u00af\3\2\2\2\2\u00b1\3\2\2\2\2\u00b3\3\2\2\2\2\u00b5\3\2\2"+ + "\2\2\u00b7\3\2\2\2\2\u00b9\3\2\2\2\2\u00bb\3\2\2\2\2\u00bd\3\2\2\2\2\u00bf"+ + "\3\2\2\2\2\u00c1\3\2\2\2\2\u00c3\3\2\2\2\2\u00c5\3\2\2\2\2\u00c7\3\2\2"+ + "\2\2\u00c9\3\2\2\2\2\u00cb\3\2\2\2\2\u00cd\3\2\2\2\2\u00cf\3\2\2\2\2\u00d1"+ + "\3\2\2\2\2\u00d3\3\2\2\2\2\u00d5\3\2\2\2\2\u00d7\3\2\2\2\2\u00d9\3\2\2"+ + "\2\2\u00db\3\2\2\2\2\u00e3\3\2\2\2\2\u00e9\3\2\2\2\2\u00eb\3\2\2\2\2\u00ed"+ + "\3\2\2\2\2\u00ef\3\2\2\2\3\u00f1\3\2\2\2\5\u00f8\3\2\2\2\7\u00fa\3\2\2"+ + "\2\t\u0102\3\2\2\2\13\u0104\3\2\2\2\r\u0106\3\2\2\2\17\u0108\3\2\2\2\21"+ + "\u010a\3\2\2\2\23\u010c\3\2\2\2\25\u010e\3\2\2\2\27\u0116\3\2\2\2\31\u011e"+ + "\3\2\2\2\33\u0127\3\2\2\2\35\u012a\3\2\2\2\37\u012e\3\2\2\2!\u0135\3\2"+ + "\2\2#\u013d\3\2\2\2%\u0142\3\2\2\2\'\u014b\3\2\2\2)\u0154\3\2\2\2+\u015d"+ + "\3\2\2\2-\u0167\3\2\2\2/\u016d\3\2\2\2\61\u0174\3\2\2\2\63\u017b\3\2\2"+ + "\2\65\u0181\3\2\2\2\67\u018a\3\2\2\29\u0191\3\2\2\2;\u019a\3\2\2\2=\u01a4"+ + "\3\2\2\2?\u01a7\3\2\2\2A\u01ac\3\2\2\2C\u01b2\3\2\2\2E\u01b5\3\2\2\2G"+ + "\u01b9\3\2\2\2I\u01c0\3\2\2\2K\u01c7\3\2\2\2M\u01cd\3\2\2\2O\u01d6\3\2"+ + "\2\2Q\u01da\3\2\2\2S\u01e3\3\2\2\2U\u01e8\3\2\2\2W\u01ea\3\2\2\2Y\u01ed"+ + "\3\2\2\2[\u01f4\3\2\2\2]\u01fd\3\2\2\2_\u01ff\3\2\2\2a\u0201\3\2\2\2c"+ + "\u0203\3\2\2\2e\u020a\3\2\2\2g\u020f\3\2\2\2i\u0211\3\2\2\2k\u0214\3\2"+ + "\2\2m\u021b\3\2\2\2o\u0222\3\2\2\2q\u0225\3\2\2\2s\u0228\3\2\2\2u\u022a"+ + "\3\2\2\2w\u022c\3\2\2\2y\u022e\3\2\2\2{\u0230\3\2\2\2}\u0232\3\2\2\2\177"+ + "\u0235\3\2\2\2\u0081\u0238\3\2\2\2\u0083\u023a\3\2\2\2\u0085\u023c\3\2"+ + "\2\2\u0087\u023e\3\2\2\2\u0089\u0240\3\2\2\2\u008b\u0243\3\2\2\2\u008d"+ + "\u0246\3\2\2\2\u008f\u0249\3\2\2\2\u0091\u024c\3\2\2\2\u0093\u024e\3\2"+ + "\2\2\u0095\u0250\3\2\2\2\u0097\u0253\3\2\2\2\u0099\u0256\3\2\2\2\u009b"+ + "\u0258\3\2\2\2\u009d\u025b\3\2\2\2\u009f\u025e\3\2\2\2\u00a1\u0261\3\2"+ + "\2\2\u00a3\u0264\3\2\2\2\u00a5\u0267\3\2\2\2\u00a7\u026b\3\2\2\2\u00a9"+ + "\u026f\3\2\2\2\u00ab\u0272\3\2\2\2\u00ad\u0275\3\2\2\2\u00af\u0278\3\2"+ + "\2\2\u00b1\u0280\3\2\2\2\u00b3\u0289\3\2\2\2\u00b5\u028e\3\2\2\2\u00b7"+ + "\u0297\3\2\2\2\u00b9\u029d\3\2\2\2\u00bb\u02a4\3\2\2\2\u00bd\u02aa\3\2"+ + "\2\2\u00bf\u038a\3\2\2\2\u00c1\u038c\3\2\2\2\u00c3\u03bd\3\2\2\2\u00c5"+ + "\u03bf\3\2\2\2\u00c7\u03d5\3\2\2\2\u00c9\u03e6\3\2\2\2\u00cb\u03ea\3\2"+ + "\2\2\u00cd\u03ef\3\2\2\2\u00cf\u03f6\3\2\2\2\u00d1\u0407\3\2\2\2\u00d3"+ + "\u0415\3\2\2\2\u00d5\u0426\3\2\2\2\u00d7\u043a\3\2\2\2\u00d9\u043d\3\2"+ + "\2\2\u00db\u0446\3\2\2\2\u00dd\u044d\3\2\2\2\u00df\u044f\3\2\2\2\u00e1"+ + "\u0451\3\2\2\2\u00e3\u0453\3\2\2\2\u00e5\u045a\3\2\2\2\u00e7\u045c\3\2"+ + "\2\2\u00e9\u045e\3\2\2\2\u00eb\u046b\3\2\2\2\u00ed\u0471\3\2\2\2\u00ef"+ + "\u047c\3\2\2\2\u00f1\u00f2\7k\2\2\u00f2\u00f3\7o\2\2\u00f3\u00f4\7r\2"+ + "\2\u00f4\u00f5\7q\2\2\u00f5\u00f6\7t\2\2\u00f6\u00f7\7v\2\2\u00f7\4\3"+ + "\2\2\2\u00f8\u00f9\7=\2\2\u00f9\6\3\2\2\2\u00fa\u00fb\7v\2\2\u00fb\u00fc"+ + "\7{\2\2\u00fc\u00fd\7r\2\2\u00fd\u00fe\7g\2\2\u00fe\u00ff\7f\2\2\u00ff"+ + "\u0100\7g\2\2\u0100\u0101\7h\2\2\u0101\b\3\2\2\2\u0102\u0103\7.\2\2\u0103"+ + "\n\3\2\2\2\u0104\u0105\7?\2\2\u0105\f\3\2\2\2\u0106\u0107\7*\2\2\u0107"+ + "\16\3\2\2\2\u0108\u0109\7+\2\2\u0109\20\3\2\2\2\u010a\u010b\7}\2\2\u010b"+ + "\22\3\2\2\2\u010c\u010d\7\177\2\2\u010d\24\3\2\2\2\u010e\u010f\7%\2\2"+ + "\u010f\u0110\7r\2\2\u0110\u0111\7t\2\2\u0111\u0112\7c\2\2\u0112\u0113"+ + "\7i\2\2\u0113\u0114\7o\2\2\u0114\u0115\7c\2\2\u0115\26\3\2\2\2\u0116\u0117"+ + "\7t\2\2\u0117\u0118\7g\2\2\u0118\u0119\7u\2\2\u0119\u011a\7g\2\2\u011a"+ + "\u011b\7t\2\2\u011b\u011c\7x\2\2\u011c\u011d\7g\2\2\u011d\30\3\2\2\2\u011e"+ + "\u011f\7%\2\2\u011f\u0120\7t\2\2\u0120\u0121\7g\2\2\u0121\u0122\7u\2\2"+ + "\u0122\u0123\7g\2\2\u0123\u0124\7t\2\2\u0124\u0125\7x\2\2\u0125\u0126"+ + "\7g\2\2\u0126\32\3\2\2\2\u0127\u0128\7r\2\2\u0128\u0129\7e\2\2\u0129\34"+ + "\3\2\2\2\u012a\u012b\7%\2\2\u012b\u012c\7r\2\2\u012c\u012d\7e\2\2\u012d"+ + "\36\3\2\2\2\u012e\u012f\7v\2\2\u012f\u0130\7c\2\2\u0130\u0131\7t\2\2\u0131"+ + "\u0132\7i\2\2\u0132\u0133\7g\2\2\u0133\u0134\7v\2\2\u0134 \3\2\2\2\u0135"+ + "\u0136\7%\2\2\u0136\u0137\7v\2\2\u0137\u0138\7c\2\2\u0138\u0139\7t\2\2"+ + "\u0139\u013a\7i\2\2\u013a\u013b\7g\2\2\u013b\u013c\7v\2\2\u013c\"\3\2"+ + "\2\2\u013d\u013e\7n\2\2\u013e\u013f\7k\2\2\u013f\u0140\7p\2\2\u0140\u0141"+ + "\7m\2\2\u0141$\3\2\2\2\u0142\u0143\7e\2\2\u0143\u0144\7q\2\2\u0144\u0145"+ + "\7f\2\2\u0145\u0146\7g\2\2\u0146\u0147\7a\2\2\u0147\u0148\7u\2\2\u0148"+ + "\u0149\7g\2\2\u0149\u014a\7i\2\2\u014a&\3\2\2\2\u014b\u014c\7f\2\2\u014c"+ + "\u014d\7c\2\2\u014d\u014e\7v\2\2\u014e\u014f\7c\2\2\u014f\u0150\7a\2\2"+ + "\u0150\u0151\7u\2\2\u0151\u0152\7g\2\2\u0152\u0153\7i\2\2\u0153(\3\2\2"+ + "\2\u0154\u0155\7g\2\2\u0155\u0156\7p\2\2\u0156\u0157\7e\2\2\u0157\u0158"+ + "\7q\2\2\u0158\u0159\7f\2\2\u0159\u015a\7k\2\2\u015a\u015b\7p\2\2\u015b"+ + "\u015c\7i\2\2\u015c*\3\2\2\2\u015d\u015e\7%\2\2\u015e\u015f\7g\2\2\u015f"+ + "\u0160\7p\2\2\u0160\u0161\7e\2\2\u0161\u0162\7q\2\2\u0162\u0163\7f\2\2"+ + "\u0163\u0164\7k\2\2\u0164\u0165\7p\2\2\u0165\u0166\7i\2\2\u0166,\3\2\2"+ + "\2\u0167\u0168\7e\2\2\u0168\u0169\7q\2\2\u0169\u016a\7p\2\2\u016a\u016b"+ + "\7u\2\2\u016b\u016c\7v\2\2\u016c.\3\2\2\2\u016d\u016e\7g\2\2\u016e\u016f"+ + "\7z\2\2\u016f\u0170\7v\2\2\u0170\u0171\7g\2\2\u0171\u0172\7t\2\2\u0172"+ + "\u0173\7p\2\2\u0173\60\3\2\2\2\u0174\u0175\7g\2\2\u0175\u0176\7z\2\2\u0176"+ + "\u0177\7r\2\2\u0177\u0178\7q\2\2\u0178\u0179\7t\2\2\u0179\u017a\7v\2\2"+ + "\u017a\62\3\2\2\2\u017b\u017c\7c\2\2\u017c\u017d\7n\2\2\u017d\u017e\7"+ + "k\2\2\u017e\u017f\7i\2\2\u017f\u0180\7p\2\2\u0180\64\3\2\2\2\u0181\u0182"+ + "\7t\2\2\u0182\u0183\7g\2\2\u0183\u0184\7i\2\2\u0184\u0185\7k\2\2\u0185"+ + "\u0186\7u\2\2\u0186\u0187\7v\2\2\u0187\u0188\7g\2\2\u0188\u0189\7t\2\2"+ + "\u0189\66\3\2\2\2\u018a\u018b\7k\2\2\u018b\u018c\7p\2\2\u018c\u018d\7"+ + "n\2\2\u018d\u018e\7k\2\2\u018e\u018f\7p\2\2\u018f\u0190\7g\2\2\u01908"+ + "\3\2\2\2\u0191\u0192\7x\2\2\u0192\u0193\7q\2\2\u0193\u0194\7n\2\2\u0194"+ + "\u0195\7c\2\2\u0195\u0196\7v\2\2\u0196\u0197\7k\2\2\u0197\u0198\7n\2\2"+ + "\u0198\u0199\7g\2\2\u0199:\3\2\2\2\u019a\u019b\7k\2\2\u019b\u019c\7p\2"+ + "\2\u019c\u019d\7v\2\2\u019d\u019e\7g\2\2\u019e\u019f\7t\2\2\u019f\u01a0"+ + "\7t\2\2\u01a0\u01a1\7w\2\2\u01a1\u01a2\7r\2\2\u01a2\u01a3\7v\2\2\u01a3"+ + "<\3\2\2\2\u01a4\u01a5\7k\2\2\u01a5\u01a6\7h\2\2\u01a6>\3\2\2\2\u01a7\u01a8"+ + "\7g\2\2\u01a8\u01a9\7n\2\2\u01a9\u01aa\7u\2\2\u01aa\u01ab\7g\2\2\u01ab"+ + "@\3\2\2\2\u01ac\u01ad\7y\2\2\u01ad\u01ae\7j\2\2\u01ae\u01af\7k\2\2\u01af"+ + "\u01b0\7n\2\2\u01b0\u01b1\7g\2\2\u01b1B\3\2\2\2\u01b2\u01b3\7f\2\2\u01b3"+ + "\u01b4\7q\2\2\u01b4D\3\2\2\2\u01b5\u01b6\7h\2\2\u01b6\u01b7\7q\2\2\u01b7"+ + "\u01b8\7t\2\2\u01b8F\3\2\2\2\u01b9\u01ba\7u\2\2\u01ba\u01bb\7y\2\2\u01bb"+ + "\u01bc\7k\2\2\u01bc\u01bd\7v\2\2\u01bd\u01be\7e\2\2\u01be\u01bf\7j\2\2"+ + "\u01bfH\3\2\2\2\u01c0\u01c1\7t\2\2\u01c1\u01c2\7g\2\2\u01c2\u01c3\7v\2"+ + "\2\u01c3\u01c4\7w\2\2\u01c4\u01c5\7t\2\2\u01c5\u01c6\7p\2\2\u01c6J\3\2"+ + "\2\2\u01c7\u01c8\7d\2\2\u01c8\u01c9\7t\2\2\u01c9\u01ca\7g\2\2\u01ca\u01cb"+ + "\7c\2\2\u01cb\u01cc\7m\2\2\u01ccL\3\2\2\2\u01cd\u01ce\7e\2\2\u01ce\u01cf"+ + "\7q\2\2\u01cf\u01d0\7p\2\2\u01d0\u01d1\7v\2\2\u01d1\u01d2\7k\2\2\u01d2"+ + "\u01d3\7p\2\2\u01d3\u01d4\7w\2\2\u01d4\u01d5\7g\2\2\u01d5N\3\2\2\2\u01d6"+ + "\u01d7\7c\2\2\u01d7\u01d8\7u\2\2\u01d8\u01d9\7o\2\2\u01d9P\3\2\2\2\u01da"+ + "\u01db\7f\2\2\u01db\u01dc\7g\2\2\u01dc\u01dd\7h\2\2\u01dd\u01de\7c\2\2"+ + "\u01de\u01df\7w\2\2\u01df\u01e0\7n\2\2\u01e0\u01e1\7v\2\2\u01e1\u01e2"+ + "\7<\2\2\u01e2R\3\2\2\2\u01e3\u01e4\7e\2\2\u01e4\u01e5\7c\2\2\u01e5\u01e6"+ + "\7u\2\2\u01e6\u01e7\7g\2\2\u01e7T\3\2\2\2\u01e8\u01e9\7<\2\2\u01e9V\3"+ + "\2\2\2\u01ea\u01eb\7\60\2\2\u01eb\u01ec\7\60\2\2\u01ecX\3\2\2\2\u01ed"+ + "\u01ee\7u\2\2\u01ee\u01ef\7k\2\2\u01ef\u01f0\7i\2\2\u01f0\u01f1\7p\2\2"+ + "\u01f1\u01f2\7g\2\2\u01f2\u01f3\7f\2\2\u01f3Z\3\2\2\2\u01f4\u01f5\7w\2"+ + "\2\u01f5\u01f6\7p\2\2\u01f6\u01f7\7u\2\2\u01f7\u01f8\7k\2\2\u01f8\u01f9"+ + "\7i\2\2\u01f9\u01fa\7p\2\2\u01fa\u01fb\7g\2\2\u01fb\u01fc\7f\2\2\u01fc"+ + "\\\3\2\2\2\u01fd\u01fe\7,\2\2\u01fe^\3\2\2\2\u01ff\u0200\7]\2\2\u0200"+ + "`\3\2\2\2\u0201\u0202\7_\2\2\u0202b\3\2\2\2\u0203\u0204\7u\2\2\u0204\u0205"+ + "\7v\2\2\u0205\u0206\7t\2\2\u0206\u0207\7w\2\2\u0207\u0208\7e\2\2\u0208"+ + "\u0209\7v\2\2\u0209d\3\2\2\2\u020a\u020b\7g\2\2\u020b\u020c\7p\2\2\u020c"+ + "\u020d\7w\2\2\u020d\u020e\7o\2\2\u020ef\3\2\2\2\u020f\u0210\7\60\2\2\u0210"+ + "h\3\2\2\2\u0211\u0212\7/\2\2\u0212\u0213\7@\2\2\u0213j\3\2\2\2\u0214\u0215"+ + "\7u\2\2\u0215\u0216\7k\2\2\u0216\u0217\7|\2\2\u0217\u0218\7g\2\2\u0218"+ + "\u0219\7q\2\2\u0219\u021a\7h\2\2\u021al\3\2\2\2\u021b\u021c\7v\2\2\u021c"+ + "\u021d\7{\2\2\u021d\u021e\7r\2\2\u021e\u021f\7g\2\2\u021f\u0220\7k\2\2"+ + "\u0220\u0221\7f\2\2\u0221n\3\2\2\2\u0222\u0223\7/\2\2\u0223\u0224\7/\2"+ + "\2\u0224p\3\2\2\2\u0225\u0226\7-\2\2\u0226\u0227\7-\2\2\u0227r\3\2\2\2"+ + "\u0228\u0229\7-\2\2\u0229t\3\2\2\2\u022a\u022b\7/\2\2\u022bv\3\2\2\2\u022c"+ + "\u022d\7#\2\2\u022dx\3\2\2\2\u022e\u022f\7(\2\2\u022fz\3\2\2\2\u0230\u0231"+ + "\7\u0080\2\2\u0231|\3\2\2\2\u0232\u0233\7@\2\2\u0233\u0234\7@\2\2\u0234"+ + "~\3\2\2\2\u0235\u0236\7>\2\2\u0236\u0237\7>\2\2\u0237\u0080\3\2\2\2\u0238"+ + "\u0239\7\61\2\2\u0239\u0082\3\2\2\2\u023a\u023b\7\'\2\2\u023b\u0084\3"+ + "\2\2\2\u023c\u023d\7>\2\2\u023d\u0086\3\2\2\2\u023e\u023f\7@\2\2\u023f"+ + "\u0088\3\2\2\2\u0240\u0241\7?\2\2\u0241\u0242\7?\2\2\u0242\u008a\3\2\2"+ + "\2\u0243\u0244\7#\2\2\u0244\u0245\7?\2\2\u0245\u008c\3\2\2\2\u0246\u0247"+ + "\7>\2\2\u0247\u0248\7?\2\2\u0248\u008e\3\2\2\2\u0249\u024a\7@\2\2\u024a"+ + "\u024b\7?\2\2\u024b\u0090\3\2\2\2\u024c\u024d\7`\2\2\u024d\u0092\3\2\2"+ + "\2\u024e\u024f\7~\2\2\u024f\u0094\3\2\2\2\u0250\u0251\7(\2\2\u0251\u0252"+ + "\7(\2\2\u0252\u0096\3\2\2\2\u0253\u0254\7~\2\2\u0254\u0255\7~\2\2\u0255"+ + "\u0098\3\2\2\2\u0256\u0257\7A\2\2\u0257\u009a\3\2\2\2\u0258\u0259\7-\2"+ + "\2\u0259\u025a\7?\2\2\u025a\u009c\3\2\2\2\u025b\u025c\7/\2\2\u025c\u025d"+ + "\7?\2\2\u025d\u009e\3\2\2\2\u025e\u025f\7,\2\2\u025f\u0260\7?\2\2\u0260"+ + "\u00a0\3\2\2\2\u0261\u0262\7\61\2\2\u0262\u0263\7?\2\2\u0263\u00a2\3\2"+ + "\2\2\u0264\u0265\7\'\2\2\u0265\u0266\7?\2\2\u0266\u00a4\3\2\2\2\u0267"+ + "\u0268\7>\2\2\u0268\u0269\7>\2\2\u0269\u026a\7?\2\2\u026a\u00a6\3\2\2"+ + "\2\u026b\u026c\7@\2\2\u026c\u026d\7@\2\2\u026d\u026e\7?\2\2\u026e\u00a8"+ + "\3\2\2\2\u026f\u0270\7(\2\2\u0270\u0271\7?\2\2\u0271\u00aa\3\2\2\2\u0272"+ + "\u0273\7~\2\2\u0273\u0274\7?\2\2\u0274\u00ac\3\2\2\2\u0275\u0276\7`\2"+ + "\2\u0276\u0277\7?\2\2\u0277\u00ae\3\2\2\2\u0278\u0279\7m\2\2\u0279\u027a"+ + "\7k\2\2\u027a\u027b\7e\2\2\u027b\u027c\7m\2\2\u027c\u027d\7c\2\2\u027d"+ + "\u027e\7u\2\2\u027e\u027f\7o\2\2\u027f\u00b0\3\2\2\2\u0280\u0281\7t\2"+ + "\2\u0281\u0282\7g\2\2\u0282\u0283\7u\2\2\u0283\u0284\7q\2\2\u0284\u0285"+ + "\7w\2\2\u0285\u0286\7t\2\2\u0286\u0287\7e\2\2\u0287\u0288\7g\2\2\u0288"+ + "\u00b2\3\2\2\2\u0289\u028a\7w\2\2\u028a\u028b\7u\2\2\u028b\u028c\7g\2"+ + "\2\u028c\u028d\7u\2\2\u028d\u00b4\3\2\2\2\u028e\u028f\7e\2\2\u028f\u0290"+ + "\7n\2\2\u0290\u0291\7q\2\2\u0291\u0292\7d\2\2\u0292\u0293\7d\2\2\u0293"+ + "\u0294\7g\2\2\u0294\u0295\7t\2\2\u0295\u0296\7u\2\2\u0296\u00b6\3\2\2"+ + "\2\u0297\u0298\7d\2\2\u0298\u0299\7{\2\2\u0299\u029a\7v\2\2\u029a\u029b"+ + "\7g\2\2\u029b\u029c\7u\2\2\u029c\u00b8\3\2\2\2\u029d\u029e\7e\2\2\u029e"+ + "\u029f\7{\2\2\u029f\u02a0\7e\2\2\u02a0\u02a1\7n\2\2\u02a1\u02a2\7g\2\2"+ + "\u02a2\u02a3\7u\2\2\u02a3\u00ba\3\2\2\2\u02a4\u02a5\7\60\2\2\u02a5\u02a6"+ + "\7d\2\2\u02a6\u02a7\7{\2\2\u02a7\u02a8\7v\2\2\u02a8\u02a9\7g\2\2\u02a9"+ + "\u00bc\3\2\2\2\u02aa\u02ab\7%\2\2\u02ab\u00be\3\2\2\2\u02ac\u02ad\7d\2"+ + "\2\u02ad\u02ae\7t\2\2\u02ae\u038b\7m\2\2\u02af\u02b0\7q\2\2\u02b0\u02b1"+ + "\7t\2\2\u02b1\u038b\7c\2\2\u02b2\u02b3\7m\2\2\u02b3\u02b4\7k\2\2\u02b4"+ + "\u038b\7n\2\2\u02b5\u02b6\7u\2\2\u02b6\u02b7\7n\2\2\u02b7\u038b\7q\2\2"+ + "\u02b8\u02b9\7p\2\2\u02b9\u02ba\7q\2\2\u02ba\u038b\7r\2\2\u02bb\u02bc"+ + "\7c\2\2\u02bc\u02bd\7u\2\2\u02bd\u038b\7n\2\2\u02be\u02bf\7r\2\2\u02bf"+ + "\u02c0\7j\2\2\u02c0\u038b\7r\2\2\u02c1\u02c2\7c\2\2\u02c2\u02c3\7p\2\2"+ + "\u02c3\u038b\7e\2\2\u02c4\u02c5\7d\2\2\u02c5\u02c6\7r\2\2\u02c6\u038b"+ + "\7n\2\2\u02c7\u02c8\7e\2\2\u02c8\u02c9\7n\2\2\u02c9\u038b\7e\2\2\u02ca"+ + "\u02cb\7l\2\2\u02cb\u02cc\7u\2\2\u02cc\u038b\7t\2\2\u02cd\u02ce\7c\2\2"+ + "\u02ce\u02cf\7p\2\2\u02cf\u038b\7f\2\2\u02d0\u02d1\7t\2\2\u02d1\u02d2"+ + "\7n\2\2\u02d2\u038b\7c\2\2\u02d3\u02d4\7d\2\2\u02d4\u02d5\7k\2\2\u02d5"+ + "\u038b\7v\2\2\u02d6\u02d7\7t\2\2\u02d7\u02d8\7q\2\2\u02d8\u038b\7n\2\2"+ + "\u02d9\u02da\7r\2\2\u02da\u02db\7n\2\2\u02db\u038b\7c\2\2\u02dc\u02dd"+ + "\7r\2\2\u02dd\u02de\7n\2\2\u02de\u038b\7r\2\2\u02df\u02e0\7d\2\2\u02e0"+ + "\u02e1\7o\2\2\u02e1\u038b\7k\2\2\u02e2\u02e3\7u\2\2\u02e3\u02e4\7g\2\2"+ + "\u02e4\u038b\7e\2\2\u02e5\u02e6\7t\2\2\u02e6\u02e7\7v\2\2\u02e7\u038b"+ + "\7k\2\2\u02e8\u02e9\7g\2\2\u02e9\u02ea\7q\2\2\u02ea\u038b\7t\2\2\u02eb"+ + "\u02ec\7u\2\2\u02ec\u02ed\7t\2\2\u02ed\u038b\7g\2\2\u02ee\u02ef\7n\2\2"+ + "\u02ef\u02f0\7u\2\2\u02f0\u038b\7t\2\2\u02f1\u02f2\7r\2\2\u02f2\u02f3"+ + "\7j\2\2\u02f3\u038b\7c\2\2\u02f4\u02f5\7c\2\2\u02f5\u02f6\7n\2\2\u02f6"+ + "\u038b\7t\2\2\u02f7\u02f8\7l\2\2\u02f8\u02f9\7o\2\2\u02f9\u038b\7r\2\2"+ + "\u02fa\u02fb\7d\2\2\u02fb\u02fc\7x\2\2\u02fc\u038b\7e\2\2\u02fd\u02fe"+ + "\7e\2\2\u02fe\u02ff\7n\2\2\u02ff\u038b\7k\2\2\u0300\u0301\7t\2\2\u0301"+ + "\u0302\7v\2\2\u0302\u038b\7u\2\2\u0303\u0304\7c\2\2\u0304\u0305\7f\2\2"+ + "\u0305\u038b\7e\2\2\u0306\u0307\7t\2\2\u0307\u0308\7t\2\2\u0308\u038b"+ + "\7c\2\2\u0309\u030a\7d\2\2\u030a\u030b\7x\2\2\u030b\u038b\7u\2\2\u030c"+ + "\u030d\7u\2\2\u030d\u030e\7g\2\2\u030e\u038b\7k\2\2\u030f\u0310\7u\2\2"+ + "\u0310\u0311\7c\2\2\u0311\u038b\7z\2\2\u0312\u0313\7u\2\2\u0313\u0314"+ + "\7v\2\2\u0314\u038b\7{\2\2\u0315\u0316\7u\2\2\u0316\u0317\7v\2\2\u0317"+ + "\u038b\7c\2\2\u0318\u0319\7u\2\2\u0319\u031a\7v\2\2\u031a\u038b\7z\2\2"+ + "\u031b\u031c\7f\2\2\u031c\u031d\7g\2\2\u031d\u038b\7{\2\2\u031e\u031f"+ + "\7v\2\2\u031f\u0320\7z\2\2\u0320\u038b\7c\2\2\u0321\u0322\7z\2\2\u0322"+ + "\u0323\7c\2\2\u0323\u038b\7c\2\2\u0324\u0325\7d\2\2\u0325\u0326\7e\2\2"+ + "\u0326\u038b\7e\2\2\u0327\u0328\7c\2\2\u0328\u0329\7j\2\2\u0329\u038b"+ + "\7z\2\2\u032a\u032b\7v\2\2\u032b\u032c\7{\2\2\u032c\u038b\7c\2\2\u032d"+ + "\u032e\7v\2\2\u032e\u032f\7z\2\2\u032f\u038b\7u\2\2\u0330\u0331\7v\2\2"+ + "\u0331\u0332\7c\2\2\u0332\u038b\7u\2\2\u0333\u0334\7u\2\2\u0334\u0335"+ + "\7j\2\2\u0335\u038b\7{\2\2\u0336\u0337\7u\2\2\u0337\u0338\7j\2\2\u0338"+ + "\u038b\7z\2\2\u0339\u033a\7n\2\2\u033a\u033b\7f\2\2\u033b\u038b\7{\2\2"+ + "\u033c\u033d\7n\2\2\u033d\u033e\7f\2\2\u033e\u038b\7c\2\2\u033f\u0340"+ + "\7n\2\2\u0340\u0341\7f\2\2\u0341\u038b\7z\2\2\u0342\u0343\7n\2\2\u0343"+ + "\u0344\7c\2\2\u0344\u038b\7z\2\2\u0345\u0346\7v\2\2\u0346\u0347\7c\2\2"+ + "\u0347\u038b\7{\2\2\u0348\u0349\7v\2\2\u0349\u034a\7c\2\2\u034a\u038b"+ + "\7z\2\2\u034b\u034c\7d\2\2\u034c\u034d\7e\2\2\u034d\u038b\7u\2\2\u034e"+ + "\u034f\7e\2\2\u034f\u0350\7n\2\2\u0350\u038b\7x\2\2\u0351\u0352\7v\2\2"+ + "\u0352\u0353\7u\2\2\u0353\u038b\7z\2\2\u0354\u0355\7n\2\2\u0355\u0356"+ + "\7c\2\2\u0356\u038b\7u\2\2\u0357\u0358\7e\2\2\u0358\u0359\7r\2\2\u0359"+ + "\u038b\7{\2\2\u035a\u035b\7e\2\2\u035b\u035c\7o\2\2\u035c\u038b\7r\2\2"+ + "\u035d\u035e\7e\2\2\u035e\u035f\7r\2\2\u035f\u038b\7z\2\2\u0360\u0361"+ + "\7f\2\2\u0361\u0362\7e\2\2\u0362\u038b\7r\2\2\u0363\u0364\7f\2\2\u0364"+ + "\u0365\7g\2\2\u0365\u038b\7e\2\2\u0366\u0367\7k\2\2\u0367\u0368\7p\2\2"+ + "\u0368\u038b\7e\2\2\u0369\u036a\7c\2\2\u036a\u036b\7z\2\2\u036b\u038b"+ + "\7u\2\2\u036c\u036d\7d\2\2\u036d\u036e\7p\2\2\u036e\u038b\7g\2\2\u036f"+ + "\u0370\7e\2\2\u0370\u0371\7n\2\2\u0371\u038b\7f\2\2\u0372\u0373\7u\2\2"+ + "\u0373\u0374\7d\2\2\u0374\u038b\7e\2\2\u0375\u0376\7k\2\2\u0376\u0377"+ + "\7u\2\2\u0377\u038b\7e\2\2\u0378\u0379\7k\2\2\u0379\u037a\7p\2\2\u037a"+ + "\u038b\7z\2\2\u037b\u037c\7d\2\2\u037c\u037d\7g\2\2\u037d\u038b\7s\2\2"+ + "\u037e\u037f\7u\2\2\u037f\u0380\7g\2\2\u0380\u038b\7f\2\2\u0381\u0382"+ + "\7f\2\2\u0382\u0383\7g\2\2\u0383\u038b\7z\2\2\u0384\u0385\7k\2\2\u0385"+ + "\u0386\7p\2\2\u0386\u038b\7{\2\2\u0387\u0388\7t\2\2\u0388\u0389\7q\2\2"+ + "\u0389\u038b\7t\2\2\u038a\u02ac\3\2\2\2\u038a\u02af\3\2\2\2\u038a\u02b2"+ + "\3\2\2\2\u038a\u02b5\3\2\2\2\u038a\u02b8\3\2\2\2\u038a\u02bb\3\2\2\2\u038a"+ + "\u02be\3\2\2\2\u038a\u02c1\3\2\2\2\u038a\u02c4\3\2\2\2\u038a\u02c7\3\2"+ + "\2\2\u038a\u02ca\3\2\2\2\u038a\u02cd\3\2\2\2\u038a\u02d0\3\2\2\2\u038a"+ + "\u02d3\3\2\2\2\u038a\u02d6\3\2\2\2\u038a\u02d9\3\2\2\2\u038a\u02dc\3\2"+ + "\2\2\u038a\u02df\3\2\2\2\u038a\u02e2\3\2\2\2\u038a\u02e5\3\2\2\2\u038a"+ + "\u02e8\3\2\2\2\u038a\u02eb\3\2\2\2\u038a\u02ee\3\2\2\2\u038a\u02f1\3\2"+ + "\2\2\u038a\u02f4\3\2\2\2\u038a\u02f7\3\2\2\2\u038a\u02fa\3\2\2\2\u038a"+ + "\u02fd\3\2\2\2\u038a\u0300\3\2\2\2\u038a\u0303\3\2\2\2\u038a\u0306\3\2"+ + "\2\2\u038a\u0309\3\2\2\2\u038a\u030c\3\2\2\2\u038a\u030f\3\2\2\2\u038a"+ + "\u0312\3\2\2\2\u038a\u0315\3\2\2\2\u038a\u0318\3\2\2\2\u038a\u031b\3\2"+ + "\2\2\u038a\u031e\3\2\2\2\u038a\u0321\3\2\2\2\u038a\u0324\3\2\2\2\u038a"+ + "\u0327\3\2\2\2\u038a\u032a\3\2\2\2\u038a\u032d\3\2\2\2\u038a\u0330\3\2"+ + "\2\2\u038a\u0333\3\2\2\2\u038a\u0336\3\2\2\2\u038a\u0339\3\2\2\2\u038a"+ + "\u033c\3\2\2\2\u038a\u033f\3\2\2\2\u038a\u0342\3\2\2\2\u038a\u0345\3\2"+ + "\2\2\u038a\u0348\3\2\2\2\u038a\u034b\3\2\2\2\u038a\u034e\3\2\2\2\u038a"+ + "\u0351\3\2\2\2\u038a\u0354\3\2\2\2\u038a\u0357\3\2\2\2\u038a\u035a\3\2"+ + "\2\2\u038a\u035d\3\2\2\2\u038a\u0360\3\2\2\2\u038a\u0363\3\2\2\2\u038a"+ + "\u0366\3\2\2\2\u038a\u0369\3\2\2\2\u038a\u036c\3\2\2\2\u038a\u036f\3\2"+ + "\2\2\u038a\u0372\3\2\2\2\u038a\u0375\3\2\2\2\u038a\u0378\3\2\2\2\u038a"+ + "\u037b\3\2\2\2\u038a\u037e\3\2\2\2\u038a\u0381\3\2\2\2\u038a\u0384\3\2"+ + "\2\2\u038a\u0387\3\2\2\2\u038b\u00c0\3\2\2\2\u038c\u038d\7}\2\2\u038d"+ + "\u038e\7}\2\2\u038e\u0392\3\2\2\2\u038f\u0391\13\2\2\2\u0390\u038f\3\2"+ + "\2\2\u0391\u0394\3\2\2\2\u0392\u0393\3\2\2\2\u0392\u0390\3\2\2\2\u0393"+ + "\u0395\3\2\2\2\u0394\u0392\3\2\2\2\u0395\u0396\7\177\2\2\u0396\u0397\7"+ + "\177\2\2\u0397\u00c2\3\2\2\2\u0398\u0399\7d\2\2\u0399\u039a\7{\2\2\u039a"+ + "\u039b\7v\2\2\u039b\u03be\7g\2\2\u039c\u039d\7y\2\2\u039d\u039e\7q\2\2"+ + "\u039e\u039f\7t\2\2\u039f\u03be\7f\2\2\u03a0\u03a1\7f\2\2\u03a1\u03a2"+ + "\7y\2\2\u03a2\u03a3\7q\2\2\u03a3\u03a4\7t\2\2\u03a4\u03be\7f\2\2\u03a5"+ + "\u03a6\7d\2\2\u03a6\u03a7\7q\2\2\u03a7\u03a8\7q\2\2\u03a8\u03be\7n\2\2"+ + "\u03a9\u03aa\7e\2\2\u03aa\u03ab\7j\2\2\u03ab\u03ac\7c\2\2\u03ac\u03be"+ + "\7t\2\2\u03ad\u03ae\7u\2\2\u03ae\u03af\7j\2\2\u03af\u03b0\7q\2\2\u03b0"+ + "\u03b1\7t\2\2\u03b1\u03be\7v\2\2\u03b2\u03b3\7k\2\2\u03b3\u03b4\7p\2\2"+ + "\u03b4\u03be\7v\2\2\u03b5\u03b6\7n\2\2\u03b6\u03b7\7q\2\2\u03b7\u03b8"+ + "\7p\2\2\u03b8\u03be\7i\2\2\u03b9\u03ba\7x\2\2\u03ba\u03bb\7q\2\2\u03bb"+ + "\u03bc\7k\2\2\u03bc\u03be\7f\2\2\u03bd\u0398\3\2\2\2\u03bd\u039c\3\2\2"+ + "\2\u03bd\u03a0\3\2\2\2\u03bd\u03a5\3\2\2\2\u03bd\u03a9\3\2\2\2\u03bd\u03ad"+ + "\3\2\2\2\u03bd\u03b2\3\2\2\2\u03bd\u03b5\3\2\2\2\u03bd\u03b9\3\2\2\2\u03be"+ + "\u00c4\3\2\2\2\u03bf\u03c5\7$\2\2\u03c0\u03c1\7^\2\2\u03c1\u03c4\7$\2"+ + "\2\u03c2\u03c4\n\2\2\2\u03c3\u03c0\3\2\2\2\u03c3\u03c2\3\2\2\2\u03c4\u03c7"+ + "\3\2\2\2\u03c5\u03c3\3\2\2\2\u03c5\u03c6\3\2\2\2\u03c6\u03c8\3\2\2\2\u03c7"+ + "\u03c5\3\2\2\2\u03c8\u03ca\7$\2\2\u03c9\u03cb\t\3\2\2\u03ca\u03c9\3\2"+ + "\2\2\u03ca\u03cb\3\2\2\2\u03cb\u03d0\3\2\2\2\u03cc\u03ce\t\4\2\2\u03cd"+ + "\u03cf\t\5\2\2\u03ce\u03cd\3\2\2\2\u03ce\u03cf\3\2\2\2\u03cf\u03d1\3\2"+ + "\2\2\u03d0\u03cc\3\2\2\2\u03d0\u03d1\3\2\2\2\u03d1\u03d3\3\2\2\2\u03d2"+ + "\u03d4\t\3\2\2\u03d3\u03d2\3\2\2\2\u03d3\u03d4\3\2\2\2\u03d4\u00c6\3\2"+ + "\2\2\u03d5\u03d9\7)\2\2\u03d6\u03d7\7^\2\2\u03d7\u03da\t\6\2\2\u03d8\u03da"+ + "\n\7\2\2\u03d9\u03d6\3\2\2\2\u03d9\u03d8\3\2\2\2\u03da\u03db\3\2\2\2\u03db"+ + "\u03dc\7)\2\2\u03dc\u00c8\3\2\2\2\u03dd\u03de\7v\2\2\u03de\u03df\7t\2"+ + "\2\u03df\u03e0\7w\2\2\u03e0\u03e7\7g\2\2\u03e1\u03e2\7h\2\2\u03e2\u03e3"+ + "\7c\2\2\u03e3\u03e4\7n\2\2\u03e4\u03e5\7u\2\2\u03e5\u03e7\7g\2\2\u03e6"+ + "\u03dd\3\2\2\2\u03e6\u03e1\3\2\2\2\u03e7\u00ca\3\2\2\2\u03e8\u03eb\5\u00cd"+ + "g\2\u03e9\u03eb\5\u00d5k\2\u03ea\u03e8\3\2\2\2\u03ea\u03e9\3\2\2\2\u03eb"+ "\u00cc\3\2\2\2\u03ec\u03f0\5\u00cfh\2\u03ed\u03f0\5\u00d1i\2\u03ee\u03f0"+ "\5\u00d3j\2\u03ef\u03ec\3\2\2\2\u03ef\u03ed\3\2\2\2\u03ef\u03ee\3\2\2"+ "\2\u03f0\u00ce\3\2\2\2\u03f1\u03f7\7\'\2\2\u03f2\u03f3\7\62\2\2\u03f3"+ @@ -525,10 +525,10 @@ public class KickCLexer extends Lexer { "\u00e1q\2\u041f\u041e\3\2\2\2\u0420\u0421\3\2\2\2\u0421\u041f\3\2\2\2"+ "\u0421\u0422\3\2\2\2\u0422\u00d4\3\2\2\2\u0423\u0427\5\u00d9m\2\u0424"+ "\u0427\5\u00dbn\2\u0425\u0427\5\u00d7l\2\u0426\u0423\3\2\2\2\u0426\u0424"+ - "\3\2\2\2\u0426\u0425\3\2\2\2\u0427\u042b\3\2\2\2\u0428\u0429\t\7\2\2\u0429"+ - "\u042c\t\b\2\2\u042a\u042c\7n\2\2\u042b\u0428\3\2\2\2\u042b\u042a\3\2"+ + "\3\2\2\2\u0426\u0425\3\2\2\2\u0427\u042b\3\2\2\2\u0428\u0429\t\b\2\2\u0429"+ + "\u042c\t\t\2\2\u042a\u042c\7n\2\2\u042b\u0428\3\2\2\2\u042b\u042a\3\2"+ "\2\2\u042b\u042c\3\2\2\2\u042c\u00d6\3\2\2\2\u042d\u042e\7\62\2\2\u042e"+ - "\u0430\t\t\2\2\u042f\u0431\5\u00ddo\2\u0430\u042f\3\2\2\2\u0431\u0432"+ + "\u0430\t\n\2\2\u042f\u0431\5\u00ddo\2\u0430\u042f\3\2\2\2\u0431\u0432"+ "\3\2\2\2\u0432\u0430\3\2\2\2\u0432\u0433\3\2\2\2\u0433\u043b\3\2\2\2\u0434"+ "\u0436\7\'\2\2\u0435\u0437\5\u00ddo\2\u0436\u0435\3\2\2\2\u0437\u0438"+ "\3\2\2\2\u0438\u0436\3\2\2\2\u0438\u0439\3\2\2\2\u0439\u043b\3\2\2\2\u043a"+ @@ -538,20 +538,20 @@ public class KickCLexer extends Lexer { "\2\2\u0443\u0447\7z\2\2\u0444\u0445\7\62\2\2\u0445\u0447\7Z\2\2\u0446"+ "\u0441\3\2\2\2\u0446\u0442\3\2\2\2\u0446\u0444\3\2\2\2\u0447\u0449\3\2"+ "\2\2\u0448\u044a\5\u00e1q\2\u0449\u0448\3\2\2\2\u044a\u044b\3\2\2\2\u044b"+ - "\u0449\3\2\2\2\u044b\u044c\3\2\2\2\u044c\u00dc\3\2\2\2\u044d\u044e\t\n"+ - "\2\2\u044e\u00de\3\2\2\2\u044f\u0450\t\13\2\2\u0450\u00e0\3\2\2\2\u0451"+ - "\u0452\t\f\2\2\u0452\u00e2\3\2\2\2\u0453\u0457\5\u00e5s\2\u0454\u0456"+ + "\u0449\3\2\2\2\u044b\u044c\3\2\2\2\u044c\u00dc\3\2\2\2\u044d\u044e\t\13"+ + "\2\2\u044e\u00de\3\2\2\2\u044f\u0450\t\f\2\2\u0450\u00e0\3\2\2\2\u0451"+ + "\u0452\t\r\2\2\u0452\u00e2\3\2\2\2\u0453\u0457\5\u00e5s\2\u0454\u0456"+ "\5\u00e7t\2\u0455\u0454\3\2\2\2\u0456\u0459\3\2\2\2\u0457\u0455\3\2\2"+ "\2\u0457\u0458\3\2\2\2\u0458\u00e4\3\2\2\2\u0459\u0457\3\2\2\2\u045a\u045b"+ - "\t\r\2\2\u045b\u00e6\3\2\2\2\u045c\u045d\t\16\2\2\u045d\u00e8\3\2\2\2"+ + "\t\16\2\2\u045b\u00e6\3\2\2\2\u045c\u045d\t\17\2\2\u045d\u00e8\3\2\2\2"+ "\u045e\u0462\7#\2\2\u045f\u0461\5\u00e7t\2\u0460\u045f\3\2\2\2\u0461\u0464"+ "\3\2\2\2\u0462\u0460\3\2\2\2\u0462\u0463\3\2\2\2\u0463\u0466\3\2\2\2\u0464"+ - "\u0462\3\2\2\2\u0465\u0467\t\17\2\2\u0466\u0465\3\2\2\2\u0467\u0468\3"+ + "\u0462\3\2\2\2\u0465\u0467\t\20\2\2\u0466\u0465\3\2\2\2\u0467\u0468\3"+ "\2\2\2\u0468\u0466\3\2\2\2\u0468\u0469\3\2\2\2\u0469\u00ea\3\2\2\2\u046a"+ - "\u046c\t\20\2\2\u046b\u046a\3\2\2\2\u046c\u046d\3\2\2\2\u046d\u046b\3"+ + "\u046c\t\21\2\2\u046b\u046a\3\2\2\2\u046c\u046d\3\2\2\2\u046d\u046b\3"+ "\2\2\2\u046d\u046e\3\2\2\2\u046e\u046f\3\2\2\2\u046f\u0470\bv\2\2\u0470"+ "\u00ec\3\2\2\2\u0471\u0472\7\61\2\2\u0472\u0473\7\61\2\2\u0473\u0477\3"+ - "\2\2\2\u0474\u0476\n\21\2\2\u0475\u0474\3\2\2\2\u0476\u0479\3\2\2\2\u0477"+ + "\2\2\2\u0474\u0476\n\22\2\2\u0475\u0474\3\2\2\2\u0476\u0479\3\2\2\2\u0477"+ "\u0475\3\2\2\2\u0477\u0478\3\2\2\2\u0478\u047a\3\2\2\2\u0479\u0477\3\2"+ "\2\2\u047a\u047b\bw\3\2\u047b\u00ee\3\2\2\2\u047c\u047d\7\61\2\2\u047d"+ "\u047e\7,\2\2\u047e\u0482\3\2\2\2\u047f\u0481\13\2\2\2\u0480\u047f\3\2"+ diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java index e8662e9e2..313aa25c0 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java @@ -4,7 +4,6 @@ import dk.camelot64.kickc.Compiler; import dk.camelot64.kickc.NumberParser; import dk.camelot64.kickc.asm.AsmClobber; import dk.camelot64.kickc.model.*; -import dk.camelot64.kickc.model.InternalError; import dk.camelot64.kickc.model.operators.*; import dk.camelot64.kickc.model.statements.*; import dk.camelot64.kickc.model.symbols.*; @@ -1700,11 +1699,11 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { } /** The current string encoding used if no explicit encoding is specified. */ - ConstantString.Encoding currentEncoding = ConstantString.Encoding.SCREENCODE_MIXED; + private ConstantString.Encoding currentEncoding = ConstantString.Encoding.SCREENCODE_MIXED; @Override public RValue visitExprString(KickCParser.ExprStringContext ctx) { - String stringValue = ""; + StringBuilder stringValue = new StringBuilder(); String subText; String lastSuffix = ""; ConstantString.Encoding encoding = null; @@ -1719,10 +1718,15 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { encoding = suffixEncoding; } lastSuffix = suffix; - stringValue += subText.substring(1, subText.lastIndexOf('"')); + stringValue.append(subText, 1, subText.lastIndexOf('"')); } boolean zeroTerminated = !lastSuffix.contains("z"); - return new ConstantString(stringValue, encoding, zeroTerminated); + try { + return new ConstantString(ConstantString.stringEscapeToAscii(stringValue.toString()), encoding, zeroTerminated); + } catch(CompileError e) { + // Rethrow - adding statement context! + throw new CompileError(e.getMessage(), new StatementSource(ctx)); + } } /** @@ -1757,7 +1761,15 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { @Override public Object visitExprChar(KickCParser.ExprCharContext ctx) { - return new ConstantChar(ctx.getText().charAt(1), currentEncoding); + try { + String charText = ctx.getText(); + charText = charText.substring(1, charText.length() - 1); + char constChar = ConstantChar.charEscapeToAscii(charText); + return new ConstantChar(constChar, currentEncoding); + } catch (CompileError e) { + // Rethrow adding source location + throw new CompileError(e.getMessage(), new StatementSource(ctx)); + } } @Override diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureInline.java b/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureInline.java index ca555224c..dd1299a5a 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureInline.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureInline.java @@ -13,6 +13,7 @@ import dk.camelot64.kickc.model.types.SymbolType; import dk.camelot64.kickc.model.values.*; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; import java.util.ListIterator; @@ -193,6 +194,10 @@ public class Pass1ProcedureInline extends Pass1Base { StatementCall inlinedCall = new StatementCall(procCall.getlValue(), procCall.getProcedureName(), new ArrayList<>(procCall.getParameters()), procCall.getSource(), Comment.NO_COMMENTS); inlinedCall.setProcedure(procCall.getProcedure()); inlinedStatement = inlinedCall; + } else if(procStatement instanceof StatementAsm) { + StatementAsm procAsm = (StatementAsm) procStatement; + StatementAsm inlinedAsm = new StatementAsm(procAsm.getAsmBody(), new LinkedHashMap<>(procAsm.getReferenced()), procAsm.getDeclaredClobber(), procAsm.getSource(), Comment.NO_COMMENTS); + inlinedStatement = inlinedAsm; } else if(procStatement instanceof StatementConditionalJump) { StatementConditionalJump procConditional = (StatementConditionalJump) procStatement; LabelRef procDestinationRef = procConditional.getDestination(); diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 813020305..7f96a30ed 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -36,6 +36,31 @@ public class TestPrograms { public TestPrograms() { } + @Test + public void testStringEscapesErr1() throws IOException, URISyntaxException { + assertError("string-escapes-err-1", "Illegal string escape sequence"); + } + + @Test + public void testStringEscapesErr0() throws IOException, URISyntaxException { + assertError("string-escapes-err-0", "Unfinished string escape sequence at end of string"); + } + + @Test + public void testStringEscapes2() throws IOException, URISyntaxException { + compileAndCompare("string-escapes-2"); + } + + @Test + public void testStringEscapes1() throws IOException, URISyntaxException { + compileAndCompare("string-escapes-1"); + } + + @Test + public void testStringEscapes0() throws IOException, URISyntaxException { + compileAndCompare("string-escapes-0"); + } + //@Test //public void testLoopheadProblem() throws IOException, URISyntaxException { // compileAndCompare("loophead-problem"); diff --git a/src/test/kc/string-escapes-0.kc b/src/test/kc/string-escapes-0.kc new file mode 100644 index 000000000..280e1f1ae --- /dev/null +++ b/src/test/kc/string-escapes-0.kc @@ -0,0 +1,10 @@ +// Test using some simple supported string escapes \r \f \n \' \" + +char[] MESSAGE = "\r\f\n\"\'"; +char* SCREEN = 0x0400; + +void main() { + byte i=0; + while(MESSAGE[i]) + SCREEN[i] = MESSAGE[i++]; +} \ No newline at end of file diff --git a/src/test/kc/string-escapes-1.kc b/src/test/kc/string-escapes-1.kc new file mode 100644 index 000000000..a4150a799 --- /dev/null +++ b/src/test/kc/string-escapes-1.kc @@ -0,0 +1,23 @@ +// Test using some simple supported string escape \n in both string and char + +char[] MESSAGE = "hello\nworld"; +char* SCREEN = 0x0400; + +void main() { + byte* line = 0x0400; + byte* cursor = line; + byte* msg = MESSAGE; + + while(*msg) { + switch(*msg) { + case '\n': + line += 0x28; + cursor = line; + break; + default: + *cursor++ = *msg; + } + msg++; + } + +} \ No newline at end of file diff --git a/src/test/kc/string-escapes-2.kc b/src/test/kc/string-escapes-2.kc new file mode 100644 index 000000000..e4a14b6fa --- /dev/null +++ b/src/test/kc/string-escapes-2.kc @@ -0,0 +1,21 @@ +// Test using some simple supported string escape characters in PETSCII + +#pragma encoding(petscii_mixed) + +char[] MESSAGE = "hello\nworld"; + +const char* memA = 0xff; + +void main() { + byte i=0; + while(MESSAGE[i]) + chrout(MESSAGE[i++]); +} + +void chrout(char c) { + *memA = c; + asm { + lda memA + jsr $ffd2 + } +} \ No newline at end of file diff --git a/src/test/kc/string-escapes-err-0.kc b/src/test/kc/string-escapes-err-0.kc new file mode 100644 index 000000000..38ce5d426 --- /dev/null +++ b/src/test/kc/string-escapes-err-0.kc @@ -0,0 +1,11 @@ +// Test errors using string escape sequences +// Unfinished escape at end of string + +char[] MESSAGE = "qwe\"; +char* SCREEN = 0x0400; + +void main() { + byte i=0; + while(MESSAGE[i]) + SCREEN[i] = MESSAGE[i++]; +} \ No newline at end of file diff --git a/src/test/kc/string-escapes-err-1.kc b/src/test/kc/string-escapes-err-1.kc new file mode 100644 index 000000000..e9d03fc63 --- /dev/null +++ b/src/test/kc/string-escapes-err-1.kc @@ -0,0 +1,11 @@ +// Test errors using string escape sequences +// Unsupported escape sequence + +char[] MESSAGE = "qwe\qasd"; +char* SCREEN = 0x0400; + +void main() { + byte i=0; + while(MESSAGE[i]) + SCREEN[i] = MESSAGE[i++]; +} \ No newline at end of file diff --git a/src/test/ref/string-escapes-0.asm b/src/test/ref/string-escapes-0.asm new file mode 100644 index 000000000..79bff9cf6 --- /dev/null +++ b/src/test/ref/string-escapes-0.asm @@ -0,0 +1,20 @@ +// Test using some simple supported string escapes \r \f \n \' \" +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label SCREEN = $400 +main: { + ldy #0 + b1: + lda #0 + cmp MESSAGE,y + bne b2 + rts + b2: + lda MESSAGE,y + sta SCREEN,y + iny + jmp b1 +} + MESSAGE: .text @"\r\f\n\"'" + .byte 0 diff --git a/src/test/ref/string-escapes-0.cfg b/src/test/ref/string-escapes-0.cfg new file mode 100644 index 000000000..b0e0f53e5 --- /dev/null +++ b/src/test/ref/string-escapes-0.cfg @@ -0,0 +1,23 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] phi() + to:main::@1 +main::@1: scope:[main] from main main::@2 + [5] (byte) main::i#2 ← phi( main/(byte) 0 main::@2/(byte) main::i#1 ) + [6] if((byte) 0!=*((const byte[]) MESSAGE#0 + (byte) main::i#2)) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [7] return + to:@return +main::@2: scope:[main] from main::@1 + [8] *((const byte*) SCREEN#0 + (byte) main::i#2) ← *((const byte[]) MESSAGE#0 + (byte) main::i#2) + [9] (byte) main::i#1 ← ++ (byte) main::i#2 + to:main::@1 diff --git a/src/test/ref/string-escapes-0.log b/src/test/ref/string-escapes-0.log new file mode 100644 index 000000000..8350f486c --- /dev/null +++ b/src/test/ref/string-escapes-0.log @@ -0,0 +1,370 @@ +Warning! Adding boolean cast to non-boolean condition *((byte[]) MESSAGE + (byte) main::i) +Identified constant variable (byte*) SCREEN +Culled Empty Block (label) main::@4 +Culled Empty Block (label) main::@3 +Culled Empty Block (label) main::@5 +Culled Empty Block (label) main::@6 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (byte[]) MESSAGE#0 ← (const string) $0 + (byte*) SCREEN#0 ← ((byte*)) (number) $400 + to:@1 +main: scope:[main] from @1 + (byte) main::i#0 ← (number) 0 + to:main::@1 +main::@1: scope:[main] from main main::@2 + (byte) main::i#2 ← phi( main/(byte) main::i#0 main::@2/(byte) main::i#1 ) + (bool~) main::$0 ← (number) 0 != *((byte[]) MESSAGE#0 + (byte) main::i#2) + if((bool~) main::$0) goto main::@2 + to:main::@return +main::@2: scope:[main] from main::@1 + (byte) main::i#3 ← phi( main::@1/(byte) main::i#2 ) + *((byte*) SCREEN#0 + (byte) main::i#3) ← *((byte[]) MESSAGE#0 + (byte) main::i#3) + (byte) main::i#1 ← ++ (byte) main::i#3 + to:main::@1 +main::@return: scope:[main] from main::@1 + return + to:@return +@1: scope:[] from @begin + call main + to:@2 +@2: scope:[] from @1 + to:@end +@end: scope:[] from @2 + +SYMBOL TABLE SSA +(const string) $0 = (string) " +"'" +(label) @1 +(label) @2 +(label) @begin +(label) @end +(byte[]) MESSAGE +(byte[]) MESSAGE#0 +(byte*) SCREEN +(byte*) SCREEN#0 +(void()) main() +(bool~) main::$0 +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i +(byte) main::i#0 +(byte) main::i#1 +(byte) main::i#2 +(byte) main::i#3 + +Adding number conversion cast (unumber) 0 in (byte) main::i#0 ← (number) 0 +Adding number conversion cast (unumber) 0 in (bool~) main::$0 ← (number) 0 != *((byte[]) MESSAGE#0 + (byte) main::i#2) +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (byte*) SCREEN#0 ← (byte*)(number) $400 +Inlining cast (byte) main::i#0 ← (unumber)(number) 0 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast 0 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias (byte) main::i#2 = (byte) main::i#3 +Successful SSA optimization Pass2AliasElimination +Simple Condition (bool~) main::$0 [5] if((byte) 0!=*((byte[]) MESSAGE#0 + (byte) main::i#2)) goto main::@2 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const byte[]) MESSAGE#0 = $0 +Constant (const byte*) SCREEN#0 = (byte*) 1024 +Constant (const byte) main::i#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +Inlining constant with var siblings (const byte) main::i#0 +Constant inlined $0 = (const byte[]) MESSAGE#0 +Constant inlined main::i#0 = (byte) 0 +Successful SSA optimization Pass2ConstantInlining +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +CALL GRAPH +Calls in [] to main:2 + +Created 1 initial phi equivalence classes +Coalesced [11] main::i#4 ← main::i#1 +Coalesced down to 1 phi equivalence classes +Culled Empty Block (label) @2 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] phi() + to:main::@1 +main::@1: scope:[main] from main main::@2 + [5] (byte) main::i#2 ← phi( main/(byte) 0 main::@2/(byte) main::i#1 ) + [6] if((byte) 0!=*((const byte[]) MESSAGE#0 + (byte) main::i#2)) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [7] return + to:@return +main::@2: scope:[main] from main::@1 + [8] *((const byte*) SCREEN#0 + (byte) main::i#2) ← *((const byte[]) MESSAGE#0 + (byte) main::i#2) + [9] (byte) main::i#1 ← ++ (byte) main::i#2 + to:main::@1 + + +VARIABLE REGISTER WEIGHTS +(byte[]) MESSAGE +(byte*) SCREEN +(void()) main() +(byte) main::i +(byte) main::i#1 22.0 +(byte) main::i#2 18.333333333333332 + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +Allocated zp ZP_BYTE:2 [ main::i#2 main::i#1 ] + +INITIAL ASM +Target platform is c64basic + // File Comments +// Test using some simple supported string escapes \r \f \n \' \" + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + .label i = 2 + // [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + // [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta.z i + jmp b1 + // main::@1 + b1: + // [6] if((byte) 0!=*((const byte[]) MESSAGE#0 + (byte) main::i#2)) goto main::@2 -- vbuc1_neq_pbuc2_derefidx_vbuz1_then_la1 + lda #0 + ldy.z i + cmp MESSAGE,y + bne b2 + jmp breturn + // main::@return + breturn: + // [7] return + rts + // main::@2 + b2: + // [8] *((const byte*) SCREEN#0 + (byte) main::i#2) ← *((const byte[]) MESSAGE#0 + (byte) main::i#2) -- pbuc1_derefidx_vbuz1=pbuc2_derefidx_vbuz1 + ldy.z i + lda MESSAGE,y + sta SCREEN,y + // [9] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 + inc.z i + // [5] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + b1_from_b2: + // [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy + jmp b1 +} + // File Data + MESSAGE: .text @"\r\f\n\"'" + .byte 0 + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [6] if((byte) 0!=*((const byte[]) MESSAGE#0 + (byte) main::i#2)) goto main::@2 [ main::i#2 ] ( main:2 [ main::i#2 ] ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Statement [8] *((const byte*) SCREEN#0 + (byte) main::i#2) ← *((const byte[]) MESSAGE#0 + (byte) main::i#2) [ main::i#2 ] ( main:2 [ main::i#2 ] ) always clobbers reg byte a +Statement [6] if((byte) 0!=*((const byte[]) MESSAGE#0 + (byte) main::i#2)) goto main::@2 [ main::i#2 ] ( main:2 [ main::i#2 ] ) always clobbers reg byte a +Statement [8] *((const byte*) SCREEN#0 + (byte) main::i#2) ← *((const byte[]) MESSAGE#0 + (byte) main::i#2) [ main::i#2 ] ( main:2 [ main::i#2 ] ) always clobbers reg byte a +Potential registers zp ZP_BYTE:2 [ main::i#2 main::i#1 ] : zp ZP_BYTE:2 , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 40.33: zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Uplift Scope [] + +Uplifting [main] best 333 combination reg byte y [ main::i#2 main::i#1 ] +Uplifting [] best 333 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test using some simple supported string escapes \r \f \n \' \" + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + // [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + // [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuyy=vbuc1 + ldy #0 + jmp b1 + // main::@1 + b1: + // [6] if((byte) 0!=*((const byte[]) MESSAGE#0 + (byte) main::i#2)) goto main::@2 -- vbuc1_neq_pbuc2_derefidx_vbuyy_then_la1 + lda #0 + cmp MESSAGE,y + bne b2 + jmp breturn + // main::@return + breturn: + // [7] return + rts + // main::@2 + b2: + // [8] *((const byte*) SCREEN#0 + (byte) main::i#2) ← *((const byte[]) MESSAGE#0 + (byte) main::i#2) -- pbuc1_derefidx_vbuyy=pbuc2_derefidx_vbuyy + lda MESSAGE,y + sta SCREEN,y + // [9] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuyy=_inc_vbuyy + iny + // [5] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + b1_from_b2: + // [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy + jmp b1 +} + // File Data + MESSAGE: .text @"\r\f\n\"'" + .byte 0 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b1 +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction b1_from_main: +Removing instruction breturn: +Removing instruction b1_from_b2: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction bbegin: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(byte[]) MESSAGE +(const byte[]) MESSAGE#0 MESSAGE = (string) " +"'" +(byte*) SCREEN +(const byte*) SCREEN#0 SCREEN = (byte*) 1024 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte y 22.0 +(byte) main::i#2 reg byte y 18.333333333333332 + +reg byte y [ main::i#2 main::i#1 ] + + +FINAL ASSEMBLER +Score: 261 + + // File Comments +// Test using some simple supported string escapes \r \f \n \' \" + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @begin + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] call main + // [4] phi from @1 to main [phi:@1->main] + // [3] phi from @1 to @end [phi:@1->@end] + // @end + // main +main: { + // [5] phi from main to main::@1 [phi:main->main::@1] + // [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuyy=vbuc1 + ldy #0 + // main::@1 + b1: + // while(MESSAGE[i]) + // [6] if((byte) 0!=*((const byte[]) MESSAGE#0 + (byte) main::i#2)) goto main::@2 -- vbuc1_neq_pbuc2_derefidx_vbuyy_then_la1 + lda #0 + cmp MESSAGE,y + bne b2 + // main::@return + // } + // [7] return + rts + // main::@2 + b2: + // SCREEN[i] = MESSAGE[i++] + // [8] *((const byte*) SCREEN#0 + (byte) main::i#2) ← *((const byte[]) MESSAGE#0 + (byte) main::i#2) -- pbuc1_derefidx_vbuyy=pbuc2_derefidx_vbuyy + lda MESSAGE,y + sta SCREEN,y + // SCREEN[i] = MESSAGE[i++]; + // [9] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuyy=_inc_vbuyy + iny + // [5] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + // [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy + jmp b1 +} + // File Data + MESSAGE: .text @"\r\f\n\"'" + .byte 0 + diff --git a/src/test/ref/string-escapes-0.sym b/src/test/ref/string-escapes-0.sym new file mode 100644 index 000000000..010d706d9 --- /dev/null +++ b/src/test/ref/string-escapes-0.sym @@ -0,0 +1,17 @@ +(label) @1 +(label) @begin +(label) @end +(byte[]) MESSAGE +(const byte[]) MESSAGE#0 MESSAGE = (string) " +"'" +(byte*) SCREEN +(const byte*) SCREEN#0 SCREEN = (byte*) 1024 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte y 22.0 +(byte) main::i#2 reg byte y 18.333333333333332 + +reg byte y [ main::i#2 main::i#1 ] diff --git a/src/test/ref/string-escapes-1.asm b/src/test/ref/string-escapes-1.asm new file mode 100644 index 000000000..9f6898772 --- /dev/null +++ b/src/test/ref/string-escapes-1.asm @@ -0,0 +1,59 @@ +// Test using some simple supported string escape \n in both string and char +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +main: { + .label cursor = 6 + .label msg = 2 + .label line = 4 + lda #<$400 + sta.z cursor + lda #>$400 + sta.z cursor+1 + lda #<$400 + sta.z line + lda #>$400 + sta.z line+1 + lda #MESSAGE + sta.z msg+1 + b1: + ldy #0 + lda (msg),y + cmp #0 + bne b2 + rts + b2: + lda #-$33 + ldy #0 + cmp (msg),y + beq b3 + lda (msg),y + sta (cursor),y + inc.z cursor + bne !+ + inc.z cursor+1 + !: + b5: + inc.z msg + bne !+ + inc.z msg+1 + !: + jmp b1 + b3: + lda #$28 + clc + adc.z line + sta.z cursor + lda #0 + adc.z line+1 + sta.z cursor+1 + lda.z cursor + sta.z line + lda.z cursor+1 + sta.z line+1 + jmp b5 +} + MESSAGE: .text @"hello\nworld" + .byte 0 diff --git a/src/test/ref/string-escapes-1.cfg b/src/test/ref/string-escapes-1.cfg new file mode 100644 index 000000000..9c705a6dd --- /dev/null +++ b/src/test/ref/string-escapes-1.cfg @@ -0,0 +1,38 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] phi() + to:main::@1 +main::@1: scope:[main] from main main::@5 + [5] (byte*) main::cursor#3 ← phi( main/(byte*) 1024 main::@5/(byte*) main::cursor#6 ) + [5] (byte*) main::line#2 ← phi( main/(byte*) 1024 main::@5/(byte*) main::line#5 ) + [5] (byte*) main::msg#2 ← phi( main/(const byte[]) MESSAGE#0 main::@5/(byte*) main::msg#1 ) + [6] if((byte) 0!=*((byte*) main::msg#2)) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [7] return + to:@return +main::@2: scope:[main] from main::@1 + [8] if(*((byte*) main::msg#2)==(byte) ' +') goto main::@3 + to:main::@4 +main::@4: scope:[main] from main::@2 + [9] *((byte*) main::cursor#3) ← *((byte*) main::msg#2) + [10] (byte*) main::cursor#2 ← ++ (byte*) main::cursor#3 + to:main::@5 +main::@5: scope:[main] from main::@3 main::@4 + [11] (byte*) main::cursor#6 ← phi( main::@3/(byte*) main::cursor#1 main::@4/(byte*) main::cursor#2 ) + [11] (byte*) main::line#5 ← phi( main::@3/(byte*~) main::line#8 main::@4/(byte*) main::line#2 ) + [12] (byte*) main::msg#1 ← ++ (byte*) main::msg#2 + to:main::@1 +main::@3: scope:[main] from main::@2 + [13] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28 + [14] (byte*~) main::line#8 ← (byte*) main::cursor#1 + to:main::@5 diff --git a/src/test/ref/string-escapes-1.log b/src/test/ref/string-escapes-1.log new file mode 100644 index 000000000..08ce2e1f7 --- /dev/null +++ b/src/test/ref/string-escapes-1.log @@ -0,0 +1,663 @@ +Warning! Adding boolean cast to non-boolean condition *((byte*) main::msg) +Identified constant variable (byte*) SCREEN +Culled Empty Block (label) main::@9 +Culled Empty Block (label) main::@3 +Culled Empty Block (label) main::@10 +Culled Empty Block (label) main::@11 +Culled Empty Block (label) main::@12 +Culled Empty Block (label) main::@7 +Culled Empty Block (label) main::@8 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (byte[]) MESSAGE#0 ← (const string) $0 + to:@1 +main: scope:[main] from @1 + (byte*) main::line#0 ← ((byte*)) (number) $400 + (byte*) main::cursor#0 ← (byte*) main::line#0 + (byte*) main::msg#0 ← (byte[]) MESSAGE#0 + to:main::@1 +main::@1: scope:[main] from main main::@6 + (byte*) main::cursor#5 ← phi( main/(byte*) main::cursor#0 main::@6/(byte*) main::cursor#6 ) + (byte*) main::line#4 ← phi( main/(byte*) main::line#0 main::@6/(byte*) main::line#5 ) + (byte*) main::msg#2 ← phi( main/(byte*) main::msg#0 main::@6/(byte*) main::msg#1 ) + (bool~) main::$0 ← (number) 0 != *((byte*) main::msg#2) + if((bool~) main::$0) goto main::@2 + to:main::@return +main::@2: scope:[main] from main::@1 + (byte*) main::cursor#4 ← phi( main::@1/(byte*) main::cursor#5 ) + (byte*) main::line#3 ← phi( main::@1/(byte*) main::line#4 ) + (byte*) main::msg#3 ← phi( main::@1/(byte*) main::msg#2 ) + if(*((byte*) main::msg#3)==(byte) ' +') goto main::@4 + to:main::@5 +main::@4: scope:[main] from main::@2 + (byte*) main::msg#6 ← phi( main::@2/(byte*) main::msg#3 ) + (byte*) main::line#2 ← phi( main::@2/(byte*) main::line#3 ) + (byte*) main::line#1 ← (byte*) main::line#2 + (number) $28 + (byte*) main::cursor#1 ← (byte*) main::line#1 + to:main::@6 +main::@5: scope:[main] from main::@2 + (byte*) main::line#6 ← phi( main::@2/(byte*) main::line#3 ) + (byte*) main::cursor#3 ← phi( main::@2/(byte*) main::cursor#4 ) + (byte*) main::msg#4 ← phi( main::@2/(byte*) main::msg#3 ) + *((byte*) main::cursor#3) ← *((byte*) main::msg#4) + (byte*) main::cursor#2 ← ++ (byte*) main::cursor#3 + to:main::@6 +main::@6: scope:[main] from main::@4 main::@5 + (byte*) main::cursor#6 ← phi( main::@4/(byte*) main::cursor#1 main::@5/(byte*) main::cursor#2 ) + (byte*) main::line#5 ← phi( main::@4/(byte*) main::line#1 main::@5/(byte*) main::line#6 ) + (byte*) main::msg#5 ← phi( main::@4/(byte*) main::msg#6 main::@5/(byte*) main::msg#4 ) + (byte*) main::msg#1 ← ++ (byte*) main::msg#5 + to:main::@1 +main::@return: scope:[main] from main::@1 + return + to:@return +@1: scope:[] from @begin + call main + to:@2 +@2: scope:[] from @1 + to:@end +@end: scope:[] from @2 + +SYMBOL TABLE SSA +(const string) $0 = (string) "hello +world" +(label) @1 +(label) @2 +(label) @begin +(label) @end +(byte[]) MESSAGE +(byte[]) MESSAGE#0 +(void()) main() +(bool~) main::$0 +(label) main::@1 +(label) main::@2 +(label) main::@4 +(label) main::@5 +(label) main::@6 +(label) main::@return +(byte*) main::cursor +(byte*) main::cursor#0 +(byte*) main::cursor#1 +(byte*) main::cursor#2 +(byte*) main::cursor#3 +(byte*) main::cursor#4 +(byte*) main::cursor#5 +(byte*) main::cursor#6 +(byte*) main::line +(byte*) main::line#0 +(byte*) main::line#1 +(byte*) main::line#2 +(byte*) main::line#3 +(byte*) main::line#4 +(byte*) main::line#5 +(byte*) main::line#6 +(byte*) main::msg +(byte*) main::msg#0 +(byte*) main::msg#1 +(byte*) main::msg#2 +(byte*) main::msg#3 +(byte*) main::msg#4 +(byte*) main::msg#5 +(byte*) main::msg#6 + +Adding number conversion cast (unumber) 0 in (bool~) main::$0 ← (number) 0 != *((byte*) main::msg#2) +Adding number conversion cast (unumber) $28 in (byte*) main::line#1 ← (byte*) main::line#2 + (number) $28 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (byte*) main::line#0 ← (byte*)(number) $400 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast 0 +Simplifying constant integer cast $28 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) $28 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias (byte*) main::cursor#0 = (byte*) main::line#0 +Alias (byte*) main::msg#2 = (byte*) main::msg#3 (byte*) main::msg#6 (byte*) main::msg#4 +Alias (byte*) main::line#2 = (byte*) main::line#3 (byte*) main::line#4 (byte*) main::line#6 +Alias (byte*) main::cursor#3 = (byte*) main::cursor#4 (byte*) main::cursor#5 +Alias (byte*) main::cursor#1 = (byte*) main::line#1 +Successful SSA optimization Pass2AliasElimination +Alias (byte*) main::msg#2 = (byte*) main::msg#5 +Successful SSA optimization Pass2AliasElimination +Simple Condition (bool~) main::$0 [6] if((byte) 0!=*((byte*) main::msg#2)) goto main::@2 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const byte[]) MESSAGE#0 = $0 +Constant (const byte*) main::cursor#0 = (byte*) 1024 +Successful SSA optimization Pass2ConstantIdentification +Constant (const byte*) main::msg#0 = MESSAGE#0 +Successful SSA optimization Pass2ConstantIdentification +Inlining constant with var siblings (const byte*) main::cursor#0 +Inlining constant with var siblings (const byte*) main::msg#0 +Constant inlined $0 = (const byte[]) MESSAGE#0 +Constant inlined main::cursor#0 = (byte*) 1024 +Constant inlined main::msg#0 = (const byte[]) MESSAGE#0 +Successful SSA optimization Pass2ConstantInlining +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +CALL GRAPH +Calls in [] to main:2 + +Created 5 initial phi equivalence classes +Coalesced [12] main::line#9 ← main::line#2 +Coalesced [13] main::cursor#9 ← main::cursor#2 +Coalesced [16] main::msg#7 ← main::msg#1 +Coalesced (already) [17] main::line#7 ← main::line#5 +Coalesced [18] main::cursor#7 ← main::cursor#6 +Not coalescing [20] main::line#8 ← main::cursor#1 +Coalesced [21] main::cursor#8 ← main::cursor#1 +Coalesced down to 3 phi equivalence classes +Culled Empty Block (label) @2 +Renumbering block main::@4 to main::@3 +Renumbering block main::@5 to main::@4 +Renumbering block main::@6 to main::@5 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] phi() + to:main::@1 +main::@1: scope:[main] from main main::@5 + [5] (byte*) main::cursor#3 ← phi( main/(byte*) 1024 main::@5/(byte*) main::cursor#6 ) + [5] (byte*) main::line#2 ← phi( main/(byte*) 1024 main::@5/(byte*) main::line#5 ) + [5] (byte*) main::msg#2 ← phi( main/(const byte[]) MESSAGE#0 main::@5/(byte*) main::msg#1 ) + [6] if((byte) 0!=*((byte*) main::msg#2)) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [7] return + to:@return +main::@2: scope:[main] from main::@1 + [8] if(*((byte*) main::msg#2)==(byte) ' +') goto main::@3 + to:main::@4 +main::@4: scope:[main] from main::@2 + [9] *((byte*) main::cursor#3) ← *((byte*) main::msg#2) + [10] (byte*) main::cursor#2 ← ++ (byte*) main::cursor#3 + to:main::@5 +main::@5: scope:[main] from main::@3 main::@4 + [11] (byte*) main::cursor#6 ← phi( main::@3/(byte*) main::cursor#1 main::@4/(byte*) main::cursor#2 ) + [11] (byte*) main::line#5 ← phi( main::@3/(byte*~) main::line#8 main::@4/(byte*) main::line#2 ) + [12] (byte*) main::msg#1 ← ++ (byte*) main::msg#2 + to:main::@1 +main::@3: scope:[main] from main::@2 + [13] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28 + [14] (byte*~) main::line#8 ← (byte*) main::cursor#1 + to:main::@5 + + +VARIABLE REGISTER WEIGHTS +(byte[]) MESSAGE +(void()) main() +(byte*) main::cursor +(byte*) main::cursor#1 16.5 +(byte*) main::cursor#2 22.0 +(byte*) main::cursor#3 8.25 +(byte*) main::cursor#6 16.5 +(byte*) main::line +(byte*) main::line#2 6.6000000000000005 +(byte*) main::line#5 16.5 +(byte*~) main::line#8 22.0 +(byte*) main::msg +(byte*) main::msg#1 22.0 +(byte*) main::msg#2 6.875 + +Initial phi equivalence classes +[ main::msg#2 main::msg#1 ] +[ main::line#2 main::line#5 main::line#8 ] +[ main::cursor#3 main::cursor#6 main::cursor#1 main::cursor#2 ] +Complete equivalence classes +[ main::msg#2 main::msg#1 ] +[ main::line#2 main::line#5 main::line#8 ] +[ main::cursor#3 main::cursor#6 main::cursor#1 main::cursor#2 ] +Allocated zp ZP_WORD:2 [ main::msg#2 main::msg#1 ] +Allocated zp ZP_WORD:4 [ main::line#2 main::line#5 main::line#8 ] +Allocated zp ZP_WORD:6 [ main::cursor#3 main::cursor#6 main::cursor#1 main::cursor#2 ] + +INITIAL ASM +Target platform is c64basic + // File Comments +// Test using some simple supported string escape \n in both string and char + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + .label cursor = 6 + .label msg = 2 + .label line = 4 + // [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + // [5] phi (byte*) main::cursor#3 = (byte*) 1024 [phi:main->main::@1#0] -- pbuz1=pbuc1 + lda #<$400 + sta.z cursor + lda #>$400 + sta.z cursor+1 + // [5] phi (byte*) main::line#2 = (byte*) 1024 [phi:main->main::@1#1] -- pbuz1=pbuc1 + lda #<$400 + sta.z line + lda #>$400 + sta.z line+1 + // [5] phi (byte*) main::msg#2 = (const byte[]) MESSAGE#0 [phi:main->main::@1#2] -- pbuz1=pbuc1 + lda #MESSAGE + sta.z msg+1 + jmp b1 + // main::@1 + b1: + // [6] if((byte) 0!=*((byte*) main::msg#2)) goto main::@2 -- vbuc1_neq__deref_pbuz1_then_la1 + ldy #0 + lda (msg),y + cmp #0 + bne b2 + jmp breturn + // main::@return + breturn: + // [7] return + rts + // main::@2 + b2: + // [8] if(*((byte*) main::msg#2)==(byte) ' ') goto main::@3 -- _deref_pbuz1_eq_vbuc1_then_la1 + lda #-$33 + ldy #0 + cmp (msg),y + beq b3 + jmp b4 + // main::@4 + b4: + // [9] *((byte*) main::cursor#3) ← *((byte*) main::msg#2) -- _deref_pbuz1=_deref_pbuz2 + ldy #0 + lda (msg),y + ldy #0 + sta (cursor),y + // [10] (byte*) main::cursor#2 ← ++ (byte*) main::cursor#3 -- pbuz1=_inc_pbuz1 + inc.z cursor + bne !+ + inc.z cursor+1 + !: + // [11] phi from main::@3 main::@4 to main::@5 [phi:main::@3/main::@4->main::@5] + b5_from_b3: + b5_from_b4: + // [11] phi (byte*) main::cursor#6 = (byte*) main::cursor#1 [phi:main::@3/main::@4->main::@5#0] -- register_copy + // [11] phi (byte*) main::line#5 = (byte*~) main::line#8 [phi:main::@3/main::@4->main::@5#1] -- register_copy + jmp b5 + // main::@5 + b5: + // [12] (byte*) main::msg#1 ← ++ (byte*) main::msg#2 -- pbuz1=_inc_pbuz1 + inc.z msg + bne !+ + inc.z msg+1 + !: + // [5] phi from main::@5 to main::@1 [phi:main::@5->main::@1] + b1_from_b5: + // [5] phi (byte*) main::cursor#3 = (byte*) main::cursor#6 [phi:main::@5->main::@1#0] -- register_copy + // [5] phi (byte*) main::line#2 = (byte*) main::line#5 [phi:main::@5->main::@1#1] -- register_copy + // [5] phi (byte*) main::msg#2 = (byte*) main::msg#1 [phi:main::@5->main::@1#2] -- register_copy + jmp b1 + // main::@3 + b3: + // [13] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28 -- pbuz1=pbuz2_plus_vbuc1 + lda #$28 + clc + adc.z line + sta.z cursor + lda #0 + adc.z line+1 + sta.z cursor+1 + // [14] (byte*~) main::line#8 ← (byte*) main::cursor#1 -- pbuz1=pbuz2 + lda.z cursor + sta.z line + lda.z cursor+1 + sta.z line+1 + jmp b5_from_b3 +} + // File Data + MESSAGE: .text @"hello\nworld" + .byte 0 + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [6] if((byte) 0!=*((byte*) main::msg#2)) goto main::@2 [ main::msg#2 main::line#2 main::cursor#3 ] ( main:2 [ main::msg#2 main::line#2 main::cursor#3 ] ) always clobbers reg byte a reg byte y +Statement [8] if(*((byte*) main::msg#2)==(byte) ' +') goto main::@3 [ main::msg#2 main::line#2 main::cursor#3 ] ( main:2 [ main::msg#2 main::line#2 main::cursor#3 ] ) always clobbers reg byte a reg byte y +Statement [9] *((byte*) main::cursor#3) ← *((byte*) main::msg#2) [ main::msg#2 main::line#2 main::cursor#3 ] ( main:2 [ main::msg#2 main::line#2 main::cursor#3 ] ) always clobbers reg byte a reg byte y +Statement [13] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28 [ main::msg#2 main::cursor#1 ] ( main:2 [ main::msg#2 main::cursor#1 ] ) always clobbers reg byte a +Statement [14] (byte*~) main::line#8 ← (byte*) main::cursor#1 [ main::msg#2 main::line#8 main::cursor#1 ] ( main:2 [ main::msg#2 main::line#8 main::cursor#1 ] ) always clobbers reg byte a +Potential registers zp ZP_WORD:2 [ main::msg#2 main::msg#1 ] : zp ZP_WORD:2 , +Potential registers zp ZP_WORD:4 [ main::line#2 main::line#5 main::line#8 ] : zp ZP_WORD:4 , +Potential registers zp ZP_WORD:6 [ main::cursor#3 main::cursor#6 main::cursor#1 main::cursor#2 ] : zp ZP_WORD:6 , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 63.25: zp ZP_WORD:6 [ main::cursor#3 main::cursor#6 main::cursor#1 main::cursor#2 ] 45.1: zp ZP_WORD:4 [ main::line#2 main::line#5 main::line#8 ] 28.88: zp ZP_WORD:2 [ main::msg#2 main::msg#1 ] +Uplift Scope [] + +Uplifting [main] best 1443 combination zp ZP_WORD:6 [ main::cursor#3 main::cursor#6 main::cursor#1 main::cursor#2 ] zp ZP_WORD:4 [ main::line#2 main::line#5 main::line#8 ] zp ZP_WORD:2 [ main::msg#2 main::msg#1 ] +Uplifting [] best 1443 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test using some simple supported string escape \n in both string and char + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + .label cursor = 6 + .label msg = 2 + .label line = 4 + // [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + // [5] phi (byte*) main::cursor#3 = (byte*) 1024 [phi:main->main::@1#0] -- pbuz1=pbuc1 + lda #<$400 + sta.z cursor + lda #>$400 + sta.z cursor+1 + // [5] phi (byte*) main::line#2 = (byte*) 1024 [phi:main->main::@1#1] -- pbuz1=pbuc1 + lda #<$400 + sta.z line + lda #>$400 + sta.z line+1 + // [5] phi (byte*) main::msg#2 = (const byte[]) MESSAGE#0 [phi:main->main::@1#2] -- pbuz1=pbuc1 + lda #MESSAGE + sta.z msg+1 + jmp b1 + // main::@1 + b1: + // [6] if((byte) 0!=*((byte*) main::msg#2)) goto main::@2 -- vbuc1_neq__deref_pbuz1_then_la1 + ldy #0 + lda (msg),y + cmp #0 + bne b2 + jmp breturn + // main::@return + breturn: + // [7] return + rts + // main::@2 + b2: + // [8] if(*((byte*) main::msg#2)==(byte) ' ') goto main::@3 -- _deref_pbuz1_eq_vbuc1_then_la1 + lda #-$33 + ldy #0 + cmp (msg),y + beq b3 + jmp b4 + // main::@4 + b4: + // [9] *((byte*) main::cursor#3) ← *((byte*) main::msg#2) -- _deref_pbuz1=_deref_pbuz2 + ldy #0 + lda (msg),y + ldy #0 + sta (cursor),y + // [10] (byte*) main::cursor#2 ← ++ (byte*) main::cursor#3 -- pbuz1=_inc_pbuz1 + inc.z cursor + bne !+ + inc.z cursor+1 + !: + // [11] phi from main::@3 main::@4 to main::@5 [phi:main::@3/main::@4->main::@5] + b5_from_b3: + b5_from_b4: + // [11] phi (byte*) main::cursor#6 = (byte*) main::cursor#1 [phi:main::@3/main::@4->main::@5#0] -- register_copy + // [11] phi (byte*) main::line#5 = (byte*~) main::line#8 [phi:main::@3/main::@4->main::@5#1] -- register_copy + jmp b5 + // main::@5 + b5: + // [12] (byte*) main::msg#1 ← ++ (byte*) main::msg#2 -- pbuz1=_inc_pbuz1 + inc.z msg + bne !+ + inc.z msg+1 + !: + // [5] phi from main::@5 to main::@1 [phi:main::@5->main::@1] + b1_from_b5: + // [5] phi (byte*) main::cursor#3 = (byte*) main::cursor#6 [phi:main::@5->main::@1#0] -- register_copy + // [5] phi (byte*) main::line#2 = (byte*) main::line#5 [phi:main::@5->main::@1#1] -- register_copy + // [5] phi (byte*) main::msg#2 = (byte*) main::msg#1 [phi:main::@5->main::@1#2] -- register_copy + jmp b1 + // main::@3 + b3: + // [13] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28 -- pbuz1=pbuz2_plus_vbuc1 + lda #$28 + clc + adc.z line + sta.z cursor + lda #0 + adc.z line+1 + sta.z cursor+1 + // [14] (byte*~) main::line#8 ← (byte*) main::cursor#1 -- pbuz1=pbuz2 + lda.z cursor + sta.z line + lda.z cursor+1 + sta.z line+1 + jmp b5_from_b3 +} + // File Data + MESSAGE: .text @"hello\nworld" + .byte 0 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b1 +Removing instruction jmp breturn +Removing instruction jmp b4 +Removing instruction jmp b5 +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction ldy #0 +Succesful ASM optimization Pass5UnnecesaryLoadElimination +Replacing label b5_from_b3 with b5 +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Removing instruction b5_from_b3: +Removing instruction b5_from_b4: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction b1_from_main: +Removing instruction breturn: +Removing instruction b4: +Removing instruction b1_from_b5: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction ldy #0 +Succesful ASM optimization Pass5UnnecesaryLoadElimination +Removing instruction bbegin: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(byte[]) MESSAGE +(const byte[]) MESSAGE#0 MESSAGE = (string) "hello +world" +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(label) main::@5 +(label) main::@return +(byte*) main::cursor +(byte*) main::cursor#1 cursor zp ZP_WORD:6 16.5 +(byte*) main::cursor#2 cursor zp ZP_WORD:6 22.0 +(byte*) main::cursor#3 cursor zp ZP_WORD:6 8.25 +(byte*) main::cursor#6 cursor zp ZP_WORD:6 16.5 +(byte*) main::line +(byte*) main::line#2 line zp ZP_WORD:4 6.6000000000000005 +(byte*) main::line#5 line zp ZP_WORD:4 16.5 +(byte*~) main::line#8 line zp ZP_WORD:4 22.0 +(byte*) main::msg +(byte*) main::msg#1 msg zp ZP_WORD:2 22.0 +(byte*) main::msg#2 msg zp ZP_WORD:2 6.875 + +zp ZP_WORD:2 [ main::msg#2 main::msg#1 ] +zp ZP_WORD:4 [ main::line#2 main::line#5 main::line#8 ] +zp ZP_WORD:6 [ main::cursor#3 main::cursor#6 main::cursor#1 main::cursor#2 ] + + +FINAL ASSEMBLER +Score: 1271 + + // File Comments +// Test using some simple supported string escape \n in both string and char + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + // @begin + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] call main + // [4] phi from @1 to main [phi:@1->main] + // [3] phi from @1 to @end [phi:@1->@end] + // @end + // main +main: { + .label cursor = 6 + .label msg = 2 + .label line = 4 + // [5] phi from main to main::@1 [phi:main->main::@1] + // [5] phi (byte*) main::cursor#3 = (byte*) 1024 [phi:main->main::@1#0] -- pbuz1=pbuc1 + lda #<$400 + sta.z cursor + lda #>$400 + sta.z cursor+1 + // [5] phi (byte*) main::line#2 = (byte*) 1024 [phi:main->main::@1#1] -- pbuz1=pbuc1 + lda #<$400 + sta.z line + lda #>$400 + sta.z line+1 + // [5] phi (byte*) main::msg#2 = (const byte[]) MESSAGE#0 [phi:main->main::@1#2] -- pbuz1=pbuc1 + lda #MESSAGE + sta.z msg+1 + // main::@1 + b1: + // while(*msg) + // [6] if((byte) 0!=*((byte*) main::msg#2)) goto main::@2 -- vbuc1_neq__deref_pbuz1_then_la1 + ldy #0 + lda (msg),y + cmp #0 + bne b2 + // main::@return + // } + // [7] return + rts + // main::@2 + b2: + // case '\n': + // line += 0x28; + // cursor = line; + // break; + // [8] if(*((byte*) main::msg#2)==(byte) ' ') goto main::@3 -- _deref_pbuz1_eq_vbuc1_then_la1 + lda #-$33 + ldy #0 + cmp (msg),y + beq b3 + // main::@4 + // *cursor++ = *msg + // [9] *((byte*) main::cursor#3) ← *((byte*) main::msg#2) -- _deref_pbuz1=_deref_pbuz2 + lda (msg),y + sta (cursor),y + // *cursor++ = *msg; + // [10] (byte*) main::cursor#2 ← ++ (byte*) main::cursor#3 -- pbuz1=_inc_pbuz1 + inc.z cursor + bne !+ + inc.z cursor+1 + !: + // [11] phi from main::@3 main::@4 to main::@5 [phi:main::@3/main::@4->main::@5] + // [11] phi (byte*) main::cursor#6 = (byte*) main::cursor#1 [phi:main::@3/main::@4->main::@5#0] -- register_copy + // [11] phi (byte*) main::line#5 = (byte*~) main::line#8 [phi:main::@3/main::@4->main::@5#1] -- register_copy + // main::@5 + b5: + // msg++; + // [12] (byte*) main::msg#1 ← ++ (byte*) main::msg#2 -- pbuz1=_inc_pbuz1 + inc.z msg + bne !+ + inc.z msg+1 + !: + // [5] phi from main::@5 to main::@1 [phi:main::@5->main::@1] + // [5] phi (byte*) main::cursor#3 = (byte*) main::cursor#6 [phi:main::@5->main::@1#0] -- register_copy + // [5] phi (byte*) main::line#2 = (byte*) main::line#5 [phi:main::@5->main::@1#1] -- register_copy + // [5] phi (byte*) main::msg#2 = (byte*) main::msg#1 [phi:main::@5->main::@1#2] -- register_copy + jmp b1 + // main::@3 + b3: + // line += 0x28 + // [13] (byte*) main::cursor#1 ← (byte*) main::line#2 + (byte) $28 -- pbuz1=pbuz2_plus_vbuc1 + lda #$28 + clc + adc.z line + sta.z cursor + lda #0 + adc.z line+1 + sta.z cursor+1 + // [14] (byte*~) main::line#8 ← (byte*) main::cursor#1 -- pbuz1=pbuz2 + lda.z cursor + sta.z line + lda.z cursor+1 + sta.z line+1 + jmp b5 +} + // File Data + MESSAGE: .text @"hello\nworld" + .byte 0 + diff --git a/src/test/ref/string-escapes-1.sym b/src/test/ref/string-escapes-1.sym new file mode 100644 index 000000000..1ffa2af5e --- /dev/null +++ b/src/test/ref/string-escapes-1.sym @@ -0,0 +1,29 @@ +(label) @1 +(label) @begin +(label) @end +(byte[]) MESSAGE +(const byte[]) MESSAGE#0 MESSAGE = (string) "hello +world" +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(label) main::@5 +(label) main::@return +(byte*) main::cursor +(byte*) main::cursor#1 cursor zp ZP_WORD:6 16.5 +(byte*) main::cursor#2 cursor zp ZP_WORD:6 22.0 +(byte*) main::cursor#3 cursor zp ZP_WORD:6 8.25 +(byte*) main::cursor#6 cursor zp ZP_WORD:6 16.5 +(byte*) main::line +(byte*) main::line#2 line zp ZP_WORD:4 6.6000000000000005 +(byte*) main::line#5 line zp ZP_WORD:4 16.5 +(byte*~) main::line#8 line zp ZP_WORD:4 22.0 +(byte*) main::msg +(byte*) main::msg#1 msg zp ZP_WORD:2 22.0 +(byte*) main::msg#2 msg zp ZP_WORD:2 6.875 + +zp ZP_WORD:2 [ main::msg#2 main::msg#1 ] +zp ZP_WORD:4 [ main::line#2 main::line#5 main::line#8 ] +zp ZP_WORD:6 [ main::cursor#3 main::cursor#6 main::cursor#1 main::cursor#2 ] diff --git a/src/test/ref/string-escapes-2.asm b/src/test/ref/string-escapes-2.asm new file mode 100644 index 000000000..aa0843be1 --- /dev/null +++ b/src/test/ref/string-escapes-2.asm @@ -0,0 +1,31 @@ +// Test using some simple supported string escape characters in PETSCII +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label memA = $ff +main: { + .label i = 2 + lda #0 + sta.z i + b1: + lda #0 + ldy.z i + cmp MESSAGE,y + bne b2 + rts + b2: + ldy.z i + lda MESSAGE,y + jsr chrout + inc.z i + jmp b1 +} +// chrout(byte register(A) c) +chrout: { + sta memA + jsr $ffd2 + rts +} +.encoding "petscii_mixed" + MESSAGE: .text @"hello\nworld" + .byte 0 diff --git a/src/test/ref/string-escapes-2.cfg b/src/test/ref/string-escapes-2.cfg new file mode 100644 index 000000000..4b87c8f7d --- /dev/null +++ b/src/test/ref/string-escapes-2.cfg @@ -0,0 +1,33 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] phi() + to:main::@1 +main::@1: scope:[main] from main main::@3 + [5] (byte) main::i#2 ← phi( main/(byte) 0 main::@3/(byte) main::i#1 ) + [6] if((byte) 0!=*((const byte[]) MESSAGE#0 + (byte) main::i#2)) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [7] return + to:@return +main::@2: scope:[main] from main::@1 + [8] (byte) chrout::c#0 ← *((const byte[]) MESSAGE#0 + (byte) main::i#2) + [9] call chrout + to:main::@3 +main::@3: scope:[main] from main::@2 + [10] (byte) main::i#1 ← ++ (byte) main::i#2 + to:main::@1 +chrout: scope:[chrout] from main::@2 + [11] *((const byte*) memA#0) ← (byte) chrout::c#0 + asm { ldamemA jsr$ffd2 } + to:chrout::@return +chrout::@return: scope:[chrout] from chrout + [13] return + to:@return diff --git a/src/test/ref/string-escapes-2.log b/src/test/ref/string-escapes-2.log new file mode 100644 index 000000000..6b0f881c4 --- /dev/null +++ b/src/test/ref/string-escapes-2.log @@ -0,0 +1,496 @@ +Warning! Adding boolean cast to non-boolean condition *((byte[]) MESSAGE + (byte) main::i) +Culled Empty Block (label) main::@4 +Culled Empty Block (label) main::@3 +Culled Empty Block (label) main::@5 +Culled Empty Block (label) main::@6 +Culled Empty Block (label) @1 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (byte[]) MESSAGE#0 ← (const string) $0 + (byte*) memA#0 ← ((byte*)) (number) $ff + to:@2 +main: scope:[main] from @2 + (byte) main::i#0 ← (number) 0 + to:main::@1 +main::@1: scope:[main] from main main::@7 + (byte) main::i#2 ← phi( main/(byte) main::i#0 main::@7/(byte) main::i#1 ) + (bool~) main::$1 ← (number) 0 != *((byte[]) MESSAGE#0 + (byte) main::i#2) + if((bool~) main::$1) goto main::@2 + to:main::@return +main::@2: scope:[main] from main::@1 + (byte) main::i#3 ← phi( main::@1/(byte) main::i#2 ) + (byte) chrout::c#0 ← *((byte[]) MESSAGE#0 + (byte) main::i#3) + call chrout + to:main::@7 +main::@7: scope:[main] from main::@2 + (byte) main::i#4 ← phi( main::@2/(byte) main::i#3 ) + (byte) main::i#1 ← ++ (byte) main::i#4 + to:main::@1 +main::@return: scope:[main] from main::@1 + return + to:@return +chrout: scope:[chrout] from main::@2 + (byte) chrout::c#1 ← phi( main::@2/(byte) chrout::c#0 ) + *((byte*) memA#0) ← (byte) chrout::c#1 + asm { ldamemA jsr$ffd2 } + to:chrout::@return +chrout::@return: scope:[chrout] from chrout + return + to:@return +@2: scope:[] from @begin + call main + to:@3 +@3: scope:[] from @2 + to:@end +@end: scope:[] from @3 + +SYMBOL TABLE SSA +(const string) $0 = (string) "hello +world"pm +(label) @2 +(label) @3 +(label) @begin +(label) @end +(byte[]) MESSAGE +(byte[]) MESSAGE#0 +(void()) chrout((byte) chrout::c) +(label) chrout::@return +(byte) chrout::c +(byte) chrout::c#0 +(byte) chrout::c#1 +(void()) main() +(bool~) main::$1 +(label) main::@1 +(label) main::@2 +(label) main::@7 +(label) main::@return +(byte) main::i +(byte) main::i#0 +(byte) main::i#1 +(byte) main::i#2 +(byte) main::i#3 +(byte) main::i#4 +(byte*) memA +(byte*) memA#0 + +Adding number conversion cast (unumber) 0 in (byte) main::i#0 ← (number) 0 +Adding number conversion cast (unumber) 0 in (bool~) main::$1 ← (number) 0 != *((byte[]) MESSAGE#0 + (byte) main::i#2) +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (byte*) memA#0 ← (byte*)(number) $ff +Inlining cast (byte) main::i#0 ← (unumber)(number) 0 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (byte*) 255 +Simplifying constant integer cast 0 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias (byte) main::i#2 = (byte) main::i#3 (byte) main::i#4 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values (byte) chrout::c#1 (byte) chrout::c#0 +Successful SSA optimization Pass2IdenticalPhiElimination +Simple Condition (bool~) main::$1 [5] if((byte) 0!=*((byte[]) MESSAGE#0 + (byte) main::i#2)) goto main::@2 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const byte[]) MESSAGE#0 = $0 +Constant (const byte*) memA#0 = (byte*) 255 +Constant (const byte) main::i#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +Inlining constant with var siblings (const byte) main::i#0 +Constant inlined $0 = (const byte[]) MESSAGE#0 +Constant inlined main::i#0 = (byte) 0 +Successful SSA optimization Pass2ConstantInlining +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @3 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +CALL GRAPH +Calls in [] to main:2 +Calls in [main] to chrout:10 + +Created 1 initial phi equivalence classes +Coalesced [12] main::i#5 ← main::i#1 +Coalesced down to 1 phi equivalence classes +Culled Empty Block (label) @3 +Renumbering block @2 to @1 +Renumbering block main::@7 to main::@3 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] phi() + to:main::@1 +main::@1: scope:[main] from main main::@3 + [5] (byte) main::i#2 ← phi( main/(byte) 0 main::@3/(byte) main::i#1 ) + [6] if((byte) 0!=*((const byte[]) MESSAGE#0 + (byte) main::i#2)) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [7] return + to:@return +main::@2: scope:[main] from main::@1 + [8] (byte) chrout::c#0 ← *((const byte[]) MESSAGE#0 + (byte) main::i#2) + [9] call chrout + to:main::@3 +main::@3: scope:[main] from main::@2 + [10] (byte) main::i#1 ← ++ (byte) main::i#2 + to:main::@1 +chrout: scope:[chrout] from main::@2 + [11] *((const byte*) memA#0) ← (byte) chrout::c#0 + asm { ldamemA jsr$ffd2 } + to:chrout::@return +chrout::@return: scope:[chrout] from chrout + [13] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(byte[]) MESSAGE +(void()) chrout((byte) chrout::c) +(byte) chrout::c +(byte) chrout::c#0 13.0 +(void()) main() +(byte) main::i +(byte) main::i#1 22.0 +(byte) main::i#2 11.0 +(byte*) memA + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +Added variable chrout::c#0 to zero page equivalence class [ chrout::c#0 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +[ chrout::c#0 ] +Allocated zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Allocated zp ZP_BYTE:3 [ chrout::c#0 ] + +INITIAL ASM +Target platform is c64basic + // File Comments +// Test using some simple supported string escape characters in PETSCII + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label memA = $ff + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + .label i = 2 + // [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + // [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta.z i + jmp b1 + // main::@1 + b1: + // [6] if((byte) 0!=*((const byte[]) MESSAGE#0 + (byte) main::i#2)) goto main::@2 -- vbuc1_neq_pbuc2_derefidx_vbuz1_then_la1 + lda #0 + ldy.z i + cmp MESSAGE,y + bne b2 + jmp breturn + // main::@return + breturn: + // [7] return + rts + // main::@2 + b2: + // [8] (byte) chrout::c#0 ← *((const byte[]) MESSAGE#0 + (byte) main::i#2) -- vbuz1=pbuc1_derefidx_vbuz2 + ldy.z i + lda MESSAGE,y + sta.z chrout.c + // [9] call chrout + jsr chrout + jmp b3 + // main::@3 + b3: + // [10] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 + inc.z i + // [5] phi from main::@3 to main::@1 [phi:main::@3->main::@1] + b1_from_b3: + // [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@3->main::@1#0] -- register_copy + jmp b1 +} + // chrout +// chrout(byte zeropage(3) c) +chrout: { + .label c = 3 + // [11] *((const byte*) memA#0) ← (byte) chrout::c#0 -- _deref_pbuc1=vbuz1 + lda.z c + sta memA + // asm { ldamemA jsr$ffd2 } + lda memA + jsr $ffd2 + jmp breturn + // chrout::@return + breturn: + // [13] return + rts +} + // File Data +.encoding "petscii_mixed" + MESSAGE: .text @"hello\nworld" + .byte 0 + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [6] if((byte) 0!=*((const byte[]) MESSAGE#0 + (byte) main::i#2)) goto main::@2 [ main::i#2 ] ( main:2 [ main::i#2 ] ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Statement asm { ldamemA jsr$ffd2 } always clobbers reg byte a reg byte x reg byte y +Removing always clobbered register reg byte x as potential for zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Removing always clobbered register reg byte y as potential for zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Statement [6] if((byte) 0!=*((const byte[]) MESSAGE#0 + (byte) main::i#2)) goto main::@2 [ main::i#2 ] ( main:2 [ main::i#2 ] ) always clobbers reg byte a reg byte y +Statement [8] (byte) chrout::c#0 ← *((const byte[]) MESSAGE#0 + (byte) main::i#2) [ main::i#2 chrout::c#0 ] ( main:2 [ main::i#2 chrout::c#0 ] ) always clobbers reg byte y +Statement asm { ldamemA jsr$ffd2 } always clobbers reg byte a reg byte x reg byte y +Potential registers zp ZP_BYTE:2 [ main::i#2 main::i#1 ] : zp ZP_BYTE:2 , +Potential registers zp ZP_BYTE:3 [ chrout::c#0 ] : zp ZP_BYTE:3 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 33: zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Uplift Scope [chrout] 13: zp ZP_BYTE:3 [ chrout::c#0 ] +Uplift Scope [] + +Uplifting [main] best 549 combination zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Uplifting [chrout] best 516 combination reg byte a [ chrout::c#0 ] +Uplifting [] best 516 combination +Attempting to uplift remaining variables inzp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Uplifting [main] best 516 combination zp ZP_BYTE:2 [ main::i#2 main::i#1 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test using some simple supported string escape characters in PETSCII + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label memA = $ff + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + .label i = 2 + // [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + // [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta.z i + jmp b1 + // main::@1 + b1: + // [6] if((byte) 0!=*((const byte[]) MESSAGE#0 + (byte) main::i#2)) goto main::@2 -- vbuc1_neq_pbuc2_derefidx_vbuz1_then_la1 + lda #0 + ldy.z i + cmp MESSAGE,y + bne b2 + jmp breturn + // main::@return + breturn: + // [7] return + rts + // main::@2 + b2: + // [8] (byte) chrout::c#0 ← *((const byte[]) MESSAGE#0 + (byte) main::i#2) -- vbuaa=pbuc1_derefidx_vbuz1 + ldy.z i + lda MESSAGE,y + // [9] call chrout + jsr chrout + jmp b3 + // main::@3 + b3: + // [10] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 + inc.z i + // [5] phi from main::@3 to main::@1 [phi:main::@3->main::@1] + b1_from_b3: + // [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@3->main::@1#0] -- register_copy + jmp b1 +} + // chrout +// chrout(byte register(A) c) +chrout: { + // [11] *((const byte*) memA#0) ← (byte) chrout::c#0 -- _deref_pbuc1=vbuaa + sta memA + // asm { ldamemA jsr$ffd2 } + lda memA + jsr $ffd2 + jmp breturn + // chrout::@return + breturn: + // [13] return + rts +} + // File Data +.encoding "petscii_mixed" + MESSAGE: .text @"hello\nworld" + .byte 0 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b1 +Removing instruction jmp breturn +Removing instruction jmp b3 +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction lda memA +Succesful ASM optimization Pass5UnnecesaryLoadElimination +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction b1_from_main: +Removing instruction breturn: +Removing instruction b3: +Removing instruction b1_from_b3: +Removing instruction breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction bbegin: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(byte[]) MESSAGE +(const byte[]) MESSAGE#0 MESSAGE = (string) "hello +world"pm +(void()) chrout((byte) chrout::c) +(label) chrout::@return +(byte) chrout::c +(byte) chrout::c#0 reg byte a 13.0 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@return +(byte) main::i +(byte) main::i#1 i zp ZP_BYTE:2 22.0 +(byte) main::i#2 i zp ZP_BYTE:2 11.0 +(byte*) memA +(const byte*) memA#0 memA = (byte*) 255 + +zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +reg byte a [ chrout::c#0 ] + + +FINAL ASSEMBLER +Score: 407 + + // File Comments +// Test using some simple supported string escape characters in PETSCII + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label memA = $ff + // @begin + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] call main + // [4] phi from @1 to main [phi:@1->main] + // [3] phi from @1 to @end [phi:@1->@end] + // @end + // main +main: { + .label i = 2 + // [5] phi from main to main::@1 [phi:main->main::@1] + // [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta.z i + // main::@1 + b1: + // while(MESSAGE[i]) + // [6] if((byte) 0!=*((const byte[]) MESSAGE#0 + (byte) main::i#2)) goto main::@2 -- vbuc1_neq_pbuc2_derefidx_vbuz1_then_la1 + lda #0 + ldy.z i + cmp MESSAGE,y + bne b2 + // main::@return + // } + // [7] return + rts + // main::@2 + b2: + // chrout(MESSAGE[i++]) + // [8] (byte) chrout::c#0 ← *((const byte[]) MESSAGE#0 + (byte) main::i#2) -- vbuaa=pbuc1_derefidx_vbuz1 + ldy.z i + lda MESSAGE,y + // [9] call chrout + jsr chrout + // main::@3 + // chrout(MESSAGE[i++]); + // [10] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 + inc.z i + // [5] phi from main::@3 to main::@1 [phi:main::@3->main::@1] + // [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@3->main::@1#0] -- register_copy + jmp b1 +} + // chrout +// chrout(byte register(A) c) +chrout: { + // *memA = c + // [11] *((const byte*) memA#0) ← (byte) chrout::c#0 -- _deref_pbuc1=vbuaa + sta memA + // asm + // asm { ldamemA jsr$ffd2 } + jsr $ffd2 + // chrout::@return + // } + // [13] return + rts +} + // File Data +.encoding "petscii_mixed" + MESSAGE: .text @"hello\nworld" + .byte 0 + diff --git a/src/test/ref/string-escapes-2.sym b/src/test/ref/string-escapes-2.sym new file mode 100644 index 000000000..acfa2c30e --- /dev/null +++ b/src/test/ref/string-escapes-2.sym @@ -0,0 +1,23 @@ +(label) @1 +(label) @begin +(label) @end +(byte[]) MESSAGE +(const byte[]) MESSAGE#0 MESSAGE = (string) "hello +world"pm +(void()) chrout((byte) chrout::c) +(label) chrout::@return +(byte) chrout::c +(byte) chrout::c#0 reg byte a 13.0 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@return +(byte) main::i +(byte) main::i#1 i zp ZP_BYTE:2 22.0 +(byte) main::i#2 i zp ZP_BYTE:2 11.0 +(byte*) memA +(const byte*) memA#0 memA = (byte*) 255 + +zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +reg byte a [ chrout::c#0 ]