RauliL / plorth

Embeddable Forth inspired scripting language
BSD 2-Clause "Simplified" License
35 stars 3 forks source link

Implement generators #29

Open frostburn opened 7 years ago

frostburn commented 7 years ago

Make it possible to turn quotes into interruptible programs. Proposed example:

# Push an endlessly running quote onto the stack.
(
  0
  ( true )
  (
    dup yield  # New word similar to println on top level.
    1 +
  )
  while
)
*            # Turns the quote into a generator.
next         #=> 0
drop next    #=> 1
drop 5 take  #=> [2, 3, 4, 5, 6]
drop dup     # Saves generator state
2 take       #=> [7, 8]  (Takes from the duplicate instance)
2drop
3 take       #=> [7, 8, 9]  (Resumes taking from the first instance)
frostburn commented 7 years ago

Some way of passing data back to the generator would also be nice.

RauliL commented 7 years ago

Iterators with similiar API could be implemented without any modifications to the interpreter.

frostburn commented 7 years ago

How would they work? Use an array as a stack or something?

RauliL commented 7 years ago

This is how I implemented them:

{
  "prototype": {
    "constructor": (
      "quote" swap !
    ),

    "next": (
      "state" swap has-own? ( "state" swap @ swap ) if
      "quote" swap @ rot swap call
      dup rot "state" swap ! swap
    ),
  },
} "generator" const

Usage:


( 1 + ) generator new  # Turn quote into generator.
0 swap                 # Sets initial value of generator to 0.
next println           # Returns 1
next println           # Returns 2
next println           # Returns 3
frostburn commented 7 years ago

If I understand correctly this only allows a single value to act as the state of the generator. It is also quite brittle if the quote happens to misuse the stack. Having a generator accidentally read or write onto your stack creates hard-to-debug issues. In my proposed implementation any misuse would raise a range error on the generator's local stack.