#!/usr/bin/env python3
#
# Copyright (c) 2006 Verdens Gang AS
# Copyright (c) 2006-2015 Varnish Software AS
# All rights reserved.
#
# Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
#
# SPDX-License-Identifier: BSD-2-Clause
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.

'''
   Generate various .c and .h files for the VCL compiler and the interfaces
   for it.
'''

import sys
import os.path

stv_variables = (
    ('free_space', 'BYTES', "0", 'storage.<name>.free_space' ),
    ('used_space', 'BYTES', "0", 'storage.<name>.used_space' ),
    ('happy',      'BOOL',  "1", 'storage.<name>.happy' ),
)

def file_header(fo):
    '''
       Header for all generated C files
    '''

    fo.write('/*\n')
    fo.write(' * NB:  This file is machine generated, DO NOT EDIT!\n')
    fo.write(' *\n')
    fo.write(' * Edit and run lib/libvcc/generate.py instead.\n')
    fo.write(' */\n')

def emit_strings(fo, what, *args):
    '''
       Spit out C-code that outputs the args with VSB_cat()

       The main job here is to escape stuff properly into
       C-language string constants, but for historical reasons
       we also wrap lines before the become too long and split
       C-strings before they get too long.
    '''

    w = 8+80+2          # Width of lines, after white space prefix
    maxlen = 20<<10     # Max length of string literal

    x = 0
    l = 0

    fo.write('\tVSB_cat(sb, "/* ---===### %s ###===--- */\\n\\n");\n' % what)
    for c in "\n".join(args):
        if l == 0:
            fo.write("\tVSB_cat(sb,\n\t    \"")
            l += 12
            x += 12
        if x == 0:
            fo.write("\t    \"")
        d = c
        if c == '\n':
            d = "\\n"
        elif c == '"':
            d = "\\\""
        elif c == '\\':
            d = "\\\\"

        if c == '\n':
            fo.write(d + "\"\n")
            x = 0
            continue
        if x > w - 2:
            fo.write(d + "\"\n")
            x = 0
            continue

        fo.write(d)
        x += len(d)
        l += len(d)
        if l > maxlen:
            fo.write("\");\n")
            l = 0
            x = 0
        if x > w - 3:
            fo.write("\"\n")
            x = 0
    if x != 0:
        fo.write("\"\n")
    if l != 0:
        fo.write("\t);\n")
    fo.write('\tVSB_cat(sb, "\\n");\n')

def emit_c_file(fo, fd, bn):
    '''
       Read a C-source file and spit out code that outputs it with VSB_cat()
    '''

    fn = os.path.join(fd, bn)

    with open(fn, encoding="utf8") as fi:
        fc = fi.read()

    fo.write("\n\t/* %s */\n\n" % fn)
    emit_strings(fo, bn, fc)

def emit_lines(fo, *args):
    '''
       Split lines into file
    '''

    for i in args:
        fo.write(i + '\n')

