recommit of old milestones, cleanup pass
authorken <ken@mihrtec.com>
Mon, 26 Feb 2018 04:43:54 +0000 (20:43 -0800)
committerken <ken@mihrtec.com>
Mon, 26 Feb 2018 04:43:54 +0000 (20:43 -0800)
forth.forth
forth.html
forth.js
forth.wat

index ab2e73f..aa90b03 100644 (file)
@@ -10,7 +10,6 @@
 \
 \ You should have received a copy of the GNU General Public License
 \ along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
 word ' here define
 word word find , word find find , word ; find ,
 word : here define
@@ -20,6 +19,10 @@ word : here define
 ' drop , ' LIT , ' LIT , ' , , 16720 , ' , , ' ; ,
 : ^i ' LIT , ' MODE , ' LIT , ' IWRITE-MODE , ' ! , ' ; ,
 : ^e ' LIT , ' MODE , ' LIT , ' EXECUTE-MODE , ' ! , ' ; ,
-: test \^i 20 20 + ; \^e
-test .
-5 . 
+^i
+\: FINISH-STRING DROP STRING-END ;
+\: " STRING-START
+\: KEYPUMP KEY 34 =? JNZ: FINISH-STRING STRING-PUT JMP: KEYPUMP
+\^e
+" watForth-32 Interactive CLI:
+" .s
index b35238f..66ae725 100644 (file)
@@ -3,8 +3,8 @@
   <head>
     <meta charset="utf-8">
     <title>watForth</title>
+    <script src="forth.js"></script>
   </head>
   <body>
-    <script src="forth.js"></script>
   </body>
 </html>
index 7e2f5a3..c05d444 100644 (file)
--- a/forth.js
+++ b/forth.js
     You should have received a copy of the GNU General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 'use strict'
