-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathsystem.go
146 lines (122 loc) · 4.01 KB
/
system.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package main
import (
"fmt"
"os/exec"
"strconv"
"syscall"
)
// System defines all of the inbuilt functions.
var System = map[string]func(vm *VirtualMachine, args []int){
// This is a really dodgy hack until we can properly support varargs. Each
// of the arguments will be printed with no space between them and a single
// newline will be written after any (including zero) arguments.
"display": display,
"display ?": display,
"display ? ?": display,
"display ? ? ?": display,
"display ? ? ? ?": display,
"display ? ? ? ? ?": display,
"display ? ? ? ? ? ?": display,
"display ? ? ? ? ? ? ?": display,
"display ? ? ? ? ? ? ? ? ?": display,
"display ? ? ? ? ? ? ? ? ? ?": display,
"display ? ? ? ? ? ? ? ? ? ? ?": display,
// The other built-in functions.
"set ? to ?": setVariable,
"add ? and ? into ?": add,
"subtract ? from ? into ?": subtract,
"multiply ? and ? into ?": multiply,
"divide ? by ? into ?": divide,
"run system command ?": system,
"run system command ? output into ?": systemOutput,
"run system command ? status code into ?": systemStatus,
"run system command ? output into ? status code into ?": systemOutputStatus,
}
func display(vm *VirtualMachine, args []int) {
for _, arg := range args {
// TODO: Convert this switch into an interface.
switch value := vm.GetArg(arg).(type) {
case *string: // text
_, _ = fmt.Fprintf(vm.out, "%v", *value)
case *Number:
_, _ = fmt.Fprintf(vm.out, "%v", value.String())
case nil: // blackhole
case *Backend:
response, err := value.send(&BackendRequest{
Sentence: "display ?",
Args: []string{fmt.Sprintf("%v", value)},
})
if err != nil {
panic(err)
}
_, _ = fmt.Fprintf(vm.out, "%v", response.Text)
default:
panic(value)
}
}
_, _ = fmt.Fprint(vm.out, "\n")
}
func setVariable(vm *VirtualMachine, args []int) {
switch value := vm.GetArg(args[1]).(type) {
case *string: // text
vm.SetArg(args[0], NewText(*value))
case *Number:
vm.GetNumber(args[0]).Set(value)
}
}
func add(vm *VirtualMachine, args []int) {
a := vm.GetNumber(args[0])
b := vm.GetNumber(args[1])
c := vm.GetNumber(args[2])
c.Add(a, b)
}
func subtract(vm *VirtualMachine, args []int) {
a := vm.GetNumber(args[0])
b := vm.GetNumber(args[1])
c := vm.GetNumber(args[2])
// Notice there are in reverse order because the language is
// "subtract a from b".
c.Sub(b, a)
}
func multiply(vm *VirtualMachine, args []int) {
a := vm.GetNumber(args[0])
b := vm.GetNumber(args[1])
c := vm.GetNumber(args[2])
c.Mul(a, b)
}
func divide(vm *VirtualMachine, args []int) {
a := vm.GetNumber(args[0])
b := vm.GetNumber(args[1])
c := vm.GetNumber(args[2])
c.Quo(a, b)
}
func runSystemCommand(rawCommand string) (output []byte, status int) {
cmd := exec.Command("sh", "-c", rawCommand)
var err error
output, err = cmd.CombinedOutput()
if msg, ok := err.(*exec.ExitError); ok {
status = msg.Sys().(syscall.WaitStatus).ExitStatus()
}
return
}
func system(vm *VirtualMachine, args []int) {
rawCommand := vm.GetText(args[0])
output, _ := runSystemCommand(*rawCommand)
_, _ = vm.out.Write(output)
}
func systemOutput(vm *VirtualMachine, args []int) {
rawCommand := vm.GetText(args[0])
output, _ := runSystemCommand(*rawCommand)
vm.SetArg(args[1], NewText(string(output)))
}
func systemStatus(vm *VirtualMachine, args []int) {
rawCommand := vm.GetText(args[0])
_, status := runSystemCommand(*rawCommand)
vm.SetArg(args[1], NewNumber(strconv.Itoa(status), 0))
}
func systemOutputStatus(vm *VirtualMachine, args []int) {
rawCommand := vm.GetText(args[0])
output, status := runSystemCommand(*rawCommand)
vm.SetArg(args[1], NewText(string(output)))
vm.SetArg(args[2], NewNumber(strconv.Itoa(status), 0))
}