« Oatmeal

Tagged "forth"

Follow this tag

Screenshot of Pocket Forth running on Mac OS System 7.

I’m toying with the idea of becoming a full time Mac OS System 7 developer.


Edited to add that I’ve had so much fun playing with this implementation of Forth on Mac OS System 7 that I quickly built a little microsite to help archive the info I’ve found about it.

My programming language odyssey

While I wouldn’t say I’m wicked adept at any one language, I’ve dipped my toes into many different languages. Here, I try to roughly recreate my programming language journey.

I can make websitez gud; HTML, CSS/SASS, JavaScript > CoffeeScript > TypeScript, and PHP

The web. A marvel, a terror. I started here, more out of ease of access than necessity, but was able to get far enough to make a career out of web dev. I should also add SQL to this list.

Elm is something I’d like to dip my toes into.

Want make thingz go brrrr; Common Lisp

I don’t honestly know how I first came to Common Lisp, I think through a blog post, or maybe a cute book. While I don’t use it much these days, I still carry a torch for it in my heart.

Want lovely tooling; SmallTalk

I sort of wish I’d never played with SmallTalk. It broke me. SmallTalk opened my eyes to a really integrated development environment.

Oh snap! Parenthesis are rad!; Clojure

Clojure remains my white whale. On paper it is the perfect language for me:

  • lots of parenthesis
  • good at web stuff
  • fine for game dev
  • friendly with emacs

But I’ve never felt cozy in it.

The JVM is hard, but scheme is rad!; a million flavors of scheme and scheme-like languages, (Chicken Gerbil s7 Racket Guile Chibi)

Parentheses baaaby! If I was forced to stick to a single language and never touch another, I’d probably pick a scheme…the question is then which scheme!?

Racket isn’t strictly a pure” scheme, but who cares and it can be…and has a bananas gigantic library.

Chibi is adorably tiny and the most fully featured R7RS scheme I’ve found.

Chicken has some great docs…and is called chicken,” I mean, come on!? That is lovely.

What about games though? I wanna make games!; Lua (especially using PICO-8 or Love2d)

I have in recent years become pretty jaded about the state of software and what most software is used for…but I love games, so, Lua is pretty rad for making games. Lua is also a really great teaching/learning language.

But I missing the parenthesis; Fennel

Yeah, but what if Lua was a lisp-like language?

I’ve found that many programming languages are made or broken by their community. Fennel has one of the friendliest, most supportive communities I’ve ever witnessed in a programming language.

This is neat, but what if I wanted weirder?; Janet

Janet would be another contender for a forever language — it is weird, sort of a Clojure clone, sort of a Lisp, but totally its own thing at the same time. It is tiny, portable, and fits into similar spaces that C does…but also not really. Janet is a beast utterly of its own…also the name of my grandma.

Hold up now! I said weirder!; BQN, APL, K

Alright, this was probably me going off the deep end…

Okay, too0ooo weird and my brain is goo; gforth, pforth, and lbforth

I adore languages that I can hold entirely in my head. A big thing that helps me hold a language in my head is limited semantics. You don’t get much more limited than Forth!

The ethos at the heart of Forth is clearly articulated by its inventor,

The concept that programming is something that you need special education to do is not right. It is something that is promoted by the priesthood.

— Chuck Moore

Hold those horses…I’m in love!; RetroForth

Readers of this blog will have seen me talk about Retro before…while it makes no sense as a forever language…here I am…I’m stricken…I’m totally lovesick for it. It is tiny, it is portable, it is well documented, it assumes literate programming as the norm!

That’s a mighty nice little vm you’ve got there; Uxntal

Like Forth, this is another system that strives to be pretty much completely understandable. A system that can be held in 1 person’s head…it also offers everything you need to make little graphical programs and games.

But what if assembly?; 6502 and z80 assembly

Again, this was me going off the deep end a little bit.

What if I wanted a job though?; C, C++, Go, Java, Kotlin, and Swift

Blergh — remember when I said that SmallTalk broke me? Yeah, that broken-ness really comes to rear its head when I try to use these gigantic enterprise languages that have terrible tooling (Go, C, and C++ are almost passable, but Kotlin and Swift are laughable).

