remko / waforth

Small but complete dynamic Forth Interpreter/Compiler for and in WebAssembly
https://mko.re/waforth
MIT License
499 stars 28 forks source link

Feasability of asynchronous bindings? #46

Closed RickCarlino closed 2 years ago

RickCarlino commented 2 years ago

Currently, to add a binding to WAForth we use Forth#bind:

  forth.bind("prompt", (stack) => {
    const message = stack.popString();
    const result = window.prompt(message);
    stack.push(parseInt(result));
  });

This is good for 90% of cases out there, but I was curious how a developer might handle asynchronous binding usecases. Eg:

For dealing with inbound events (such as reacting to DOM click events, HTML5 geolocation events, etc..), I think it would be really easy to write an event handler:

' my-handler on-click

CASE 2: Outbound Async I/O

For outbound I/O operations like AJAX, things might not be so simple:

s" geocities.com/tamagotchi98/foo.json" json-get ( How would `bind()` deal with this? )

Ideas

remko commented 2 years ago

@RickCarlino Thanks a lot for taking the time to write this up.

Async bindings crossed my mind as well. I haven't thought things through yet, but here are my initial thoughts:

One thing that's missing to create useful examples is an API to easily push strings back into WAForth (maybe put them in the PAD area?)

RickCarlino commented 2 years ago

Thanks for the detailed response, @remko that makes sense. In my case, I ended up bringing in DEFER / IS from the 20xx standard.

: DEFER! >BODY ! ;
: DEFER CREATE ['] ABORT , DOES> @ EXECUTE ;
: IS STATE @ IF POSTPONE ['] POSTPONE DEFER! ELSE ' DEFER! THEN ; IMMEDIATE

As far as string support goes, I think putting them in the PAD area would make sense.

Please let me know if you need any help!

remko commented 2 years ago

@RickCarlino Good tip!

I added a bindAsync to the wrapper that makes registering callbacks easier (and also added a pushString). I updated the fetch example to illustrate.

This is the core of the code:

forth.bindAsync("ip?", async () => {
  const result = await (await fetch("https://api.ipify.org?format=json")).json();
  forth.pushString(result.ip);
});

And then in Forth:

: IP?-CB ( true c-addr n | false -- )
  IF 
    ." Your IP address is " TYPE CR
  ELSE
    ." Unable to fetch IP address" CR
  THEN
;

: IP? ( -- )
  ['] IP?-CB
  S" ip?" SCALL 
;
RickCarlino commented 2 years ago

@remko Thanks so much- will give it a look this evening.