2021-11-18 19:35:24 -10:00
|
|
|
package asm
|
2021-11-18 17:43:13 -10:00
|
|
|
|
|
|
|
|
import "fmt"
|
|
|
|
|
import "strconv"
|
|
|
|
|
import "strings"
|
|
|
|
|
|
|
|
|
|
import "github.com/firestuff/subcoding/vm"
|
|
|
|
|
import "github.com/pkg/errors"
|
|
|
|
|
|
2021-11-19 20:16:01 -10:00
|
|
|
func Assemble(src []byte) (*vm.Program, error) {
|
|
|
|
|
parsed, err := parse(src)
|
2021-11-18 17:43:13 -10:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-20 18:27:06 -10:00
|
|
|
ret := &vm.Program{
|
|
|
|
|
GlobalMemorySize: parsed.GlobalMemorySize,
|
|
|
|
|
FunctionMemorySize: parsed.FunctionMemorySize,
|
|
|
|
|
}
|
2021-11-18 17:43:13 -10:00
|
|
|
|
2021-11-19 20:16:01 -10:00
|
|
|
for f, fnc := range parsed.Functions {
|
2021-11-18 17:43:13 -10:00
|
|
|
instrs, err := assembleFunction(fnc)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrapf(err, "At function index %d\n", f)
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-19 20:38:56 -10:00
|
|
|
newFunc := &vm.Function{
|
|
|
|
|
Instructions: instrs,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret.Functions = append(ret.Functions, newFunc)
|
2021-11-18 17:43:13 -10:00
|
|
|
}
|
|
|
|
|
|
2021-11-19 20:16:01 -10:00
|
|
|
return ret, nil
|
2021-11-18 17:43:13 -10:00
|
|
|
}
|
|
|
|
|
|
2021-11-19 20:16:01 -10:00
|
|
|
func AssembleString(src string) (*vm.Program, error) {
|
2021-11-18 17:43:13 -10:00
|
|
|
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{}
|
|
|
|
|
|
2021-11-18 19:48:46 -10:00
|
|
|
opCode, found := opCodeByName[strings.ToLower(in[0])]
|
2021-11-18 17:43:13 -10:00
|
|
|
if !found {
|
|
|
|
|
return nil, fmt.Errorf("Invalid op name: %s\n", in[0])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
instr.OpCode = opCode
|
|
|
|
|
|
|
|
|
|
operands := in[1:]
|
2021-11-20 17:26:58 -10:00
|
|
|
pattern := vm.OperandsByOpCode[instr.OpCode]
|
2021-11-19 16:14:38 -10:00
|
|
|
if len(operands) != len(pattern) {
|
|
|
|
|
return nil, fmt.Errorf("Incorrect number of operands: expected %d, found %d\n", len(pattern), len(operands))
|
2021-11-18 17:43:13 -10:00
|
|
|
}
|
|
|
|
|
|
2021-11-20 09:46:51 -10:00
|
|
|
for i, t := range pattern {
|
|
|
|
|
op, err := assembleOperand(operands[i], t)
|
2021-11-18 17:43:13 -10:00
|
|
|
if err != nil {
|
|
|
|
|
return nil, errors.Wrapf(err, "In first operand")
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-20 09:46:51 -10:00
|
|
|
instr.Operands[i] = op
|
2021-11-18 17:43:13 -10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return instr, nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-20 17:26:58 -10:00
|
|
|
func assembleOperand(op string, t vm.OperandNumericType) (*vm.Operand, error) {
|
2021-11-18 19:48:46 -10:00
|
|
|
op = strings.ToLower(op)
|
2021-11-20 09:46:51 -10:00
|
|
|
ret := &vm.Operand{}
|
2021-11-18 17:43:13 -10:00
|
|
|
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")
|
|
|
|
|
|
2021-11-20 17:26:58 -10:00
|
|
|
case t == vm.OperandSigned || t == vm.OperandUnsigned || t == vm.OperandSignedOrUnsigned:
|
2021-11-18 17:43:13 -10:00
|
|
|
ret.Type = vm.Literal
|
|
|
|
|
numStr = op
|
2021-11-19 16:14:38 -10:00
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return ret, fmt.Errorf("Invalid operand value type: %s", op)
|
2021-11-18 17:43:13 -10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch {
|
2021-11-20 17:26:58 -10:00
|
|
|
case t == vm.OperandSigned || (t == vm.OperandSignedOrUnsigned && (strings.HasPrefix(numStr, "+") || strings.HasPrefix(numStr, "-"))):
|
2021-11-20 10:19:08 -10:00
|
|
|
num, err := strconv.ParseInt(numStr, 0, 64)
|
2021-11-18 17:43:13 -10:00
|
|
|
if err != nil {
|
|
|
|
|
return ret, errors.Wrapf(err, "While parsing operand value %s", numStr)
|
|
|
|
|
}
|
|
|
|
|
ret.Value = uint64(num)
|
|
|
|
|
|
2021-11-20 17:26:58 -10:00
|
|
|
case t == vm.OperandUnsigned || t == vm.OperandSignedOrUnsigned || t == vm.OperandReference:
|
2021-11-20 10:19:08 -10:00
|
|
|
num, err := strconv.ParseUint(numStr, 0, 64)
|
2021-11-18 17:43:13 -10:00
|
|
|
if err != nil {
|
|
|
|
|
return ret, errors.Wrapf(err, "While parsing operand value %s", numStr)
|
|
|
|
|
}
|
|
|
|
|
ret.Value = num
|
2021-11-19 16:14:38 -10:00
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return ret, fmt.Errorf("Invalid operand value: %s", numStr)
|
2021-11-18 17:43:13 -10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret, nil
|
|
|
|
|
}
|