wip(3tcl)

πŸ“¦
version 2.0 Β· 2 versions
, in debian trixie, ubuntu questing, ubuntu resolute, ubuntu stonking

✨ wip(3tcl) β€” Word Interpreter ✨

okay bestie, buckle up, we're reading the wip(3tcl) manual page together and honestly? it's giving tiny language engine that could. πŸ’– This is the Word Interpreter β€” a whole micro interpreter for lists of words, and I'm gonna walk you through every single bit of it, no cap. πŸ¦‹


🌸 NAME 🌸

wip β€” Word Interpreter


πŸŽ€ 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 little 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 cooking up. And like, note that what's here is in essence only the core dispatch loop. There are no actual commands whatsoever. So it's definitely just a Forth feel, not an actual Forth. 🫢

The idea is derived from Colin McCormack's treeql processor, but it got remixed to require less boiler plate inside the command implementations β€” at the cost of, probably, execution speed. πŸ™ˆ And on top of that, the interface between the processor core and the commands is more complex too. Trade-offs, babe.


πŸ‘‘ GENERAL BEHAVIOUR πŸ‘‘

Word interpreters have a mapping from the names of the language commands they're supposed to recognize, over to the methods in the engine that get invoked for them β€” plus possibly some fixed arguments for those methods. This mapping is largely static, but plot twist: it is possible to change it during the execution of a word list (= program). πŸ‘€

At the time a language command gets defined, the word interpreter uses snit's introspection capabilities to figure out how many arguments the engine's method expects. Then, together with the number of fixed arguments supplied in the method prefix of the mapping, it knows exactly how many arguments the language command is expecting. That's the command's arity. ✨

⚠️ Variable-argument methods (i.e. ones with the last argument named args) are not allowed and will make the word interpreter throw an error at definition time. No args, bestie. That's the rule.

And note β€” even though I said snit's abilities, the engine object can be written any which way you like, as long as it understands the method info args, which takes a method name and returns the list of arguments for that method. Slay, flexible king. πŸ’œ

When executing a list of words (aka a program), the first word is always taken as the name of a language command, and the next words are its arguments, per the arity of the command. Command words and argument words get removed from the list, and then the associated method of the engine is executed with the argument words. Then the process repeats using the then-first word of the list. πŸ”

And note that the methods implementing the language commands may have full access to the list of words and are allowed to manipulate it however they see fit. Which means:

  1. This means, for example, that while we cannot specify variable-argument methods directly, they can consume words after their fixed arguments before returning to the execution loop. This may be under the control of their fixed arguments.
  2. Another possibility is the use of method run_next and its variants to execute commands coming after the current command, changing the order of execution.
  3. Execution can be further changed by use of the program accessor methods which allow a command implementation to modify the remaining list of words (insert, replace, prepend, append words) without executing them immediately.
  4. At last the basic run methods save and restore an existing list of words when used, enabling recursive use from within command implementations.

🌈 CLASS API 🌈

The main command of the package is:

  • ::wip wipName engine arg... β€” This command creates a new word interpreter object with an associated global Tcl command whose name is wipName. If however the string %AUTO% was used as the object name, the package will generate its own unique name for the object. πŸ’«

The engine is the object the word interpreter will dispatch all recognized commands to, and the *arg*s are a word list which defines 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, to be mapped to a method of the engine having the same name.
  • def name method_prefix β€” Defines name as a command of the language, to be mapped to the method of the engine named in the method_prefix.

The returned command may be used to invoke various operations on the object. It has the following general form:

  • wipName option ?arg arg ...? β€” Option and the *arg*s determine the exact behavior of the command.

The package additionally exports the command:

  • wip::dsl ?suffix? β€” This command is for use within snit types which wish to use one or more wip interpreters as a component. Use within the type definition installs most of the boilerplate needed to setup and use a word interpreter. 🧚

It installs a component named wip, and a method wip_setup for initializing it. This method has to be called from within the constructor of the type using the word interpreter. If further installs a series of procedures which make the object API of the word interpreter directly available to the type's methods, without having to specify the component.

Note that this does and cannot install the language to interpret, i.e. the mapping from words to engine methods. πŸŽ€

It is possible to instantiate multiple word interpreter components within a type by using different *suffix*es as arguments to the command. In that case 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. πŸ’…

✨ OBJECT API ✨

