Skip to content

Commit 3f14f3d

Browse files
committed
py, builtin, vm: make str() and repr() and implement some __str__ and __repr__
* builtin: implement repr * buitin and py/str: make str() work * py: str, int, bigint: __str__ and __repr__ for * py: Internal Str, Repr methods * vm: Make REPL use Repr for printing
1 parent de8c04a commit 3f14f3d

File tree

12 files changed

+329
-32
lines changed

12 files changed

+329
-32
lines changed

builtin/builtin.go

+10-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func init() {
5252
py.MustNewMethod("ord", builtin_ord, 0, ord_doc),
5353
py.MustNewMethod("pow", builtin_pow, 0, pow_doc),
5454
py.MustNewMethod("print", builtin_print, 0, print_doc),
55-
// py.MustNewMethod("repr", builtin_repr, 0, repr_doc),
55+
py.MustNewMethod("repr", builtin_repr, 0, repr_doc),
5656
py.MustNewMethod("round", builtin_round, 0, round_doc),
5757
py.MustNewMethod("setattr", builtin_setattr, 0, setattr_doc),
5858
// py.MustNewMethod("sorted", builtin_sorted, 0, sorted_doc),
@@ -194,6 +194,15 @@ func builtin_print(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Obje
194194
return py.None, nil
195195
}
196196

197+
const repr_doc = `repr(object) -> string
198+
199+
Return the canonical string representation of the object.
200+
For most object types, eval(repr(object)) == object.`
201+
202+
func builtin_repr(self py.Object, obj py.Object) (py.Object, error) {
203+
return py.Repr(obj)
204+
}
205+
197206
const pow_doc = `pow(x, y[, z]) -> number
198207
199208
With two arguments, equivalent to x**y. With three arguments,

builtin/tests/builtin.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,11 @@ def gen2():
111111

112112
doc="pow"
113113
assert pow(2, 10) == 1024
114-
# FIXME assert pow(2, 10, 17) == 4
114+
assert pow(2, 10, 17) == 4
115+
116+
doc="repr"
117+
assert repr(5) == "5"
118+
assert repr("hello") == "'hello'"
115119

116120
doc="print"
117121
# FIXME - need io redirection to test

notes.txt

+90-25
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,110 @@ Code Quality
44
* errchk ./...
55
* go vet ./...
66

7-
Limitations
8-
===========
7+
IDEA replace all .(cast) with CheckExactString or whatever python calls it...
8+
9+
Then can use CheckString later...
10+
11+
Limitations & Missing parts
12+
===========================
913
* string keys only in dictionaries
1014
* intermediate fix - stop it panicing!
1115
* line numbers missing in SyntaxErrors
1216
* \N{...} escapes not implemented
1317
* Interactive interpreter does single lines only
1418
* compile(..., "single") not working
1519
* lots of builtins still to implement
20+
* FIXME eq && ne should throw an error for a type which doesn' have eq implemented
21+
* repr/str
22+
* subclassing built in types - how? Need to make sure we have called the appropriate method everywhere rather than just .(String)
23+
* FIXME how do mapping types work?
1624

17-
Todo
18-
====
25+
Type ideas
26+
==========
1927

20-
FIXME move the whole of Vm into Frame! Perhaps decode the extended args inline.
28+
* Embed a zero sized struct with default implementations of all the methods to avoid interface bloat
29+
* would have a pointer to the start of the object so could do comparisons
30+
* however don't know anything about the object - can't call Type() for instance
31+
* except for String/Int/Bool/Tuple/Float/Bytes and possibly some others...
32+
* can embed this default interface whether we do fat interfaces or not...
33+
* can have heirachies of embedding
2134

22-
Speedup
2335
* Make Object a fat interface so it defines all the M__method__ or at least all the common ones
24-
* Embed a zero sized struct with default implementations of all the methods to avoid interface bloat
25-
* except for String/Int/Bool/Tuple/Float/Bytes and possibly some others...
36+
* would make the M__call__ quicker than needing a type assertion
2637
* Or Make the methods be pointers in *Type more like python
27-
* Function pointers would have to be func(a Object) (Object, error) which would mean a type assertion :-(
38+
* Closer to the way python does it and would be faster than type assertion
39+
* Function pointers would have to be func(a Object) (Object,
40+
error) which would mean a type assertion which we are trying to
41+
avoid :-(
2842
* An interface is the go way of doing this
2943

44+
What methods are a default implementation useful for?
45+
__eq__
46+
__ne__
47+
__repr__
48+
__str__
49+
__lt__ __add__ etc - could return NotImplemented
50+
__hash__ - based on pointer?
51+
52+
Minimum set of methods / attributes (eg on None)
53+
54+
Attributes
55+
56+
__class__
57+
__doc__
58+
59+
Methods
60+
61+
__bool__
62+
__delattr__
63+
__dir__
64+
__format__
65+
__getattribute__
66+
__hash__
67+
__init__
68+
__new__
69+
__reduce__ - pickle protocol - missing!
70+
__reduce_ex__ - pickle protocol - missing!
71+
__repr__
72+
__setattr__
73+
__sizeof__ - missing from py.go
74+
__str__
75+
__subclasshook__ - missing from py.go
76+
77+
Comparison
78+
79+
__eq__
80+
__ge__
81+
__gt__
82+
__le__
83+
__lt__
84+
__ne__
85+
86+
87+
genpy
88+
=====
89+
90+
Code automation to help with the boilerplate of making types and modules
91+
92+
* For modules
93+
* instrospect code
94+
* make module table
95+
* fill in arguments
96+
* fill in docstr (from comments)
97+
* Module methods should be exportable with capital letter
98+
99+
* For a type
100+
* create getters/setters from struct comments
101+
* create default implementations
102+
* independent of use 0 sized embedded struct idea
103+
104+
105+
Todo
106+
====
107+
108+
FIXME move the whole of Vm into Frame! Perhaps decode the extended args inline.
109+
110+
Speedup
30111

31112
* Getting rid of panic/defer error handling
32113

@@ -37,7 +118,6 @@ After 3278 pystone/s
37118
After compile debug out with a constant 9293 pystones/s + 180%
38119
Keep locals 9789 local 5% improvement
39120

40-
41121
Still to do
42122
* think about exception handling - do nested tracebacks work?
43123
* write a test for it!
@@ -57,12 +137,6 @@ CAN now inline the do_XXX functions into the EvalFrame which will probably speed
57137
* "select" - no idea how to implement!
58138
* "mutex"
59139

60-
* Write gengpy tool to
61-
* instrospect code
62-
* fill in module table
63-
* fill in arguments
64-
* fill in docstr (from comments)
65-
66140
FIXME need to be able to tell classes an instances apart!
67141

68142
Put C modules in sub directory
@@ -122,15 +196,6 @@ Exceptions
122196

123197
panic/recover only within single modules (like compiler/parser)
124198

125-
Missing parts
126-
=============
127-
* repr/str
128-
* subclassing built in types
129-
* integers > 64 bit
130-
* make py.Int be a wrapper for int
131-
* make it promote to py.BigInt
132-
* StringDict vs Dict
133-
134199
Polymorphism
135200
============
136201

py/bigint.go

+9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package py
44

55
import (
6+
"fmt"
67
"math"
78
"math/big"
89
)
@@ -16,6 +17,14 @@ func (o *BigInt) Type() *Type {
1617
return BigIntType
1718
}
1819

20+
func (a *BigInt) M__str__() (Object, error) {
21+
return String(fmt.Sprintf("%d", (*big.Int)(a))), nil
22+
}
23+
24+
func (a *BigInt) M__repr__() (Object, error) {
25+
return a.M__str__()
26+
}
27+
1928
// Some common BigInts
2029
var (
2130
bigInt0 = (*BigInt)(big.NewInt(0))

py/dict.go

+13-2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,20 @@
55

66
package py
77

8-
var StringDictType = NewType("dict", "dict() -> new empty dictionary\ndict(mapping) -> new dictionary initialized from a mapping object's\n (key, value) pairs\ndict(iterable) -> new dictionary initialized as if via:\n d = {}\n for k, v in iterable:\n d[k] = v\ndict(**kwargs) -> new dictionary initialized with the name=value pairs\n in the keyword argument list. For example: dict(one=1, two=2)")
8+
const dictDoc = `dict() -> new empty dictionary
9+
dict(mapping) -> new dictionary initialized from a mapping object's
10+
(key, value) pairs
11+
dict(iterable) -> new dictionary initialized as if via:
12+
d = {}
13+
for k, v in iterable:
14+
d[k] = v
15+
dict(**kwargs) -> new dictionary initialized with the name=value pairs
16+
in the keyword argument list. For example: dict(one=1, two=2)`
917

10-
var DictType = NewType("dict", "dict() -> new empty dictionary\ndict(mapping) -> new dictionary initialized from a mapping object's\n (key, value) pairs\ndict(iterable) -> new dictionary initialized as if via:\n d = {}\n for k, v in iterable:\n d[k] = v\ndict(**kwargs) -> new dictionary initialized with the name=value pairs\n in the keyword argument list. For example: dict(one=1, two=2)")
18+
var (
19+
StringDictType = NewType("dict", dictDoc)
20+
DictType = NewType("dict", dictDoc)
21+
)
1122

1223
// String to object dictionary
1324
//

py/float.go

+9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package py
44

55
import (
6+
"fmt"
67
"math"
78
"math/big"
89
"strconv"
@@ -41,6 +42,14 @@ func FloatNew(metatype *Type, args Tuple, kwargs StringDict) (Object, error) {
4142
return MakeFloat(xObj)
4243
}
4344

45+
func (a Float) M__str__() (Object, error) {
46+
return String(fmt.Sprintf("%g", a)), nil
47+
}
48+
49+
func (a Float) M__repr__() (Object, error) {
50+
return a.M__str__()
51+
}
52+
4453
// FloatFromString turns a string into a Float
4554
func FloatFromString(str string) (Object, error) {
4655
f, err := strconv.ParseFloat(str, 64)

py/int.go

+9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package py
44

55
import (
6+
"fmt"
67
"math"
78
"math/big"
89
"strconv"
@@ -174,6 +175,14 @@ func (x Int) GoInt() (int, error) {
174175
return int(r), nil
175176
}
176177

178+
func (a Int) M__str__() (Object, error) {
179+
return String(fmt.Sprintf("%d", a)), nil
180+
}
181+
182+
func (a Int) M__repr__() (Object, error) {
183+
return a.M__str__()
184+
}
185+
177186
// Arithmetic
178187

179188
// Errors

py/internal.go

+54
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
package py
66

7+
import "fmt"
8+
79
// AttributeName converts an Object to a string, raising a TypeError
810
// if it wasn't a String
911
func AttributeName(keyObj Object) (string, error) {
@@ -321,3 +323,55 @@ func DeleteAttr(self Object, keyObj Object) error {
321323
}
322324
return DeleteAttrString(self, key)
323325
}
326+
327+
// Calls __str__ on the object
328+
//
329+
// If no method was found, returns ok as false
330+
func str(self Object) (res Object, ok bool, err error) {
331+
if _, ok = self.(String); ok {
332+
return self, true, nil
333+
}
334+
if I, ok := self.(I__str__); ok {
335+
res, err = I.M__str__()
336+
return res, true, err
337+
} else if res, ok, err := TypeCall0(self, "__str__"); ok {
338+
return res, true, err
339+
}
340+
return nil, false, nil
341+
}
342+
343+
// Calls __str__ on the object
344+
func Str(self Object) (Object, error) {
345+
res, ok, err := str(self)
346+
if err != nil {
347+
return nil, err
348+
}
349+
if !ok {
350+
return nil, ExceptionNewf(TypeError, "object of type '%s' has no __str__()", self.Type().Name)
351+
}
352+
return res, err
353+
}
354+
355+
// Calls __repr__ on the object or returns a sensible default
356+
func Repr(self Object) (Object, error) {
357+
if I, ok := self.(I__repr__); ok {
358+
return I.M__repr__()
359+
} else if res, ok, err := TypeCall0(self, "__repr__"); ok {
360+
return res, err
361+
}
362+
return String(fmt.Sprintf("<%s instance at %p>", self.Type().Name, self)), nil
363+
}
364+
365+
// Returns object as a string
366+
//
367+
// Calls __str__ then __repr__ on the object then makes something up
368+
func AsString(self Object) (Object, error) {
369+
res, ok, err := str(self)
370+
if err != nil {
371+
return nil, err
372+
}
373+
if ok {
374+
return res, err
375+
}
376+
return Repr(self)
377+
}

0 commit comments

Comments
 (0)