Last active
February 17, 2017 15:30
-
-
Save derrickturk/5c01caab93bbd6bc9ddd to your computer and use it in GitHub Desktop.
m4 macros to extend Visual Basic 6 and VBA. No sane person needs or wants this.
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')dnl | |
dnl (c) 2017 dwt | terminus data science, LLC | |
dnl LICENSE: | |
dnl if you're a bad/strange enough dude/dudette to want or need to use these, | |
dnl have at it... | |
changequote(`{', `}') | |
changecom({"},{"}) | |
dnl __REPEAT(item, count) | |
define({__REPEAT},{ifelse($2,0,{},{$1{}__REPEAT({$1},eval($2-1))})}) | |
dnl __INDENTLEVEL(block) | |
define({__INDENTLEVEL},{regexp(patsubst({$1},{ | |
},{}),{\w})}) | |
dnl __ASSIGNEACH(arr, first_index, vals...) | |
define({__ASSIGNEACH},{ifelse({$3},{},{},{$1($2) = $3 | |
__ASSIGNEACH($1,eval($2+1),shift(shift(shift($@))))})}) | |
dnl __OFFSET(offset) | |
define({__OFFSET},{ifelse({$1},0,{},{ + $1})}) | |
dnl __DELIMCONCAT(delim, args...) | |
define({__DELIMCONCAT},{ifelse({$#},2,$2,{$2{}$1{}__DELIMCONCAT($1,shift(shift($@)))})}) | |
dnl __SETASSOC(assoc, key, value) | |
define({__SETASSOC},{pushdef({$1[$2]},{$3})}) | |
dnl __GETASSOC(assoc, key) | |
define({__GETASSOC},{defn({$1[$2]})}) | |
dnl __LAST(args...) | |
define({__LAST},{ifelse({$#},1,{$1},{$#},0,{},{__LAST(shift($@))})}) | |
dnl __ALLBUTLAST(args...) | |
define({__ALLBUTLAST},{ifelse({$#},1,{},{$#},0,{},{$#},2,{$1},{$1,__ALLBUTLAST(shift($@))})}) | |
dnl __FIRST(args...) | |
define({__FIRST},{ifelse({$#},0,{},{$1})}) | |
dnl __REST(args...) | |
define({__REST},{shift($@)}) | |
dnl __LEN(args...) | |
define({__LEN},{$#}) | |
dnl __SPERSE({evens...},{odds...}) | |
define({__SPERSE},{ifelse({$#},0,{},__LEN($1),1,{__FIRST($1),__FIRST($2)},dnl | |
{__SPERSE({__FIRST($1)},{__FIRST($2)}),__SPERSE({__REST($1)},{__REST($2)})})}) | |
dnl __VARSUBST(body, var1, defn1, ...) | |
define({__VARSUBST},{ifelse({$#},3,patsubst({$1},{\<$2\>},{$3}),dnl | |
{$#},0,{},dnl | |
{$#},1,{$1},dnl | |
{__VARSUBST(patsubst({$1},{\<$2\>},{$3}),shift(shift(shift($@))))})}) | |
dnl __ITEM_NAME(block) | |
define({__ITEM_NAME},{__ITEM_NAME_1(patsubst({$1},{ | |
},{}))}) | |
dnl __ITEM_NAME_1(block) | |
define({__ITEM_NAME_1},{patsubst({$1},{ *\(Public\|Private\|Declare\)? *\(Sub\|Function\|Type\) *\(\w+\).*},{\3})}) | |
dnl __STRIP_NEWLINES(block) | |
define({__STRIP_NEWLINES},{patsubst({$1},{\(^ | |
\| | |
$\)},{})}) | |
dnl __PUSHROWIDXMACRO(rowvar, ivar, array) | |
define({__PUSHROWIDXMACRO},{__PUSHROWIDXMACRO_1({$1},{$2},{$3},{$}{1})}) | |
dnl __PUSHROWIDXMACRO_1(rowvar, ivar, array, literal $1) | |
define({__PUSHROWIDXMACRO_1},{pushdef({$1},{$3}({$2}, {$4}))}) | |
dnl __PUSHCOLIDXMACRO(colvar, jvar, array) | |
define({__PUSHCOLIDXMACRO},{__PUSHCOLIDXMACRO_1({$1},{$2},{$3},{$}{1})}) | |
dnl __PUSHCOLIDXMACRO_1(colvar, jvar, array, literal $1) | |
define({__PUSHCOLIDXMACRO_1},{pushdef({$1},{$3}({$4}, {$2}))}) | |
dnl __Q(args...) | |
define({__Q},{ifelse({$#},0,{},{{$*}})}) | |
dnl ArrayLength(array) | |
define({ArrayLength},{(UBound($1) - LBound($1) + 1)}) | |
dnl NumRows2D(array) | |
define({NumRows2D},{(UBound($1, 1) - LBound($1, 1) + 1)}) | |
dnl NumCols2D(array) | |
define({NumCols2D},{(UBound($1, 2) - LBound($1, 2) + 1)}) | |
dnl FirstElem(array) | |
define({FirstElem},{$1(LBound($1))}) | |
dnl LastElem(array) | |
define({LastElem},{$1(UBound($1))}) | |
dnl FirstElem2D(array) | |
define({FirstElem2D},{$1(LBound($1, 1), LBound($1, 2))}) | |
dnl LastElem2D(array) | |
define({LastElem2D},{$1(UBound($1, 1), UBound($1, 2))}) | |
dnl BoundsOf(array) | |
define({BoundsOf},{LBound($1) To UBound($1)}) | |
dnl BoundsOf2D(array) | |
define({BoundsOf2D},{LBound($1, 1) To UBound($1, 1), LBound($1, 2) To UBound($1, 2)}) | |
dnl BoundsOfDimension(array, dimension) | |
define({BoundsOfDimension},{LBound($1, $2) To UBound($1, $2)}) | |
dnl OffsetElem(array, offset) | |
define({OffsetElem},{$1(LBound($1)__OFFSET($2))}) | |
dnl OffsetElem2D(array, offset_i, offset_j) | |
define({OffsetElem2D},{$1(LBound($1, 1)__OFFSET($2), LBound($1, 2)__OFFSET($3))}) | |
dnl OffsetElemRow2D(array, index_i, offset_j) | |
define({OffsetElemRow2D},{$1(LBound($1, 1)__OFFSET($2), $3)}) | |
dnl OffsetElemCol2D(array, index_i, offset_j) | |
define({OffsetElemCol2D},{$1($2, LBound($1, 2)__OFFSET($3))}) | |
dnl OffsetIndex(array, offset) | |
dnl OffsetIndex(array, dimension, offset) | |
define({OffsetIndex},{ifelse({$#},3,dnl | |
LBound($1, $2)__OFFSET($3),dnl | |
LBound($1)__OFFSET($2))}) | |
dnl OffsetIndexDimension(array, dimension, offset) | |
define({OffsetIndexDimension},{LBound($1, $2)__OFFSET($3)}) | |
dnl ForEnumerate(var, array, block) | |
define({ForEnumerate},{ifdef({__NODIM},{},{Dim $1 As Long | |
__REPEAT({ },eval(__INDENTLEVEL({$3})-4))})For $1 = LBound($2) To UBound($2)dnl | |
$3{}Next $1}) | |
dnl ForEnumerateDimension(var, array, dimension, block) | |
define({ForEnumerateDimension},{ifdef({__NODIM},{},{Dim $1 As Long | |
__REPEAT({ },eval(__INDENTLEVEL({$4})-4))})For $1 = LBound($2, $3) To UBound($2, $3)dnl | |
$4{}Next $1}) | |
dnl ForEnumerate2D(i_var, j_var, array, block) | |
define({ForEnumerate2D},{ifdef({__NODIM},{},{Dim $1 As Long | |
__REPEAT({ },eval(__INDENTLEVEL({$4})-4))Dim $2 As Long | |
__REPEAT({ },eval(__INDENTLEVEL({$4})-4))})For $1 = LBound($3, 1) To UBound($3, 1) | |
__REPEAT({ },eval(__INDENTLEVEL({$4})))For $2 = LBound($3, 2) To UBound($3, 2)dnl | |
patsubst({$4},{^\( | |
?\)\(\W+\w\)},{\1 \2}){}__REPEAT({ },4)Next $2 | |
__REPEAT({ },eval(__INDENTLEVEL({$4})-4))Next $1}) | |
dnl ForEach(var, array, block, Optional<i_var>) | |
define({ForEach},{pushdef({__IVAR},ifelse({$#},4,{$4},{i}))ifdef({__NODIM},{},{Dim __IVAR As Long | |
__REPEAT({ },eval(__INDENTLEVEL({$3})-4))})For __IVAR = LBound($2) To UBound($2)dnl | |
patsubst({$3},{\<$1\>},{$2(__IVAR)}){}Next __IVAR{}popdef({__IVAR})}) | |
dnl ForEach2D(var, array, block, Optional<i_var>, Optional<j_var>) | |
define({ForEach2D},{pushdef({__IVAR},ifelse({$#},5,{$4},{i}))pushdef({__JVAR},ifelse({$#},5,{$5},{j}))ifdef({__NODIM},{},{Dim __IVAR As Long | |
__REPEAT({ },eval(__INDENTLEVEL({$3})-4))Dim __JVAR As Long | |
__REPEAT({ },eval(__INDENTLEVEL({$3})-4))})For __IVAR = LBound($2, 1) To UBound($2, 1) | |
__REPEAT({ },eval(__INDENTLEVEL({$3})))For __JVAR = LBound($2, 2) To UBound($2, 2)dnl | |
patsubst(patsubst({$3},{\<$1\>},{$2(__IVAR, __JVAR)}),{^\( | |
?\)\(\W+\w\)},{\1 \2}){}__REPEAT({ },4)Next __JVAR | |
__REPEAT({ },eval(__INDENTLEVEL({$3})-4))Next __IVAR{}popdef({__IVAR})popdef({__JVAR})}) | |
dnl ForEachRow2D(var, array, block, Optional<i_var>) | |
define({ForEachRow2D},{pushdef({__IVAR},ifelse({$#},4,{$4},{i}))ifdef({__NODIM},{},{Dim __IVAR As Long | |
__REPEAT({ },eval(__INDENTLEVEL({$3})-4))})For __IVAR = LBound($2, 1) To UBound($2, 1)dnl | |
dnl define our $1 as a macro expanding to an indexing operation | |
__PUSHROWIDXMACRO({$1},{__IVAR},{$2})dnl | |
$3{}dnl | |
popdef({$1}){}Next __IVAR{}popdef({__IVAR})}) | |
dnl ForEachCol2D(var, array, block, Optional<j_var>) | |
define({ForEachCol2D},{pushdef({__JVAR},ifelse({$#},4,{$4},{j}))ifdef({__NODIM},{},{Dim __JVAR As Long | |
__REPEAT({ },eval(__INDENTLEVEL({$3})-4))})For __JVAR = LBound($2, 2) To UBound($2, 2)dnl | |
dnl define our $1 as a macro expanding to an indexing operation | |
__PUSHCOLIDXMACRO({$1},{__JVAR},{$2})dnl | |
$3{}dnl | |
popdef({$1}){}Next __JVAR{}popdef({__JVAR})}) | |
dnl NoDim(block) | |
define({NoDim},{pushdef({__NODIM},{}){}$1{}popdef({__NODIM})}) | |
dnl ArrayLiteral(name, type, values...) | |
define({ArrayLiteral},{Function $1() As $2() | |
Static initialized As Boolean | |
Static arr(0 To eval({$#} - 3)) As $2 | |
If Not initialized Then | |
patsubst(__ASSIGNEACH(arr, 0, shift(shift($@))),{^},{ })initialized = True | |
End If | |
$1 = arr | |
End Function}) | |
dnl DynArray(var, type, bounds) | |
define({DynArray},{Dim $1() As $2 : ReDim $1($3)}) | |
dnl DynamicCast(obj, interface, var, Optional<errlabel>) | |
define({DynamicCast},{pushdef({__ERRLABEL},ifelse({$#},4,{$4},{$3_CASTFAIL}))ifdef({__NODIM},{},{Dim $3 As $2 | |
})On Error GoTo __ERRLABEL | |
Set $3 = $1 | |
GoTo END{}__ERRLABEL | |
__ERRLABEL: | |
Set $3 = Nothing | |
END{}__ERRLABEL: | |
On Error GoTo 0{}popdef({__ERRLABEL})}) | |
dnl ErrorHook(block, Optional<label>) | |
define({ErrorHook},{pushdef({__ERRLABEL},ifelse({$#},2,{$2},{CATCH}))On Error GoTo __ERRLABEL | |
__REPEAT({ },eval(__INDENTLEVEL({$1})-4))GoTo END{}__ERRLABEL | |
__ERRLABEL: | |
patsubst({$1},{^\( | |
\)?\W+\(\w\)},{__REPEAT({ },eval(__INDENTLEVEL({$1})-4))\2})Resume Next | |
END{}__ERRLABEL:}) | |
dnl TryCatch(tag, block, catch, [witherr]) | |
dnl this is awkward because vb6 error handling is awkward | |
dnl we want to allow the catch to raise additional errors | |
dnl without triggering recursion; but we also want to have | |
dnl a chance to use the Err object. | |
dnl so, code in the optional witherr block must not raise | |
dnl additional errors, but runs before catch and has a chance | |
dnl to use the Err object. | |
define({TryCatch},{dnl | |
On Error GoTo $1_FAIL{}dnl | |
patsubst({$2},{^ },{})dnl | |
On Error GoTo 0 | |
__REPEAT({ },eval(__INDENTLEVEL({$2})-4))GoTo $1_SUCCESS | |
$1_FAIL:patsubst({$4},{^ },{}) | |
__REPEAT({ },eval(__INDENTLEVEL({$3})-4))On Error GoTo 0{}dnl | |
patsubst({$3},{^ },{})dnl | |
$1_SUCCESS:dnl | |
}) | |
dnl OpAssign(lhs, op, rhs) | |
dnl read: lhs op= rhs | |
define({OpAssign},{$1 = $1 $2 $3}) | |
dnl FnAssign(lhs, fn, args...) | |
dnl read: lhs = fn(lhs, args...) | |
define({FnAssign},{{$1} = {$2}(ifelse({$#},2,{$1},{$1, __DELIMCONCAT({Comma },shift(shift($*)))}))}) | |
dnl DefineGeneric(types..., body) | |
define({DefineGeneric},{dnl | |
pushdef({__FN},__ITEM_NAME(__LAST($@)))dnl | |
__SETASSOC({__GENERICDEFN},__FN,__STRIP_NEWLINES(__Q(__LAST($@))))dnl | |
__SETASSOC({__GENERICVARS},__FN,__Q(__ALLBUTLAST($@)))dnl | |
' {Generic}(__GETASSOC({__GENERICVARS},__FN)) | |
patsubst(__GETASSOC({__GENERICDEFN},__FN),{^},{' }){}dnl | |
popdef({__FN})}) | |
dnl GenericInstance(fn_or_type, {types...}) | |
define({GenericInstance},{dnl | |
' Instance of {Generic} $1 | |
' for $2 | |
__VARSUBST(patsubst(__GETASSOC({__GENERICDEFN},{$1}),{$1},{$1{}_{}__DELIMCONCAT(_,$2)}),__SPERSE(__GETASSOC({__GENERICVARS},{$1}),{$2}))}) | |
dnl Generic(type, {types...}) | |
define({Generic},{$1{}_{}__DELIMCONCAT(_,$2)}) | |
dnl Comma() | |
define({Comma},{{,}}) | |
dnl Module(name) | |
define({Module},{Attribute VB_Name = "$1"}) | |
dnl ClassModule(name) | |
define({ClassModule},{VERSION 1.0 CLASS | |
BEGIN | |
MultiUse = -1 'True | |
END | |
Attribute VB_Name = "$1" | |
Attribute VB_GlobalNameSpace = False | |
Attribute VB_Creatable = False | |
Attribute VB_PredeclaredId = False | |
Attribute VB_Exposed = False}) | |
dnl Describe([thing], description) | |
define({Describe},{Attribute dnl | |
ifelse({$#},2,dnl | |
$1.VB_Description = "$2",dnl | |
VB_Description = "$1")}) | |
divert{}dnl |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment