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:
Dynamic interfaces, which fngi supports with Roles. These prevent code explosion while allowing the programmer to focus on the interface of the input types, but have the cost of dynamic method lookup.
templates / aka type-aware text macros. Even Rust's trait system seems to boil down to this in most cases. In some cases Rust gives some better debug messages, in other cases they don't.
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.
[ ] Support a "stack" of srcFile's to be used ONLY for text-macros
change TY_FN_COMMENT -> TY_FN_TEXT and re-purpose the logic for text macros. They are executed immediately in scan and the scan is simply re-run when they are encountered.
when parsing, items are moved into the token Buf. If the file is exhausted, it is dropped and the next file down is used.
text macros will likely require their own arena. Fortunately they are processed recursively, so it is possible to use the existing one and not waste memory.
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:
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.
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.scan
and the scan is simply re-run when they are encountered.User interface: defining a text macro
The basic syntax is
txtMacro <macroName> <variables...> [VA <...varargs...>] do <Str> [then <Str>...]
So
$myMacro('foo, 'MyTy, '3, '7)
would be pushed to fileSrc as: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)
orreader.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.