wip(n)

πŸ“¦
version 2.0 Β· 2 versions
, in fedora 43, fedora 44, nixos 26.05, nixpkgs unstable

🌸 wip(n) β€” Word Interpreter 🌸

okay bestie, welcome to the wip manual page πŸ’– this is the one where we build a whole lil micro interpreter out of nothing but a list of words. it's giving Forth but make it fashion. sit down, hydrate, we're doing this. 🫢


✨ NAME ✨

wip β€” Word Interpreter. that's it, that's the name, periodt. πŸ’…


πŸ¦‹ SYNOPSIS πŸ¦‹

package require Tcl 8.5 9

package require wip ?2.3?

package require snit ?1.4?

package require struct::set

::wip wipName engine arg...

def name

def name method_prefix

wipName option ?arg arg ...?

wip::dsl ?suffix?

wipName def name ?method_prefix?

wipName defl names

wipName defd dict

wipName deflva name...

wipName defdva (name method_prefix)...

wipName undefl names

wipName undefva name...

wipName unknown cmdprefix

wipName runl wordlist

wipName run word...

wipName run_next

wipName run_next_while acceptable

wipName run_next_until rejected

wipName run_next_if acceptable

wipName run_next_ifnot rejected

wipName next

wipName peek

wipName peekall

wipName insertl at wordlist

wipName replacel wordlist

wipName pushl wordlist

wipName addl wordlist

wipName insert at word...

wipName replace word...

wipName push word...

wipName add word...

πŸ’– DESCRIPTION πŸ’–

so this package hands you a micro interpreter for lists of words. if you build a domain specific language on top of it, it's gonna have a bit of a Forth feel β€” the input stream gets chopped into words and literally all the other structuring is left up to whatever language you're making. and listen: what you get here is only the core dispatch loop. there are no actual commands whatsoever. so it's Forth-flavored, not an actual Forth. don't come for me. πŸ™ˆ

the whole idea came from Colin McCormack's treeql processor, but reworked so the command implementations need way less boilerplate. the tradeoff? probably slower execution, lowkey. also the interface between the processor core and the commands is more complicated too. no cap. πŸ’€


πŸŽ€ GENERAL BEHAVIOUR πŸŽ€

word interpreters have a mapping from the names of the language commands they should recognize, over to the methods in the engine to invoke for them β€” plus possibly some fixed arguments for those methods. this mapping is mostly static, but plot twist: you can change it mid-execution while a word list (aka a program) is running. main character energy fr fr. πŸ‘‘

the moment a language command gets defined, the word interpreter uses snit's introspection powers to figure out how many arguments the engine's method expects. add in however many fixed arguments you supplied in the mapping's method prefix, and boom β€” it knows exactly how many arguments the language command wants. that number is the command's arity. ✨

⚠️ heads up: variable-argument methods (the ones where the last argument is named args) are not allowed. if you try, the word interpreter throws an error at definition time. so don't. πŸ’…

quick note: even though I keep saying "snit's abilities", the engine object can literally be written any which way β€” it just has to understand the method info args, which takes a method name and returns the list of arguments for that method. flexible, iconic, we love that for us. πŸ¦‹

when you're executing a list of words (aka a program), the first word is always taken as the name of a language command, and the words after it are its arguments, per the command's arity. the command word and its argument words get removed from the list, and then the associated method of the engine runs with those argument words. then the whole thing repeats using whatever word is now first in the list. over and over. slay. πŸ”₯

and btw β€” the methods implementing the language commands get full access to the list of words and are allowed to manipulate it however they see fit. yes, really. go feral. 🫢

  1. so like, this means that even though we can't directly specify variable-argument methods, those methods can still consume words after their fixed arguments before returning to the execution loop. and that consumption can be controlled by their fixed arguments. sneaky. πŸ’…
  2. another option is using the method run_next and its variants to execute commands that come after the current command, which changes up the order of execution. chaotic, but make it intentional. 🌈
  3. execution can be changed even further with the program accessor methods, which let a command implementation modify the remaining list of words β€” insert, replace, prepend, append β€” without executing them immediately. the restraint. the discipline. πŸ’œ
  4. and finally, the basic run methods save and restore an existing list of words when they're used, which enables recursive use from inside command implementations. recursion bestie, we're doing it. 🧚

