Compiler update (basic sc ready) (#31)

* refactored structs, the scope is not needed anymore + fix passing struct in func arguments.

* implemented byte arrays and added runtime tests

* Added sc examples in compiler README + added quick nested if test.

* Updated README
This commit is contained in:
Anthony De Meulemeester 2018-02-27 10:04:24 +01:00 committed by GitHub
parent de3395fb51
commit 2345858238
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 464 additions and 215 deletions

View file

@ -89,13 +89,6 @@ func (c *codegen) emitLoadLocal(name string) {
emitOpcode(c.prog, vm.Opickitem)
}
func (c *codegen) emitLoadStructField(sName, fName string) {
strct := c.scope.loadStruct(sName)
pos := strct.loadField(fName)
emitInt(c.prog, int64(pos))
emitOpcode(c.prog, vm.Opickitem)
}
func (c *codegen) emitStoreLocal(pos int) {
emitOpcode(c.prog, vm.Ofromaltstack)
emitOpcode(c.prog, vm.Odup)
@ -111,10 +104,13 @@ func (c *codegen) emitStoreLocal(pos int) {
emitOpcode(c.prog, vm.Osetitem)
}
func (c *codegen) emitStoreStructField(sName, fName string) {
strct := c.scope.loadStruct(sName)
pos := strct.loadField(fName)
emitInt(c.prog, int64(pos))
func (c *codegen) emitLoadStructField(i int) {
emitInt(c.prog, int64(i))
emitOpcode(c.prog, vm.Opickitem)
}
func (c *codegen) emitStoreStructField(i int) {
emitInt(c.prog, int64(i))
emitOpcode(c.prog, vm.Orot)
emitOpcode(c.prog, vm.Osetitem)
}
@ -178,11 +174,11 @@ func (c *codegen) convertFuncDecl(file *ast.File, decl *ast.FuncDecl) {
if decl.Recv != nil {
for _, arg := range decl.Recv.List {
ident := arg.Names[0]
t, ok := c.typeInfo.Defs[ident].Type().Underlying().(*types.Struct)
// Currently only method receives for struct types is supported.
_, ok := c.typeInfo.Defs[ident].Type().Underlying().(*types.Struct)
if !ok {
log.Fatal("method receiver is not a struct type")
log.Fatal("method receives for non-struct types is not yet supported")
}
c.scope.newStruct(t)
l := c.scope.newLocal(ident.Name)
c.emitStoreLocal(l)
}
@ -235,12 +231,12 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
switch n.Tok {
case token.ADD_ASSIGN, token.SUB_ASSIGN, token.MUL_ASSIGN, token.QUO_ASSIGN:
c.emitLoadLocal(t.Name)
ast.Walk(c, n.Rhs[0])
ast.Walk(c, n.Rhs[0]) // can only add assign to 1 expr on the RHS
c.convertToken(n.Tok)
l := c.scope.loadLocal(t.Name)
c.emitStoreLocal(l)
default:
ast.Walk(c, n.Rhs[0])
ast.Walk(c, n.Rhs[i])
l := c.scope.loadLocal(t.Name)
c.emitStoreLocal(l)
}
@ -249,8 +245,12 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
switch expr := t.X.(type) {
case *ast.Ident:
ast.Walk(c, n.Rhs[i])
c.emitLoadLocal(expr.Name) // load the struct
c.emitStoreStructField(expr.Name, t.Sel.Name) // store the field
typ := c.typeInfo.ObjectOf(expr).Type().Underlying()
if strct, ok := typ.(*types.Struct); ok {
c.emitLoadLocal(expr.Name) // load the struct
i := indexOfStruct(strct, t.Sel.Name) // get the index of the field
c.emitStoreStructField(i) // store the field
}
default:
log.Fatal("nested selector assigns not supported yet")
}
@ -320,6 +320,11 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
typ = c.typeInfo.ObjectOf(t.Sel).Type().Underlying()
default:
ln := len(n.Elts)
// ByteArrays need a different approach then normal arrays.
if isByteArray(n, c.typeInfo) {
c.convertByteArray(n)
return nil
}
for i := ln - 1; i >= 0; i-- {
c.emitLoadConst(c.typeInfo.Types[n.Elts[i]])
}
@ -430,17 +435,18 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
switch t := n.X.(type) {
case *ast.Ident:
typ := c.typeInfo.ObjectOf(t).Type().Underlying()
switch typ.(type) {
case *types.Struct:
c.emitLoadLocal(t.Name) // load the struct
c.emitLoadStructField(t.Name, n.Sel.Name) // load the field
default:
log.Fatal("non struct import selections not yet implemented, please use functions instead")
if strct, ok := typ.(*types.Struct); ok {
c.emitLoadLocal(t.Name) // load the struct
i := indexOfStruct(strct, n.Sel.Name)
c.emitLoadStructField(i) // load the field
}
default:
log.Fatal("nested selectors not supported yet")
}
return nil
case *ast.UnaryExpr:
// fmt.Println(n)
}
return c
}
@ -454,24 +460,33 @@ func (c *codegen) convertSyscall(name string) {
emitOpcode(c.prog, vm.Onop)
}
func (c *codegen) convertByteArray(lit *ast.CompositeLit) {
buf := make([]byte, len(lit.Elts))
for i := 0; i < len(lit.Elts); i++ {
t := c.typeInfo.Types[lit.Elts[i]]
val, _ := constant.Int64Val(t.Value)
buf[i] = byte(val)
}
emitBytes(c.prog, buf)
}
func (c *codegen) convertStruct(lit *ast.CompositeLit) {
// Create a new structScope to initialize and store
// the positions of its variables.
t, ok := c.typeInfo.TypeOf(lit).Underlying().(*types.Struct)
strct, ok := c.typeInfo.TypeOf(lit).Underlying().(*types.Struct)
if !ok {
log.Fatalf("the given literal is not of type struct: %v", lit)
}
strct := c.scope.newStruct(t)
emitOpcode(c.prog, vm.Onop)
emitInt(c.prog, int64(strct.t.NumFields()))
emitInt(c.prog, int64(strct.NumFields()))
emitOpcode(c.prog, vm.Onewstruct)
emitOpcode(c.prog, vm.Otoaltstack)
// We need to locally store all the fields, even if they are not initialized.
// We will initialize all fields to their "zero" value.
for i := 0; i < strct.t.NumFields(); i++ {
sField := strct.t.Field(i)
for i := 0; i < strct.NumFields(); i++ {
sField := strct.Field(i)
fieldAdded := false
// Fields initialized by the program.
@ -481,7 +496,7 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit) {
if sField.Name() == fieldName {
ast.Walk(c, f.Value)
pos := strct.loadField(fieldName)
pos := indexOfStruct(strct, fieldName)
c.emitStoreLocal(pos)
fieldAdded = true
break
@ -490,7 +505,9 @@ func (c *codegen) convertStruct(lit *ast.CompositeLit) {
if fieldAdded {
continue
}
c.emitLoadConst(strct.typeAndValues[sField.Name()])
typeAndVal := typeAndValueForField(sField)
c.emitLoadConst(typeAndVal)
c.emitStoreLocal(i)
}
emitOpcode(c.prog, vm.Ofromaltstack)
@ -552,6 +569,8 @@ func CodeGen(info *buildInfo) (*bytes.Buffer, error) {
log.Fatal("could not find func main. did you forgot to declare it?")
}
funUsage := analyzeFuncUsage(info.program.AllPackages)
// Bring all imported functions into scope
for _, pkg := range info.program.AllPackages {
for _, f := range pkg.Files {
@ -565,11 +584,14 @@ func CodeGen(info *buildInfo) (*bytes.Buffer, error) {
// Generate the code for the program
for _, pkg := range info.program.AllPackages {
c.typeInfo = &pkg.Info
for _, f := range pkg.Files {
for _, decl := range f.Decls {
switch n := decl.(type) {
case *ast.FuncDecl:
if n.Name.Name != mainIdent {
// Dont convert the function if its not used. This will save alot
// of bytecode space.
if n.Name.Name != mainIdent && funUsage.funcUsed(n.Name.Name) {
c.convertFuncDecl(f, n)
}
}