-let txtdiv = document.createElement("div")
-document.body.appendChild(txtdiv)
-const output = {
-  print: (string) => txtdiv.textContent += string
-}
-let wasmMem
-let forth
+const initialize = Promise.all([
+  fetch('forth.forth').then((re) => re.text()),
+  fetch('forth.wasm').then(re => re.arrayBuffer())
+])
+window.onload = () => {
+  let forthdiv = document.getElementById("forth")
+  if (forthdiv === null) {
+    forthdiv = document.createElement("div")
+    forthdiv.setAttribute("style","height:400px;")
+    document.body.appendChild(forthdiv)
+  }
+  const outframe = document.createElement("div")
+  outframe.setAttribute("style", "overflow-y:hidden;height:40%;")
+  const txtoutput = document.createElement("pre")
+  txtoutput.setAttribute("style", "white-space:pre-wrap;")
+  const txtinput = document.createElement("textarea")
+  let txtinputrows = "1"
+  txtinput.setAttribute("autofocus", "true")  
+  txtinput.setAttribute("cols", "60")
+  txtinput.setAttribute("rows", txtinputrows)
+  outframe.appendChild(txtoutput)
+  forthdiv.appendChild(outframe)
+  forthdiv.appendChild(txtinput)
+  const output = {
+    print: (string) => txtoutput.textContent += string
+  }
+  let wasmMem
+  let forth
 
-/* Input capture */
-let stdin = ""
-document.addEventListener('keydown', (event) => {
-  console.log(`keydown: ${event.key}`)
-  if (event.key != "F5") {
-    event.preventDefault()
-    event.stopPropagation()
+  /* Input capture */
+  let stdin = ""
+  txtinput.addEventListener('keydown', (event) => {
     switch (event.key) {
     case "Enter":
-      txtdiv = document.createElement("div")
-      document.body.appendChild(txtdiv)
-      forth()
-      output.print("ok.")
-      txtdiv = document.createElement("div")
-      document.body.appendChild(txtdiv)
+      if (event.ctrlKey) {
+        txtinput.value += '\n'
+        txtinputrows = (Number.parseInt(txtinputrows, 10) + 1).toString()
+        txtinput.setAttribute("rows", txtinputrows)
+      }
+      else {
+        stdin += txtinput.value
+        txtoutput.textContent += txtinput.value + '\n'
+        txtinput.value = ""
+        txtinputrows = "1"
+        txtinput.setAttribute("rows", txtinputrows)
+        event.preventDefault()
+        event.stopPropagation()
+        forth()
+        txtoutput.textContent += " _ok.\n"
+        outframe.scrollTop = outframe.scrollHeight;
+      }
       break
     case "Backspace":
-      stdin = stdin.substring(0, stdin.length - 1)
-      txtdiv.textContent = txtdiv.textContent.substring(0, txtdiv.textContent.length - 1)
+      if (txtinput.value.charCodeAt(txtinput.value.length - 1) === 10) {
+        txtinputrows = (Number.parseInt(txtinputrows, 10) - 1).toString()
+        txtinput.setAttribute("rows", txtinputrows)
+      }
       break
     default:
-      if (event.key.length == 1) {
-       stdin += event.key
-        output.print(event.key)
-      }
       break
     }
-  }
-})
-
-const channels = [{
-  read: (writeAddr, maxBytes) => {
-    const maxChars = maxBytes >> 1
-    const bufView = new Uint16Array(wasmMem.buffer, writeAddr, maxChars)
-    let i
-    for (i = 0; i < maxChars && i < stdin.length; i++)
-      bufView[i] = stdin.charCodeAt(i)
-    stdin = stdin.substring(maxChars)
-    return i << 1
-  },
-  write: (readAddr, maxBytes) =>
-    output.print(String.fromCharCode.apply(
-      null,
-      new Uint16Array(wasmMem.buffer, readAddr, maxBytes >> 1)
-    ))
-}]
-const simstack = []
-const rstack = []
-const dictionary = {
-  ';': 1,
-  'LIT': 2,
-  RINIT: 3,
-  WORD: 16500,
-  KEY: 5,
-  DUP: 6,
-  '+': 7,
-  'NOOP2': 8,
-  '.': 9,
-  '@': 10,
-  '!': 11,
-  EXECUTE: 12,
-  NOOP: 13,
-  'JZ:': 14,
-  'JNZ:': 15,
-  DROP: 16,
-  'WS?': 17,
-  'JMP:': 18,
-  'WPUTC': 19,
-  'WB0': 20,
-  'FIND': 21,
-  'NUMBER': 22,
-  'W!LEN': 23,
-  'J-1:': 24,
-  'BYE': 25,
-  'SWAP': 26,
-  'WORDS': 27,
-  'HERE': 28,
-  'DEFINE': 29,
-  '2DUP': 30,
-  'ROT': 31,
-  '2DROP': 32,
-  ',': 33,
-  '-': 34,
-  'CHANNEL!': 35,
-  'HERE!': 36,
-  '=?': 37,
-  '.s': 38,
-  ':': 16800,
-  'MODE': 14336,
-  'EXECUTE-MODE': 16680,
-  'QUIT': 16384,
-  'INTERPRET': 16400
-}
-const wasmImport = {
-  env: {
-    pop: () => simstack.pop(),
-    push: (val) => simstack.push(val),
-    rinit: () => rstack.length = 0,
-    rpop: () => rstack.pop(),
-    rpush: (val) => rstack.push(val),
-    sys_write: (channel, addr, u) => {
-      if (channels[channel] === undefined)
-       return
-      console.log(`write ch:${channel} addr:${addr} len:${u}`)
-      channels[channel].write(addr, u)
-    },
-    sys_read: (channel, toBuffer) => {
-      console.log(`read ch:${channel} buf:${toBuffer} current: ${stdin}`)
-      const lenView = new DataView(wasmMem.buffer, toBuffer, 8)
-      const maxBytes = lenView.getUint32(0,true)
-      console.log(`read blen:${lenView.getUint32(0,true)} cstrlen:${lenView.getUint32(4,true)}`)
-      /* If the channel is undefined, or if there isn't enough room in
-       * toBuffer for even one character, then, if there is enough
-       * room for an int write a zero, exit */
-      if (channels[channel] === undefined || maxBytes < 6) {
-       if (maxBytes >= 4)
-         lenView.setUint32(4,0,true)
+  })
+/*  document.addEventListener('keydown', (event) => {
+    //console.log(`keydown: ${event.key}`)
+    if (event.key != "F5") {
+      event.preventDefault()
+      event.stopPropagation()
+      switch (event.key) {
+      case "Enter":
+        txtoutput.textContent += '\n'
+        stdin += '\n'
+        forth()
+        output.print(" _ok.")
+        outframe.scrollTop = outframe.scrollHeight;
+        txtoutput.textContent += '\n'
+        break
+      case "Backspace":
+        stdin = stdin.substring(0, stdin.length - 1)
+        txtoutput.textContent = txtoutput.textContent.substring(0, txtoutput.textContent.length - 1)
+        break
+      default:
+        if (event.key.length == 1) {
+          const input = event.ctrlKey ? ` \\\^${event.key} ` : event.key
+          stdin += input
+          output.print(input)
+        }
+        break
       }
-      const numBytes = channels[channel].read(toBuffer + 8, maxBytes - 4)
-      lenView.setUint32(4,numBytes,true);
-      console.log(`read wrote ${lenView.getUint32(4,true)} bytes, remainder: ${stdin}`)
-      console.log(`read blen:${lenView.getUint32(0,true)} cstrlen:${lenView.getUint32(4,true)}`)   },
-    sys_listen: (reqAddr, cbAddr) => {
-      //TODO: call into the module to wasm fn "event" to push an event
-      //to forth, which pushes esi to the return stack and queues the
-      //callback function provided from listen. reqaddr could be
-      //"fetch", in which case no channel is used.  otherwise very
-      //similar to sys_fetch.
-    },
-    sys_fetch: (channel, reqAddr) => {
-      //TODO: map to fetch promise, write to channel buffer,
-      //javascript "fetch" as fallback, explicit handles for
-      //"textEntry" or any third party protocols like activitypub
-      console.log(`fetch ${channel} ${reqAddr}`)
+    }
+  })*/
+
+  const channels = [{
+    read: (writeAddr, maxBytes) => {
+      const maxChars = maxBytes >> 1
+      const bufView = new Uint16Array(wasmMem.buffer, writeAddr, maxChars)
+      let i
+      for (i = 0; i < maxChars && i < stdin.length; i++)
+        bufView[i] = stdin.charCodeAt(i)
+      stdin = stdin.substring(maxChars)
+      return i << 1
     },
-    sys_echo: (val) => output.print(`${val} `),
-    sys_echochar: (val) => output.print(String.fromCharCode(val)),
-    sys_reflect: (addr) => {
-      console.log(`reflect: ${addr}: ${
+    write: (readAddr, maxBytes) =>
+      output.print(String.fromCharCode.apply(
+        null,
+        new Uint16Array(wasmMem.buffer, readAddr, maxBytes >> 1)
+      ))
+  }]
+  const simstack = []
+  const rstack = []
+  const dictionary = {
+    ';': 1,
+    'LIT': 2,
+    RINIT: 3,
+    WORD: 16500,
+    KEY: 5,
+    DUP: 6,
+    '+': 7,
+    'NOOP2': 8,
+    '.': 9,
+    '@': 10,
+    '!': 11,
+    EXECUTE: 12,
+    NOOP: 13,
+    'JZ:': 14,
+    'JNZ:': 15,
+    DROP: 16,
+    'WS?': 17,
+    'JMP:': 18,
+    'WPUTC': 19,
+    'WB0': 20,
+    'FIND': 21,
+    'NUMBER': 22,
+    'W!LEN': 23,
+    'J-1:': 24,
+    'BYE': 25,
+    'SWAP': 26,
+    'WORDS': 27,
+    'HERE': 28,
+    'DEFINE': 29,
+    '2DUP': 30,
+    'ROT': 31,
+    '2DROP': 32,
+    ',': 33,
+    '-': 34,
+    'CHANNEL!': 35,
+    'HERE!': 36,
+    '=?': 37,
+    '.S': 38,
+    'STRING-START': 39,
+    'STRING-PUT': 40,
+    'STRING-END': 41,
+    ':': 16800,
+    'MODE': 14336,
+    'EXECUTE-MODE': 16680,
+    'QUIT': 16384,
+    'INTERPRET': 16400
+  }
+  const wasmImport = {
+    env: {
+      pop: () => simstack.pop(),
+      push: (val) => simstack.push(val),
+      rinit: () => rstack.length = 0,
+      rpop: () => rstack.pop(),
+      rpush: (val) => rstack.push(val),
+      sys_write: (channel, addr, u) => channels[channel] === undefined ? 0 : channels[channel].write(addr, u),
+      sys_read: (channel, addr, u) => channels[channel] === undefined ? 0 : channels[channel].read(addr, u),
+      sys_listen: (reqAddr, cbAddr) => {
+        //TODO: call into the module to wasm fn "event" to push an event
+        //to forth, which pushes esi to the return stack and queues the
+        //callback function provided from listen. reqaddr could be
+        //"fetch", in which case no channel is used.  otherwise very
+        //similar to sys_fetch.
+      },
+      sys_fetch: (channel, reqAddr) => {
+        //TODO: map to fetch promise, write to channel buffer,
+        //javascript "fetch" as fallback, explicit handles for
+        //"textEntry" or any third party protocols like activitypub
+        console.log(`fetch ${channel} ${reqAddr}`)
+      },
+      sys_echo: (val) => output.print(`${val} `),
+      sys_echochar: (val) => output.print(String.fromCharCode(val)),
+      sys_reflect: (addr) => {
+        console.log(`reflect: ${addr}: ${
         new DataView(wasmMem.buffer, addr, 4)
               .getUint32(0,true)
       }`)
-    },
-    vocab_get: (addr, u) => {
-      const word = String.fromCharCode.apply(
-       null,
-       new Uint16Array(wasmMem.buffer, addr, u >> 1)
-      )
-      const answer = dictionary[word.toUpperCase()]
-      console.log(`vocab_get ${word}: ${answer}`)
-      if (answer === undefined)
-       return 0
-      return answer
-    },
-    vocab_set: (addr, u, num) => {
-      console.log(`vocab set ${addr} ${u} ${num}`)
-      const word = String.fromCharCode.apply(
-       null,
-       new Uint16Array(wasmMem.buffer, addr, u >> 1)
-      )
-      console.log(`vocab_set ${word}: ${num}`)
-      dictionary[word.toUpperCase()] = num
-      return 0
-    },
-    is_whitespace: (key) => /\s/.test(String.fromCharCode(key)),
-    sys_stack: () => console.log(`[${simstack}]`),
-    sys_parsenum: (addr, u, base) => {
-      const word = String.fromCharCode.apply(
-       null,
-       new Uint16Array(wasmMem.buffer, addr, u >> 1)
-      )
-      const answer = Number.parseInt(word, base)
-      console.log(`parsenum: ${addr} ${u} ${base}`)
-      console.log(`parsenum: ${word} ${answer}`)
-      console.log(`parsenum: ${Number.isNaN(answer)}`)
-      if (Number.isNaN(answer))
-        return -1
-      new DataView(wasmMem.buffer, addr, 4).setUint32(0,answer,true)
-      return 0
-    },
-    sys_words: () => {
-      output.print(Object.getOwnPropertyNames(dictionary).toString().split(',').join('  '))
+      },
+      vocab_get: (addr, u) => {
+        const word = String.fromCharCode.apply(
+         null,
+         new Uint16Array(wasmMem.buffer, addr, u >> 1)
+        )
+        const answer = dictionary[word.toUpperCase()]
+        if (answer === undefined)
+         return 0
+        return answer
+      },
+      vocab_set: (addr, u, num) => {
+        const word = String.fromCharCode.apply(
+         null,
+         new Uint16Array(wasmMem.buffer, addr, u >> 1)
+        )
+        dictionary[word.toUpperCase()] = num
+        return 0
+      },
+      is_whitespace: (key) => /\s/.test(String.fromCharCode(key)),
+      sys_stack: () => console.log(`[${simstack}]`),
+      sys_parsenum: (addr, u, base) => {
+        const word = String.fromCharCode.apply(
+         null,
+         new Uint16Array(wasmMem.buffer, addr, u >> 1)
+        )
+        const answer = Number.parseInt(word, base)
+        if (Number.isNaN(answer))
+          return -1
+        new DataView(wasmMem.buffer, addr, 4).setUint32(0,answer,true)
+        return 0
+      },
+      sys_words: () => {
+        output.print(Object.getOwnPropertyNames(dictionary).toString().split(',').join('  '))
+      }
     }
   }
-}
-
-fetch('forth.forth').then((re) => re.text()).then((txt) => {
-  stdin = txt
-  fetch('forth.wasm')
-  .then(re => re.arrayBuffer())
-  .then(buf => WebAssembly.instantiate(buf, wasmImport))
-  .then(result => {
-    wasmMem = result.instance.exports.memory
-    forth = result.instance.exports.main
-    console.log('wasm loaded');
-    forth()
+  initialize.then((results) => {
+    stdin = results[0]
+    WebAssembly.instantiate(results[1], wasmImport).then((module) => {
+      wasmMem = module.instance.exports.memory
+      forth = module.instance.exports.main
+      console.log('wasm loaded')
+      forth()
+    })
   })
-})
+}
index 43dc195..a3a61e5 100644 (file)
--- a/forth.wat
+++ b/forth.wat
   (type $FUNCSIGiii (func))
   (type $FUNCSIGiv  (func (param i32 i32) (result i32)))
   (type $FUNCSIG$v  (func (param i32) (result i32)))
-  (type $FUNCSIG$vi (func (param i32 i32) (result i32)))  
+  (type $FUNCSIG$vi (func (param i32 i32 i32) (result i32)))  
   (import "env" "pop" (func $pop (result i32)))
   (import "env" "push" (func $push (param i32)))
   (import "env" "rinit" (func $rinit))
   (import "env" "rpop" (func $rpop (result i32)))
   (import "env" "rpush" (func $rpush (param i32)))
-  (import "env" "sys_read" (func $sys_read (param i32 i32) (result i32)))
+  (import "env" "sys_read" (func $sys_read (param i32 i32 i32) (result i32)))
   (import "env" "sys_fetch" (func $sys_fetch (param i32 i32) (result i32)))
   (import "env" "sys_listen" (func $sys_listen (param i32) (result i32)))  
   (import "env" "sys_write" (func $sys_write (param i32 i32 i32) (result i32)))
@@ -43,7 +43,7 @@
   (global $inbuf_size i32 (i32.const 12292))
   (global $inbuf_data i32 (i32.const 12296))
   (global $kvars i32 (i32.const 14336))   ;; 0x3800 Size: 2048
-  (data (i32.const 12288) "\fc\07\00\00") ;; 2044 len
+  (data (i32.const 12288) "\f8\07\00\00") ;; 2040 len
   (data (i32.const 14336) "\28\41\00\00") ;; MODE
   (data (i32.const 14340) "\04\42\00\00") ;; HERE
   (data (i32.const 14344) "\00\40\00\00") ;; START
   (data (i32.const 16752) "\19\00\00\00") ;; BYE
   (; Do Backslash ;)
   (data (i32.const 16788) "\05\00\00\00") ;; KEY
-  (data (i32.const 16792) "\02\00\00\00") ;; LIT
-  (data (i32.const 16796) "\20\00\00\00") ;; 32 (space)
-  (data (i32.const 16800) "\25\00\00\00") ;; =?
-  (data (i32.const 16804) "\0f\00\00\00") ;; JNZ:
-  (data (i32.const 16808) "\bc\41\00\00") ;; addr of keypump
-  (data (i32.const 16812) "\cc\40\00\00") ;; WORDLOOP + 1
-  (data (i32.const 16816) "\28\41\00\00") ;; EXECUTE-MODE
-  (data (i32.const 16820) "\01\00\00\00") ;; RET
+  (data (i32.const 16792) "\11\00\00\00") ;; WS?
+  (data (i32.const 16796) "\0f\00\00\00") ;; JNZ:
+  (data (i32.const 16800) "\c8\41\00\00") ;; addr of keypump + 3
+  (data (i32.const 16804) "\cc\40\00\00") ;; WORDLOOP + 1
+  (data (i32.const 16808) "\28\41\00\00") ;; EXECUTE-MODE
+  (data (i32.const 16812) "\01\00\00\00") ;; RET
+  (; Do Comment ;)
   (data (i32.const 16828) "\18\00\00\00") ;; j-1: <-- keypump
   (data (i32.const 16832) "\e0\41\00\00") ;; addr of end
   (data (i32.const 16836) "\05\00\00\00") ;; KEY
     set_local $channel
     block $bye
     loop $next
-        call $sys_stack
-      get_local $esi
-      call $sys_reflect
       get_local $esi
       get_local $esi
       i32.const 4
       block $dictget block $parsenum block $wordfinish block $jneg1 block $swap
       block $words block $here block $dictset block $dup2 block $rot block $drop2
       block $comma block $subtract block $keychan block $sethere block $eqbool
-      block $echostring
+      block $echostring block $strstart block $strput block $strend
         get_local $eax
         br_table $op0 $ret (;2;)$lit $rinit (;4;)$word $key (;6;)$dup $plus
         (;8;)$jmp $emit (;10;)$fetch $set (;12;)$execute $noop (;14;)$jz $jnz
         (;16;)$drop $wsbool (;18;)$jmp $wordputc (;20;)$wordstart $dictget
        (;22;)$parsenum $wordfinish (;24;)$jneg1 $bye (;26;)$swap $words
         (;28;)$here $dictset (;30;)$dup2 $rot (;32;)$drop2 $comma
-        (;34;)$subtract $keychan (;36;)$sethere $eqbool (;38;)$echostring $default
+        (;34;)$subtract $keychan (;36;)$sethere $eqbool (;38;)$echostring $strstart
+        (;40;)$strput $strend $default
+      end ;; strend
+        get_local $stringbelt_tail
+       get_local $stringbelt_head
+        get_local $stringbelt_tail
+       i32.const 4
+       i32.add
+        i32.sub
+        tee_local $eax (; n bytes ;)
+        i32.store
+        (; align to 32-bit ;)
+        get_local $stringbelt_head
+        i32.const 3
+        i32.add
+        i32.const 8188
+        i32.and
+        set_local $stringbelt_head
+        (; /align ;)
+        get_local $stringbelt_tail
+        i32.const 4
+        i32.add
+        call $push
+        get_local $eax
+        call $push
+        br $next
+      end ;; strput
+        block $sbhasspace2
+          get_local $stringbelt_head
+          get_global $wordbelt
+          i32.lt_u
+          br_if $sbhasspace2
+          i32.const 0
+          tee_local $stringbelt_head
+          get_local $stringbelt_tail
+          i32.load
+          i32.store
+          get_local $stringbelt_head
+          i32.const 4
+          i32.add
+          set_local $stringbelt_head
+          get_local $stringbelt_tail
+          i32.const 4
+          i32.add
+          set_local $stringbelt_tail
+          loop $copystringtostart
+            get_local $stringbelt_head
+            get_local $stringbelt_tail
+            i32.load16_u
+            i32.store16
+            get_local $stringbelt_head
+            i32.const 2
+            i32.add
+            set_local $stringbelt_head
+            get_local $stringbelt_tail
+            i32.const 2
+            i32.add
+            tee_local $stringbelt_tail
+            get_global $wordbelt
+            i32.le_u
+            br_if $copystringtostart
+          end
+          i32.const 0
+          set_local $stringbelt_tail
+        end
+       get_local $stringbelt_head
+        call $pop
+       i32.store16
+       get_local $stringbelt_head
+       i32.const 2
+       i32.add
+        set_local $stringbelt_head
+       br $next
+      end ;; strstart
+        block $sbhasspace
+          get_local $stringbelt_head
+          get_global $wordbelt
+          i32.const 8
+          i32.sub
+          i32.le_u
+          br_if $sbhasspace
+          i32.const 0
+          set_local $stringbelt_head
+        end
+        get_local $stringbelt_head
+        get_local $stringbelt_head
+        tee_local $stringbelt_tail
+        i32.const 0
+        i32.store
+        i32.const 4
+        i32.add
+        set_local $stringbelt_head
+        br $next
       end ;; echostring
         get_local $channel
         call $pop
         get_local $eax
        call $push
        call $push
-        call $sys_stack
         br $next
       end ;; drop
         call $pop
             set_local $inbuf_head
             br $next
           end ;; key_read
+          get_global $inbuf_size
           get_local $channel
+          get_global $inbuf_data
           get_global $inbuf
+          i32.load
           call $sys_read
+          i32.store
           block $nullread
             get_global $inbuf_size
             i32.load
             i32.eqz
             br_if $nullread
+            get_global $inbuf_data
+            set_local $inbuf_head
             br $key_loop
           end ;; nullread
           i32.const -1 ;; <- keyval sent if sz == 0