class Tokens():

    '''
       Everything related to VCL language tokens
       -----------------------------------------
    '''

    tokens = {
        "T_INC":        "++",
        "T_DEC":        "--",
        "T_CAND":       "&&",
        "T_COR":        "||",
        "T_LEQ":        "<=",
        "T_EQ":         "==",
        "T_NEQ":        "!=",
        "T_GEQ":        ">=",
        "T_SHR":        ">>",
        "T_SHL":        "<<",
        "T_INCR":       "+=",
        "T_DECR":       "-=",
        "T_MUL":        "*=",
        "T_DIV":        "/=",
        "T_NOMATCH":    "!~",

        # Single char tokens, for convenience on one line
        None:           "{}()*+-/%><=;!&.|~,",

        # These have handwritten recognizers
        "ID":           None,
        "CNUM":         None,
        "FNUM":         None,
        "CSTR":         None,
        "CSRC":         None,
        "CBLOB":        None,

        # End of token list
        "EOI":      None,
    }

    def __init__(self):

        # Expand single char tokens
        st = self.tokens[None]
        del self.tokens[None]
        for i in st:
            self.tokens["'" + i + "'"] = i

    def emit_vcc_token_defs(self, fn):
        '''
           Emit #define values for the tokens
        '''

        with open(fn, "w", encoding="utf8") as fo:
            file_header(fo)
            for n, i in enumerate(sorted(i for i in self.tokens if i[0] != "'")):
                assert n < 128
                fo.write("#define\t%s %d\n" % (i, 128 + n))

    def emit_vcl_tnames(self, fo):
        '''
           Emit the vcl_tnames (token->string) conversion array
        '''

        fo.write("\nconst char * const vcl_tnames[256] = {\n")
        for i, j in sorted(self.tokens.items()):
            if j is None or i[0] == "'":
                fo.write("\t[%s] = \"%s\",\n" % (i, i))
            else:
                fo.write("\t[%s] = \"%s\",\n" % (i, j))
        fo.write("};\n")

    def emit_vcl_fixed_token(self, fo):
        '''
           Emit a function to recognize tokens in a string
        '''

        fo.write('#define M1()\tdo {*q = p + 1; return (p[0]); } while (0)\n')
        fo.write('#define M2(c,t)\tdo {if (p[1] == (c)) { *q = p + 2; return (t); }} while (0)\n')

        fo.write('\nunsigned\n')
        fo.write('vcl_fixed_token(const char *p, const char **q)\n')
        fo.write('{\n')
        fo.write('\tswitch (p[0]) {\n')

        last_initial = None
        for i,j in sorted(((y, x) for x,y in self.tokens.items() if y)):
            if i[0] != last_initial:
                if last_initial:
                    fo.write("\t\tM1();\n")
                fo.write("\tcase '%s':\n" % i[0])
                last_initial = i[0]
            if len(i) == 2:
                fo.write("\t\tM2('%s', %s);\n" % (i[1], j))
        fo.write("\t\tM1();\n")
        fo.write("\tdefault:\n\t\treturn (0);\n\t}\n}\n")


