Skip to content

Commit 3c0ad78

Browse files
committed
compile: class definitions and module docstrings
1 parent 8f4bd41 commit 3c0ad78

File tree

4 files changed

+481
-44
lines changed

4 files changed

+481
-44
lines changed

compile/compile.go

+146-41
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ type compiler struct {
5757
SymTable *symtable.SymTable
5858
scopeType compilerScopeType
5959
qualname string
60+
private string
6061
parent *compiler
6162
depth int
6263
}
@@ -135,7 +136,7 @@ func (c *compiler) compileAst(Ast ast.Ast, filename string, futureFlags int, don
135136
valueOnStack := false
136137
switch node := Ast.(type) {
137138
case *ast.Module:
138-
c.Stmts(node.Body)
139+
c.Stmts(c.docString(node.Body, false))
139140
case *ast.Interactive:
140141
c.Stmts(node.Body)
141142
case *ast.Expression:
@@ -153,7 +154,41 @@ func (c *compiler) compileAst(Ast ast.Ast, filename string, futureFlags int, don
153154
case *ast.FunctionDef:
154155
code.Name = string(node.Name)
155156
c.setQualname() // FIXME is this in the right place!
156-
c.Stmts(c.docString(node.Body))
157+
c.Stmts(c.docString(node.Body, true))
158+
case *ast.ClassDef:
159+
code.Name = string(node.Name)
160+
/* load (global) __name__ ... */
161+
c.NameOp("__name__", ast.Load)
162+
/* ... and store it as __module__ */
163+
c.NameOp("__module__", ast.Store)
164+
c.setQualname() // FIXME is this in the right place!
165+
if c.qualname == "" {
166+
panic("Need qualname")
167+
}
168+
169+
c.LoadConst(py.String(c.qualname))
170+
c.NameOp("__qualname__", ast.Store)
171+
172+
/* compile the body proper */
173+
c.Stmts(c.docString(node.Body, false))
174+
175+
if SymTable.NeedsClassClosure {
176+
/* return the (empty) __class__ cell */
177+
i := c.FindId("__class__", code.Cellvars)
178+
if i != 0 {
179+
panic("__class__ must be first constant")
180+
}
181+
/* Return the cell where to store __class__ */
182+
c.OpArg(vm.LOAD_CLOSURE, uint32(i))
183+
} else {
184+
if len(code.Cellvars) != 0 {
185+
panic("Can't have cellvars without closure")
186+
}
187+
/* This happens when nobody references the cell. Return None. */
188+
c.LoadConst(py.None)
189+
}
190+
c.Op(vm.RETURN_VALUE)
191+
157192
default:
158193
panic(py.ExceptionNewf(py.SyntaxError, "Unknown ModuleBase: %v", Ast))
159194
}
@@ -172,18 +207,29 @@ func (c *compiler) compileAst(Ast ast.Ast, filename string, futureFlags int, don
172207
}
173208

174209
// Check for docstring as first Expr in body and remove it and set the
175-
// first constant if found.
176-
func (c *compiler) docString(body []ast.Stmt) []ast.Stmt {
210+
// first constant if found if fn is set, or set __doc__ if it isn't
211+
func (c *compiler) docString(body []ast.Stmt, fn bool) []ast.Stmt {
212+
var docstring *ast.Str
177213
if len(body) > 0 {
178214
if expr, ok := body[0].(*ast.ExprStmt); ok {
179-
if docstring, ok := expr.Value.(*ast.Str); ok {
180-
c.Const(docstring.S)
181-
return body[1:]
215+
if docstring, ok = expr.Value.(*ast.Str); ok {
216+
body = body[1:]
182217
}
183218
}
184219
}
185-
// If no docstring put None in
186-
c.Const(py.None)
220+
if fn {
221+
if docstring != nil {
222+
c.Const(docstring.S)
223+
} else {
224+
// If no docstring put None in
225+
c.Const(py.None)
226+
}
227+
} else {
228+
if docstring != nil {
229+
c.LoadConst(docstring.S)
230+
c.NameOp("__doc__", ast.Store)
231+
}
232+
}
187233
return body
188234
}
189235

@@ -297,9 +343,8 @@ func (c *compiler) getRefType(name string) symtable.Scope {
297343
}
298344

299345
// makeClosure constructs the function or closure for a func/class/lambda etc
300-
func (c *compiler) makeClosure(code *py.Code, args uint32, child *compiler) {
346+
func (c *compiler) makeClosure(code *py.Code, args uint32, child *compiler, qualname string) {
301347
free := uint32(len(code.Freevars))
302-
qualname := child.qualname
303348
if qualname == "" {
304349
qualname = c.qualname
305350
}
@@ -407,12 +452,12 @@ func (c *compiler) setQualname() {
407452
}
408453

409454
// Compile a function
410-
func (c *compiler) compileFunc(Ast ast.Ast, Args *ast.Arguments, DecoratorList []ast.Expr, Returns ast.Expr) {
455+
func (c *compiler) compileFunc(compilerScope compilerScopeType, Ast ast.Ast, Args *ast.Arguments, DecoratorList []ast.Expr, Returns ast.Expr) {
411456
newSymTable := c.SymTable.FindChild(Ast)
412457
if newSymTable == nil {
413458
panic("No symtable found for function")
414459
}
415-
newC := newCompiler(c, compilerScopeFunction)
460+
newC := newCompiler(c, compilerScope)
416461
code, err := newC.compileAst(Ast, c.Code.Filename, 0, false, newSymTable)
417462
if err != nil {
418463
panic(err)
@@ -468,14 +513,67 @@ func (c *compiler) compileFunc(Ast ast.Ast, Args *ast.Arguments, DecoratorList [
468513
posdefaults := uint32(len(Args.Defaults))
469514
kwdefaults := uint32(len(Args.KwDefaults))
470515
args := uint32(posdefaults + (kwdefaults << 8) + (num_annotations << 16))
471-
c.makeClosure(code, args, newC)
516+
c.makeClosure(code, args, newC, newC.qualname)
472517

473518
// Call decorators
474519
for _ = range DecoratorList {
475520
c.OpArg(vm.CALL_FUNCTION, 1) // 1 positional, 0 keyword pair
476521
}
477522
}
478523

524+
// Compile class definition
525+
func (c *compiler) class(Ast ast.Ast, class *ast.ClassDef) {
526+
// Load decorators onto stack
527+
for _, expr := range class.DecoratorList {
528+
c.Expr(expr)
529+
}
530+
531+
/* ultimately generate code for:
532+
<name> = __build_class__(<func>, <name>, *<bases>, **<keywords>)
533+
where:
534+
<func> is a function/closure created from the class body;
535+
it has a single argument (__locals__) where the dict
536+
(or MutableSequence) representing the locals is passed
537+
<name> is the class name
538+
<bases> is the positional arguments and *varargs argument
539+
<keywords> is the keyword arguments and **kwds argument
540+
This borrows from compiler_call.
541+
*/
542+
543+
/* 1. compile the class body into a code object */
544+
newSymTable := c.SymTable.FindChild(Ast)
545+
if newSymTable == nil {
546+
panic("No symtable found for class")
547+
}
548+
newC := newCompiler(c, compilerScopeClass)
549+
/* use the class name for name mangling */
550+
newC.private = string(class.Name)
551+
code, err := newC.compileAst(Ast, c.Code.Filename, 0, false, newSymTable)
552+
if err != nil {
553+
panic(err)
554+
}
555+
556+
/* 2. load the 'build_class' function */
557+
c.Op(vm.LOAD_BUILD_CLASS)
558+
559+
/* 3. load a function (or closure) made from the code object */
560+
c.makeClosure(code, 0, newC, string(class.Name))
561+
562+
/* 4. load class name */
563+
c.LoadConst(py.String(class.Name))
564+
565+
/* 5. generate the rest of the code for the call */
566+
c.callHelper(2, class.Bases, class.Keywords, class.Starargs, class.Kwargs)
567+
568+
/* 6. apply decorators */
569+
for _ = range class.DecoratorList {
570+
c.OpArg(vm.CALL_FUNCTION, 1) // 1 positional, 0 keyword pair
571+
}
572+
573+
/* 7. store into <name> */
574+
c.NameOp(string(class.Name), ast.Store)
575+
}
576+
479577
// Compile statement
480578
func (c *compiler) Stmt(stmt ast.Stmt) {
481579
switch node := stmt.(type) {
@@ -485,7 +583,7 @@ func (c *compiler) Stmt(stmt ast.Stmt) {
485583
// Body []Stmt
486584
// DecoratorList []Expr
487585
// Returns Expr
488-
c.compileFunc(stmt, node.Args, node.DecoratorList, node.Returns)
586+
c.compileFunc(compilerScopeFunction, stmt, node.Args, node.DecoratorList, node.Returns)
489587
c.NameOp(string(node.Name), ast.Store)
490588

491589
case *ast.ClassDef:
@@ -496,7 +594,7 @@ func (c *compiler) Stmt(stmt ast.Stmt) {
496594
// Kwargs Expr
497595
// Body []Stmt
498596
// DecoratorList []Expr
499-
panic("FIXME compile: ClassDef not implemented")
597+
c.class(stmt, node)
500598
case *ast.Return:
501599
// Value Expr
502600
if node.Value != nil {
@@ -752,6 +850,8 @@ func (c *compiler) NameOp(name string, ctx ast.ExprContext) {
752850
}
753851
case symtable.ScopeGlobalExplicit:
754852
optype = OP_GLOBAL
853+
case symtable.ScopeInvalid:
854+
// scope can be unset
755855
default:
756856
panic(fmt.Sprintf("NameOp: Invalid scope %v for %q", scope, mangled))
757857
}
@@ -833,6 +933,34 @@ func (c *compiler) NameOp(name string, ctx ast.ExprContext) {
833933
c.OpArg(op, c.Index(mangled, dict))
834934
}
835935

936+
// Call a function which is already on the stack with n arguments already on the stack
937+
func (c *compiler) callHelper(n int, Args []ast.Expr, Keywords []*ast.Keyword, Starargs ast.Expr, Kwargs ast.Expr) {
938+
args := len(Args) + n
939+
for i := range Args {
940+
c.Expr(Args[i])
941+
}
942+
kwargs := len(Keywords)
943+
for i := range Keywords {
944+
kw := Keywords[i]
945+
c.LoadConst(py.String(kw.Arg))
946+
c.Expr(kw.Value)
947+
}
948+
op := byte(vm.CALL_FUNCTION)
949+
if Starargs != nil {
950+
c.Expr(Starargs)
951+
if Kwargs != nil {
952+
c.Expr(Kwargs)
953+
op = vm.CALL_FUNCTION_VAR_KW
954+
} else {
955+
op = vm.CALL_FUNCTION_VAR
956+
}
957+
} else if Kwargs != nil {
958+
c.Expr(Kwargs)
959+
op = vm.CALL_FUNCTION_KW
960+
}
961+
c.OpArg(op, uint32(args+kwargs<<8))
962+
}
963+
836964
// Compile expressions
837965
func (c *compiler) Expr(expr ast.Expr) {
838966
switch node := expr.(type) {
@@ -914,7 +1042,7 @@ func (c *compiler) Expr(expr ast.Expr) {
9141042
// Args *Arguments
9151043
// Body Expr
9161044
// newC := Compiler
917-
c.compileFunc(expr, node.Args, nil, nil)
1045+
c.compileFunc(compilerScopeLambda, expr, node.Args, nil, nil)
9181046
case *ast.IfExp:
9191047
// Test Expr
9201048
// Body Expr
@@ -1035,30 +1163,7 @@ func (c *compiler) Expr(expr ast.Expr) {
10351163
// Starargs Expr
10361164
// Kwargs Expr
10371165
c.Expr(node.Func)
1038-
args := len(node.Args)
1039-
for i := range node.Args {
1040-
c.Expr(node.Args[i])
1041-
}
1042-
kwargs := len(node.Keywords)
1043-
for i := range node.Keywords {
1044-
kw := node.Keywords[i]
1045-
c.LoadConst(py.String(kw.Arg))
1046-
c.Expr(kw.Value)
1047-
}
1048-
op := byte(vm.CALL_FUNCTION)
1049-
if node.Starargs != nil {
1050-
c.Expr(node.Starargs)
1051-
if node.Kwargs != nil {
1052-
c.Expr(node.Kwargs)
1053-
op = vm.CALL_FUNCTION_VAR_KW
1054-
} else {
1055-
op = vm.CALL_FUNCTION_VAR
1056-
}
1057-
} else if node.Kwargs != nil {
1058-
c.Expr(node.Kwargs)
1059-
op = vm.CALL_FUNCTION_KW
1060-
}
1061-
c.OpArg(op, uint32(args+kwargs<<8))
1166+
c.callHelper(0, node.Args, node.Keywords, node.Starargs, node.Kwargs)
10621167
case *ast.Num:
10631168
// N Object
10641169
c.LoadConst(node.N)

0 commit comments

Comments
 (0)