From 6e36152b6aa13358c006e59859f2b86d1666be34 Mon Sep 17 00:00:00 2001 From: Ian Gulliver Date: Fri, 19 Nov 2021 16:14:38 -1000 Subject: [PATCH] Typed operands --- asm/assemble.go | 23 +++++++++++++-------- asm/opcodes.go | 53 ++++++++++++++++++++++++++++-------------------- test/asm_test.go | 35 ++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 30 deletions(-) create mode 100644 test/asm_test.go diff --git a/asm/assemble.go b/asm/assemble.go index ec9bd8f..6076ec1 100644 --- a/asm/assemble.go +++ b/asm/assemble.go @@ -61,12 +61,13 @@ func assembleInstruction(in instruction) (*vm.Instruction, error) { 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)) + 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)) } if len(operands) >= 1 { - op1, err := assembleOperand(operands[0]) + op1, err := assembleOperand(operands[0], pattern[0]) if err != nil { return nil, errors.Wrapf(err, "In first operand") } @@ -75,7 +76,7 @@ func assembleInstruction(in instruction) (*vm.Instruction, error) { } if len(operands) >= 2 { - op2, err := assembleOperand(operands[1]) + op2, err := assembleOperand(operands[1], pattern[1]) if err != nil { return nil, errors.Wrapf(err, "In second operand") } @@ -86,7 +87,7 @@ func assembleInstruction(in instruction) (*vm.Instruction, error) { return instr, nil } -func assembleOperand(op string) (vm.Operand, error) { +func assembleOperand(op string, t operandType) (vm.Operand, error) { op = strings.ToLower(op) ret := vm.Operand{} numStr := "" @@ -100,25 +101,31 @@ func assembleOperand(op string) (vm.Operand, error) { ret.Type = vm.GlobalMemoryIndex numStr = strings.TrimPrefix(op, "g") - default: + 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 strings.HasPrefix(numStr, "+") || strings.HasPrefix(numStr, "-"): + 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) - default: + 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 diff --git a/asm/opcodes.go b/asm/opcodes.go index 46c8be8..4a31c0c 100644 --- a/asm/opcodes.go +++ b/asm/opcodes.go @@ -50,29 +50,38 @@ var opCodeByName = map[string]vm.OpCodeType{ "jmpf": vm.OpJmpF, } -var operandsByOpCode = map[vm.OpCodeType]int{ - vm.OpNop: 0, - vm.OpCal: 1, - vm.OpRet: 0, +type operandType int - vm.OpMov: 2, - vm.OpAdd: 2, - vm.OpSub: 2, - vm.OpMul: 2, - vm.OpDivU: 2, - vm.OpDivS: 2, +const ( + u operandType = 0 + s = 1 + us = 2 + r = 3 +) - vm.OpEq: 2, - vm.OpLTU: 2, - vm.OpLTS: 2, - vm.OpGTU: 2, - vm.OpGTS: 2, - vm.OpLTEU: 2, - vm.OpLTES: 2, - vm.OpGTEU: 2, - vm.OpGTES: 2, +var operandsByOpCode = map[vm.OpCodeType][]operandType{ + vm.OpNop: []operandType{}, + vm.OpCal: []operandType{s}, + vm.OpRet: []operandType{}, - vm.OpJmp: 1, - vm.OpJmpT: 1, - vm.OpJmpF: 1, + vm.OpMov: []operandType{r, us}, + vm.OpAdd: []operandType{r, us}, + vm.OpSub: []operandType{r, us}, + vm.OpMul: []operandType{r, us}, + vm.OpDivU: []operandType{r, u}, + vm.OpDivS: []operandType{r, s}, + + vm.OpEq: []operandType{us, us}, + vm.OpLTU: []operandType{u, u}, + vm.OpLTS: []operandType{s, s}, + vm.OpGTU: []operandType{u, u}, + vm.OpGTS: []operandType{s, s}, + vm.OpLTEU: []operandType{u, u}, + vm.OpLTES: []operandType{s, s}, + vm.OpGTEU: []operandType{u, u}, + vm.OpGTES: []operandType{s, s}, + + vm.OpJmp: []operandType{s}, + vm.OpJmpT: []operandType{s}, + vm.OpJmpF: []operandType{s}, } diff --git a/test/asm_test.go b/test/asm_test.go new file mode 100644 index 0000000..e10e812 --- /dev/null +++ b/test/asm_test.go @@ -0,0 +1,35 @@ +package test + +import "testing" + +import "github.com/firestuff/subcoding/asm" + +func TestTooManyOperands(t *testing.T) { + _, err := asm.AssembleString(` +functions: +- - [nop, 0] +`) + if err == nil { + t.Fatal("Lack of error when passing operand to nop") + } +} + +func TestTooFewOperands(t *testing.T) { + _, err := asm.AssembleString(` +functions: +- - [mov, g0] +`) + if err == nil { + t.Fatal("Lack of error when passing only one operand to mov") + } +} + +func TestIncorrectSigned(t *testing.T) { + _, err := asm.AssembleString(` +functions: +- - [ltu, 0, -1] +`) + if err == nil { + t.Fatal("Lack of error when passing signed value to ltu") + } +}