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, 0, 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, 0, 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 }