Skip to content

Instantly share code, notes, and snippets.

@derrickturk
Last active February 17, 2017 15:30
Show Gist options
  • Save derrickturk/5c01caab93bbd6bc9ddd to your computer and use it in GitHub Desktop.
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.
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