def main():

    srcroot = "../.."
    buildroot = "../.."
    if len(sys.argv) == 3:
        srcroot = sys.argv[1]
        buildroot = sys.argv[2]
    elif len(sys.argv) != 1:
        print("Two arguments or none")
        exit(2)

    tokens = Tokens()

    #######################################################################
    # Our methods and actions

    returns = []

    def parse_subs(ln, c):
        ctx = ['C', 'B', 'H']
        rets = []
        for i in ln[2:]:
            if (len(i) > 4 and i.startswith('  | ``') and i.endswith('``')):
                rets.append(i[6:-2].split('(')[0])
        returns.append((ln[0][4:], ctx[c], rets))

    def parse_subs_doc(fn):
        l = []
        j = -1
        start = False
        for i in open(fn, encoding="utf8"):
            if i.startswith('##########'):
                j += 1
                l.append([])
                start = True
                continue
            if start:
                l[j].append(i.rstrip())
        for i in range(0, len(l)):
            for n in range(0, len(l[i])):
                if len(l[i][n]) < 5 or not l[i][n].startswith("~~~~~"):
                    continue
                m = n + 1
                while m < len(l[i]) and (len(l[i][m]) < 5 or not l[i][m].startswith("~~~~~")):
                    m += 1
                if m != len(l[i]):
                    m -= 2
                parse_subs(l[i][n-1:m], i)

    parse_subs_doc(os.path.join(srcroot, "doc/sphinx/reference/vcl_step.rst"))

    #######################################################################
    # Variables available in sessions
    #
    # 'all' means all methods
    # 'client' means all methods tagged "C"
    # 'backend' means all methods tagged "B"
    # 'both' means all methods tagged "B" or "C"

    varprotos = {}

    def varproto(s):
        if not s in varprotos:
            fh.write(s + ";\n")
            varprotos[s] = True

    def var_is_wildcard(sym):
        return sym[-2:] == '.*'

    def var_symbol_name(sym):
        if var_is_wildcard(sym):
            return sym[:-2]
        return sym

    class VarDef():
        def __init__(self, sym, typ, rd, wr, wu, al, vlo, vhi):
            self.sym = sym
            self.nam = var_symbol_name(sym)
            self.typ = typ
            self.rd = rd
            self.wr = wr
            self.uns = wu
            self.al = al
            self.vlo = vlo
            self.vhi = vhi

            if al is None:
                self.emit_var()
            else:
                self.emit_alias()

        def emit_var(self):
            fh.write("\n")
            fo.write("\n")
            cnam = self.nam.replace(".", "_")
            ctyp = vcltypes[self.typ]

            # fo.write("\t{ \"%s\", %s,\n" % (nm, self.typ))
            fo.write("\tsym = VCC_MkSym(tl, \"%s\", SYM_MAIN," % self.nam)
            if var_is_wildcard(self.sym):
                fo.write(" SYM_NONE, %d, %d);\n" % (self.vlo, self.vhi))
                fo.write("\tAN(sym);\n")
                fo.write("\tsym->wildcard = vcc_Var_Wildcard;\n")
            else:
                fo.write(" SYM_VAR, %d, %d);\n" % (self.vlo, self.vhi))
            fo.write("\tAN(sym);\n")
            fo.write("\tsym->type = %s;\n" % self.typ)
            if self.typ == "HEADER" and not var_is_wildcard(self.sym):
                fo.write("\tsym->eval = vcc_Eval_ProtectedHeader;\n")
            else:
                fo.write("\tsym->eval = vcc_Eval_Var;\n")

            if var_is_wildcard(self.sym):
                fo.write('\tsym->rname = "HDR_')
                fo.write(self.nam.split(".")[0].upper())
                fo.write('";\n')
            elif self.rd and self.typ != "HEADER":
                fo.write('\tsym->rname = "VRT_r_%s(ctx)";\n' % cnam)
                varproto("VCL_" + self.typ + " VRT_r_%s(VRT_CTX)" % cnam)
            fo.write("\tsym->r_methods =\n")
            restrict(fo, self.rd)
            fo.write(";\n")

            if var_is_wildcard(self.sym):
                fo.write('\tsym->lname = "HDR_')
                fo.write(self.nam.split(".")[0].upper())
                fo.write('";\n')
            elif self.wr:
                fo.write('\tsym->lname = "VRT_l_%s(ctx, ";\n' % cnam)
                s = "void VRT_l_%s(VRT_CTX, " % cnam
                if self.typ == "STRING":
                    s += ctyp.c + ", VCL_STRANDS)"
                elif self.typ == "BODY":
                    s += "enum lbody_e, const char *, " + ctyp.c + ")"
                else:
                    s += "VCL_" + self.typ + ")"
                varproto(s)
            fo.write("\tsym->w_methods =\n")
            restrict(fo, self.wr)
            fo.write(";\n")

            if self.uns:
                varproto("void VRT_u_%s(VRT_CTX)" % cnam)
                fo.write('\tsym->uname = "VRT_u_%s(ctx)";\n' % cnam)
            fo.write('\tsym->u_methods =\n')
            restrict(fo, self.uns)
            fo.write(";\n")

        def emit_alias(self):
            var_aliases.append("\tsym = VCC_MkSymAlias(tl, \"%s\", \"%s\");\n" %
                    (self.nam, self.al))
            var_aliases.append("\tAN(sym);\n")

    def parse_vcl(x):
        vlo, vhi = (0, 99)
        x = x.split()
        if x[0] == "VCL" and x[1] == "<=":
            vhi = int(float(x[2]) * 10)
        elif x[0] == "VCL" and x[1] == ">=":
            vlo = int(float(x[2]) * 10)
        else:
            print("Unknown variable version spec")
            print("XXX", x, vlo, vhi)
            exit(2)
        return vlo, vhi

    def parse_var(ln):
        l1 = ln.pop(0).split("``")
        assert len(l1) in (1, 3)
        vn = l1[0].strip()
        if len(l1) == 3:
            vlo, vhi = parse_vcl(l1[1])
        else:
            vlo, vhi = 0, 99
        vr = []
        vw = []
        vu = []
        va = None
        while True:
            l = ln.pop(0)
            if l == "":
                continue
            j = l.split()
            if j[0] == "Type:":
                assert len(j) == 2
                vt = j[1]
                continue
            if j[0] == "Readable" and j[1] == "from:":
                for i in j[2:]:
                    vr.append(i.strip(",."))
                continue
            if j[0] == "Writable" and j[1] == "from:":
                for i in j[2:]:
                    vw.append(i.strip(",."))
                continue
            if j[0] == "Unsettable" and j[1] == "from:":
                for i in j[2:]:
                    vu.append(i.strip(",."))
                continue
            if j[0] == "Alias" and j[1] == "of:":
                va = j[2]
                continue
            break
        if vn[:8] != "storage.":
            VarDef(vn, vt, vr, vw, vu, va, vlo, vhi)

    def parse_var_doc(fn):
        l = []
        with open(fn, encoding="utf8") as fi:
            for i in fi:
                l.append(i.rstrip())
        for n,x in enumerate(l):
            j = x.split()
            if len(j) != 2 or j[0] != "Type:" or not x[0].isspace():
                continue
            m = n
            while m < len(l) and (l[m] == "" or l[m][0].isspace()):
                m += 1
            parse_var(l[n-2:m-1])

    #######################################################################
    # VCL to C type conversion

    vcltypes = {}

    class VclType():
        def __init__(self, name, ctype, internal=False):
            self.name = name
            self.c = ctype
            self.internal = internal
            vcltypes[name] = self


    VclType("STRINGS", "void", True)
    VclType("SUB", "void*", True)

    with open(os.path.join(srcroot, "include/vrt.h"), encoding="utf8") as fi:
        for i in fi:
            j = i.split()
            if len(j) < 3:
                continue
            if j[0] != "typedef":
                continue
            if j[-1][-1] != ";":
                continue
            if j[-1][-2] == ")":
                continue
            if j[-1][:4] != "VCL_":
                continue
            d = " ".join(j[1:-1])
            VclType(j[-1][4:-1], d)

    #######################################################################
    # Nothing is easily configurable below this line.
    #######################################################################

    #######################################################################
    def lint_start(fo):
        fo.write('/*lint -save -e525 -e539 */\n\n')

    def lint_end(fo):
        fo.write('\n/*lint -restore */\n')

    #######################################################################

    tokens.emit_vcc_token_defs(os.path.join(buildroot, "lib/libvcc/vcc_token_defs.h"))

    #######################################################################

    rets = {}
    vcls = []
    vcls_client = []
    vcls_backend = []
    for i in returns:
        vcls.append(i[0])
        for j in i[1]:
            if j == "B":
                vcls_backend.append(i[0])
            elif j == "C":
                vcls_client.append(i[0])
        for j in i[2]:
            rets[j] = True

    #######################################################################

    with open(os.path.join(buildroot, "include/tbl/vcl_returns.h"), "w", encoding="utf8") as fo:

        file_header(fo)

        lint_start(fo)

        fo.write("#ifdef VCL_RET_MAC\n")
        ll = sorted(returns)
        for i in sorted(rets.keys()):
            fo.write("VCL_RET_MAC(%s, %s" % (i.lower(), i.upper()))
            s = ",\n\t"
            for j in ll:
                if i in j[2]:
                    fo.write("%sVCL_MET_%s" % (s, j[0].upper()))
                    s = " |\n\t"
            fo.write("\n)\n\n")
        fo.write("#undef VCL_RET_MAC\n")
        fo.write("#endif\n")

        fo.write("\n#ifdef VCL_MET_MAC\n")
        for i in ll:
            fo.write("VCL_MET_MAC(%s, %s, %s," %
                     (i[0].lower(), i[0].upper(), i[1]))
            p = " (\n\t"
            for j in sorted(i[2]):
                fo.write("%s(1U << VCL_RET_%s)" % (p, j.upper()))
                p = " |\n\t"
            fo.write(")\n)\n\n")
        fo.write("#undef VCL_MET_MAC\n")
        fo.write("#endif\n")
        lint_end(fo)

    #######################################################################

    with open(os.path.join(buildroot, "include/vcl.h"), "w", encoding="utf8") as fo:

        file_header(fo)

        emit_lines(
            fo,
            '',
            '#ifdef VCL_H_INCLUDED',
            '#  error "vcl.h included multiple times"',
            '#endif',
            '#define VCL_H_INCLUDED',
            '',
            '#ifndef VRT_H_INCLUDED',
            '#  error "include vrt.h before vcl.h"',
            '#endif',
        )

        def tbl40(a, b):
            while len(a.expandtabs()) < 40:
                a += "\t"
            return a + b

        fo.write("\n/* VCL Methods */\n")
        task = {}
        n = 1
        for i in returns:
            fo.write(tbl40("#define VCL_MET_%s" % i[0].upper(), "(1U << %d)\n" % n))
            if not i[1] in task:
                task[i[1]] = []
            task[i[1]].append("VCL_MET_" + i[0].upper())
            n += 1

        fo.write("\n" + tbl40("#define VCL_MET_MAX", "%d\n" % n))
        fo.write("\n" + tbl40("#define VCL_MET_MASK", "0x%x\n" % ((1 << n) - 1)))

        fo.write("\n")
        for i in sorted(task.keys()):
            fo.write(tbl40("#define VCL_MET_TASK_%s" % i.upper(),
                           "( " + (" | \\\n\t\t\t\t\t  ").join(task[i]) + " )\n"))

        fo.write("\n")
        fo.write(tbl40("#define VCL_MET_TASK_ALL", "( VCL_MET_TASK_"))
        fo.write(" | \\\n\t\t\t\t\t  VCL_MET_TASK_".join(map(str.upper, task.keys())))
        fo.write(" )")

        fo.write("\n/* VCL Returns */\n")
        n = 1
        for i in sorted(rets.keys()):
            fo.write(tbl40("#define VCL_RET_%s" % i.upper(), "%d\n" % n))
            n += 1

        fo.write("\n" + tbl40("#define VCL_RET_MAX", "%d\n" % n))

        fo.write("\n/* VCL Types */\n")
        emit_lines(
            fo,
            'struct vrt_type {',
            '\tunsigned\t\tmagic;',
            '#define VRT_TYPE_MAGIC\t\t0xa943bc32',
            '\tconst char\t\t*lname;',
            '\tconst char\t\t*uname;',
            '\tconst char\t\t*ctype;',
            '\tsize_t\t\t\tszof;',
            '};',
            '',
        )

        for vcltype in sorted(vcltypes.keys()):
            fo.write("extern const struct vrt_type VCL_TYPE_%s[1];\n" % vcltype)

        emit_lines(
            fo,
            '',
            '/* Compiled VCL Interface */',
            'typedef int vcl_event_f(VRT_CTX, enum vcl_event_e);',
            'typedef int vcl_init_f(VRT_CTX);',
            'typedef void vcl_fini_f(VRT_CTX);',
            '',
            'struct VCL_conf {',
    	    '	unsigned		magic;',
            '#define VCL_CONF_MAGIC		0x7406c509      /* from /dev/random */',
            '',
    	    '	unsigned		syntax;',
    	    '	VCL_BACKEND		*default_director;',
    	    '	VCL_PROBE		default_probe;',
    	    '	unsigned		nref;',
    	    '	const struct vpi_ref	*ref;',
            '',
    	    '	unsigned		nsrc;',
    	    '	unsigned		nsub;',
    	    '	const char		**srcname;',
    	    '	const char		**srcbody;',
            '',
    	    '	int			nvmod;',
    	    '	const struct vpi_ii	*instance_info;',
            '',
    	    '	vcl_event_f		*event_vcl;',
            '',
        )

        for i in returns:
            fo.write("\tvcl_func_f\t\t*" + i[0] + "_func;\n")

        fo.write("\n};\n")
    fo.close()

    #######################################################################

    with open(os.path.join(buildroot, "include/tbl/vcl_context.h"), "w", encoding="utf8") as fo:
        file_header(fo)

        fo.write("/*lint -save -e525 -e539 */\n")
        for i in returns:
            fo.write("\nVCL_CTX(vcl_%s,%s)" % (i[0],i[0].upper()))
        fo.write("\nVCL_CTX(backend, TASK_B)")
        fo.write("\nVCL_CTX(client, TASK_C)")
        fo.write("\nVCL_CTX(housekeeping, TASK_H)")
        fo.write("/*lint -restore */\n")
        fo.write("\n")
        fo.write("\n#undef VCL_CTX")
        fo.write("\n")

    #######################################################################

    def restrict(fo, spec):
        d = {}
        for j in spec:
            if j[:4] == "vcl_":
                j = j[4:]
            if j == 'all':
                for i in vcls:
                    d[i] = True
            elif j == 'backend':
                for i in vcls_backend:
                    d[i] = True
            elif j == 'client':
                for i in vcls_client:
                    d[i] = True
            elif j == 'both':
                for i in vcls_client:
                    d[i] = True
                for i in vcls_backend:
                    d[i] = True
            else:
                if not j in vcls:
                    print("JJ", j)
                assert j in vcls
                d[j] = True
        p = ""
        l = list(d.keys())
        l.sort()
        w = 0
        fo.write("\t\t")
        for j in l:
            x = p + "VCL_MET_" + j.upper()
            if w + len(x) > 60:
                fo.write("\n\t\t")
                w = 0
            fo.write(x)
            w += len(x)
            p = " | "
        if not d:
            fo.write("0")

    #######################################################################

    with open(os.path.join(buildroot, "include/vrt_obj.h"), "w", encoding="utf8") as fh:
        with open(os.path.join(buildroot, "lib/libvcc/vcc_obj.c"), "w", encoding="utf8") as fo:
            file_header(fh)
            file_header(fo)

            fo.write('\n#include "config.h"\n\n#include "vcc_compile.h"\n')
            fo.write('\nvoid\nvcc_Var_Init(struct vcc *tl)\n{\n\tstruct symbol *sym;\n')

            var_aliases = []
            parse_var_doc(os.path.join(srcroot, "doc/sphinx/reference/vcl_var.rst"))
            for al in var_aliases:
                fo.write(al)
            fo.write("}\n")

            for i in stv_variables:
                fh.write("VCL_" + i[1] + " VRT_stevedore_" + i[0] + "(VCL_STEVEDORE);\n")

            fo.write("\n/* VCL type identifiers */\n")

            for vn in sorted(vcltypes.keys()):
                v = vcltypes[vn]
                if v.internal:
                    continue
                fo.write("const struct vrt_type VCL_TYPE_%s[1] = { {\n" % v.name)
                fo.write("\t.magic = VRT_TYPE_MAGIC,\n")
                fo.write('\t.lname = "%s",\n' % v.name.lower())
                fo.write('\t.uname = "%s",\n' % v.name)
                fo.write('\t.ctype = "%s",\n' % v.c)
                if v.c != "void":
                    fo.write('\t.szof = sizeof(VCL_%s),\n' % v.name)
                fo.write("}};\n")

    #######################################################################

    with open(os.path.join(buildroot, "lib/libvcc/vcc_fixed_token.c"), "w", encoding="utf8") as fo:

        file_header(fo)
        fo.write('\n#include "config.h"\n\n#include "vcc_compile.h"\n')

        tokens.emit_vcl_fixed_token(fo)
        tokens.emit_vcl_tnames(fo)

        fo.write('\nvoid\nvcl_output_lang_h(struct vsb *sb)\n{\n')

        emit_c_file(fo, srcroot, "include/vdef.h")
        emit_c_file(fo, srcroot, "include/vrt.h")
        emit_c_file(fo, buildroot, "include/vcl.h")
        emit_c_file(fo, buildroot, "include/vrt_obj.h")
        emit_c_file(fo, srcroot, "include/vcc_interface.h")
        emit_strings(
            fo,
            "vgc asserts (generate.py)",
            '#define assert(e) 								\\',
            '	do {									\\',
    	    '		if (!(e)) {							\\',
            '	        	VPI_Fail(__func__, __FILE__, __LINE__, #e);		\\',
    	    '		}								\\',
            '	} while (0)',
            '',
        )

        fo.write("\n}\n")

    #######################################################################

    with open(os.path.join(buildroot, "lib/libvcc/vcc_types.h"), "w", encoding="utf8") as ft:
        file_header(ft)

        lint_start(ft)

        for vcltype in sorted(vcltypes.keys()):
            ft.write("VCC_TYPE(" + vcltype + ", " + vcltype.lower() +")\n")
        ft.write("#undef VCC_TYPE\n")
        lint_end(ft)

    #######################################################################

    with open(os.path.join(buildroot, "include/tbl/vrt_stv_var.h"), "w", encoding="utf8") as fo:

        file_header(fo)
        lint_start(fo)

        for i in stv_variables:
            ct = vcltypes[i[1]]
            fo.write("VRTSTVVAR(" + i[0] + ",\t" + i[1] + ",\t")
            fo.write(ct.c + ",\t" + i[2] + ")")
            fo.write("\n")

        fo.write("#undef VRTSTVVAR\n")
        lint_end(fo)

if __name__ == "__main__":
    main()
