behavior.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. package vm
  2. import (
  3. "fmt"
  4. . "kumachan/runtime/common"
  5. "kumachan/runtime/common/rx"
  6. "kumachan/runtime/lib"
  7. )
  8. func assert(ok bool, msg string) {
  9. if !ok { panic(msg) }
  10. }
  11. func execute(p Program, m *Machine) {
  12. var L = len(p.Functions) + len(p.Constants) + len(p.Constants)
  13. assert(L <= GlobalSlotMaxSize, "maximum registry size exceeded")
  14. var N = lib.NativeFunctions // TODO: change to GetNativeFunction(i)
  15. m.globalSlot = make([]Value, 0, L)
  16. for _, v := range p.DataValues {
  17. m.globalSlot = append(m.globalSlot, v.ToValue())
  18. }
  19. for i, _ := range p.Functions {
  20. var f = &(p.Functions[i])
  21. m.globalSlot = append(m.globalSlot, f.ToValue(N))
  22. }
  23. for i, _ := range p.Closures {
  24. var f = &(p.Closures[i])
  25. m.globalSlot = append(m.globalSlot, f.ToValue(nil))
  26. }
  27. for i, _ := range p.Constants {
  28. var f = &(p.Constants[i])
  29. var v = f.ToValue(N)
  30. m.globalSlot = append(m.globalSlot, m.Call(v, nil))
  31. }
  32. var ctx = rx.Background()
  33. for i, _ := range p.Effects {
  34. var f = &(p.Effects[i])
  35. var v = f.ToValue(nil)
  36. var e = (m.Call(v, nil)).(rx.Effect)
  37. m.scheduler.RunTopLevel(e, rx.Receiver {
  38. Context: ctx,
  39. Values: nil,
  40. Error: nil,
  41. })
  42. }
  43. }
  44. func call(f FunctionValue, arg Value, m *Machine) Value {
  45. var ec = m.contextPool.Get().(*ExecutionContext)
  46. defer (func() {
  47. var err = recover()
  48. if err != nil {
  49. PrintRuntimeErrorMessage(err, ec)
  50. panic(err)
  51. }
  52. }) ()
  53. ec.pushCall(f, arg)
  54. outer: for len(ec.callStack) > 0 {
  55. var code = ec.workingFrame.function.Code
  56. var base_addr = ec.workingFrame.baseAddr
  57. var inst_ptr_ref = &(ec.workingFrame.instPtr)
  58. for *inst_ptr_ref < uint(len(code)) {
  59. var inst = code[*inst_ptr_ref]
  60. *inst_ptr_ref += 1
  61. switch inst.OpCode {
  62. case NOP:
  63. // do nothing
  64. case NIL:
  65. ec.pushValue(nil)
  66. case GLOBAL:
  67. var id = inst.GetGlobalIndex()
  68. var gv = m.globalSlot[id]
  69. ec.pushValue(gv)
  70. case LOAD:
  71. var offset = inst.GetOffset()
  72. var value = ec.dataStack[base_addr + offset]
  73. ec.pushValue(value)
  74. case STORE:
  75. var offset = inst.GetOffset()
  76. var value = ec.popValue()
  77. ec.dataStack[base_addr + offset] = value
  78. case SUM:
  79. var index = inst.GetRawShortIndexOrSize()
  80. var value = ec.popValue()
  81. ec.pushValue(SumValue {
  82. Index: index,
  83. Value: value,
  84. })
  85. case JIF:
  86. switch sum := ec.getCurrentValue().(type) {
  87. case SumValue:
  88. if sum.Index == inst.GetRawShortIndexOrSize() {
  89. var new_inst_ptr = inst.GetDestAddr()
  90. assert(new_inst_ptr < uint(len(code)),
  91. "JIF: invalid address")
  92. *inst_ptr_ref = new_inst_ptr
  93. } else {
  94. // do nothing
  95. }
  96. default:
  97. panic("JIF: cannot execute on non-sum value")
  98. }
  99. case JMP:
  100. var new_inst_ptr = inst.GetDestAddr()
  101. assert(new_inst_ptr < uint(len(code)),
  102. "JMP: invalid address")
  103. *inst_ptr_ref = new_inst_ptr
  104. case PROD:
  105. var size = inst.GetShortIndexOrSize()
  106. var elements = make([]Value, size)
  107. for i := uint(0); i < size; i += 1 {
  108. elements[size-1-i] = ec.popValue()
  109. }
  110. ec.pushValue(ProductValue {
  111. Elements: elements,
  112. })
  113. case GET:
  114. var index = inst.GetShortIndexOrSize()
  115. switch prod := ec.getCurrentValue().(type) {
  116. case ProductValue:
  117. assert(index < uint(len(prod.Elements)),
  118. "GET: invalid index")
  119. ec.pushValue(prod.Elements[index])
  120. default:
  121. panic("GET: cannot execute on non-product value")
  122. }
  123. case SET:
  124. var index = inst.GetShortIndexOrSize()
  125. var value = ec.popValue()
  126. switch prod := ec.popValue().(type) {
  127. case ProductValue:
  128. var L = uint(len(prod.Elements))
  129. assert(index < L, "SET: invalid index")
  130. var draft = make([]Value, L)
  131. copy(draft, prod.Elements)
  132. draft[index] = value
  133. ec.pushValue(ProductValue {
  134. Elements: draft,
  135. })
  136. default:
  137. panic("GET: cannot execute on non-product value")
  138. }
  139. case CTX:
  140. var is_recursive = (inst.Arg1 != 0)
  141. switch prod := ec.popValue().(type) {
  142. case ProductValue:
  143. var ctx = prod.Elements
  144. switch f := ec.popValue().(type) {
  145. case FunctionValue:
  146. var required = int(f.Underlying.BaseSize.Context)
  147. var given = len(ctx)
  148. if is_recursive { given += 1 }
  149. assert(given == required, "CTX: invalid context size")
  150. assert((len(f.ContextValues) == 0), "CTX: context already injected")
  151. if is_recursive { ctx = append(ctx, nil) }
  152. var fv = FunctionValue {
  153. Underlying: f.Underlying,
  154. ContextValues: ctx,
  155. }
  156. if is_recursive { ctx[len(ctx)-1] = fv }
  157. ec.pushValue(fv)
  158. default:
  159. panic("CTX: cannot inject context for non-function value")
  160. }
  161. default:
  162. panic("CTX: cannot use non-product value as context")
  163. }
  164. case CALL:
  165. switch f := ec.popValue().(type) {
  166. case FunctionValue:
  167. // check if the function is valid
  168. var required = int(f.Underlying.BaseSize.Context)
  169. var current = len(f.ContextValues)
  170. assert(current == required,
  171. "CALL: missing correct context")
  172. var arg = ec.popValue()
  173. // tail call optimization
  174. var L = uint(len(code))
  175. var next_inst_ptr = *inst_ptr_ref
  176. if next_inst_ptr < L {
  177. var next = code[next_inst_ptr]
  178. if next.OpCode == JMP && next.GetDestAddr() == L-1 {
  179. ec.popTailCall()
  180. }
  181. } else {
  182. ec.popTailCall()
  183. }
  184. // push the function to call stack
  185. ec.pushCall(f, arg)
  186. // check if call stack size exceeded
  187. var stack_size = uint(len(ec.dataStack))
  188. assert(stack_size < m.maxStackSize,
  189. "CALL: stack overflow")
  190. // work on the pushed new frame
  191. continue outer
  192. case NativeFunctionValue:
  193. var arg = ec.popValue()
  194. var ret = f(arg, m)
  195. ec.pushValue(ret)
  196. default:
  197. panic("CALL: cannot execute on non-callable value")
  198. }
  199. case ARRAY:
  200. var size = inst.GetGlobalIndex()
  201. ec.pushValue(make([]Value, 0, size))
  202. case APPEND:
  203. var val = ec.popValue()
  204. var arr, ok = ec.popValue().([]Value)
  205. if !ok {
  206. panic("APPEND: cannot append to non-array value")
  207. }
  208. ec.pushValue(append(arr, val))
  209. default:
  210. panic(fmt.Sprintf("invalid instruction %+v", inst))
  211. }
  212. }
  213. ec.popCall()
  214. }
  215. var ret = ec.popValue()
  216. ec.workingFrame = CallStackFrame {}
  217. for i, _ := range ec.callStack {
  218. ec.callStack[i] = CallStackFrame {}
  219. }
  220. ec.callStack = ec.callStack[:0]
  221. for i, _ := range ec.dataStack {
  222. ec.dataStack[i] = nil
  223. }
  224. ec.dataStack = ec.dataStack[:0]
  225. m.contextPool.Put(ec)
  226. return ret
  227. }
  228. func (ec *ExecutionContext) getCurrentValue() Value {
  229. return ec.dataStack[len(ec.dataStack) - 1]
  230. }
  231. func (ec *ExecutionContext) pushValue(v Value) {
  232. ec.dataStack = append(ec.dataStack, v)
  233. }
  234. func (ec *ExecutionContext) popValue() Value {
  235. var L = len(ec.dataStack)
  236. assert(L > 0, "cannot pop empty data stack")
  237. var cur = (L - 1)
  238. var popped = ec.dataStack[cur]
  239. ec.dataStack[cur] = nil
  240. ec.dataStack = ec.dataStack[:cur]
  241. return popped
  242. }
  243. func (ec *ExecutionContext) popValuesTo(addr uint) {
  244. var L = uint(len(ec.dataStack))
  245. assert(L > 0, "cannot pop empty data stack")
  246. assert(addr < L, "invalid data stack address")
  247. for i := addr; i < L; i += 1 {
  248. ec.dataStack[i] = nil
  249. }
  250. ec.dataStack = ec.dataStack[:addr]
  251. }
  252. func (ec *ExecutionContext) pushCall(f FunctionValue, arg Value) {
  253. var context_size = int(f.Underlying.BaseSize.Context)
  254. var reserved_size = int(f.Underlying.BaseSize.Reserved)
  255. assert(context_size == len(f.ContextValues),
  256. "invalid number of context values")
  257. var new_base_addr = uint(len(ec.dataStack))
  258. for i := 0; i < context_size; i += 1 {
  259. ec.pushValue(f.ContextValues[i])
  260. }
  261. for i := 0; i < reserved_size; i += 1 {
  262. ec.pushValue(nil)
  263. }
  264. ec.pushValue(arg)
  265. ec.callStack = append(ec.callStack, ec.workingFrame)
  266. ec.workingFrame = CallStackFrame {
  267. function: f.Underlying,
  268. baseAddr: new_base_addr,
  269. instPtr: 0,
  270. }
  271. }
  272. func (ec *ExecutionContext) popCall() {
  273. var L = len(ec.callStack)
  274. assert(L > 0, "cannot pop empty call stack")
  275. var cur = (L - 1)
  276. var popped = ec.callStack[cur]
  277. ec.callStack[cur] = CallStackFrame {}
  278. ec.callStack = ec.callStack[:cur]
  279. var ret = ec.popValue()
  280. ec.popValuesTo(ec.workingFrame.baseAddr)
  281. ec.pushValue(ret)
  282. ec.workingFrame = popped
  283. }
  284. func (ec *ExecutionContext) popTailCall() {
  285. var L = len(ec.callStack)
  286. assert(L > 0, "cannot pop empty call stack")
  287. var cur = (L - 1)
  288. var popped = ec.callStack[cur]
  289. ec.callStack[cur] = CallStackFrame {}
  290. ec.callStack = ec.callStack[:cur]
  291. ec.popValuesTo(ec.workingFrame.baseAddr)
  292. ec.workingFrame = popped
  293. }