πŸ‘‘ CLASS API πŸ‘‘

the main command of the package is:

  • ::wip wipName engine arg... β€” this creates a brand new word interpreter object with an associated global Tcl command whose name is wipName. BUT if you use the string %AUTO% as the object name, the package will generate its own unique name for the object instead. cute of it. ✨

the engine is the object that the word interpreter dispatches all recognized commands to, and the arg... arguments are a word list defining an initial mapping from language words to engine methods.

the recognized language of this word list is:

  • def name β€” defines name as a command of the language, mapped to a method of the engine that has the same name. easy. πŸ’…
  • def name method_prefix β€” defines name as a command of the language, mapped to the method of the engine named in method_prefix. mix and match, bestie. πŸŽ€

the returned command can be used to invoke all sorts of operations on the object. its general form is:

wipName option ?arg arg ...?

option and the args decide exactly what the command does. that's the whole vibe check. πŸ¦‹


the package additionally exports this command:

  • wip::dsl ?suffix? β€” this one is for use inside snit types that want to use one or more wip interpreters as a component. use it inside the type definition and it installs most of the boilerplate you'd need to set up and use a word interpreter. thank u, next (to the boilerplate). 😭

it installs a component named wip, plus a method wip_setup for initializing it. you have to call this method from inside the constructor of the type that's using the word interpreter. it also installs a whole series of procedures that make the object API of the word interpreter directly available to the type's methods β€” no need to specify the component every single time. slay. πŸ‘‘

ngl, important caveat: this does not β€” and cannot β€” install the language to interpret, i.e. the mapping from words to engine methods. you still gotta do that yourself. πŸ’€

and plot twist, you can instantiate multiple word interpreter components inside one type by passing different suffices as arguments to the command. when you do that:

  • the name of the component changes to wip_$suffix
  • the setup command becomes wip_$suffix_setup
  • and all the procedures also get the suffix _$suffix

multi-wip household, we love to see it. 🌈

πŸ’… OBJECT API πŸ’…

okay bestie, this is the part where we talk to the word interpreter objects directly. these are the commands you can throw at a wip object, and yes there are a LOT of them, so grab a snack πŸ“


wipName def name ?method_prefix?

Defines a language command name and maps it to the method named in the engine's method_prefix. If you don't hand over the method_prefix, no worries, it's just literally the name of the language command. simple as πŸ’…

wipName defl names

Defines a whole series of language commands at once, given as the list of names β€” and every single one of them maps to an engine method with the same name. bulk deal, iconic ✨

wipName defd dict

Also defines a series of language commands, but this time you pass a dictionary dict of names and method prefixes. choose your own mapping, main character energy πŸ‘‘

wipName deflva name...

Same vibe as method defl, except the list of names comes in through multiple arguments instead of one list.

wipName defdva (name method_prefix)...

Same vibe as method defd, except the dictionary of names and method prefixes comes in through multiple arguments.

wipName undefl names

Removes the named series of language commands from the mapping. bye bye πŸ‘‹

wipName undefva name...

Same vibe as method undefl, except the list of names comes in through multiple arguments.

wipName unknown cmdprefix

Sets the handler for unknown words to cmdprefix. This command prefix takes one argument β€” the current word β€” and it either throws some error, or returns the result of executing the word, as defined by the handler. The default handler just throws an error, no cap. 😭

wipName runl wordlist

Treats the list of words in wordlist as a program and executes the contained command one by one. The result of the command executed last is returned as the result of this command.

