require "python-semantics-boolean-ops.k" require "python-semantics-slicing.k" require "machine-integers.k" require "unicode.k" module PYTHON-SEMANTICS-STRINGS imports PYTHON-SEMANTICS-BOOLEAN-OPS imports PYTHON-SEMANTICS-SLICING imports MACHINE-INTEGERS imports UNICODE rule invokeBuiltin(obj("add_str",_), ListItem(O:Object) ListItem(O2:Object), .) => test(istype(O2, ref("str")), strvalue(O) +String strvalue(O2), raiseInternal("TypeError", ("Can't convert '" + name(gettype(O2)) + "' object to str implicitly"))) ... rule invokeBuiltin(obj("eq_str",_), ListItem(O) ListItem(O2), .) => binaryOp(O, O2, "str", "str", bool(strvalue(O) ==String strvalue(O2))) ... rule invokeBuiltin(obj("ne_str",_), ListItem(O) ListItem(O2), .) => binaryOp(O, O2, "str", "str", bool(strvalue(O) =/=String strvalue(O2))) ... rule [hash-str]: invokeBuiltin(obj("hash_str",_), ListItem(O), .) => test(lengthString(strvalue(O)) ==Int 0, 0, accumulate(strvalue(O), (ordChar(substrString(strvalue(O), 0, 1)) < ... "PYTHONHASHSEED" |-> Seed "sys.hash_info.width" |-> Width ... rule invokeBuiltin(obj("str.__new__",_), ListItem(Cls:Object), .) => newHelper(Cls, ref("str"), .) ~> immutable("", Cls) rule invokeBuiltin(obj("str.__new__",_), ListItem(Cls:Object) ListItem(O:Object), .) => newHelper(Cls, ref("str"), .) ~> immutable(strvalue(getmember(O, "__str__", true, false, true)(.Arguments)), Cls) context immutable(strvalue(HOLE), _) rule invokeBuiltin(obj("str.__str__",_), ListItem(O), .) => strvalue(O) syntax ObjRef ::= accumulate(String, Int, Int) syntax Int ::= hashPrefix(Int, Int) [function] | hashSuffix(Int, Int) [function] rule hashSuffix(Seed, Width) => wrap(lcgRandom(Seed, Width /Int 4, Width) >>Int Width, Width) rule hashPrefix(Seed, Width) => wrap(lcgRandom(Seed, Width /Int 4, Width) &Int (2 ^Int Width -Int 1), Width) rule accumulate((S:String => substrString(S, 1, lengthString(S))), (I:Int => (mult(Imag, I, Width) xorInt ordChar(substrString(S, 0, 1)))), _) ... ... "sys.hash_info.width" |-> Width "sys.hash_info.imag" |-> Imag ... when lengthString(S) =/=Int 0 rule accumulate("", I, Len:Int) => test((I xorInt Len xorInt hashSuffix(Seed, Width)) ==Int -1, -2, (I xorInt Len xorInt hashSuffix(Seed, Width))) ... ... "PYTHONHASHSEED" |-> Seed "sys.hash_info.width" |-> Width ... rule invokeBuiltin(obj("rpartition_str",_), ListItem(O) ListItem(Sep), .) => test(istype(Sep, ref("str")), #if rfindString(strvalue(O), strvalue(Sep), lengthString(strvalue(O))) ==Int -1 #then tuple("", "", strvalue(O)) #else tuple(substrString(strvalue(O), 0, rfindString(strvalue(O), strvalue(Sep), lengthString(strvalue(O)))), strvalue(Sep), substrString(strvalue(O), rfindString(strvalue(O), strvalue(Sep), lengthString(strvalue(O))) +Int lengthString(strvalue(Sep)), lengthString(strvalue(O)))) #fi, raiseInternal("TypeError", "Cannot convert object to str implicitly")) rule invokeBuiltin(obj("partition_str",_), ListItem(O) ListItem(Sep), .) => test(istype(Sep, ref("str")), #if findString(strvalue(O), strvalue(Sep), 0) ==Int -1 #then tuple(strvalue(O), "", "") #else tuple(substrString(strvalue(O), 0, findString(strvalue(O), strvalue(Sep), 0)), strvalue(Sep), substrString(strvalue(O), findString(strvalue(O), strvalue(Sep), 0) +Int lengthString(strvalue(Sep)), lengthString(strvalue(O)))) #fi, raiseInternal("TypeError", "Cannot convert object to str implicitly")) rule invokeBuiltin(obj("ord",_), ListItem(O), .) => test(istype(O, ref("str")), test(length(strvalue(O), UTF-8) =/=Int 1, raiseInternal("TypeError", "ord() expected a character, but string of length " +String Int2String(length(strvalue(O), UTF-8)) +String " found"), codePoint(strvalue(O), UTF-8, 0)), raiseInternal("TypeError", "ord() expected string of length 1")) rule invokeBuiltin(obj("len_str",_), ListItem(O), .) => length(strvalue(O), UTF-8) rule invokeBuiltin(obj("rsplit_str",_), ListItem(O:Object), .) => ref("rsplit_str")(O, ref("None"), -1) rule invokeBuiltin(obj("rsplit_str",_), ListItem(O:Object) ListItem(Sep:Object), .) => ref("rsplit_str")(O, Sep, -1) rule invokeBuiltin(obj("rsplit_str",_), ListItem(O:Object) ListItem(Sep:Object) ListItem(MaxSplit:Object), .) => if Sep is ref("None") : rsplit(strvalue(O), ref("index")(MaxSplit)) else: test(istype(Sep, ref("str")), rsplit(strvalue(O), ref("index")(MaxSplit), strvalue(Sep)), raiseInternal("TypeError", "Cannot convert object to str implicitly")) syntax ObjRef ::= rsplit(Exp, Exp) [strict] | rsplit(Exp, Exp, String) [strict(1, 2)] rule rsplit(S:Object, MaxSplit:Object, Sep:String) => test(intvalue(MaxSplit) ==Int 0, [S], test(rfindString(strvalue(S), Sep, lengthString(strvalue(S))) ==Int -1, [strvalue(S)], rsplit(ref("rpartition_str")(strvalue(S), Sep)[0], intvalue(MaxSplit) -Int 1, Sep) + [ref("rpartition_str")(strvalue(S), Sep)[2]])) //TODO: rsplit(None, ...) rule invokeBuiltin(obj("format_str",_), ListItem(Format) L:List, M:Map) => format(strvalue(Format), tuple(DeListify(L)), {DeMapify(M)}, .K) syntax ObjRef ::= format(String, Exp, Exp, K) [strict(2, 3)] | formatField(String, String, Object, Object, K) | lookupField(String, String, String, Object, Object, K) | lookupField(String, K, String, String, Object, Object, K) [strict(2)] | convertField(Object, String) | checkFormat(Exp) [strict] syntax K ::= lookupArgName(String) | lookupElementIndex(String, Object) [function] syntax Int ::= "notFound" rule notFound +Int I:Int => notFound [simplification] rule notFound -Int I:Int => notFound [simplification] rule notFound ==Int notFound => true [simplification] rule notFound ==Int I:#Int => false [simplification] rule I:#Int ==Int notFound => false [simplification] rule minInt(notFound, I:Int) => I [simplification] rule minInt(I:Int, notFound) => I [simplification] rule substrString(S, notFound, _) => "" [simplification] rule substrString(S, Start, notFound) => substrString(S, Start, lengthString(S)) [simplification] syntax Int ::= findCharsInString(String, String, Int) [function] rule findCharsInString(S, Pattern, I) => minInt(#if findString(S, substrString(Pattern, 0, 1), I) ==Int -1 #then notFound #else findString(S, substrString(Pattern, 0, 1), I) #fi, findCharsInString(S, substrString(Pattern, 1, lengthString(Pattern)), I)) when Pattern =/=String "" rule findCharsInString(S, "", _) => notFound rule format(Format, L, M, IsAuto) => substrString(Format, 0, findCharsInString(Format, "{}", 1)) + format(substrString(Format, findCharsInString(Format, "{}", 1), lengthString(Format)), L, M, IsAuto) when substrString(Format, 0, 1) =/=String "{" andBool substrString(Format, 0, 1) =/=String "}" andBool Format =/=String "" rule format("", _, _, _) => "" rule format(Format, L, M, IsAuto) => raiseInternal("ValueError", "single '}' encountered in format string") when substrString(Format, 0, 1) ==String "}" andBool substrString(Format, 0, 2) =/=String "}}" rule format(Format, L, M, IsAuto) => "{" + format(substrString(Format, 2, lengthString(Format)), L, M, IsAuto) when substrString(Format, 0, 2) ==String "{{" rule format(Format, L, M, IsAuto) => "}" + format(substrString(Format, 2, lengthString(Format)), L, M, IsAuto) when substrString(Format, 0, 2) ==String "}}" rule format(Format, L, M, IsAuto) => raiseInternal("ValueError", "unmatched '{' in format") when substrString(Format, 0, 1) ==String "{" andBool substrString(Format, findCharsInString(Format, "{}", 1), findCharsInString(Format, "{}", 1) +Int 1) ==String "{" rule format(Format, L, M, IsAuto) => raiseInternal("ValueError", "single '{' encountered in format string") when substrString(Format, 0, 1) ==String "{" andBool findCharsInString(Format, "{}", 1) ==Int notFound rule format(Format, L:Object, M:Object, IsAuto) => formatField(substrString(Format, 1, findCharsInString(Format, "{}", 1)), substrString(Format, findCharsInString(Format, "{}", 1) +Int 1, lengthString(Format)), L, M, IsAuto) when substrString(Format, 0, 1) ==String "{" andBool substrString(Format, findCharsInString(Format, "{}", 1), findCharsInString(Format, "{}", 1) +Int 1) ==String "}" rule formatField(Field, Format, L, M, IsAuto) => lookupField(substrString(Field, 0, findCharsInString(Field, "!:", 0)), Field, Format, L, M, IsAuto) rule lookupField(FieldName, Field, Format, L, M, IsAuto) => lookupField(substrString(FieldName, findCharsInString(FieldName, ".[", 0), lengthString(FieldName)), lookupArgName(substrString(FieldName, 0, findCharsInString(FieldName, ".[", 0))), Field, Format, L, M, IsAuto) rule [format-getattr]: lookupField(FieldName, O:Object, Field, Format, L, M, IsAuto) => lookupField(substrString(FieldName, findCharsInString(FieldName, ".[", 1), lengthString(FieldName)), ref("getattr")(O, substrString(FieldName, 1, findCharsInString(FieldName, ".[", 1))), Field, Format, L, M, IsAuto) when substrString(FieldName, 0, 1) ==String "." rule [format-getitem]: lookupField(FieldName, O:Object, Field, Format, L, M, IsAuto) => lookupField(substrString(FieldName, findCharsInString(FieldName, "]", 1) +Int 1, lengthString(FieldName)), lookupElementIndex(substrString(FieldName, 1, findCharsInString(FieldName, "]", 1)), O), Field, Format, L, M, IsAuto) when substrString(FieldName, 0, 1) ==String "[" context lookupField(_, HOLE, _, _, _, _, _) when isExp(HOLE) rule lookupField(_, lookupArgName(""), _, _, _, _, -1) => raiseInternal("ValueError", "cannot switch from manual field specification to automatic field numbering") rule [auto-to-manual]: lookupField(_, lookupArgName(ArgName), _, _, _, _, I) => raiseInternal("ValueError", "cannot switch from automatic field numbering to manual field specification") when I >=Int 0 andBool isInt(String2Int(ArgName)) rule lookupField(_, (lookupArgName("") => L[#if IsAuto ==K .K #then 0 #else IsAuto #fi]), _, _, L, _, (IsAuto => #if IsAuto ==K .K #then 1 #else IsAuto +Int 1 #fi)) when IsAuto =/=K -1 rule lookupField(_, (lookupArgName(ArgName) => L[String2Int(ArgName)]), _, _, L, _, (IsAuto => -1)) when (IsAuto ==K -1 orBool IsAuto ==K .K) andBool isInt(String2Int(ArgName)) rule lookupField(_, (lookupArgName(ArgName) => M[ArgName]), _, _, _, M, _) when isInt(String2Int(ArgName)) =/=K true rule lookupElementIndex(ElementIndex, O) => O[String2Int(ElementIndex)] when isInt(String2Int(ElementIndex)) rule lookupElementIndex(ElementIndex, O) => O[ElementIndex] when isInt(String2Int(ElementIndex)) =/=K true rule lookupField(_, O:Object, Field, Format, L, M, IsAuto) => ref("format")(convertField(O, substrString(Field, findCharsInString(Field, ":!", 0), findCharsInString(Field, ":!", 0) +Int 3)), substrString(Field, findCharsInString(Field, ":", 0) +Int 1, lengthString(Field))) + format(Format, L, M, IsAuto) rule convertField(O:Object, Conversion) => ref("str")(O) when Conversion ==String "!s" orBool Conversion ==String "!s:" rule convertField(O:Object, Conversion) => ref("repr")(O) when Conversion ==String "!r" orBool Conversion ==String "!r:" rule convertField(O:Object, Conversion) => ref("ascii")(O) when Conversion ==String "!a" orBool Conversion ==String "!a:" rule convertField(O:Object, Conversion) => O when Conversion ==String "" orBool substrString(Conversion, 0, 1) ==String ":" rule convertField(O:Object, Conversion) => raiseInternal("ValueError", "unknown conversion specifier" +String substrString(Conversion, 1, 2)) when substrString(Conversion, 0, 1) ==String "!" andBool (substrString(Conversion, 2, 3) ==String ":" orBool lengthString(Conversion) ==Int 2) rule convertField(O:Object, Conversion) => raiseInternal("ValueError", "expected ':' after format specifier") when substrString(Conversion, 0, 1) ==String "!" andBool (substrString(Conversion, 1, 2) ==String "s" orBool substrString(Conversion, 1, 2) ==String "r" orBool substrString(Conversion, 1, 2) ==String "a") andBool substrString(Conversion, 2, 3) =/=String "" andBool substrString(Conversion, 2, 3) =/=String ":" rule invokeBuiltin(obj("format",_), ListItem(O:Object), .) => ref("format")(O, "") rule invokeBuiltin(obj("format",_), ListItem(O:Object) ListItem(Format:Object), .) => checkFormat(getmember(O, "__format__", true, false, true)(Format)) rule checkFormat(O:Object) => test(istype(O, ref("str")), O, raiseInternal("TypeError", "__format__ method did not return string")) rule invokeBuiltin(obj("format_object",_), ListItem(O:Object) ListItem(Format:Object), .) => getmember(ref("str")(O), "__format__", true, false, true)(Format) syntax FormatSpec ::= parseFormatSpec(String) [function] | parseFormatSpec(String, String) [function] | parseFormatSpec(String, String, String) [function] | parseFormatSpec(String, String, String, String) [function] | parseFormatSpec(String, String, String, String, String) [function] | parseFormatSpec(String, String, String, String, String, String) [function] | parseFormatSpec(String, String, String, String, String, String, String) [function] | parseFormatSpec(String, String, String, String, String, String, String, String) [function] | formatSpec(String, String, String, String, String, String, String, String) | "invalidFormatSpec" syntax ObjRef ::= doFormatStr(String, FormatSpec) rule invokeBuiltin(obj("__format___str",_), ListItem(O:Object) ListItem(Format:Object), .) => doFormatStr(strvalue(O), parseFormatSpec(strvalue(Format))) rule parseFormatSpec(Format) => #if substrString(Format, 1, 2) =/=String "<" andBool substrString(Format, 1, 2) =/=String ">" andBool substrString(Format, 1, 2) =/=String "=" andBool substrString(Format, 1, 2) =/=String "^" #then parseFormatSpec("", Format) #else parseFormatSpec(substrString(Format, 0, 1), substrString(Format, 1, lengthString(Format))) #fi rule parseFormatSpec(Fill, Format) => #if substrString(Format, 0, 1) ==String "<" orBool substrString(Format, 0, 1) ==String ">" orBool substrString(Format, 0, 1) ==String "=" orBool substrString(Format, 0, 1) ==String "^" #then parseFormatSpec(Fill, substrString(Format, 0, 1), substrString(Format, 1, lengthString(Format))) #else parseFormatSpec(Fill, "", Format) #fi rule parseFormatSpec(Fill, Align, Format) => #if substrString(Format, 0, 1) ==String "+" orBool substrString(Format, 0, 1) ==String "-" orBool substrString(Format, 0, 1) ==String " " #then parseFormatSpec(Fill, Align, substrString(Format, 0, 1), substrString(Format, 1, lengthString(Format))) #else parseFormatSpec(Fill, Align, "", Format) #fi rule parseFormatSpec(Fill, Align, Sign, Format) => #if substrString(Format, 0, 1) ==String "#" #then parseFormatSpec(Fill, Align, Sign, "#", substrString(Format, 1, lengthString(Format))) #else parseFormatSpec(Fill, Align, Sign, "", Format) #fi rule parseFormatSpec(Fill, Align, Sign, AltForm, Format) => #if substrString(Format, 0, 1) ==String "0" #then parseFormatSpec(#if Fill ==String "" #then "0" #else Fill #fi, #if Align ==String "" #then "=" #else Align #fi, Sign, AltForm, "", substrString(Format, 1, lengthString(Format))) #else parseFormatSpec(Fill, Align, Sign, AltForm, "", Format) #fi rule parseFormatSpec(Fill, Align, Sign, AltForm, Width, Format) => parseFormatSpec(Fill, Align, Sign, AltForm, Width +String substrString(Format, 0, 1), substrString(Format, 1, lengthString(Format))) when ordChar(substrString(Format, 0, 1)) >=Int 48 andBool ordChar(substrString(Format, 0, 1)) <=Int 57 rule parseFormatSpec(Fill, Align, Sign, AltForm, Width, Format) => #if substrString(Format, 0, 1) ==String "," #then parseFormatSpec(Fill, Align, Sign, AltForm, Width, ",", substrString(Format, 1, lengthString(Format))) #else parseFormatSpec(Fill, Align, Sign, AltForm, Width, "", Format) #fi when ordChar(substrString(Format, 0, 1)) Int 57 orElseBool Format ==String "" rule parseFormatSpec(Fill, Align, Sign, AltForm, Width, Thousands, Format) => #if substrString(Format, 0, 1) ==String "." #then parseFormatSpec(Fill, Align, Sign, AltForm, Width, Thousands, ".", substrString(Format, 1, lengthString(Format))) #else parseFormatSpec(Fill, Align, Sign, AltForm, Width, Thousands, "", Format) #fi rule parseFormatSpec(Fill, Align, Sign, AltForm, Width, Thousands, Precision, Format) => parseFormatSpec(Fill, Align, Sign, AltForm, Width, Thousands, Precision +String substrString(Format, 0, 1), substrString(Format, 1, lengthString(Format))) when ordChar(substrString(Format, 0, 1)) >=Int 48 andBool ordChar(substrString(Format, 0, 1)) <=Int 57 andBool lengthString(Precision) >Int 0 rule parseFormatSpec(Fill, Align, Sign, AltForm, Width, Thousands, Precision, Format) => formatSpec(Fill, Align, Sign, AltForm, Width, Thousands, Precision, Format) when (ordChar(substrString(Format, 0, 1)) Int 57 orElseBool lengthString(Precision) ==Int 0 orElseBool Format ==String "") andBool lengthString(Format) <=Int 1 rule parseFormatSpec(_, _, _, _, _, _, Precision, Format) => invalidFormatSpec when (ordChar(substrString(Format, 0, 1)) Int 57 orBool lengthString(Precision) ==Int 0) andBool lengthString(Format) >Int 1 rule doFormatStr(_, invalidFormatSpec) => raiseInternal("ValueError", "Invalid format specifier") rule doFormatStr(_, formatSpec(_, "=", _, _, _, _, _, _)) => raiseInternal("ValueError", "'=' alignment not allowed in string format specifier") rule doFormatStr(_, formatSpec(_, _, Sign, _, _, _, _, _)) => raiseInternal("ValueError", "Sign not allowed in string format specifier") when Sign =/=String "" rule doFormatStr(_, formatSpec(_, _, _, "#", _, _, _, _)) => raiseInternal("ValueError", "Alternate form (#) not allowed in string format specifier") rule doFormatStr(_, formatSpec(_, _, _, _, _, ",", _, _)) => raiseInternal("ValueError", "Cannot specify ',' with 's'") rule doFormatStr(_, formatSpec(_, _, _, _, _, _, ".", _)) => raiseInternal("ValueError", "Format specifier missing precision") rule doFormatStr(_, formatSpec(_, _, _, _, _, _, _, Type)) => raiseInternal("ValueError", "Unknown format code '" +String Type +String "' for string object") when Type =/=String "s" andBool Type =/=String "" rule doFormatStr(S, formatSpec(Fill, Align, "", "", Width, "", Precision, Type)) => alignString(#if Precision ==String "" #then S #else substrString(S, 0, String2Int(substrString(Precision, 1, lengthString(Precision)))) #fi, #if Fill ==String "" #then " " #else Fill #fi, #if Align ==String "" #then "<" #else Align #fi, #if Width ==String "" #then 0 #else String2Int(Width) #fi) when (Type ==String "s" orBool Type ==String "") andBool Align =/=String "=" syntax String ::= alignString(String, String, String, Int) [function] rule alignString(S, Fill, "<", Width) => #if lengthString(S) >=Int Width #then S #else S +String (Fill *String (Width -Int lengthString(S))) #fi rule alignString(S, Fill, ">", Width) => #if lengthString(S) >=Int Width #then S #else (Fill *String (Width -Int lengthString(S))) +String S #fi rule alignString(S, Fill, "^", Width) => #if lengthString(S) >=Int Width #then S #else (Fill *String ((Width -Int lengthString(S)) /Int 2)) +String S +String (Fill *String ((Width -Int lengthString(S)) /Int 2 +Int (Width -Int lengthString(S)) %Int 2)) #fi rule invokeBuiltin(obj("startswith_str",_), ListItem(S:Object) ListItem(Prefix:Object), .) => test(istype(Prefix, ref("str")), startswith(strvalue(S), Prefix), test(istype(Prefix, ref("tuple")), startswith(strvalue(S), listvalue(Prefix)), raiseInternal("TypeError", "startswith first arg must be str or tuple of str"))) rule invokeBuiltin(obj("startswith_str",_), ListItem(S:Object) ListItem(Prefix:Object) ListItem(Start:Object), .) => ref("startswith_str")(S, Prefix, Start, lengthString(strvalue(S))) rule invokeBuiltin(obj("startswith_str",_), ListItem(S:Object) ListItem(Prefix:Object) ListItem(Start:Object) ListItem(End:Object), .) => ref("startswith_str")(S[Start : End : ], Prefix) syntax Exp ::= startswith(String, Exp) [strict(2)] syntax Exp ::= startswith(String, List) rule startswith(S:String, Prefix:Object) => test(istype(Prefix, ref("str")), #if findString(S, strvalue(Prefix), 0) ==Int 0 #then ref("True") #else ref("False") #fi, raiseInternal("TypeError", "Cannot convert object to str implicitly")) rule startswith(S:String, ListItem(ref(N)) L:List) => startswith(S, ref(N)) or startswith(S, L) rule startswith(S:String, .List) => ref("False") //TODO: fix ref(id()). We need this right now because if I pass an object then strictness will refuse to heat because it's already a KResult. Need to wait for improved heating/cooling rule invokeBuiltin(obj("getitem_str",_), ListItem(S:Object) ListItem(Item:Object), .) => test(istype(Item, ref("slice")), reduce(slice(S, Item), "", '_+_), getitemString(strvalue(S), ref(id(Item)))) syntax Exp ::= getitemString(String, Exp) rule getitemString(S, E:Exp) => ref("index")(E) ~> getitemString(S, HOLE) when isInt(E) =/=K true [heat] rule O:Object ~> getitemString(S, HOLE) => getitemString(S, intvalue(O)) [cool] rule getitemString(S:String, I:Int) => test(I >=Int 0 -Int length(S, UTF-8) andBool I =Int 0, bytes(S, UTF-8, I), bytes(S, UTF-8, I +Int length(S, UTF-8))), raiseInternal("IndexError", "string index out of range")) rule invokeBuiltin(obj("str.__mul__",_), ListItem(S) ListItem(I), .) => multiplyString(strvalue(S), ref(id(I))) rule invokeBuiltin(obj("str.__rmul__",_), ListItem(S) ListItem(I), .) => multiplyString(strvalue(S), ref(id(I))) syntax Exp ::= multiplyString(String, Exp) rule multiplyString(S, E:Exp) => ref("index")(E) ~> multiplyString(S, HOLE) when isInt(E) =/=K true [heat] rule O:Object ~> multiplyString(S, HOLE) => multiplyString(S, intvalue(O)) [cool] rule multiplyString(S:String, I:Int) => S *String I rule invokeBuiltin(obj("join_str",_), ListItem(S) ListItem(L), .) => joinString(strvalue(S), iterate(L, .)) //this is a bit dumb of a structure, but oh well syntax String ::= joinString(String, Exp) [strict(2)] syntax String ::= joinString2(String, Exp, String) [function] rule joinString(S, list(L:List)) => joinString2(S, list(L:List), "") rule joinString2(S, list(ListItem(O) ListItem(O2) L:List), S2) => test(istype(O, ref("str")), joinString2(S, list(ListItem(O2) L), S2 +String strvalue(O) +String S), raiseInternal("TypeError", "expected str instance as sequence item")) rule joinString2(_, list(ListItem(O)), S) => test(istype(O, ref("str")), S +String strvalue(O), raiseInternal("TypeError", "expected str instance as sequence item")) rule joinString2(_, list(.), S) => S rule invokeBuiltin(obj("repr",_), ListItem(S), .) => checkRepr(getmember(S, "__repr__", true, false, true) (.Arguments)) syntax Exp ::= checkRepr(Exp) [strict] rule checkRepr(O:Object) => test(istype(O, ref("str")), O, raiseInternal("TypeError", "__repr__ returned non-string")) rule invokeBuiltin(obj("str.__repr__",_), ListItem(S), .) => reprString(strvalue(S), #if findString(strvalue(S), "'", 0) =/=Int -1 andBool findString(strvalue(S), "\"", 0) ==Int -1 #then "\"" #else "'" #fi) syntax String ::= reprString(String, String) [function] rule reprString(S, Quote) => Quote +String reprString2(S, Quote) +String Quote syntax String ::= reprString2(String, String) [function] rule reprString2(S, Quote) => reprString3(bytes(S, UTF-8, 0), Quote) +String reprString2(substrString(S, lengthString(bytes(S, UTF-8, 0)), lengthString(S)), Quote) when S =/=String "" rule reprString2("", _) => "" syntax String ::= reprString3(String, String) [function] rule reprString3(Quote, Quote) => "\\" +String Quote rule reprString3("\\", _) => "\\\\" rule reprString3("\t", _) => "\\t" rule reprString3("\n", _) => "\\n" rule reprString3("\r", _) => "\\r" rule reprString3(S, Quote) => reprString4(codePoint(S, UTF-8, 0), S) when S =/=String Quote andBool S =/=String "\\" andBool S =/=String "\t" andBool S =/=String "\n" andBool S =/=String "\r" syntax String ::= reprString4(Int, String) [function] rule reprString4(CodePoint, _) => "\\x" +String Int2Hex(CodePoint, 2) when CodePoint S when CodePoint >=Int 32 andBool CodePoint S when CodePoint >Int 127 andBool isPrintable(CodePoint) //rule reprString4(CodePoint, _) => "\\x" +String Int2Hex(CodePoint, 2) when CodePoint >Int 127 andBool CodePoint "\\u" +String Int2Hex(CodePoint, 4) when CodePoint >=Int 256 andBool CodePoint "\\U" +String Int2Hex(CodePoint, 8) when CodePoint >=Int 65536 andBool notBool isPrintable(CodePoint) endmodule