package compiler
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/brettlangdon/gython/ast"
|
|
"github.com/brettlangdon/gython/bytecode"
|
|
"github.com/brettlangdon/gython/gython"
|
|
)
|
|
|
|
type Compiler struct {
|
|
nestLevel int
|
|
currentScope *Scope
|
|
scopeStack []*Scope
|
|
}
|
|
|
|
func NewCompiler() *Compiler {
|
|
return &Compiler{
|
|
nestLevel: 0,
|
|
currentScope: nil,
|
|
scopeStack: make([]*Scope, 0),
|
|
}
|
|
}
|
|
|
|
func (compiler *Compiler) enterScope() *Scope {
|
|
scope := NewScope()
|
|
compiler.scopeStack = append(compiler.scopeStack, scope)
|
|
compiler.nestLevel++
|
|
compiler.currentScope = scope
|
|
return scope
|
|
}
|
|
|
|
func (compiler *Compiler) exitScope() *Scope {
|
|
last := len(compiler.scopeStack) - 1
|
|
scope := compiler.scopeStack[last]
|
|
compiler.scopeStack = compiler.scopeStack[:last]
|
|
if last > 0 {
|
|
compiler.currentScope = compiler.scopeStack[last-1]
|
|
} else {
|
|
compiler.currentScope = nil
|
|
}
|
|
compiler.nestLevel--
|
|
return scope
|
|
}
|
|
|
|
func (compiler *Compiler) assemble(addNone bool) *gython.CodeObject {
|
|
if addNone {
|
|
compiler.addOpWithObject(bytecode.LOAD_CONST, gython.None, compiler.currentScope.Constants)
|
|
compiler.addOp(bytecode.RETURN_VALUE)
|
|
}
|
|
|
|
codeobject := gython.NewCodeObject([]byte{}, []byte{}, 0)
|
|
for _, instr := range compiler.currentScope.Instructions {
|
|
arg := 0
|
|
ext := 0
|
|
size := instr.Size()
|
|
if instr.Hasarg {
|
|
arg = int(instr.Oparg.Value)
|
|
ext = arg >> 16
|
|
}
|
|
|
|
if size == 6 {
|
|
codeobject.AppendOpcode(bytecode.EXTENDED_ARG)
|
|
codeobject.AppendInt(ext & 0xff)
|
|
codeobject.AppendInt(ext >> 8)
|
|
arg &= 0xffff
|
|
}
|
|
codeobject.AppendOpcode(instr.Opcode)
|
|
if instr.Hasarg {
|
|
codeobject.AppendInt(arg & 0xff)
|
|
codeobject.AppendInt(arg >> 8)
|
|
}
|
|
}
|
|
return codeobject
|
|
}
|
|
|
|
func (compiler *Compiler) addOp(op bytecode.Opcode) {
|
|
instr := NewInstruction(op, nil, false)
|
|
compiler.currentScope.AddInstruction(instr)
|
|
}
|
|
|
|
func (compiler *Compiler) addOpWithObject(op bytecode.Opcode, value gython.Object, args *gython.Dict) {
|
|
oparg := args.Length()
|
|
args.SetItem(oparg, value)
|
|
instr := NewInstruction(op, oparg, true)
|
|
compiler.currentScope.AddInstruction(instr)
|
|
}
|
|
|
|
func (compiler *Compiler) visitExpression(expr ast.Expression) bool {
|
|
switch expr := expr.(type) {
|
|
case *ast.Num:
|
|
compiler.addOpWithObject(bytecode.LOAD_CONST, expr.Value, compiler.currentScope.Constants)
|
|
case *ast.Name:
|
|
compiler.addOpWithObject(bytecode.STORE_NAME, expr.Identifier, compiler.currentScope.Constants)
|
|
default:
|
|
fmt.Println(expr)
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (compiler *Compiler) visitStatement(stmt ast.Statement) bool {
|
|
switch stmt := stmt.(type) {
|
|
case *ast.Assign:
|
|
compiler.visitExpression(stmt.Value)
|
|
length := len(stmt.Targets)
|
|
for i := 0; i < length; i++ {
|
|
if i < length-1 {
|
|
// compiler.addOp(bytecode.DUP_TOP)
|
|
}
|
|
compiler.visitExpression(stmt.Targets[i])
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (compiler *Compiler) compileBody(stmts []ast.Statement) bool {
|
|
// TODO: Check for docstring
|
|
for _, stmt := range stmts {
|
|
compiler.visitStatement(stmt)
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (compiler *Compiler) CompileMod(root ast.Mod) *gython.CodeObject {
|
|
addNone := true
|
|
compiler.enterScope()
|
|
var codeobject *gython.CodeObject
|
|
switch root := root.(type) {
|
|
case *ast.Module:
|
|
if !compiler.compileBody(root.Body) {
|
|
compiler.exitScope()
|
|
return nil
|
|
}
|
|
}
|
|
|
|
codeobject = compiler.assemble(addNone)
|
|
compiler.exitScope()
|
|
return codeobject
|
|
}
|