Skip to content
UnrelatedString edited this page Oct 25, 2019 · 13 revisions

All that stuff about a stack? Screw that. Bins

This will be a stack-based golflang that I'll probably implement in SWI-Prolog, with the core idea of every command being a function mapping one stack state to another, and some attempt at homoiconicity. Control flow will most likely revolve around executing lists of commands represented as actual lists in the code, either on the same stack or a sub-stack supplied as a list, and use of code written on lines other than the first (or last?) will probably be by accessing their representations as lists of commands, although I'm not entirely sure how to best go about that (idea: each line is determined to use so many other lines, and it's all parsed like that?), or if that will be the main way of representing code as data for use by other code.

Eventually it'll be used mostly with a single-byte code page, probably also defined to use UTF-8 within string literals for flexibility, but it will first use a Forthy sort of syntax with tokens written out as words and delimited by whitespace, so I can start doing things before deciding on what builtins to map to what byte sequences. (Hell, I might add some builtins that I never bother to map to byte sequences!) There'll be some sort of builtins for working with either encoding in both encodings.

I'm also not really sure what to do about reaching the bottom of the stack: infinite zeroes? Cycle the input? It might be best to provide special behavior for every individual command hitting an empty stack, but it might be difficult to make that useful, although it could be as simple as having especially useful default values supplied for multiple-argument commands, and pushing distinct constants for single-argument commands, maybe even make running out of stack a directly useful base case for recursive reduce-like operations.

To make output backtrackable from the start, it could be implemented as an output queue, which gets printed at the end of a program or at any cut operation (or maybe not), but imperative write might still end up being the main write? Using an output deque could serve the secondary purpose of allowing formatted output to also be used within the program, if I don't decide to just make directly pushing a formatted string to the data stack the shorter option or an equal one. (Maybe formats could be expressed as their own type, and work as functions doing the formatting, sorta like 05ab1e's ÿ? Could complicate a sscanf builtin if I elect to actually make one.) An output deque, if implemented as global, could also work as sort of a hacky way to emulate global variables if they don't seem necessary enough that I add them as their own thing.

For the each-line-has-an-arity idea, each line gets its own stack (or queue) of lines, and either functions which take lists of functions as arguments automatically take those arguments from the line stack or there's some specific function to move a list from the line stack to the main stack. Both raise questions about how to apply higher-order functions to single functions... some implicit maps and reduces are probably a good idea anyhow, but it would still be great to use higher order functions on builtins in only one byte. I might need to think of a concrete use case though, since something obvious like "add three to every element of a list" would need a full list expressing "push three, then add". Using a stack or queue for the lines associated with a line might be a problem for things that might not execute, though, so maybe they should just be implicitly filled in as the function-list arguments... but then how do we construct or modify lists of functions? I guess the solution is probably to have higher-order functions first look for a function list on the main stack, then take a line, but that could be problematic for implicitly binding each line to something that takes a function list because it would require determining what would take it from the main stack. Maybe the line stack idea could work, possibly with some control flow operations implicitly popping a line? I'm almost tempted to make the line stack global instead, with some less-than-stacky behavior to facilitate using lines that use lines.

The implicit access lines idea is a good idea, but maybe lines should just be limited to explicit access, and function lists with lengths 1-9 could just be expressed inline with special digit tokens at the start. In the written-out encoding they could be something like f#, and maybe the code page could express them as some variety of counting rod numerals (or just subscript digits, since counting rod numerals don't seem to cooperate with DejaVu Sans Mono...)? Even if I don't use that for function lists, it's still a good idea for string literals (although that could be a bit silly in the written-out encoding), and maybe non-function list literals as well, if there ends up being a difference.

Think carefully about balancing input-type overloads with possible output-type overloads... not that it always has to be unambiguous, since the "wrong" choice may often end up failing further down the line

Clone this wiki locally