Last active
July 14, 2021 19:39
-
-
Save derrickturk/34dfd245c0a634baab76a80e90188e49 to your computer and use it in GitHub Desktop.
fun with m4
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
divert(-1) | |
changequote(`{', `}') | |
# gensym counter | |
define({%n}, 0) | |
# association for generic bodies | |
# _generic_setbody(name, body) -> store generic body by name | |
define({_generic_setbody}, {define({%gen[$1]}, $2)}) | |
# _generic_getbody(name) -> fetch generic body by name | |
define({_generic_getbody}, {defn({%gen[$1]})}) | |
# _gensym() -> fresh variable name | |
define({_gensym}, {φdefn({%n})define({%n}, incr(defn({%n})))}) | |
# ForEach(var, arr, body) -> foreach loop | |
define({ForEach}, {pushdef({%i}, _gensym())dnl | |
Dim defn({%i}) As Long | |
For defn({%i}) = LBound($2) To UBound($2) | |
pushdef({$1}, {$2}(defn({%i})))$3{}popdef({$1}) | |
Next defn({%i})dnl | |
popdef({%i})}) | |
# _generic_body_argify(n, args..., body) -> replace args in body with $n... placeholders | |
define({_generic_body_argify}, {ifelse({$#}, 2, {$2},dnl | |
{pushdef({$2}, σ$1)dnl | |
_generic_body_argify(incr($1), shift(shift($@)))dnl | |
popdef({$2})})}) | |
# _generic_body_apply(n, args..., body) -> replace placeholders in body with args | |
define({_generic_body_apply}, {ifelse({$#}, 2, $2,dnl | |
{patsubst(_generic_body_apply(incr($1), shift(shift($@))), {σ$1}, {$2})})}) | |
# _generic_namify(name, args...) -> mangle name with type args | |
define({_generic_namify}, {ifelse({$#}, 1, {$1}, {$1_{}_generic_namify(shift($@))})}) | |
# DefGeneric(name, args..., body) -> define generic thing | |
define({DefGeneric}, {_generic_setbody($1, _generic_body_argify(1, shift($@)))}) | |
# SpecGeneric(name, args...) -> instantiate generic thing | |
define({SpecGeneric}, {dnl | |
patsubst(_generic_body_apply(1, shift($@), {_generic_getbody({$1})}),dnl | |
{$1}, {_generic_namify($@)})dnl | |
define(%seen[_generic_namify($@)], 1)dnl | |
}) | |
# Generic(name, args...) -> name generic thing (and instantiate if needed) | |
define({Generic}, {ifelse(defn(%seen[_generic_namify($@)]),dnl | |
{},dnl | |
{m4wrap({SpecGeneric($@)})}define(%seen[_generic_namify($@)], 1))dnl | |
_generic_namify($@)}) | |
changecom({"}, {"}) | |
divert(0)dnl | |
Option Explicit | |
DefGeneric(Sum, T, { | |
Public Function Sum(ByRef xs() as T) as T | |
Sum = 0 | |
ForEach(x, xs, { | |
Sum = Sum + x | |
}) | |
End Function | |
}) | |
"SpecGeneric(Sum, Long) could be used here" | |
Public Sub Test() | |
Dim xs(1 To 10) As Long | |
Dim val As Long | |
val = 1 | |
ForEach(x, xs, { | |
x = val | |
val = val + 3 | |
}) | |
Debug.Print Generic(Sum, Long)(xs) | |
Debug.Print Generic(Sum, Long)(xs) | |
End Sub |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment