Assembler working
This commit is contained in:
30
assembler/assemble_test.go
Normal file
30
assembler/assemble_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package assembler
|
||||
|
||||
import "testing"
|
||||
|
||||
import "github.com/firestuff/subcoding/vm"
|
||||
|
||||
func TestAssembleExecute(t *testing.T) {
|
||||
prog, err := AssembleString(`
|
||||
functions:
|
||||
- - [add, f0, 1]
|
||||
- [call, +1]
|
||||
- [ltu, f0, 3]
|
||||
- [jmpt, -3]
|
||||
|
||||
- - [add, g0, 1]
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
state, err := vm.NewState(prog)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = state.Execute()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
138
assembler/assembler.go
Normal file
138
assembler/assembler.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package assembler
|
||||
|
||||
import "fmt"
|
||||
import "strconv"
|
||||
import "strings"
|
||||
|
||||
import "github.com/firestuff/subcoding/vm"
|
||||
import "github.com/pkg/errors"
|
||||
|
||||
var opCodeByName = map[string]vm.OpCodeType{
|
||||
"add": vm.OpAdd,
|
||||
"call": vm.OpCall,
|
||||
"ltu": vm.OpLTU,
|
||||
"jmpt": vm.OpJmpT,
|
||||
}
|
||||
|
||||
var operandsByOpCode = map[vm.OpCodeType]int{
|
||||
vm.OpAdd: 2,
|
||||
vm.OpCall: 1,
|
||||
vm.OpLTU: 2,
|
||||
vm.OpJmpT: 1,
|
||||
}
|
||||
|
||||
func Assemble(src []byte) ([][]*vm.Instruction, error) {
|
||||
prog, err := parse(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fncs := [][]*vm.Instruction{}
|
||||
|
||||
for f, fnc := range prog.Functions {
|
||||
instrs, err := assembleFunction(fnc)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "At function index %d\n", f)
|
||||
}
|
||||
|
||||
fncs = append(fncs, instrs)
|
||||
}
|
||||
|
||||
return fncs, nil
|
||||
}
|
||||
|
||||
func AssembleString(src string) ([][]*vm.Instruction, error) {
|
||||
return Assemble([]byte(src))
|
||||
}
|
||||
|
||||
func assembleFunction(fnc function) ([]*vm.Instruction, error) {
|
||||
instrs := []*vm.Instruction{}
|
||||
|
||||
for i, in := range fnc {
|
||||
instr, err := assembleInstruction(in)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "At instruction index %d\n", i)
|
||||
}
|
||||
|
||||
instrs = append(instrs, instr)
|
||||
}
|
||||
|
||||
return instrs, nil
|
||||
}
|
||||
|
||||
func assembleInstruction(in instruction) (*vm.Instruction, error) {
|
||||
if len(in) < 1 {
|
||||
return nil, fmt.Errorf("Empty instruction")
|
||||
}
|
||||
|
||||
instr := &vm.Instruction{}
|
||||
|
||||
opCode, found := opCodeByName[in[0]]
|
||||
if !found {
|
||||
return nil, fmt.Errorf("Invalid op name: %s\n", in[0])
|
||||
}
|
||||
|
||||
instr.OpCode = opCode
|
||||
|
||||
operands := in[1:]
|
||||
if len(operands) != operandsByOpCode[instr.OpCode] {
|
||||
return nil, fmt.Errorf("Incorrect number of operands: expected %d, found %d\n", operandsByOpCode[instr.OpCode], len(operands))
|
||||
}
|
||||
|
||||
if len(operands) >= 1 {
|
||||
op1, err := assembleOperand(operands[0])
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "In first operand")
|
||||
}
|
||||
|
||||
instr.Operand1 = op1
|
||||
}
|
||||
|
||||
if len(operands) >= 2 {
|
||||
op2, err := assembleOperand(operands[0])
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "In second operand")
|
||||
}
|
||||
|
||||
instr.Operand2 = op2
|
||||
}
|
||||
|
||||
return instr, nil
|
||||
}
|
||||
|
||||
func assembleOperand(op string) (vm.Operand, error) {
|
||||
ret := vm.Operand{}
|
||||
numStr := ""
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(op, "f"):
|
||||
ret.Type = vm.FunctionMemoryIndex
|
||||
numStr = strings.TrimPrefix(op, "f")
|
||||
|
||||
case strings.HasPrefix(op, "g"):
|
||||
ret.Type = vm.GlobalMemoryIndex
|
||||
numStr = strings.TrimPrefix(op, "g")
|
||||
|
||||
default:
|
||||
ret.Type = vm.Literal
|
||||
numStr = op
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(numStr, "+") || strings.HasPrefix(numStr, "-"):
|
||||
num, err := strconv.ParseInt(numStr, 10, 64)
|
||||
if err != nil {
|
||||
return ret, errors.Wrapf(err, "While parsing operand value %s", numStr)
|
||||
}
|
||||
ret.Value = uint64(num)
|
||||
|
||||
default:
|
||||
num, err := strconv.ParseUint(numStr, 10, 64)
|
||||
if err != nil {
|
||||
return ret, errors.Wrapf(err, "While parsing operand value %s", numStr)
|
||||
}
|
||||
ret.Value = num
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
@@ -2,4 +2,10 @@ module github.com/firestuff/subcoding/assembler
|
||||
|
||||
go 1.17
|
||||
|
||||
require gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
require (
|
||||
github.com/firestuff/subcoding/vm v0.0.0-20211119013410-6de2e87342fa
|
||||
github.com/pkg/errors v0.9.1
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
github.com/firestuff/subcoding/vm v0.0.0-20211119013410-6de2e87342fa h1:xx9ht6+TpFa19jx954x5+4koxcyxQHRbxItLnzPv6ug=
|
||||
github.com/firestuff/subcoding/vm v0.0.0-20211119013410-6de2e87342fa/go.mod h1:s02/Ta9wsxRR9W5Uom7LoQEu4bpFQfJdhaIHaXzN0IY=
|
||||
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc=
|
||||
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
|
||||
@@ -2,24 +2,21 @@ package assembler
|
||||
|
||||
import "gopkg.in/yaml.v2"
|
||||
|
||||
type Program struct {
|
||||
Functions []*Function `yaml:"functions"`
|
||||
type program struct {
|
||||
Functions []function `yaml:"functions"`
|
||||
}
|
||||
|
||||
type Function []*Instruction
|
||||
type Instruction []string
|
||||
type function []instruction
|
||||
|
||||
func NewProgramFromBytes(in []byte) (*Program, error) {
|
||||
prog := &Program{}
|
||||
type instruction []string
|
||||
|
||||
err := yaml.UnmarshalStrict(in, &prog)
|
||||
func parse(src []byte) (*program, error) {
|
||||
prog := &program{}
|
||||
|
||||
err := yaml.UnmarshalStrict(src, &prog)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return prog, nil
|
||||
}
|
||||
|
||||
func NewProgramFromString(in string) (*Program, error) {
|
||||
return NewProgramFromBytes([]byte(in))
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
package assembler
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestSimple(t *testing.T) {
|
||||
prog, err := NewProgramFromString(`
|
||||
functions:
|
||||
- - [add, f0, 1]
|
||||
- [call, +1]
|
||||
- [ltu, f0, 3]
|
||||
- [jmpt, -3]
|
||||
|
||||
- - [add, g0, 1]
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("%+v", prog)
|
||||
}
|
||||
Reference in New Issue
Block a user