Files
subcoding/asm/assemble.go
2021-11-20 09:46:51 -10:00

128 lines
2.7 KiB
Go

package asm
import "fmt"
import "strconv"
import "strings"
import "github.com/firestuff/subcoding/vm"
import "github.com/pkg/errors"
func Assemble(src []byte) (*vm.Program, error) {
parsed, err := parse(src)
if err != nil {
return nil, err
}
ret := &vm.Program{}
for f, fnc := range parsed.Functions {
instrs, err := assembleFunction(fnc)
if err != nil {
return nil, errors.Wrapf(err, "At function index %d\n", f)
}
newFunc := &vm.Function{
Instructions: instrs,
}
ret.Functions = append(ret.Functions, newFunc)
}
return ret, nil
}
func AssembleString(src string) (*vm.Program, 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[strings.ToLower(in[0])]
if !found {
return nil, fmt.Errorf("Invalid op name: %s\n", in[0])
}
instr.OpCode = opCode
operands := in[1:]
pattern := operandsByOpCode[instr.OpCode]
if len(operands) != len(pattern) {
return nil, fmt.Errorf("Incorrect number of operands: expected %d, found %d\n", len(pattern), len(operands))
}
for i, t := range pattern {
op, err := assembleOperand(operands[i], t)
if err != nil {
return nil, errors.Wrapf(err, "In first operand")
}
instr.Operands[i] = op
}
return instr, nil
}
func assembleOperand(op string, t operandType) (*vm.Operand, error) {
op = strings.ToLower(op)
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")
case t == s || t == u || t == us:
ret.Type = vm.Literal
numStr = op
default:
return ret, fmt.Errorf("Invalid operand value type: %s", op)
}
switch {
case t == s || (t == us && (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)
case t == u || t == us || t == r:
num, err := strconv.ParseUint(numStr, 10, 64)
if err != nil {
return ret, errors.Wrapf(err, "While parsing operand value %s", numStr)
}
ret.Value = num
default:
return ret, fmt.Errorf("Invalid operand value: %s", numStr)
}
return ret, nil
}