From a3dfc792ddf8ecda9413c3546ef9b90532b3ed44 Mon Sep 17 00:00:00 2001 From: Dong-hee Na Date: Wed, 31 Jul 2019 16:16:30 +0900 Subject: [PATCH] builtin: Implement builtin_ascii --- builtin/builtin.go | 15 +++++- builtin/tests/builtin.py | 6 +++ py/string.go | 105 +++++++++++++++++++++------------------ 3 files changed, 77 insertions(+), 49 deletions(-) diff --git a/builtin/builtin.go b/builtin/builtin.go index a984928a..8ffda37d 100644 --- a/builtin/builtin.go +++ b/builtin/builtin.go @@ -24,7 +24,7 @@ func init() { py.MustNewMethod("abs", builtin_abs, 0, abs_doc), py.MustNewMethod("all", builtin_all, 0, all_doc), py.MustNewMethod("any", builtin_any, 0, any_doc), - // py.MustNewMethod("ascii", builtin_ascii, 0, ascii_doc), + py.MustNewMethod("ascii", builtin_ascii, 0, ascii_doc), // py.MustNewMethod("bin", builtin_bin, 0, bin_doc), // py.MustNewMethod("callable", builtin_callable, 0, callable_doc), py.MustNewMethod("chr", builtin_chr, 0, chr_doc), @@ -309,6 +309,19 @@ func builtin_any(self, seq py.Object) (py.Object, error) { return py.False, nil } +const ascii_doc = ` +` + +func builtin_ascii(self, o py.Object) (py.Object, error) { + reprObj, err := py.Repr(o) + if err != nil { + return nil, err + } + repr := reprObj.(py.String) + out := py.StringEscape(repr, true) + return py.String(out), err +} + const round_doc = `round(number[, ndigits]) -> number Round a number to a given precision in decimal digits (default 0 digits). diff --git a/builtin/tests/builtin.py b/builtin/tests/builtin.py index 1349c891..0b547f3f 100644 --- a/builtin/tests/builtin.py +++ b/builtin/tests/builtin.py @@ -19,6 +19,12 @@ assert any(["hello", "world"]) == True assert any([]) == False +doc="ascii" +assert ascii('hello world') == "'hello world'" +assert ascii('안녕 세상') == "'\\uc548\\ub155 \\uc138\\uc0c1'" +assert ascii(chr(0x10001)) == "'\\U00010001'" +assert ascii('안녕 gpython') == "'\\uc548\\ub155 gpython'" + doc="chr" assert chr(65) == "A" assert chr(163) == "£" diff --git a/py/string.go b/py/string.go index 33d27b6f..83440f4b 100644 --- a/py/string.go +++ b/py/string.go @@ -35,6 +35,61 @@ or repr(object). encoding defaults to sys.getdefaultencoding(). errors defaults to 'strict'.`, StrNew, nil) +// Escape the py.String +func StringEscape(a String, ascii bool) string { + s := string(a) + var out bytes.Buffer + quote := '\'' + if strings.ContainsRune(s, '\'') && !strings.ContainsRune(s, '"') { + quote = '"' + } + if !ascii { + out.WriteRune(quote) + } + for _, c := range s { + switch { + case c < 0x20: + switch c { + case '\t': + out.WriteString(`\t`) + case '\n': + out.WriteString(`\n`) + case '\r': + out.WriteString(`\r`) + default: + fmt.Fprintf(&out, `\x%02x`, c) + } + case !ascii && c < 0x7F: + if c == '\\' || (quote == '\'' && c == '\'') || (quote == '"' && c == '"') { + out.WriteRune('\\') + } + out.WriteRune(c) + case c < 0x100: + if ascii || strconv.IsPrint(c) { + out.WriteRune(c) + } else { + fmt.Fprintf(&out, "\\x%02x", c) + } + case c < 0x10000: + if !ascii && strconv.IsPrint(c) { + out.WriteRune(c) + } else { + fmt.Fprintf(&out, "\\u%04x", c) + } + default: + if !ascii && strconv.IsPrint(c) { + out.WriteRune(c) + } else { + fmt.Fprintf(&out, "\\U%08x", c) + } + } + } + if !ascii { + out.WriteRune(quote) + } + return out.String() +} + // standard golang strings.Fields doesn't have a 'first N' argument func fieldsN(s string, n int) []string { out := []string{} @@ -194,54 +249,8 @@ func (a String) M__str__() (Object, error) { } func (a String) M__repr__() (Object, error) { - // FIXME combine this with parser/stringescape.go into file in py? - s := string(a) - var out bytes.Buffer - quote := '\'' - if strings.ContainsRune(s, '\'') && !strings.ContainsRune(s, '"') { - quote = '"' - } - out.WriteRune(quote) - for _, c := range s { - switch { - case c < 0x20: - switch c { - case '\t': - out.WriteString(`\t`) - case '\n': - out.WriteString(`\n`) - case '\r': - out.WriteString(`\r`) - default: - fmt.Fprintf(&out, `\x%02x`, c) - } - case c < 0x7F: - if c == '\\' || (quote == '\'' && c == '\'') || (quote == '"' && c == '"') { - out.WriteRune('\\') - } - out.WriteRune(c) - case c < 0x100: - if strconv.IsPrint(c) { - out.WriteRune(c) - } else { - fmt.Fprintf(&out, "\\x%02x", c) - } - case c < 0x10000: - if strconv.IsPrint(c) { - out.WriteRune(c) - } else { - fmt.Fprintf(&out, "\\u%04x", c) - } - default: - if strconv.IsPrint(c) { - out.WriteRune(c) - } else { - fmt.Fprintf(&out, "\\U%08x", c) - } - } - } - out.WriteRune(quote) - return String(out.String()), nil + out := StringEscape(a, false) + return String(out), nil } func (s String) M__bool__() (Object, error) {