Skip to content

Commit 0b3a137

Browse files
committed
Implement compile() by cheating and calling python3.3
This is a temporary measure to allow more progress to be made on the runtime without having to port the compiler yet.
1 parent a74eee3 commit 0b3a137

File tree

3 files changed

+170
-2
lines changed

3 files changed

+170
-2
lines changed

builtin/builtin.go

+100-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package builtin
33

44
import (
55
"fmt"
6+
"github.com/ncw/gpython/compile"
67
"github.com/ncw/gpython/py"
78
"github.com/ncw/gpython/vm"
89
"unicode/utf8"
@@ -24,7 +25,7 @@ func init() {
2425
// py.NewMethod("bin", builtin_bin, 0, bin_doc),
2526
// py.NewMethod("callable", builtin_callable, 0, callable_doc),
2627
// py.NewMethod("chr", builtin_chr, 0, chr_doc),
27-
// py.NewMethod("compile", builtin_compile, 0, compile_doc),
28+
py.NewMethod("compile", builtin_compile, 0, compile_doc),
2829
// py.NewMethod("delattr", builtin_delattr, 0, delattr_doc),
2930
// py.NewMethod("dir", builtin_dir, 0, dir_doc),
3031
// py.NewMethod("divmod", builtin_divmod, 0, divmod_doc),
@@ -444,3 +445,101 @@ func builtin_setattr(self py.Object, args py.Tuple) py.Object {
444445

445446
return py.SetAttr(v, name, value)
446447
}
448+
449+
// Reads the source as a string
450+
func source_as_string(cmd py.Object, funcname, what string /*, PyCompilerFlags *cf */) string {
451+
// FIXME only understands strings, not bytes etc at the moment
452+
if str, ok := cmd.(py.String); ok {
453+
// FIXME cf->cf_flags |= PyCF_IGNORE_COOKIE;
454+
return string(str)
455+
}
456+
// } else if (!PyObject_CheckReadBuffer(cmd)) {
457+
panic(py.ExceptionNewf(py.TypeError, "%s() arg 1 must be a %s object", funcname, what))
458+
// } else if (PyObject_AsReadBuffer(cmd, (const void **)&str, &size) < 0) {
459+
// return nil;
460+
}
461+
462+
const compile_doc = `compile(source, filename, mode[, flags[, dont_inherit]]) -> code object
463+
464+
Compile the source string (a Python module, statement or expression)
465+
into a code object that can be executed by exec() or eval().
466+
The filename will be used for run-time error messages.
467+
The mode must be 'exec' to compile a module, 'single' to compile a
468+
single (interactive) statement, or 'eval' to compile an expression.
469+
The flags argument, if present, controls which future statements influence
470+
the compilation of the code.
471+
The dont_inherit argument, if non-zero, stops the compilation inheriting
472+
the effects of any future statements in effect in the code calling
473+
compile; if absent or zero these statements do influence the compilation,
474+
in addition to any features explicitly specified.`
475+
476+
func builtin_compile(self py.Object, args py.Tuple, kwargs py.StringDict) py.Object {
477+
// FIXME lots of unsupported stuff here!
478+
var filename py.Object
479+
var startstr py.Object
480+
// var mode = -1
481+
var dont_inherit py.Object = py.Int(0)
482+
var supplied_flags py.Object = py.Int(0)
483+
var optimizeInt py.Object = py.Int(-1)
484+
//is_ast := false
485+
// var cf PyCompilerFlags
486+
var cmd py.Object
487+
kwlist := []string{"source", "filename", "mode", "flags", "dont_inherit", "optimize"}
488+
// start := []int{Py_file_input, Py_eval_input, Py_single_input}
489+
var result py.Object
490+
491+
py.ParseTupleAndKeywords(args, kwargs, "Oss|iii:compile", kwlist,
492+
&cmd,
493+
&filename,
494+
&startstr,
495+
&supplied_flags,
496+
&dont_inherit,
497+
&optimizeInt)
498+
499+
// cf.cf_flags = supplied_flags | PyCF_SOURCE_IS_UTF8
500+
501+
// if supplied_flags&^(PyCF_MASK|PyCF_MASK_OBSOLETE|PyCF_DONT_IMPLY_DEDENT|PyCF_ONLY_AST) != 0 {
502+
// panic(py.ExceptionNewf(py.ValueError, "compile(): unrecognised flags"))
503+
// }
504+
// XXX Warn if (supplied_flags & PyCF_MASK_OBSOLETE) != 0?
505+
506+
optimize := int(optimizeInt.(py.Int))
507+
if optimize < -1 || optimize > 2 {
508+
panic(py.ExceptionNewf(py.ValueError, "compile(): invalid optimize value"))
509+
}
510+
511+
if dont_inherit.(py.Int) != 0 {
512+
// PyEval_MergeCompilerFlags(&cf)
513+
}
514+
515+
// switch string(startstr.(py.String)) {
516+
// case "exec":
517+
// mode = 0
518+
// case "eval":
519+
// mode = 1
520+
// case "single":
521+
// mode = 2
522+
// default:
523+
// panic(py.ExceptionNewf(py.ValueError, "compile() arg 3 must be 'exec', 'eval' or 'single'"))
524+
// }
525+
526+
// is_ast = PyAST_Check(cmd)
527+
// if is_ast {
528+
// if supplied_flags & PyCF_ONLY_AST {
529+
// result = cmd
530+
// } else {
531+
532+
// arena := PyArena_New()
533+
// mod := PyAST_obj2mod(cmd, arena, mode)
534+
// PyAST_Validate(mod)
535+
// result = PyAST_CompileObject(mod, filename, &cf, optimize, arena)
536+
// PyArena_Free(arena)
537+
// }
538+
// } else {
539+
str := source_as_string(cmd, "compile", "string, bytes or AST" /*, &cf*/)
540+
// result = py.CompileStringExFlags(str, filename, start[mode], &cf, optimize)
541+
result = compile.Compile(str, string(filename.(py.String)), string(startstr.(py.String)), int(supplied_flags.(py.Int)), dont_inherit.(py.Int) != 0)
542+
// }
543+
544+
return result
545+
}

compile/compile.go

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// compile python code
2+
//
3+
// Need to port the 10,000 lines of compiling machinery, into a
4+
// different module probably.
5+
//
6+
// In the mean time, cheat horrendously by calling python3.3 to do our
7+
// dirty work under the hood!
8+
9+
package compile
10+
11+
import (
12+
"bytes"
13+
"fmt"
14+
"github.com/ncw/gpython/marshal"
15+
"github.com/ncw/gpython/py"
16+
"os"
17+
"os/exec"
18+
"strings"
19+
)
20+
21+
// Compile(source, filename, mode, flags, dont_inherit) -> code object
22+
//
23+
// Compile the source string (a Python module, statement or expression)
24+
// into a code object that can be executed by exec() or eval().
25+
// The filename will be used for run-time error messages.
26+
// The mode must be 'exec' to compile a module, 'single' to compile a
27+
// single (interactive) statement, or 'eval' to compile an expression.
28+
// The flags argument, if present, controls which future statements influence
29+
// the compilation of the code.
30+
// The dont_inherit argument, if non-zero, stops the compilation inheriting
31+
// the effects of any future statements in effect in the code calling
32+
// compile; if absent or zero these statements do influence the compilation,
33+
// in addition to any features explicitly specified.
34+
func Compile(str, filename, mode string, flags int, dont_inherit bool) py.Object {
35+
dont_inherit_str := "False"
36+
if dont_inherit {
37+
dont_inherit_str = "True"
38+
}
39+
// FIXME escaping in filename
40+
code := fmt.Sprintf(`import sys, marshal
41+
str = sys.stdin.buffer.read().decode("utf-8")
42+
code = compile(str, "%s", "%s", %d, %s)
43+
marshalled_code = marshal.dumps(code)
44+
sys.stdout.buffer.write(marshalled_code)
45+
sys.stdout.close()`,
46+
filename,
47+
mode,
48+
flags,
49+
dont_inherit_str,
50+
)
51+
cmd := exec.Command("python3.3", "-c", code)
52+
cmd.Stdin = strings.NewReader(str)
53+
var out bytes.Buffer
54+
cmd.Stdout = &out
55+
var stderr bytes.Buffer
56+
cmd.Stderr = &stderr
57+
err := cmd.Run()
58+
if err != nil {
59+
fmt.Fprintf(os.Stderr, "--- Failed to run python3.3 compile ---\n")
60+
fmt.Fprintf(os.Stderr, "--------------------\n")
61+
os.Stderr.Write(stderr.Bytes())
62+
fmt.Fprintf(os.Stderr, "--------------------\n")
63+
panic(err)
64+
}
65+
obj, err := marshal.ReadObject(bytes.NewBuffer(out.Bytes()))
66+
if err != nil {
67+
panic(err)
68+
}
69+
return obj
70+
}

marshal/marshal.go

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"encoding/binary"
77
"errors"
88
"fmt"
9-
_ "github.com/ncw/gpython/builtin" // force builtin to be loaded before marshal
109
"github.com/ncw/gpython/py"
1110
"github.com/ncw/gpython/vm"
1211
"io"

0 commit comments

Comments
 (0)