go-vise

Constrained Size Output Virtual Machine
Info | Log | Files | Refs | README | LICENSE

vm.go (5912B)


      1 package vm
      2 
      3 import (
      4 	"encoding/binary"
      5 	"fmt"
      6 )
      7 
      8 // NewLine creates a new instruction line for the VM.
      9 func NewLine(instructionList []byte, instruction uint16, strargs []string, byteargs []byte, numargs []uint8) []byte {
     10 	if instructionList == nil {
     11 		instructionList = []byte{}
     12 	}
     13 	b := []byte{0x00, 0x00}
     14 	binary.BigEndian.PutUint16(b, instruction)
     15 	for _, arg := range strargs {
     16 		b = append(b, uint8(len(arg)))
     17 		b = append(b, []byte(arg)...)
     18 	}
     19 	if byteargs != nil {
     20 		b = append(b, uint8(len(byteargs)))
     21 		b = append(b, byteargs...)
     22 	}
     23 	if numargs != nil {
     24 		b = append(b, numargs...)
     25 	}
     26 	return append(instructionList, b...)
     27 }
     28 
     29 // ParseOp verifies and extracts the expected opcode portion of an instruction
     30 func ParseOp(b []byte) (Opcode, []byte, error) {
     31 	op, b, err := opSplit(b)
     32 	if err != nil {
     33 		return NOOP, b, err
     34 	}
     35 	return op, b, nil
     36 }
     37 
     38 // ParseLoad parses and extracts the expected argument portion of a LOAD instruction
     39 func ParseLoad(b []byte) (string, uint32, []byte, error) {
     40 	return parseSymLen(b)
     41 }
     42 
     43 // ParseReload parses and extracts the expected argument portion of a RELOAD instruction
     44 func ParseReload(b []byte) (string, []byte, error) {
     45 	return parseSym(b)
     46 }
     47 
     48 // ParseMap parses and extracts the expected argument portion of a MAP instruction
     49 func ParseMap(b []byte) (string, []byte, error) {
     50 	return parseSym(b)
     51 }
     52 
     53 // ParseMove parses and extracts the expected argument portion of a MOVE instruction
     54 func ParseMove(b []byte) (string, []byte, error) {
     55 	return parseSym(b)
     56 }
     57 
     58 // ParseHalt parses and extracts the expected argument portion of a HALT instruction
     59 func ParseHalt(b []byte) ([]byte, error) {
     60 	return parseNoArg(b)
     61 }
     62 
     63 // ParseCatch parses and extracts the expected argument portion of a CATCH instruction
     64 func ParseCatch(b []byte) (string, uint32, bool, []byte, error) {
     65 	return parseSymSig(b)
     66 }
     67 
     68 // ParseCroak parses and extracts the expected argument portion of a CROAK instruction
     69 func ParseCroak(b []byte) (uint32, bool, []byte, error) {
     70 	return parseSig(b)
     71 }
     72 
     73 // ParseInCmp parses and extracts the expected argument portion of a INCMP instruction
     74 func ParseInCmp(b []byte) (string, string, []byte, error) {
     75 	return parseTwoSym(b)
     76 }
     77 
     78 // ParseMPrev parses and extracts the expected argument portion of a MPREV instruction
     79 func ParseMPrev(b []byte) (string, string, []byte, error) {
     80 	return parseTwoSym(b)
     81 }
     82 
     83 // ParseMNext parses and extracts the expected argument portion of a MNEXT instruction
     84 func ParseMNext(b []byte) (string, string, []byte, error) {
     85 	return parseTwoSym(b)
     86 }
     87 
     88 // ParseMSink parses and extracts the expected argument portion of a MSINK instruction
     89 func ParseMSink(b []byte) ([]byte, error) {
     90 	return parseNoArg(b)
     91 	//	if len(b) < 2 {
     92 	//		return 0, 0, b, fmt.Errorf("argument too short")
     93 	//	}
     94 	//
     95 	// r := uint32(b[0])
     96 	// rr := uint32(b[1])
     97 	// b = b[2:]
     98 	// return b, nil
     99 }
    100 
    101 // ParseMOut parses and extracts the expected argument portion of a MOUT instruction
    102 func ParseMOut(b []byte) (string, string, []byte, error) {
    103 	return parseTwoSym(b)
    104 }
    105 
    106 // noop
    107 func parseNoArg(b []byte) ([]byte, error) {
    108 	return b, nil
    109 }
    110 
    111 // parse and extract two length-prefixed string values
    112 func parseSym(b []byte) (string, []byte, error) {
    113 	sym, b, err := instructionSplit(b)
    114 	if err != nil {
    115 		return "", b, err
    116 	}
    117 	return sym, b, nil
    118 }
    119 
    120 // parse and extract two length-prefixed string values
    121 func parseTwoSym(b []byte) (string, string, []byte, error) {
    122 	symOne, b, err := instructionSplit(b)
    123 	if err != nil {
    124 		return "", "", b, err
    125 	}
    126 	symTwo, b, err := instructionSplit(b)
    127 	if err != nil {
    128 		return "", "", b, err
    129 	}
    130 	return symOne, symTwo, b, nil
    131 }
    132 
    133 // parse and extract one length-prefixed string value, and one length-prefixed integer value
    134 func parseSymLen(b []byte) (string, uint32, []byte, error) {
    135 	sym, b, err := instructionSplit(b)
    136 	if err != nil {
    137 		return "", 0, b, err
    138 	}
    139 	sz, b, err := intSplit(b)
    140 	if err != nil {
    141 		return "", 0, b, err
    142 	}
    143 	return sym, sz, b, nil
    144 }
    145 
    146 // parse and extract one length-prefixed string value, and one single byte of integer
    147 func parseSymSig(b []byte) (string, uint32, bool, []byte, error) {
    148 	sym, b, err := instructionSplit(b)
    149 	if err != nil {
    150 		return "", 0, false, b, err
    151 	}
    152 	sig, b, err := intSplit(b)
    153 	if err != nil {
    154 		return "", 0, false, b, err
    155 	}
    156 	if len(b) == 0 {
    157 		return "", 0, false, b, fmt.Errorf("instruction too short")
    158 	}
    159 	matchmode := b[0] > 0
    160 	b = b[1:]
    161 
    162 	return sym, sig, matchmode, b, nil
    163 }
    164 
    165 // parse and extract one single byte of integer
    166 func parseSig(b []byte) (uint32, bool, []byte, error) {
    167 	sig, b, err := intSplit(b)
    168 	if err != nil {
    169 		return 0, false, b, err
    170 	}
    171 	if len(b) == 0 {
    172 		return 0, false, b, fmt.Errorf("instruction too short")
    173 	}
    174 	matchmode := b[0] > 0
    175 	b = b[1:]
    176 
    177 	return sig, matchmode, b, nil
    178 }
    179 
    180 // split bytecode into head and b using length-prefixed integer
    181 func intSplit(b []byte) (uint32, []byte, error) {
    182 	l := uint8(b[0])
    183 	sz := uint32(l)
    184 	b = b[1:]
    185 	if l > 0 {
    186 		r := []byte{0, 0, 0, 0}
    187 		c := 0
    188 		ll := 4 - l
    189 		var i uint8
    190 		for i = 0; i < 4; i++ {
    191 			if i >= ll {
    192 				r[i] = b[c]
    193 				c += 1
    194 			}
    195 		}
    196 		sz = binary.BigEndian.Uint32(r)
    197 		b = b[l:]
    198 	}
    199 	return sz, b, nil
    200 }
    201 
    202 // split bytecode into head and b using length-prefixed string
    203 func instructionSplit(b []byte) (string, []byte, error) {
    204 	if len(b) == 0 {
    205 		return "", nil, fmt.Errorf("argument is empty")
    206 	}
    207 	sz := uint8(b[0])
    208 	if sz == 0 {
    209 		return "", nil, fmt.Errorf("zero-length argument")
    210 	}
    211 	bSz := len(b)
    212 	if bSz < int(sz) {
    213 		return "", nil, fmt.Errorf("corrupt instruction, len %v less than symbol length: %v", bSz, sz)
    214 	}
    215 	r := string(b[1 : 1+sz])
    216 	return r, b[1+sz:], nil
    217 }
    218 
    219 // split bytecode into head and b using opcode
    220 func opSplit(b []byte) (Opcode, []byte, error) {
    221 	l := len(b)
    222 	if l < 2 {
    223 		return 0, b, fmt.Errorf("input size %v too short for opcode", l)
    224 	}
    225 	op := binary.BigEndian.Uint16(b)
    226 	if op > _MAX {
    227 		return 0, b, fmt.Errorf("invalid opcode %v", op)
    228 	}
    229 	return Opcode(op), b[2:], nil
    230 }