# generate variadic calls to test the
# abi implementation

from random import seed, randint, uniform
from struct import unpack

I, D = 'd', 'g'

formats = [
	# list of formats to test
	[I],
	[D],
	[I,D],
	[D,D],
	[I,I,I,I],
	[D,D,D,D],
	[I,D,I,D],
	[D,D,I,I],
	[I,I,D,D],
	[],
]

generate = [
	# numbers of fixed integer and
	# floating point arguments to
	# test
	(0, 0), (1, 0), (0, 1), (4, 0),
	(0, 6), (5, 7), (10, 10), (9, 0),
]

def mkargs(nargs, type, name):
	args = map(
		lambda n: ''.join([type, name, str(n), ', ']),
		range(nargs)
	)
	return ''.join(args)

def mkfstr(fmt):
	fstr = map(
		lambda x: {I: '%d ', D: '%g '}[x],
		fmt
	)
	return '"' + ''.join(fstr) + '\\n"'

def randargs(fmt):
	ra = {
		I: lambda: '{}'.format(randint(-10, 10)),
		D: lambda: '{0:.4g}'.format(uniform(-10, 10))
	}
	return list(map(lambda x: ra[x](), fmt))

def genssa(qbeprint, qbecall):
	funcs = [('qbeprint', qbeprint), ('qbecall', qbecall)]
	for fnum, (nia, nfa) in enumerate(generate):
		params = "{}{}l %fmt, ...".format(
			mkargs(nia, 'w ', '%argw'),
			mkargs(nfa, 'd ', '%argd')
		)
		for name, code in funcs:
			print('export function ${}{}({}) {}'
				.format(name, fnum, params, code)
			)

def gendriver():
	print('# >>> driver')
	print('# #include <stdarg.h>')
	print('# #include <stdio.h>')

	for fnum, (nia, nfa) in enumerate(generate):
		params = "{}{}char *, ...".format(
			mkargs(nia, 'int ', 'argw'),
			mkargs(nfa, 'double ', 'argd')
		)
		for name in ['qbeprint', 'qbecall']:
			print('# extern void {}{}({});'
				.format(name, fnum, params)
			)

	output = ''
	print('# int print(char *fmt, va_list *ap) {')
	print('#    return vprintf(fmt, *ap);');
	print('# }')
	print('# int main() {')

	for fnum, (nia, nfa) in enumerate(generate):
		info = '# ({} int, {} double)'.format(nia, nfa)
		print('# 	puts("{}");'.format(info))
		output += '# {}\n'.format(info)
		for fmt in formats:
			ra = randargs(fmt)
			vaargs = ', '.join(ra)
			expect = ' '.join(ra)
			if fmt:
				vaargs = ', ' + vaargs
				expect = expect + ' '
			args = ''.join(
				['0, '] * (nia+nfa) +
				[mkfstr(fmt), vaargs]
			)
			for name in ['qbeprint', 'qbecall']:
				print('# 	{}{}({});'
					.format(name, fnum, args)
				)
				output += '# {}\n'.format(expect)

	print('# }')
	print('# <<<')

	print('\n# >>> output\n' + output + '# <<<')


qbeprint="""{{
@start
	%fmtdbl =l alloc4 4
	%fmtint =l alloc4 4
	%emptys =l alloc4 4
	storew {}, %fmtint
	storew {}, %fmtdbl
	storew 0, %emptys
	%vp =l alloc8 32
	%fmt1 =l add 1, %fmt
	vastart %vp
@loop
	%p =l phi @start %fmt1, @casef %p1, @cased %p1
	%c =w loadsb %p
	%p1 =l add 3, %p
	jnz %c, @loop1, @end
@loop1
	%isg =w ceqw %c, 103
	jnz %isg, @casef, @cased
@casef
	%dbl =d vaarg %vp
	%r =w call $printf(l %fmtdbl, ..., d %dbl)
	jmp @loop
@cased
	%int =w vaarg %vp
	%r =w call $printf(l %fmtint, ..., w %int)
	jmp @loop
@end
	%r =w call $puts(l %emptys)
	ret
}}
""".format(
	unpack("i", b'%d \x00')[0],
	unpack("i", b'%g \x00')[0]
)

qbecall="""{
@start
	%vp =l alloc8 32
	vastart %vp
	%r =w call $print(l %fmt, l %vp)
	ret
}
"""


if __name__ == "__main__":
	seed(42)
	genssa(qbeprint, qbecall)
	gendriver()
