package asm import "fmt" import "strconv" import "strings" import "github.com/firestuff/subcoding/vm" import "github.com/pkg/errors" 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[strings.ToLower(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[1]) if err != nil { return nil, errors.Wrapf(err, "In second operand") } instr.Operand2 = op2 } return instr, nil } func assembleOperand(op string) (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") 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 }