umx_compiler

UMX virtual machine "Monkey" interpreter / bytecode compiler
git clone git://bsandro.tech/umx_compiler
Log | Files | Refs | README | LICENSE

instruction.go (2638B)


      1 package asm
      2 
      3 import (
      4 	"bytes"
      5 	"fmt"
      6 	"log"
      7 	"regexp"
      8 	"strconv"
      9 	"strings"
     10 )
     11 
     12 type Instruction struct {
     13 	Opcode           Opcode
     14 	RegA, RegB, RegC Register
     15 	Value            uint32
     16 }
     17 
     18 func NewInstruction(in string) *Instruction {
     19 	parsingRe := regexp.MustCompile(`[a-zA-Z0-9']+`)
     20 	input := parsingRe.FindAllString(in, -1)
     21 	cnt := len(input)
     22 	if cnt == 0 {
     23 		log.Fatal("invalid input - array cannot be empty")
     24 	}
     25 	opcode := NewOpcode(strings.ToUpper(input[0]))
     26 	instr := Instruction{Opcode: opcode}
     27 	errmsg := fmt.Sprintf("invalid number of operands for opcode %s", input[0])
     28 
     29 	switch opcode {
     30 	case CMOV, ARRI, ARRA, ADD, MUL, DIV, NOTA:
     31 		if cnt != 4 {
     32 			log.Fatal(errmsg)
     33 		}
     34 		instr.RegA = NewRegister(input[1])
     35 		instr.RegB = NewRegister(input[2])
     36 		instr.RegC = NewRegister(input[3])
     37 	case HALT: // no operands
     38 	case ALLO, LOAD:
     39 		if cnt != 3 {
     40 			log.Fatal(errmsg)
     41 		}
     42 		instr.RegB = NewRegister(input[1])
     43 		instr.RegC = NewRegister(input[2])
     44 	case ABAN, OUTP, INP:
     45 		if cnt != 2 {
     46 			log.Fatal(errmsg)
     47 		}
     48 		instr.RegC = NewRegister(input[1])
     49 	case ORTH:
     50 		if cnt != 3 {
     51 			log.Fatal(errmsg)
     52 		}
     53 		instr.RegA = NewRegister(input[1])
     54 		instr.Value = parseValue(input[2])
     55 	}
     56 
     57 	return &instr
     58 }
     59 
     60 func parseValue(s string) uint32 {
     61 	var val uint32
     62 	if strings.ContainsRune(s, '\'') {
     63 		s = strings.Trim(s, "'")
     64 		if len(s) != 1 {
     65 			log.Fatalf("invalid character value '%s'", s)
     66 		}
     67 		val = uint32(s[0])
     68 	} else {
     69 		if v, err := strconv.ParseUint(s, 10, 32); err != nil {
     70 			log.Fatal(err)
     71 		} else {
     72 			val = uint32(v)
     73 		}
     74 	}
     75 	return val
     76 }
     77 
     78 //@todo make some kind of polymorphism for different opcodes
     79 func (i *Instruction) Pack() uint32 {
     80 	const valueMask uint32 = 0x1FFFFFF
     81 	var ret uint32 = uint32(i.Opcode) << 28
     82 	if i.Opcode == ORTH {
     83 		var v uint32 = i.Value & valueMask
     84 		var r1 uint32 = uint32(i.RegA) << 25
     85 		ret = ret | r1 | v
     86 	} else {
     87 		var r1 uint32 = uint32(i.RegA) << 6
     88 		var r2 uint32 = uint32(i.RegB) << 3
     89 		var r3 uint32 = uint32(i.RegC)
     90 		ret = ret | r1 | r2 | r3
     91 	}
     92 	return ret
     93 }
     94 
     95 func (i *Instruction) String() string {
     96 	var buf bytes.Buffer
     97 	buf.WriteString(i.Opcode.String())
     98 	buf.WriteString(" ")
     99 
    100 	switch i.Opcode {
    101 	case CMOV, ARRI, ARRA, ADD, MUL, DIV, NOTA:
    102 		dumpRegisters(&buf, i.RegA, i.RegB, i.RegC)
    103 	case HALT:
    104 		// no operands
    105 	case ALLO, LOAD:
    106 		dumpRegisters(&buf, i.RegB, i.RegC)
    107 	case ABAN, OUTP, INP:
    108 		dumpRegisters(&buf, i.RegC)
    109 	case ORTH:
    110 		dumpRegisters(&buf, i.RegA)
    111 		buf.WriteString(strconv.Itoa(int(i.Value)))
    112 	}
    113 
    114 	return buf.String()
    115 }
    116 
    117 func dumpRegisters(buf *bytes.Buffer, regs ...Register) {
    118 	for _, reg := range regs {
    119 		buf.WriteString(reg.String())
    120 		buf.WriteString(" ")
    121 	}
    122 }