civboot / fngi

a readable language that grows from the silicon
The Unlicense
60 stars 3 forks source link

text macros #15

Open vitiral opened 1 year ago

vitiral commented 1 year ago

There is a lot of hate for text macros. However, they are by far the easiest way to write large amounts of semi-boilerplate code. With a suitably powerful non-text macro system like fngi's they can also enable very simple generics.

Generics in most languages can really be summed up as a "typed text macro system" -- they honestly are not much more. Obviously there are exceptions like Haskell (which I am not super familiar with) but in Java, Python, Rust and (from what I've seen) C++ all the "simple" and legitimately useful pieces of the generic type system fall under one of two categories:

Design

Text macros are simple: srcFile is replaced with a stack of srcFiles, each with their own line/etc information. Text macros are a new function type similar to COMMENT -- they are executed by scan() and expected to push their own file onto the srcFiles stack. The scanner automatically drops the srcFiles as they are consumed.

User interface: defining a text macro

The basic syntax is txtMacro <macroName> <variables...> [VA <...varargs...>] do <Str> [then <Str>...]

txtMacro myMacro name T VA varg
do |
fn %%name v: T, other: T -> T do (
  v.add(other)
|
then VA |
  v.add(%%varg)|  \ happens once per varg
then    |       \ happens after previous
  )|

So $myMacro('foo, 'MyTy, '3, '7) would be pushed to fileSrc as:

fn foo v: MyTy, other: MyTy -> MyTy do (
  v.add(other)
  v.add(3)
  v.add(7)
)

It would then be compiled verbatim and dropped. Any compiler errors could print the expansion (or sub-expansions, if there are any!)

Memory

The arguments to a txtMacro are either Str, &StrLL or Reader arguments (it inspects the type stack to allow for any). This allows it to work with complex/large string processing function or even open files. They must all allocate from bbaTxt -- which is used for text storage in-general.

All of them are converted to a Reader under the hood. Any allocations done have to be recorded in a txtLL and dropped in the reverse order -- with the underlying buffer of the passed-in type being dropped last (so arena.free(str.dat), strLL.drop(arena) or reader.drop(arena))

I will probably create a Droppable type for handling this, which is a Resource LL that drops things in some standard order.

Extensions

Folks can easily use functions like tName:MyType to get the full-name of a specific type if that is what they want. It will NOT be built-into the syntax.

I want the fact that text macros work on strings (and ONLY) strings to be explicit at all levels so there can be no confusion. Other macros work on tokens/types/etc -- text macros do exactly what they appear to do and no more.