-
Notifications
You must be signed in to change notification settings - Fork 94
/
notes.txt
256 lines (179 loc) · 7.06 KB
/
notes.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
Code Quality
============
* errchk ./...
* go vet ./...
Limitations
===========
* string keys only in dictionaries
* ints only 64 bit
FIXME interesting crash with compiling `int("42"), sausage=11)`
Compile error: SystemError: [interface conversion: *ast.Call is not ast.SetCtxer: missing method SetCtx]
Todo
====
FIXME move the whole of Vm into Frame! perpahs decode the extended args inline.
Speedup
* Make Object a fat interface so it defines all the M__method__ or at least all the common ones
* Embed a zero sized struct with default implementations of all the methods to avoid interface bloat
* except for String/Int/Bool/Tuple/Float/Bytes and possibly some others...
* Or Make the methods be pointers in *Type more like python
* Function pointers would have to be func(a Object) (Object, error) which would mean a type assertion :-(
* An interface is the go way of doing this
* Getting rid of panic/defer error handling
Speed 35% improvement
Before 2430 pystone/s
After 3278 pystone/s
After compile debug out with a constant 9293 pystones/s + 180%
Keep locals 9789 local 5% improvement
Still to do
* think about exception handling - do nested tracebacks work?
* write a test for it!
* Make exception catching only catch py objects?
* perhaps make it convert go io errors into correct py error etc
* stop panics escaping symtable package
CAN now inline the do_XXX functions into the EvalFrame which will probably speed things up!
* also make vm.STACK into frame.STACK and keep a pointer to frame
* probably makes vm struct redundant? move its contents as locals.
* move frame into vm module? Then can't use *Frame in
* tried this - was 20% slower
* Write go module
* "go" command which is go(fn, args, kwargs) - implement with a builtin special in the interpreter probably
* "chan" to make a channel with methods get and put
* "select" - no idea how to implement!
* "mutex"
* Write gengpy tool to
* instrospect code
* fill in module table
* fill in arguments
* fill in docstr (from comments)
FIXME need to be able to tell classes an instances apart!
Put C modules in sub directory
Make an all submodule so can insert all of them easily with
import _ "github.com/ncw/gpython/stdlib/all"
Factor main code into submodule so gpython is minimal.
Make a gpython-minimal with no built in stdlib
Bytecode compile the standard library and embed into go files - can then make a gpython with no external files.
NB Would probably be a lot quicker to define
Arg struct {
Name string
Value Object
}
And pass args using []Arg instead of StringDict
Compiler
========
Complete but without optimisation.
Easy wins
* Constant folding, eg -1 is LOAD_CONST 1; NEGATE
* Jump optimisation - compiler emits lots of jumps to jumps
Testing
=======
python3 -m dis hello.py
python3 -mcompileall hello.py
mv __pycache__/hello.cpython-33.pyc hello.pyc
python3 -c 'import hello; import dis; dis.dis(hello.fn)'
go build ./... && go build
./gpython hello.pyc
When converting C code, run it through astyle first as a first pass
astyle --style=java --add-brackets < x.c > x.go
Roadmap
=======
* make pystone.py work - DONE
* make enough of internals work so can run python tests - DONE
* import python tests and write go test runner to run them
* make lots of them pass
Exceptions
==========
panic/recover only within single modules (like compiler/parser)
Missing parts
=============
* repr/str
* subclassing built in types
* integers > 64 bit
* make py.Int be a wrapper for int
* make it promote to py.BigInt
* StringDict vs Dict
Polymorphism
============
Want to try to retain I__XXX__ and M__XXX__ for speed
so first try
ok, I := obj.(.I__XXX__)
if ok {
res = I.M__XXX__(obj)
}
However want to be able to look up "__XXX__" in dict
ok, res := obj.Type().Call0("__XXX__")
ok, res := obj.Type().Call1("__XXX__", a)
ok, res := obj.Type().Call2("__XXX__", a, b)
ok is method found
res is valid if found (or maybe NotImplemented)
Call() looks up name in methods and if found calls it either C wise or py-wise
Calling C-wise is easy enough, but how to call py-wise?
all together
var ok bool
var res Object
if ok, I := obj.(.I__XXX__); ok {
res = I.M__XXX__(b)
} else ok, res = obj.Type().Call1("__XXX__", b); !ok {
res = NotImplemented
}
ObjectType in *Type is probably redundant
- except that Base can be nil
Difference between gpython and cpython
======================================
Uses native types for some of the objects, eg String, Int, Tuple, StringDict
The immutable types String, Int are passed by value. Tuple is an
[]Object which is a reference type as is StringDict which is a
map[string]Object. Note that Tuples can't appended to.
Note that strings are kept as Go native utf-8 encoded strings. This
makes for a small amount of awkwardness when indexing etc, but makes
life much easier interfacing with Go.
List is a struct with a []Object in it so append can mutate the list.
All other types are pointers to structures.
Does not support threading, so no thread state or thread local
variables as the intention is to support go routines and channels via
the threading module.
Attributes of built in types
============================
Use introspection to find methods of built in types and to find attributes
When the built in type is created with TypeNew fill up the type Dict
with the methods and data descriptors (which implement __get__
and __set__ as appropriate)
Introspect attributes by looking for `py:"args"` in the structure
type. If it is writable it has a ",w" flag `py:"args,w"` - we'll use
the type of the structure member to convert the incoming arg, so if it
is a Tuple() we'll call MakeTuple() on it, etc. The attribute can
also have help on it, eg `py:"__cause__,r,exception cause"`
Introspect methods by looking at all public methods
Transform the names, so that initial "M__" becomes "__" then lowercase
the method. Only if the method matches one of the defined function types will it be exported.
FIXME meta data for help would be nice? How attache metadata to go function?
Introspection idea
==================
var method string
if strings.HasPrefix(key, "_") {
method = "M" + key
} else {
method = strings.Title(key)
}
fmt.Printf("*** looking for method %q (key %q)\n", method, key)
r := reflect.TypeOf(self)
if m, ok := r.MethodByName(method); ok {
fmt.Printf("*** m = %#v\n", m.Func)
fmt.Printf("*** type = %#v\n", m.Func.Type())
var fptr func(Object) Object
// fptr is a pointer to a function.
// Obtain the function value itself (likely nil) as a reflect.Value
// so that we can query its type and then set the value.
fn := reflect.ValueOf(fptr).Elem()
// Make a function of the right type.
v := reflect.MakeFunc(fn.Type(), m.Func)
// Assign it to the value fn represents.
fn.Set(v)
fmt.Printf("fptr = %v\n", fptr)
} else {
fmt.Printf("*** method not found\n")
}
Parser
======
Used Python's Grammar file - converted into parser/grammar.y
Used go tool yacc and a hand built lexer in parser/lexer.go
DONE