I also once upon a time tried Rust but it literally melted a component on my laptop so I gave up.

Fuck it! Those are no fun! Go go gadget make your own programming language!; Guava

I mean…did I really make my own programming language? No. But, Guava does carry with it a lot of what I’ve liked about other languages along the way.


So, where next? What next? I’m a habitual breadth over depth kinda person. I wanna say it is time to go deep on one language…but…who knows!?

A quick and dirty intro to the .pbm file format

I’ve been fiddling with writing programs that draw pictures. I started with PostScript for this, but have since moved to writing programs that output in the .pbm format.

My goal here is to write noise to a .pbm file.

A .pbm file is the lowest common denominator among image file formats.

An example of the format,

P1
# comment describing the file 

5 5
1 0 1 0 1
0 1 0 1 0
1 0 1 0 1
0 1 0 1 0
1 0 1 0 1

The first line is a required magic number, the next a comment.

Then comes a blank line, followed by declaration of height and width.

Last but not least is the image data, where 1 is a black pixel and 0 is white/blank.

The most fun part of this exercise comes from a suggestion from crc, the main developer behind RetroForth, and allows for the writing of all the data to the file in one go by using c:puts hook. For more context, see here. For more on hooks, see this excellent blog post from a friend!

'output.pbm file:open-for-writing 'PBM-Target var-n

:c:to-file [ @PBM-Target file:write ] &c:put set-hook ;
:c:to-display &c:put unhook ;

Next I whip out my trusty word that returns a random number within a range.

:n:ranged-random
    (lower,upper-random)
    over - n:inc n:random swap mod + ;

Next comes some housekeeping, defining the width and height the file will be. Of note, I’m not sure if these integers are referencing pixels, printer points, or just like blocks or something…

#100 !w
#100 !h

Last but not least comes the (veggie)meat of the thing: we open a file for writing into, define the body to be written into the file, and then spew it all into the file. Last but not least we display a string to let us know everything is Done.