The wordlist gets stored in the object so the other run-methods and the general program accessor methods (scroll down, they're right there) can reach it. A previously stored wordlist is saved while this method runs and restored before it returns β€” which is what makes recursive execution of word lists within word lists possible. nesting, but make it fashion πŸͺ†

wipName run word...

Same vibe as method runl, except the list of words to execute is specified through multiple arguments.

wipName run_next

Low-level method. Figures out the next word in the list of words, plus its arguments, and then executes it. The result of the executed word is the result of this method.

It's exposed so you can use it inside command implementations. The methods run and runl use it to execute words until their word list is exhausted. πŸ’€

wipName run_next_while acceptable

Low-level method. Invokes the method run_next for as long as the next word is in the set of acceptable words, and the program is not empty. The result of the command executed last is returned as the result of this command.

Exposed for use within command implementations to change the order of execution. πŸ¦‹

wipName run_next_until rejected

Low-level method. Invokes the method run_next until the next word is in the set of rejected words, and the program is not empty. The result of the command executed last is returned as the result of this command.

Exposed for use within command implementations to change the order of execution.

wipName run_next_if acceptable

Low-level method. Invokes the method run_next if the next word is in the set of acceptable words, and the program is not empty. The result of the command executed last is returned as the result of this command.

Exposed for use within command implementations to change the order of execution.

wipName run_next_ifnot rejected

Low-level method. Invokes the method run_next if the next word is not in the set of rejected words, and the program is not empty. The result of the command executed last is returned as the result of this command.

Exposed for use within command implementations to change the order of execution.

wipName next

Returns the next word in the program. The word is also removed. poof πŸ«₯

wipName peek

Returns the next word in the program without removing it. look but don't touch, bestie πŸ™ˆ

wipName peekall

Returns the remaining program in toto. the whole thing, the entire vibe 🌈

wipName insertl at wordlist

Basic program accessor method. Inserts the specified wordlist into the program, just before the word at position at. Positions are counted from zero, like a real programmer ✨

wipName replacel wordlist

Basic program accessor method. Replaces the whole stored program with the specified wordlist. out with the old πŸ”₯

wipName pushl wordlist

Program accessor method. The specified wordlist is added to the front of the remaining program. Equivalent to

$wip insertl 0 $wordlist

wipName addl wordlist

Program accessor method. The specified wordlist is appended at the end of the remaining program. Equivalent to

$wip insertl end $wordlist

wipName insert at word...

Like method insertl, except the words are specified through multiple arguments.

wipName replace word...

Like method setl, except the words are specified through multiple arguments.

fr fr, that's what the page says β€” setl. not me double-checking, it's just what's written πŸ’€

wipName push word...

Like method pushl, except the words are specified through multiple arguments.

wipName add word...

Like method addl, except the words are specified through multiple arguments.


🌸 EXAMPLES 🌸

No examples yet. 😭 (we're all waiting, bestie)


πŸ’€ BUGS, IDEAS, FEEDBACK πŸ’€

This document, and the package it describes, will undoubtedly contain bugs and other problems. Please report such in the category wip of the Tcllib Trackers at http://core.tcl.tk/tcllib/reportlist. Please also report any ideas for enhancements you may have for either the package and/or the documentation. we love feedback, it's giving community 🫢

When proposing code changes, please provide unified diffs, i.e the output of diff -u.

Note further that attachments are strongly preferred over inlined patches. Attachments can be made by going to the Edit form of the ticket immediately after its creation, and then using the left-most button in the secondary navigation bar. iykyk ✨


πŸŽ€ KEYWORDS πŸŽ€

  • interpreter
  • list
  • word

⭐ CATEGORY ⭐

Programming tools


Copyright (c) 2007-2010 Andreas Kupries <andreas_kupries@users.sourceforge.net>


and that's a wrap on this one, bestie πŸŽ€ brought to you by the fine folks over at tcllib, version 2.3, living in section wip(n) β€” go forth and interpret some words, you absolute icon πŸ’–βœ¨

πŸ“¦ where this page comes from