forked from goby-lang/goby
/
instruction_translator.go
130 lines (103 loc) · 3.18 KB
/
instruction_translator.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package vm
import (
"fmt"
"github.com/goby-lang/goby/compiler/bytecode"
"strconv"
)
// instructionTranslator is responsible for parsing bytecodes
type instructionTranslator struct {
vm *VM
line int
setTable map[setType]map[string][]*instructionSet
blockTable map[string]*instructionSet
filename filename
program *instructionSet
}
// newInstructionTranslator initializes instructionTranslator and its instruction set table then returns it
func newInstructionTranslator(file filename) *instructionTranslator {
it := &instructionTranslator{filename: file}
it.blockTable = make(map[string]*instructionSet)
it.setTable = map[setType]map[string][]*instructionSet{
bytecode.MethodDef: make(map[string][]*instructionSet),
bytecode.ClassDef: make(map[string][]*instructionSet),
}
return it
}
func (it *instructionTranslator) setMetadata(is *instructionSet, set *bytecode.InstructionSet) {
t := set.Type()
n := set.Name()
is.name = n
switch t {
case bytecode.Program:
it.program = is
case bytecode.Block:
it.blockTable[n] = is
default:
it.setTable[t][n] = append(it.setTable[t][n], is)
}
}
func (it *instructionTranslator) parseBooleanParam(param string) bool {
boolValue, err := strconv.ParseBool(param)
// Can happen only in case of programmatic error, as the `param` value
// is the string version of a boolean.
if err != nil {
panic(fmt.Sprintf("Unknown boolean value: %s", param))
}
return boolValue
}
func (it *instructionTranslator) parseParam(param string) interface{} {
integer, e := strconv.ParseInt(param, 0, 64)
if e != nil {
return param
}
i := int(integer)
return i
}
func (it *instructionTranslator) transferInstructionSets(sets []*bytecode.InstructionSet) []*instructionSet {
iss := []*instructionSet{}
for _, set := range sets {
it.transferInstructionSet(iss, set)
}
return iss
}
func (it *instructionTranslator) transferInstructionSet(iss []*instructionSet, set *bytecode.InstructionSet) {
is := &instructionSet{filename: it.filename}
it.setMetadata(is, set)
for _, i := range set.Instructions {
it.transferInstruction(is, i)
}
is.paramTypes = set.ArgTypes()
iss = append(iss, is)
}
// transferInstruction transfer a bytecode.Instruction into an vm instruction and append it into given instruction set.
func (it *instructionTranslator) transferInstruction(is *instructionSet, i *bytecode.Instruction) {
var params []interface{}
act := i.Action
action := builtinActions[act]
if action == nil {
panic(fmt.Sprintf("Unknown command: %s. line: %d", act, i.Line()))
}
switch act {
case bytecode.PutBoolean:
params = append(params, it.parseBooleanParam(i.Params[0]))
case bytecode.PutString:
params = append(params, i.Params[0])
case bytecode.BranchUnless, bytecode.BranchIf, bytecode.Jump:
line, err := i.AnchorLine()
if err != nil {
panic(err.Error())
}
params = append(params, line)
case bytecode.Send:
for _, param := range i.Params {
params = append(params, it.parseParam(param))
}
params = append(params, i.ArgSet)
default:
for _, param := range i.Params {
params = append(params, it.parseParam(param))
}
}
vmI := is.define(i.Line(), action, params...)
vmI.sourceLine = i.SourceLine() + 1
}