c:to-file
'P1 s:put nl
'#_sample_pbm_file_made_from_retro s:put nl nl
@h '_ @w n:put s:put n:put nl
@h [ @w [ #0 #1 n:ranged-random n:put '_ s:put ] times nl ] times nl
c:to-display

'Done s:put nl
@PBM-Target file:close

An example .pbm file, converted to png

With this as a basis, I’m excited to start putting together more complicated patterns.

Forth, a tool for cultivating community

I watch most of the recordings of the Forth2020’s Zoom chats. A topic that comes up from time to time is how to get more folks interested in Forth — especially younger folks. In my weird little corner of the internet I can say that there are certainly young folks interested in Forth!

I wonder if the issue at play is less one of interest, and more one of cross communication between these communities? From what I’ve gleaned, what I’d call the old guard” of Forth meets up pretty regularly over Zoom, and does a lot of organizing on Facebook. The younger folks I know who are interested in Forth aren’t on Facebook, and, at least speaking for myself here, don’t have time to make a Zoom call — I do watch most of the recordings though.

I think another aspect of Forth not being seen as more viable to many folks is a quirk of history — there is no technical reason that Forth couldn’t be used more widely in spaces other than embedded programming, but, because of its long history and its ease of use in embedded systems, most of the old guard seem to be focused on embedded programming.

In both my career as a programmer, and in my free time, I’ve had very little exposure to embedded programming; I’m interested in it, but struggle to even conceptualize what sort of stuff I’d do in that setting. My background is in web and app development. When I dream-up hobby projects they’re always informed by what I already know how to do — games, web development, and mobile apps…I always want to reach for Forth for these sorts of projects, but, so far, I’ve struggled to find a Forth system that is particularly well suited for these spaces.

Now, I know that I could probably make my own Forth system (part of the beauty of Forth is how approachable it is to implement) that is a bit more suited for these other scenarios…but…I don’t know…so far I haven’t cracked that nut (but I’ve tried).

With this post I’m not looking to necessarily solve” anything, but I’d love to start to explore ways to bridge the gap between Forth’s old guard and the younger enthusiasts like me, looking to do more with Forth. I think the tools to bridge this gap are of two flavors: social, as well as technical. On the technical side I am curious to explore Forth’s Clojure moment” — a way of leveraging packages and tooling from a more widely used language, but taking advantage of Forth’s expressiveness. On the social-side of things I … am a broken record … and always want to return to something like mentorship or apprenticeship, but, as a stepping stone towards that sort of model I wonder about a space that is equally as accessible to both Forth’s old guard as well as younger folks, maybe a forum? A mailing list? A planet, a la Planet Lisp? All of these things may well exist, I just haven’t found em yet. If you have thoughts about any of this, I’d love to chat!

Introducing Guava

I’ve been fascinated by Forth and concatenative programming for a while now. I can’t remember how I initially stumbled in to it, but once I got going I’ve been unable to stop. I’m a wee bit in love with it.

Wanting to play a bit with implementing my own spin on things and having opinions about tooling, I picked up a little scripting language called Ripen, by Felix, and started to extend it. I call the results Guava.

Guava

Guava is a stack-based, concatenative language and editor for the browser intended to be used for scripting and as a DSL for small web projects.

It is a toy for exploring concatenative language design, and the DOM. It could also easily be used as a scripting interface for a larger project. It is likely to change a bit over time, and is by no means done — but I think it is in a pretty decent state for a toy to draw to the DOM.

Guava supports arithmetic, control structures, basic DOM manipulation, object parsing and very simple HTTP requests (GET and POST).

Guava is an unusual language inspired by other unusual languages. While not strictly necessary, being loosely familiar with Forth is helpful when playing with Guava. Guava isn’t a Forth, but it is closer to a Forth than it is to something like JavaScript, C, or Lua.

Here is a good place to start with learning Forth. Or here, and for fun, here, too!

Dictionary

A major goal of Guava is keeping the entire language tiny and easily learnable. With this as a goal, the dictionary is kept relatively limited.

The dictionary is broken into 2 categories — words and sigils.

Words are akin to keywords or in-built functions in other languages while sigils are prefixes that guide the interpreter, switching it into different modes (roughly speaking).

Words

#> ( ) * */ + ++ - -- -eq? -if -rot . .s / /* /mod 
2dup ; <# [ ] abs alert and assert assert:false 
assert:true buffer clear confirm cr dec depth drop 
dup el:a el:append el:b el:clear el:h1 el:h2 el:h3 
el:html el:i el:p el:root emit eq? execute false 
gt? gteq? http:get http:post if inc kv:get kv:remove 
kv:set lt? lteq? max min mod negate not obj:parse 
or over pokedex prompt repeat rot sigils space swap 
times true until while words { }

Sigils

! ' / : ?

Introduction

Like most stack-based languages, Guava relies on reverse Polish notation and a LIFO — “last in, first out” — memory stack.

Most programming languages use infix notation, e.g. 3 + 3. If you’ve used Lisp or Scheme you may be familiar with prefix notation, e.g. + 3 3. Reverse Polish notation is the opposite of prefix notation — 3 3 +.

The stack is how data flows through a stack-based language where data is added and removed from the top,” sort of like a stack of dishes. At first blush it seems like stack manipulation is all that a programmer using a stack-based language would be doing all day long, while in reality most folks using stack-based languages tend to keep the stack pretty shallow — Guava also offers an escape hatch, allowing for easy use of variables outside of the context of the stack…which some may say is cheating, but we don’t need that kinda gate keeping.

For more info on how to use Guava take a look at the cookbook.

Editor

A lot of programming languages are pretty similar to one another — once you learn the core constructs of programming (control flow, data handling and storage, etc.) it becomes relatively easy to pick up new languages…the hiccup, I find, comes from needing to learn new tooling. Tooling can be inscrutable, and it is often assumed that you just sort of know it…but it is rarely really taught. For this reason an editor is provided for Guava.

The editor is comprised for 2 parts, an area to display output and an area to collect input. Full disclosure, the area to display output (e.g. console) has a few quirks to it that I’m still trying to work out. If these are a hindrance, you can open up the console of your browser for a more direct view of what is going down.

Screenshot of the Guava editor in action

Code can be input into the lower textarea, clicking the run” button will interpret the code. Any text entered into the textarea is saved to the browser’s local storage with each keystroke.

The clean” button empties the stack and clears the data from the output area.

The destroy” button completely clears all inputted code and resets the environment.

Clicking export” prompts to save input code to disk.

At the far right is a choose file” button — this allows you to load data from disk. NOTE the data is read in from the disk, but any edits aren’t saved back to the disk unless you click the export” button.

An instance of Guava is currently accessible at https://txt.eli.li/pb/guava.

License

Ripen was originally released under the ARTISTIC LICENSE 2.0. With permission from Felix, Guava is licensed under the MIT LICENSE.

For more, check out Guava’s git repository.

Code Log: In which we split a string with RetroForth

Today’s goal? To write a bit of retro that mimic’s JavaScript’s inbuilt .split() function.

Retro ships with both s:split/string and s:split/char. They work the same way as far as I can tell, but one uses a string pattern to split on while the other splits based on a single character.

In JavaScript if I write something like,

let str = "banana"
str.split("a") 

// Returns an array:
// => [ "b", "n", "n", "" ]

Meanwhile, with retro’s s:split/string I get something different back.

'banana 'a s:split/string s:put nl s:put nl

That returns 2 strings,

b
anana

That isn’t what I want!

So, back to our challenge! How to get retro to mimic JavaScript’s .split()?

As a first step, I’m going to explore s:replace-all.

From retro’s glossary:

s:replace-all

Data: sss-s Addr: - Float: -

Replace all instances of s2 in s1 with s3.

So, the following should return the string bXnXnX

'banana 'a 'X s:replace-all s:put

This indicates to me that retro already has a way to work over each element of a string, and I can rely on some inbuilt magic, perhaps, to inspect each element of a string.

The next words that seem worth exploring are s:map and s:filter. Both of these words apply a quote to each element of a string.

Again, from the glossary:

s:map

Data: sq-s Addr: - Float: -

Execute the specified quote once for each character in the string. Builds a new string from the return value of the quote. The quote should return only one value.

And then,

s:filter

Data: sq-s Addr: - Float: -

Execute the quote once for each value in the string. If the quote returns TRUE, append the value into a new string. If FALSE the value will be discarded.

I think that s:filter is where I want to start.

'banana [ 'a s:eq? ] s:filter s:put nl

Reading the description of how s:filter works I guessed that the above example would return something like 'aaa

Instead it returns nothing…so, that isn’t heaps useful to me at the moment.

'banana [ nl 'boom! s:put ] s:map