okay bestie, these are all the commands you can throw at a word interpreter object. buckle up, there's a bunch, but every single one of them is doing something cute πŸ’–

  • wipName def name ?method_prefix? β€” defines a language command called name and maps it to the method that lives in the engine's method_prefix. if you don't pass a method_prefix at all? it's just the name of the language command, no extra drama πŸ’…
  • wipName defl names β€” defines a whole series of language commands, handed to it as the list names. every one of them gets mapped to the engine method with the same name. so many at once, so efficient, we love a batch operation 🫢
  • wipName defd dict β€” same energy as above but with a dictionary dict of names and method prefixes. you get to pick the mapping, main character behaviour honestly πŸ‘‘
  • wipName deflva name... β€” literally the same as the method defl, except the list of names comes in through multiple arguments instead of one list.
  • wipName defdva (name method_prefix)... β€” literally the same as the 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 (the list names) from the mapping. goodbye, we hardly knew ye 😭
  • wipName undefva name... β€” same as the method undefl, but the list of names comes in through multiple arguments.
  • wipName unknown cmdprefix β€” sets the handler for unknown words to cmdprefix. this command prefix takes exactly one argument β€” the current word β€” and then it either throws some error, or returns the result of executing the word, however the handler decides to define that. the default handler? it simply throws an error. no vibes, just rejection πŸ’€
  • wipName runl wordlist β€” treats the list of words in wordlist as a program and executes the commands inside it one by one. the result of the command that ran last is what gets returned as the result of this command.

the wordlist is stored inside the object so the other run-methods and the general program accessor methods (they're further down, don't worry) can get at it. a previously stored wordlist gets saved while this method runs and restored before it returns. why do we care? because that's what makes it possible to execute word lists inside word lists, recursively, forever, iconic πŸŒ€

  • wipName run word... β€” same as the method runl, except the list of words to execute arrives through multiple arguments.
  • wipName run_next β€” low-level method alert. it figures out the next word in the list of words plus that word's arguments, and then executes it. the result of the executed word is the result of this method.

this one is exposed so you can use it inside command implementations. the methods run and runl use it to keep executing words until their word list runs out.

  • wipName run_next_while acceptable β€” low-level method. calls the method run_next for as long as the next word is in the set of acceptable words and the program isn't empty. the result of the command that ran last is returned as the result of this command.

exposed for use inside command implementations, specifically so you can change the order of execution. chaos, but make it deliberate ✨

  • wipName run_next_until rejected β€” low-level method. calls the method run_next until the next word is in the set of rejected words, and the program isn't empty. the result of the command that ran last is returned as the result of this command.

exposed for use inside command implementations to change the order of execution.

  • wipName run_next_if acceptable β€” low-level method. calls the method run_next if the next word is in the set of acceptable words, and the program isn't empty. the result of the command that ran last is returned as the result of this command.

exposed for use inside command implementations to change the order of execution.

  • wipName run_next_ifnot rejected β€” low-level method. calls the method run_next if the next word is not in the set of rejected words, and the program isn't empty. the result of the command that ran last is returned as the result of this command.

exposed for use inside command implementations to change the order of execution.

  • wipName next β€” returns the next word in the program. and yeah, the word gets removed too. one-time deal, no takebacks πŸ’…
  • wipName peek β€” returns the next word in the program, but without removing it. just looking, bestie, not touching πŸ™ˆ
  • wipName peekall β€” returns the remaining program in toto. the whole thing, all of it, at once πŸŽ€
  • wipName insertl at wordlist β€” basic program accessor method. inserts the given wordlist into the program, right before the word at position at. positions are counted from zero, like a real programmer.
  • wipName replacel wordlist β€” basic program accessor method. replaces the entire stored program with the given wordlist. total glow-up, the old program is gone.
  • wipName pushl wordlist β€” program accessor method. the given wordlist gets added to the front of the remaining program. it's exactly the same as
$wip insertl 0 $wordlist
  • wipName addl wordlist β€” program accessor method. the given wordlist gets appended at the end of the remaining program. it's exactly the same as
$wip insertl end $wordlist
  • wipName insert at word... β€” like the method insertl, except the words come in through multiple arguments.
  • wipName replace word... β€” like the method setl, except the words come in through multiple arguments.
  • wipName push word... β€” like the method pushl, except the words come in through multiple arguments.
  • wipName add word... β€” like the method addl, except the words come in through multiple arguments.

🌸 EXAMPLES 🌸

No examples yet. πŸ’€ (ngl, same. the author said "no examples yet" and honestly? respect for the honesty.)


🫢 BUGS, IDEAS, FEEDBACK 🫢

this document, and the package it describes, will undoubtedly contain bugs and other problems. that's just how software is, bestie, no cap. please report such in the category wip of the Tcllib Trackers at [http://core.tcl.tk/tcllib/reportlist]. and please also report any ideas for enhancements you may have for either the package and/or the documentation β€” we want them, we're obsessed, bring them to us πŸŽ€

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

also note further that attachments are strongly preferred over inlined patches. how do you attach one? go to the Edit form of the ticket immediately after its creation, and then use the left-most button in the secondary navigation bar. that's the one. that's the button. πŸ’…


⭐ KEYWORDS ⭐

interpreter, list, word


πŸ‘‘ CATEGORY πŸ‘‘

Programming tools


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


and that's a wrap on the wip(3tcl) page, bestie πŸ¦‹ this one ships from tcllib, version 2.3, and it's dated right there in the footer next to the wip(3tcl) title. go forth and interpret those words, you absolute legend πŸ‘‘βœ¨

πŸ“¦ where this page comes from