mastodon.social is one of the many independent Mastodon servers you can use to participate in the fediverse.
The original server operated by the Mastodon gGmbH non-profit

Administered by:

Server stats:

329K
active users

2024
Week 2
Language: Forth

Confidence level: Low

PREV WEEK: mastodon.social/@mcc/113743302
NEXT WEEK: mastodon.social/@mcc/113867584
RULES: mastodon.social/@mcc/113676228

So today's challenge looks *absurdly* easy, to the point I'm mostly just suspicious that part 2 will get hard. I figure this is an okay time to burn Forth.

I'm wanting to save Fortran for a week I can use the matrix ops. This puzzle looks suspiciously like part 2 will turn into a 2-dimensional array problem.

I *think* I'm doing this in pforth, for the simple reason that gforth, uh, isn't maintained anymore it seems, and so got dropped out of Debian Testing (which I have now)? I *think* I'd be *happier* using RetroForth, which is a "modern" Forth, but I guess it's better to learn the standardized, ANS Forth first. Even though everyone hates ANS Forth? Including the inventor of Forth…?

My biggest fear is there appears to be no way to read numbers written in ASCII from a file. We're predating ASCII

First problem I hit is comments don't work. The documentation specifically says text in parenthesis are comments, but it isn't accepted.

After some staring at the docs, I realize in all the examples, there are spaces. It turns out (Comment) is not a comment, but ( Comment ) is a comment. Because ( isn't a pure operator built in the language, rather there's a FORTH word ( that eats all words until ) is found. Holy crap. I never thought I'd say this but maybe it IS possible to self-host too hard

So this documentation is kinda very bad!

Lacunae I have noticed:

- They define a `KEY` operator for taking a character from STDIN, but don't explain what happens if `KEY` receives an EOF (experimentally: I seem to get a -1?)

- They explain a special syntax `CHAR n` for inserting the ASCII value of n directly into the code, but don't explain how the fuck you're supposed to represent the ASCII value for a non-character symbol such as a space or newline

More pforth documentation horrors

- The pforth tutorial is not a tutorial for pforth but rather a general forth tutorial, and therefore hedges itself frequently. For example, notice this section where it explains that "many forths" have a CASE statement. "Many forths"? What about THIS forth I'm reading the documentation to RIGHT NOW?

- ABORT not documented. The documentation lists it as a reserved word but not what it does

Wrote a version 1.0 of my program, testing it. All I've got so far is the character parser, reading the numbers in and decoding ASCII and that's it.

This is… bad. I would describe this as bad behavior for a programming language interpreter

The forth interpreter isn't completely busted, my test.f screenshot above worked. A simple "echo ascii values" program I wrote ( BEGIN KEY DUP . CR 0< UNTIL ) worked. But my 32 line, mildly more sophisticated program just… signal 11s. I do not know how to proceed. I am using "Debian Testing", which is TECHNICALLY a beta OS, so maybe the pforth is broken *subtly*. gforth isn't in dpkg. I don't… I don't know what to do next if the compiler crashes.

Maybe I ssh into a VPS and run gforth there? :(

So here's my *current* code, which crashes in pforth:

github.com/mcclure/aoc2024/blo

I run it in gforth:

cat "data/sample-2.txt" | gforth src/puzzle.f

I get:

in file included from *OS command line*:-1
src/puzzle.f:13: Interpreting a compile-only word
>>>BEGIN<<< ( Line )
Backtrace:
$7F5C6D55EB30 throw

Line 13 is indeed the word "BEGIN". According to the tutorial, that is how you open a UNTIL loop.

fuck the in What?

Advent of Code 2024 challenge (laid-back/"babel" version) - mcclure/aoc2024
GitHubaoc2024/02-01-nuclear/src/puzzle.f at ec7ac2f3ad500d65e07dfeeee5877bf70a667c17 · mcclure/aoc2024Advent of Code 2024 challenge (laid-back/"babel" version) - mcclure/aoc2024

Okay. So some updates.

It turns out loops (BEGIN..UNTIL) are a "premium" Forth feature and are only available inside functions. So I need to wrap the whole program in a : function ; . Well not the whole program, not the VARIABLEs, and I don't think you can nest the functions, and… never mind. I do the nest. New code:

github.com/mcclure/aoc2024/tre

gforth fails with :

src/puzzle.f:38: Invalid memory address

I wrapped my program in a : run [code here] ; run . Line 38 is "run".

I'm still lost.

Advent of Code 2024 challenge (laid-back/"babel" version) - GitHub - mcclure/aoc2024 at 85890d80d89ebe82df779e20607f78cd4275f8db
GitHubGitHub - mcclure/aoc2024 at 85890d80d89ebe82df779e20607f78cd4275f8dbAdvent of Code 2024 challenge (laid-back/"babel" version) - GitHub - mcclure/aoc2024 at 85890d80d89ebe82df779e20607f78cd4275f8db

A thing worth noting here is if you read my posts carefully above, you'll find I successfully executed a BEGIN .. UNTIL program in pforth. So pforth just relaxes the requirements of gforth. I don't think I've hit what is causing pforth to crash yet. I'm just trying to satisfy gforth's requirements for running the software at all. Maybe I should have read gforth's manual instead of assuming pforth's is adequate :(

This raises an interesting problem. I could have used a "nice" Forth like RetroForth or Factor(?) but I wanted to learn ANS Forth before I moved on to specializations. However, now I realize there are *only* specializations. Pforth is apparently giving me all kinds of niceties, the premium DLC is included at the toplevel. And I know for a fact gforth (of course, because that stands for GNU Forth) contains GNU extensions. So there are two standard Forths in Linux, neither actually standard.

Okay. So this explains my segfault! I can now run in pforth.

xoxo.zone/@clarity/11378379402

I accidentally wrote on one line `partial TRUE !` ; correct would be `TRUE partial !`. All variable assignments in Forth are done with pointer dereferences, so the wrong order was like trying to write to memory address TRUE (-1). It is as if C++ allowed you to write "true = x;" by accident instead of "x = true;" and write the address of x to 0x1

I am unblocked, but still can't do jack shit in gforth

XOXO Zoneclarity flowers (@clarity@xoxo.zone)@mcc@mastodon.social you have `partial TRUE !` and `TRUE partial !`, I forget which, but one of those is wrong (I think the latter is correct?). This is your invalid memory access (you're trying to set the data stored at 0x01)

Sorry, I kinda disappeared in the middle of asking a question. Me and @spookysquid were doing something extremely normal

I need a modem so I can hack my N64, and Miguel thought he might have a modem for his old Compaq Portable III, so we're trying to see if the Compaq will boot. You see.

*kicking and throwing things*

The tutorial for pforth softsynth.com/pforth/pf_tut.ph says if you want an ASCII constant you write: CHAR A

I want to print an A. I write CHAR A EMIT. I get:

A ? - unrecognized word!
INCLUDE error on line #18 , level = 1
CHAR A EMIT
^^^^^^

I write CHAR 'A' EMIT. This works. The documentation lies! The documentation lies!!

I'm getting unexpected results for .S. Wait, what results do I expect? Like ABORT, PFORTH DOCUMENTS .S EXISTS, BUT NOT WHAT IT DOES.

www.softsynth.compForth - portable Forth in 'C'

I cannot work under these conditions. I now see why everyone writes their own Forth interpreter instead of using a prepackaged one. BECAUSE IT'S THE ONLY WAY TO KNOW WHAT ANYTHING DOES.

I asked a while back how Forth is for text parsing. I was thinking like… structured text parsing. I did not think that "input a sequence of space-separated ASCII numbers" was going to turn out to be basically a workday and then I wouldn't have it working at the end

The number 101 is somehow poisoning my stack. I don't know why but the longer the program run the more instances of the number 101 show up on my stack. I don't know why. Once it begins the logic spirals out of control because 101 shouldn't be there. That's ASCII 'e' but I can't imagine why ASCII 'e' would wind up on my stack. This would be so much easier if I felt COMPLETELY SURE I knew what all the builtin words do, but it's lying to me about CHAR, so what else could it be lying about.

I think what I really need here is some kind of live debugger that steps ONE WORD AT A TIME and prints the stack out after EVERY word executes. I'm trying to put in debug prints, but SOMETHING IS PUTTING JUNK ON MY STACK and I don't know how to tell if it's my debug prints that are creating the stack clutter

UPDATE: I have found a Forth debugger of the type I was looking for, but it only runs in DOS

holonforth.com/debugforth.html

EDIT: Before anyone asks, no, the Compaq Portable III does not help here, it boots but we have no way to get data on or off of it

holonforth.comDebug Forth

Alright! Progress.

While I was on the bus, @unlambda looked it up and pforth *has* a symbol-by-symbol trace (it's called… TRACE). Unlike most things in pforth, *this* feature is well documented. Now it also requires me to use INCLUDE, which *isn't* documented, and which I couldn't get to work, but n/m, I just pasted the whole program into the terminal each time.

What I found: Remember me not getting `CHAR A` to work, and experimentally finding `CHAR 'A'` as substitute?

…It's not a substitute.

I jokingly referred to a Forth "premium" mode before. Non-jokingly, this is called "compiled" mode. Inside a function is "compiled"; outside is "interpreted". Apparently running CHAR in compiled mode puts the number 82 on the stack. Why? I don't know. That's ASCII "R". I don't get it. Then 'A' *separately* puts 64 (ASCII A) on the stack.

@unlambda also found the solution here: I need to say `[CHAR] A`. The brackets mean "use the interpreted-mode version, not the compiled-mode version". (1/2)

Should the tutorial have mentioned this? Should the reference manual have? Should the "Starting Forth" book I read a big chunk of, with the friendly cartoons, have mentioned it? Should, for that matter, the spec— which defines separate interpreted and compiled semantics for *many* words, but not CHAR ( forth-standard.org/standard/co )— have mentioned it? None of them did. I found out by word of mouth on Mastodon that CHAR (and possibly some other stuff?) needs square brackets to act normal. (2/2).

forth-standard.orgCHAR - CORE

Once I got inside of TRACE, and was watching the code execute word by word, suddenly Forth was the "elegant", clear, mechanically-precise machine I had assumed I would find Forth to be coming in. I understood what each word did, and if something went wrong I understood what. This is interesting, as when working with full programs Forth has felt like sentient jello. (1/2)

My takeaway here is that Forth is a chaotic system. Small changes in initial conditions lead to large differences in outcomes. The stack means the meaning of any one statement is dependent on the entire history of the program to that point! That means Forth is precise if you understand exactly what you're doing, but if your understanding is even a *little* off— say, because the documentation for just one keyword in the whole program is unclear— you get chaotic behavior and all is lost. (2/2)

Anyway here's my 45-line code for a harness that reads in lines of ASCII numbers and prints and dumps the stack after each.

github.com/mcclure/aoc2024/blo

It's… awkward in places. It would have helped to have else-if, character literals for whitespace (like '\t' and '\r'), and a better story for complex boolean exps
(`DUP DUP DUP 9 = SWAP 13 = OR SWAP 32 = OR`. Ouch.) Parts feel nice and parts feel real bad.

I expect the ACTUAL PROGRAM will be a few lines long, and possibly only take mere minutes.

Advent of Code 2024 challenge (laid-back/"babel" version) - mcclure/aoc2024
GitHubaoc2024/02-01-nuclear/src/puzzle.f at beba6adc719247e61123ad1be7d2339c4a6f7f22 · mcclure/aoc2024Advent of Code 2024 challenge (laid-back/"babel" version) - mcclure/aoc2024

So functions in ANS forth, as far as I can tell, can't have locally-scoped variables…

…but there's no restrictions on what characters you can put in a variable name, so I can just store the variables in paths *cackles evilly*

Examples of valid comments in ANS Rust:

( You )
( You can (not) redo )
( You (can not) advance )

Examples of invalid comments in ANS Rust:

(You)
( You can ( not ) redo )
( You ( can not ) advance )

No, no, I'm sorry. I was fooled by my syntax highlighter. In testing with the actual pforth executable it appears literally all of the above examples are invalid uses of Forth comments except ( You ) .

( You can ( not ) nest comments. )

Bluh

github.com/mcclure/aoc2024/blo

Well, it didn't take minutes. And it wasn't "a few" lines, it was 48, about the same length as the input code. But I will say once I was doing "forthy things" (pure computation) instead of stuff Forth's bad at (regular programming) it was way quicker, easier. It did occasionally feel like flying. Some of these lines have a real clarity to them.

It's now very important I don't try to do part 2 tonight. In fact, I shouldn't even read it, so I don't *reads it* dammit

I have to get up SO early tomorrow, y'all

*Head in hands after unwisely reading part 2* Part 2 is basically one big chunk of Regular Programming. Hey uh. Does anyone know if there's a way in Forth to copy THE ENTIRE STACK somewhere and then recall it later? I can think of a way to *destructively* back up the stack (move it to the return stack temporarily) but *copying* the stack… I got nothing.

UGGGH no i think i can do this with DEPTH CELLS ALLOT ( forth.com/starting-forth/8-var ) and then… and then some other nonsense. I think. Okay I absolutely have to stop thinking about this now. This may not have been the wisest of all possible uses of my Monday

FORTH, Inc8. Variables, Constants, and ArraysAs we have seen throughout the previous seven chapters, Forth programmers use the stack to store numbers temporarily while they perform calculations or to

@mcc my suggestion with forth is always: consider linked lists as an option

mcc

@clarity no primitives for this though, right?

@clarity thanks. for reasons i will not explain, your suggestion instantly makes sense to me because i have written a lot of ML