vm.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. const JSZM = require('./jszm-async')
  2. const defer = () => {
  3. const deferred = {
  4. resolve: value => (deferred._resolved = value),
  5. reject: err => (deferred._rejected = err)
  6. }
  7. const promise = new Promise((resolve, reject) => {
  8. if (deferred.hasOwnProperty('_resolved')) return resolve(deferred._resolved)
  9. if (deferred.hasOwnProperty('_rejected')) return reject(deferred._rejected)
  10. deferred.resolve = resolve
  11. deferred.reject = reject
  12. })
  13. deferred.promise = promise
  14. return deferred
  15. }
  16. class VM {
  17. constructor(data, save) {
  18. this.zm = new JSZM(data)
  19. const vm = this
  20. let printBuffer = []
  21. const transcript = []
  22. this.inputBuffer = []
  23. this.outputBuffer = []
  24. this.saveData = save
  25. this.zm.save = function*(buf) {
  26. vm.saveData = buf
  27. return true
  28. }
  29. this.zm.restore = function*() {
  30. return vm.saveData
  31. }
  32. this.zm.highlight = function*(a) {
  33. //printBuffer.push(`[${a}]`)
  34. }
  35. this.zm.print = function*(text, scripting) {
  36. printBuffer.push(text)
  37. if (scripting) {
  38. transcript.push(text)
  39. }
  40. }
  41. const flush = () => {
  42. const output = printBuffer.join('')
  43. printBuffer = []
  44. if (this.outputBuffer.length) {
  45. const deferred = this.outputBuffer.shift()
  46. deferred.resolve(output)
  47. } else {
  48. this.initialOutput = output
  49. }
  50. }
  51. vm.reader = null
  52. this.zm.read = async function*(maxlen) {
  53. flush() // Should only read after done writing, so flush what it's written.
  54. if (vm.reader) throw new Error('Simultaneous reads')
  55. if (vm.inputBuffer.length) {
  56. const text = this.inputBuffer.shift()
  57. return text
  58. } else {
  59. const deferred = defer()
  60. vm.reader = deferred
  61. const text = await deferred.promise
  62. vm.reader = null
  63. return text
  64. }
  65. }
  66. }
  67. start() {
  68. if (this._started) throw new Error('Already started')
  69. const deferred = defer()
  70. this.outputBuffer.push(deferred)
  71. this.zm.run().next()
  72. return deferred.promise
  73. }
  74. play(text) {
  75. const deferred = defer()
  76. this.outputBuffer.push(deferred)
  77. if (this.reader) {
  78. this.reader.resolve(text)
  79. } else {
  80. this.inputBuffer.push(text)
  81. }
  82. return deferred.promise
  83. }
  84. save() {
  85. const ret = [Buffer.from(this.zm.serialize([], [])).toString('base64')]
  86. if (this.saveData) {
  87. ret.push(Buffer.from(this.saveData).toString('base64'))
  88. }
  89. return ret.join('.')
  90. }
  91. restore(save) {
  92. if (typeof save === 'string') {
  93. const datas = save.split('.')
  94. this.zm.deserialize(Buffer.from(datas[0], 'base64'))
  95. if (datas.length > 1) {
  96. this.saveData = Buffer.from(datas[1], 'base64')
  97. }
  98. }
  99. }
  100. }
  101. VM.play = async (data, save, input) => {
  102. const vm = new VM(data)
  103. const ret = {}
  104. ret.output = await vm.start()
  105. if (save) {
  106. vm.restore(save)
  107. }
  108. if (input) {
  109. ret.output = await vm.play(input)
  110. }
  111. ret.save = vm.save()
  112. return ret
  113. }
  114. module.exports = VM