Meanwhile, the above returns boom! once for each letter in banana!

BANG! That is something!

…but then I had a sudden realization — I’m a goober caught off guard by a type system — classic!

Rewinding to s:filter!

'banana [ $a eq? ] s:filter s:put nl

The issue was that I was checking for string equality when s:filter is breaking the string into individual…you guessed it (you probably figured it out before I did)…characters!

So, the above now does indeed return 'aaa!

COOKING WITH FIRE! Next steps!? How do we leverage our new-found Promethean-power to split strings?

I think s:map may still be what we want to use, but now we know to do it with characters instead of strings.

A first attempt:

'banana [ $a eq? [ nl 'BOOM_it's_a_match! s:put nl ] if ] s:map 

My first thought was to nest two quotes to check for a matching character. This doesn’t work, though. I’m not 100% certain why. The documentation says that nested quotes are kosher and my inner quote works a-okay when it is tested on its own:

$a $a eq? [ nl 'BOOM_it's_a_match! s:put nl ] if

Perhaps s:map isn’t passing characters into the quotation like with s:filter? We can test that theory easily enough:

'banana [ $a eq? [ nl 'Match s:put nl ] if ] s:filter s:put nl

That also doesn’t work — interestingly, though, it doesn’t work in the exact same way that s:map didn’t work! In both instances I was dropped into the repl when I ran retro split.retro (the name of this file). The expected result was for the code to be run, stuff be barfed to stdout and then to be returned to the shell. I’m not sure how to act on this new information, but I’m gonna stash it away as something of note for the time being. Another clue. Another Scooby Snack.

A return to s:filter, and abandoning nested quotes for the time being.

Success is in sight!

Rather than use a nested quotation, lets filter out all the $a in 'banana, then, because there is no way to visualize an array in retro, lets iterate over that array to show each element in it! This is looking a whole lot like .split() in JavaScript!

'banana [ $a -eq? ] s:filter a:from-string [ c:put nl ] a:for-each

GLORIOUS! Success — the above code returns the following,

b
n
n

It was a bit of a journey but I’ve written a bit of retro that can mimic the behavior of .split(), at least at first blush.


Thanks to the heroic efforts of a friend on IRC it was pointed out that the issue with my nested quotes is nothing to do with their being nested quotes and everything to do with the stack being unbalanced!

'banana [ $a eq? dup [ nl 'Match s:put nl ] if ] s:filter s:put nl

Gotta dup!


Discussion of this post on /r/Forth.

Code Log: In which I explore RetroForth

I like systems that I can hold completely in my head. I like teeny tiny things that are, more or less, totally knowable. This is why I like Forth.

The most useful (perhaps better read as good for the sorts of things I’m interested in doing” (aka, not embedded systems programming”)) Forth system that I’ve run across in my adventures is RetroForth. Retro describes itself as:

…not a traditional Forth. Drawing influence from colorForth, it uses prefixes to guide the compiler. From Joy and Factor, it uses quotations (anonymous, nestable functions) and combinators (functions that operate on functions) for much of the stack and flow control. It also adds vocabularies for working with strings, arrays, and other data types.

It runs on a bespoke virtual machine that is implemented in C, Python, Pascal, C#, JavaScript, TypeScript, Nim and Retro itself. This means that Retro is bananas portable.

Best of all, perhaps, if you ask me, is that Retro treats literate programming as the norm! Here is a collection of examples maintained by the mind behind Retro, @crc and here is a little snippet I wrote. They’re all valid Retro programs, complete with human-readable prose.

I’ve probably got another blog blergh in me about why I like literate programming, and what I think the advantages to literate programming are over, say, really heavily commented programs…another day! Here is pbat.ch on the subject of literate programming.


Have I sold you on Retro? If so (I hope so) here is a very quick, naïve tour of the barest of bones text game engine that I’m working on.

First and foremost, this is a derivative work, based heavily on some example code shared with me by @crc on irc.

My goal writing this was to produce a basic starting point for a text-based game. I set out to achieve a few things:

  • Define and maintain global state
  • Display info about this global state
  • Provide a very basic game ui
  • Process player input and act accordingly to this input
  • Allow a player to exit the game (arguably the most important feature of any game…)

State

My state lives in variables of the shape Player.thing, e.g. Player.xp, Player.location, etc.

I build these variables by iterating over an array of strings, and applying each to my prefix (Player). This leaves me with a bunch of empty variables.

{ 'xp 'loc 'whoops } [ 'Player.%s s:format var ] a:for-each

Next up, I define initial values for the newly minted variables. This is done by defining a new word that dumps values (here, all integers) into the variables. Note — I’ve only defined the word to do this as this point, I haven’t actually called the word, yet, so the variables are, at this point, all still empty.

I also define a word to display the current game state. This works by putting the current game state, @Player.xp and @Player.loc, onto the stack, then a format string (this should be a wee bit familiar to anyone familiar with format strings from C), and then barfing that contents out to stdout with s:put, the string-specific equivalent to a more classical Forth system’s . word.

:state-init
    #7 !Player.loc
    #0 !Player.whoops
    #100 !Player.xp ;

:state-display
    @Player.whoops
    @Player.xp
    @Player.loc
    '_LOC:_%n\n__XP:_%n\nUhOh:_%n\n s:format s:put ;

With these three items complete I have what I need for the time being to manage state. First I define some variables to hold my state, then a word to initialize those variables with some values, and finally a word to display the state to the player.

Process player input

