#!/usr/bin/env python3
# ------------------------------------------------------------------------------
# This file is part of solidity.
#
# solidity is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# solidity is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with solidity.  If not, see 
#------------------------------------------------------------------------------
#
# Script that generates a dlsym-wrapper for Z3 from the header files.
# Expects all Z3 headers as arguments and outputs the wrapper code to stdout.
import sys
import re
# Patterns to match Z3 API entry point definitions.
def_pat = re.compile(" *def_API(.*)")
extradef_pat = re.compile(" *extra_API(.*)")
# Pattern to extract name and arguments from the above.
def_args_pat = re.compile("\('([^']*)'[^\(\)]*\((.*)\)\s*\)")
# Pattern to extract a list of arguments from the above.
arg_list_pat = re.compile("[^\(]*\([^\)]*\)[, ]*")
def generateEntryPoint(line, args):
    m = def_args_pat.match(args)
    if not m:
        raise Exception('Could not parse entry point definition: ' + line)
    name = m.group(1)
    num_args = len(arg_list_pat.findall(m.group(2)))
    arglist = ', '.join(f"_{i}" for i in range(num_args))
    paramlist = ', '.join(f"ArgType<&{name}, {i}> _{i}" for i in range(num_args))
    print(f'ResultType<&{name}> Z3_API {name}({paramlist})')
    print('{')
    print(f'\tstatic auto sym = reinterpret_cast(Z3Loader::get().loadSymbol(\"{name}\"));')
    print(f'\treturn sym({arglist});')
    print('}')
print(r"""// This file is auto-generated from genz3wrapper.py
#include 
#include 
#include 
namespace
{
template
struct FunctionTrait;
template
struct FunctionTrait
{
    using ResultType = R;
    template
    using ArgType = std::tuple_element_t>;
};
template
using ResultType = typename FunctionTrait::ResultType;
template
using ArgType = typename FunctionTrait::template ArgType;
}
using namespace solidity;
using namespace solidity::smtutil;
extern "C"
{
void Z3_API Z3_set_error_handler(Z3_context c, Z3_error_handler h)
{
    static auto sym = reinterpret_cast(Z3Loader::get().loadSymbol("Z3_set_error_handler"));
    sym(c, h);
}
""")
for header in sys.argv[1:]:
    with open(header, 'r') as f:
        for line in f:
            line = line.strip('\r\n\t ')
            m = def_pat.match(line)
            if m:
                generateEntryPoint(line, m.group(1).strip('\r\n\t '))
            m = extradef_pat.match(line)
            if m:
                generateEntryPoint(line, m.group(1).strip('\r\n\t '))
print('}')