Skip to content

Instantly share code, notes, and snippets.

@derrickturk
Last active July 14, 2021 19:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save derrickturk/34dfd245c0a634baab76a80e90188e49 to your computer and use it in GitHub Desktop.
Save derrickturk/34dfd245c0a634baab76a80e90188e49 to your computer and use it in GitHub Desktop.
fun with m4
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