Skip to content

Commit a0c3930

Browse files
committed
compile: make a simple function compile
* Make Compile() return errors rather than panics * Integrate compile with symtable * Port NameOp code * In tests, diff code differences with external program
1 parent f7003ed commit a0c3930

File tree

8 files changed

+328
-82
lines changed

8 files changed

+328
-82
lines changed

compile/compile.go

Lines changed: 179 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/ncw/gpython/marshal"
2020
"github.com/ncw/gpython/parser"
2121
"github.com/ncw/gpython/py"
22+
"github.com/ncw/gpython/symtable"
2223
"github.com/ncw/gpython/vm"
2324
)
2425

@@ -91,25 +92,37 @@ sys.stdout.close()`,
9192
// the effects of any future statements in effect in the code calling
9293
// compile; if absent or zero these statements do influence the compilation,
9394
// in addition to any features explicitly specified.
94-
func Compile(str, filename, mode string, flags int, dont_inherit bool) py.Object {
95+
func Compile(str, filename, mode string, flags int, dont_inherit bool) (py.Object, error) {
96+
// Parse Ast
9597
Ast, err := parser.ParseString(str, mode)
9698
if err != nil {
97-
panic(err) // FIXME error handling!
99+
return nil, err
98100
}
99-
return CompileAst(Ast, filename, flags, dont_inherit)
101+
// Make symbol table
102+
SymTable, err := symtable.NewSymTable(Ast)
103+
if err != nil {
104+
return nil, err
105+
}
106+
return CompileAst(Ast, filename, flags, dont_inherit, SymTable)
100107
}
101108

102109
// As Compile but takes an Ast
103-
func CompileAst(Ast ast.Ast, filename string, flags int, dont_inherit bool) *py.Code {
110+
func CompileAst(Ast ast.Ast, filename string, flags int, dont_inherit bool, SymTable *symtable.SymTable) (code *py.Code, err error) {
111+
defer func() {
112+
if r := recover(); r != nil {
113+
err = py.MakeException(r)
114+
}
115+
}()
104116
//fmt.Println(ast.Dump(Ast))
105-
code := &py.Code{
117+
code = &py.Code{
106118
Filename: filename,
107119
Firstlineno: 1, // FIXME
108120
Name: "<module>", // FIXME
109121
Flags: int32(flags | py.CO_NOFREE), // FIXME
110122
}
111123
c := &compiler{
112-
Code: code,
124+
Code: code,
125+
SymTable: SymTable,
113126
}
114127
valueOnStack := false
115128
switch node := Ast.(type) {
@@ -143,7 +156,7 @@ func CompileAst(Ast ast.Ast, filename string, flags int, dont_inherit bool) *py.
143156
}
144157
code.Code = c.OpCodes.Assemble()
145158
code.Stacksize = int32(c.OpCodes.StackDepth())
146-
return code
159+
return code, nil
147160
}
148161

149162
// Loop
@@ -176,15 +189,17 @@ func (ls loopstack) Top() *loop {
176189

177190
// State for the compiler
178191
type compiler struct {
179-
Code *py.Code // code being built up
180-
OpCodes Instructions
181-
loops loopstack
192+
Code *py.Code // code being built up
193+
OpCodes Instructions
194+
loops loopstack
195+
SymTable *symtable.SymTable
182196
}
183197

184198
// Compiles a python constant
185199
//
186200
// Returns the index into the Consts tuple
187201
func (c *compiler) Const(obj py.Object) uint32 {
202+
// FIXME back this with a dict to stop O(N**2) behaviour on lots of consts
188203
for i, c := range c.Code.Consts {
189204
if obj.Type() == c.Type() && py.Eq(obj, c) == py.True {
190205
return uint32(i)
@@ -199,19 +214,25 @@ func (c *compiler) LoadConst(obj py.Object) {
199214
c.OpArg(vm.LOAD_CONST, c.Const(obj))
200215
}
201216

202-
// Compiles a python name
203-
//
204-
// Returns the index into the Name tuple
205-
func (c *compiler) Name(Id ast.Identifier) uint32 {
206-
for i, s := range c.Code.Names {
207-
if string(Id) == s {
217+
// Returns the index into the slice provided, updating the slice if necessary
218+
func (c *compiler) Index(Id string, Names *[]string) uint32 {
219+
// FIXME back this with a dict to stop O(N**2) behaviour on lots of vars
220+
for i, s := range *Names {
221+
if Id == s {
208222
return uint32(i)
209223
}
210224
}
211-
c.Code.Names = append(c.Code.Names, string(Id))
225+
*Names = append(*Names, Id)
212226
return uint32(len(c.Code.Names) - 1)
213227
}
214228

229+
// Compiles a python name
230+
//
231+
// Returns the index into the Name tuple
232+
func (c *compiler) Name(Id ast.Identifier) uint32 {
233+
return c.Index(string(Id), &c.Code.Names)
234+
}
235+
215236
// Compiles an instruction with an argument
216237
func (c *compiler) OpArg(Op byte, Arg uint32) {
217238
if !vm.HAS_ARG(Op) {
@@ -268,7 +289,14 @@ func (c *compiler) Stmt(stmt ast.Stmt) {
268289
// Body []Stmt
269290
// DecoratorList []Expr
270291
// Returns Expr
271-
code := CompileAst(node, c.Code.Filename, int(c.Code.Flags)|py.CO_OPTIMIZED|py.CO_NEWLOCALS, false) // FIXME pass on compile args
292+
newSymTable := c.SymTable.FindChild(stmt)
293+
if newSymTable == nil {
294+
panic("No symtable found for function")
295+
}
296+
code, err := CompileAst(node, c.Code.Filename, int(c.Code.Flags)|py.CO_OPTIMIZED|py.CO_NEWLOCALS, false, newSymTable) // FIXME pass on compile args
297+
if err != nil {
298+
panic(err)
299+
}
272300
code.Argcount = int32(len(node.Args.Args))
273301
code.Name = string(node.Name)
274302
code.Kwonlyargcount = int32(len(node.Args.Kwonlyargs))
@@ -555,6 +583,129 @@ func (c *compiler) Stmt(stmt ast.Stmt) {
555583
}
556584
}
557585

586+
// Compile a NameOp
587+
func (c *compiler) NameOp(name string, ctx ast.ExprContext) {
588+
// int op, scope;
589+
// Py_ssize_t arg;
590+
const (
591+
OP_FAST = iota
592+
OP_GLOBAL
593+
OP_DEREF
594+
OP_NAME
595+
)
596+
597+
dict := &c.Code.Names
598+
// PyObject *mangled;
599+
/* XXX AugStore isn't used anywhere! */
600+
601+
// FIXME mangled = _Py_Mangle(c->u->u_private, name);
602+
mangled := name
603+
604+
if name == "None" || name == "True" || name == "False" {
605+
panic("NameOp: Can't compile None, True or False")
606+
}
607+
608+
op := byte(0)
609+
optype := OP_NAME
610+
scope := c.SymTable.GetScope(mangled)
611+
switch scope {
612+
case symtable.ScopeFree:
613+
dict = &c.Code.Freevars
614+
optype = OP_DEREF
615+
case symtable.ScopeCell:
616+
dict = &c.Code.Cellvars
617+
optype = OP_DEREF
618+
case symtable.ScopeLocal:
619+
if c.SymTable.Type == symtable.FunctionBlock {
620+
optype = OP_FAST
621+
}
622+
case symtable.ScopeGlobalImplicit:
623+
if c.SymTable.Type == symtable.FunctionBlock && c.SymTable.Unoptimized == 0 {
624+
optype = OP_GLOBAL
625+
}
626+
case symtable.ScopeGlobalExplicit:
627+
optype = OP_GLOBAL
628+
default:
629+
panic(fmt.Sprintf("NameOp: Invalid scope %v for %q", scope, mangled))
630+
}
631+
632+
/* XXX Leave assert here, but handle __doc__ and the like better */
633+
// FIXME assert(scope || PyUnicode_READ_CHAR(name, 0) == '_')
634+
635+
switch optype {
636+
case OP_DEREF:
637+
switch ctx {
638+
case ast.Load:
639+
if c.SymTable.Type == symtable.ClassBlock {
640+
op = vm.LOAD_CLASSDEREF
641+
} else {
642+
op = vm.LOAD_DEREF
643+
}
644+
case ast.Store:
645+
op = vm.STORE_DEREF
646+
case ast.AugLoad:
647+
case ast.AugStore:
648+
case ast.Del:
649+
op = vm.DELETE_DEREF
650+
case ast.Param:
651+
panic("NameOp: param invalid for deref variable")
652+
default:
653+
panic("NameOp: ctx invalid for deref variable")
654+
}
655+
case OP_FAST:
656+
switch ctx {
657+
case ast.Load:
658+
op = vm.LOAD_FAST
659+
case ast.Store:
660+
op = vm.STORE_FAST
661+
case ast.Del:
662+
op = vm.DELETE_FAST
663+
case ast.AugLoad:
664+
case ast.AugStore:
665+
case ast.Param:
666+
panic("NameOp: param invalid for local variable")
667+
default:
668+
panic("NameOp: ctx invalid for local variable")
669+
}
670+
dict = &c.Code.Varnames
671+
case OP_GLOBAL:
672+
switch ctx {
673+
case ast.Load:
674+
op = vm.LOAD_GLOBAL
675+
case ast.Store:
676+
op = vm.STORE_GLOBAL
677+
case ast.Del:
678+
op = vm.DELETE_GLOBAL
679+
case ast.AugLoad:
680+
case ast.AugStore:
681+
case ast.Param:
682+
panic("NameOp: param invalid for global variable")
683+
default:
684+
panic("NameOp: ctx invalid for global variable")
685+
}
686+
case OP_NAME:
687+
switch ctx {
688+
case ast.Load:
689+
op = vm.LOAD_NAME
690+
case ast.Store:
691+
op = vm.STORE_NAME
692+
case ast.Del:
693+
op = vm.DELETE_NAME
694+
case ast.AugLoad:
695+
case ast.AugStore:
696+
case ast.Param:
697+
panic("NameOp: param invalid for name variable")
698+
default:
699+
panic("NameOp: ctx invalid for name variable")
700+
}
701+
break
702+
}
703+
if op == 0 {
704+
panic("NameOp: Op not set")
705+
}
706+
c.OpArg(op, c.Index(mangled, dict))
707+
}
708+
558709
// Compile expressions
559710
func (c *compiler) Expr(expr ast.Expr) {
560711
switch node := expr.(type) {
@@ -636,7 +787,15 @@ func (c *compiler) Expr(expr ast.Expr) {
636787
// Args *Arguments
637788
// Body Expr
638789
// newC := Compiler
639-
code := CompileAst(node.Body, c.Code.Filename, int(c.Code.Flags)|py.CO_OPTIMIZED|py.CO_NEWLOCALS, false) // FIXME pass on compile args
790+
newSymTable := c.SymTable.FindChild(expr)
791+
if newSymTable == nil {
792+
panic("No symtable found for lambda")
793+
}
794+
code, err := CompileAst(node.Body, c.Code.Filename, int(c.Code.Flags)|py.CO_OPTIMIZED|py.CO_NEWLOCALS, false, newSymTable) // FIXME pass on compile args
795+
if err != nil {
796+
panic(err)
797+
}
798+
640799
code.Argcount = int32(len(node.Args.Args))
641800
c.LoadConst(code)
642801
c.LoadConst(py.String("<lambda>"))
@@ -819,19 +978,7 @@ func (c *compiler) Expr(expr ast.Expr) {
819978
case *ast.Name:
820979
// Id Identifier
821980
// Ctx ExprContext
822-
switch node.Ctx {
823-
case ast.Load:
824-
c.OpArg(vm.LOAD_NAME, c.Name(node.Id))
825-
case ast.Store:
826-
c.OpArg(vm.STORE_NAME, c.Name(node.Id))
827-
case ast.Del:
828-
c.OpArg(vm.DELETE_NAME, c.Name(node.Id))
829-
// case ast.AugLoad:
830-
// case ast.AugStore:
831-
// case ast.Param:
832-
default:
833-
panic(fmt.Sprintf("FIXME ast.Name Ctx=%v not implemented", node.Ctx))
834-
}
981+
c.NameOp(string(node.Id), node.Ctx)
835982
case *ast.List:
836983
// Elts []Expr
837984
// Ctx ExprContext

0 commit comments

Comments
 (0)