Next I will define what I’ll call my UX words — these are the words triggered by player input. They’re all pretty trivial and follow the same pattern — they dump a new line, nl onto the stack, then a string (anything starting with a single quote, ' with underscores in place of spaces) and print both of those to stdout. Next, another nl for cleanliness and then I do something with my global state, either incrementing or decrementing a variable.

:mic-check 
    nl 'testing_testing_1_2_3! s:put nl &Player.xp v:inc ;

:banana-boat
    nl 'banana_boat! s:put nl &Player.xp v:dec ;

:unknown-input
    nl '_is_not_a_known_input s:append s:put nl &Player.whoops v:inc ;

The most interesting of my UX words is the final one, unknown-input. It does basically the same thing as the other two, but appends whatever the player input to a predefined string to let the player know that the program doesn’t know what to do with their input…and then it increments our whoops” counter.

Now that the UX words are defined I can put them to work! They’re all going to be triggered by specific player input. If a player inputs a t I’ll trigger mic-check, a b will trigger banana-boat, q will trigger the in-built bye word, exiting the program, and anything else will fall back to the unknown-input word.

I’ve also defined a hint word that triggers the state-display word, defined earlier, as well as displaying a string acting as the game’s ui.

:process-input
    't [ mic-check ] s:case
    'b [ banana-boat ] s:case
    'q [ bye ] s:case
    unknown-input ;

:hint
    nl state-display nl '(t)est_(b)anana_(q)uit s:put nl nl ;

Game loop

Now in the home stretch, the next step is to define a basic game loop that pulls everything we’ve written together. The core of the game loop, like most game loops, is a while loop. Before the while loop is started, though, I’ve gotta initiate my starting state using state-init, and displaying a friendly welcome message. That done, I then start the game loop itself. The game loop listens for player input using s:get (akin to something like io.read in Lua), triggers a turn (which is a wrapper around the process-input word) and then returns TRUE. As long as the game loop continues to return TRUE the game will march on.

:turn
    clear process-input hint ;

:welcome-player
    nl 'Welcome!_╰(˙ᗜ˙)੭━☆゚.*・。゚ s:put nl ;

state-init welcome-player hint
[ s:get turn TRUE ] while

That is it! That is a wicked minimal text based game thing!

Here is a link to a cleaned up version of the same code.

Follow up

For more on Forth and Retro, check out these links:


Hold it!?” you may have shouted — “why the heck should I care about Forth when I’ve got Swift, TypeScript and JAVA!?”

Touche. Those languages are wickedly more powerful and … dare I say … pragmatic than Forth. Even if you don’t plan to write a heap of Forth, not a single line even, I think Forth is worth learning a bit about. Forth is built around the core concept of a stack. Stacks aren’t unique to Forth; they are everywhere. Understanding how to leverage the stack can be really useful. I’ve found my love of Forth has made it easier for me to groke how C and Lua inter-op. When you call a Lua function from C you don’t pass the parameters into the function directly, first you push the Lua function on to the stack, then any parameters that the function takes. Then you call the function and tell C what items from the stack to pop into it. Stacks and stacks!

Another point I wasn’t able to sneak in up above is that most Forth systems (with some exceptions) have very minimal supporting ecosystems. This is a blessing and a curse. A curse because if you want to do a thing you probably have to do that thing fully or near-to-fully on your own. A blessing because there isn’t a package manager or a complicated build tool to get eaten by. Heck! I don’t even use syntax highlighting when I write Forth most of the time.

I’ve been oggling forth in a few different flavors for a little over a year now — I’ve never really gotten into one, per se, but I’m interested in forth nonetheless.

Retro has been the forth that I’ve been the most interested in, mostly for its handy documentation, active IRC channel and literate style — this said, I’ve accomplished nothing of note in it.

The tooling around factor is totally gorgeous and got me wicked excited, but it seems to be crumbling. It mostly works under linux, but is missing most of the icing (e.g. the wicked IDE), and recently broke for me on macOS Big Sur.

gForth is the logical place to focus, being the purest” forth…and where I’d like to end up, but I haven’t yet got my brain in the place where I can actually totally grok it, yet and I think I need more libraries than gforth seems to offer.

Last but not least is play-lang, a new arrival on the scene that has got me very interested. Not much to see yet, but I’ve been keeping tabs on its development and am excited to see where it goes. Also, it is using fossil over git!

Link logging

I haven’t done a link log in ages. Here is a miniature one for you!