<commit_before>import io/[File, FileWriter]
import structs/[Bag, HashBag]
import text/json/Generator
import text/Buffer

import ../../frontend/BuildParams

import ../../middle/[Visitor]

import ../../middle/[Module, FunctionDecl, FunctionCall, Expression, Type,
    BinaryOp, IntLiteral, CharLiteral, StringLiteral, RangeLiteral,
    VariableDecl, If, Else, While, Foreach, Conditional, ControlStatement,
    VariableAccess, Include, Import, Use, TypeDecl, ClassDecl, CoverDecl,
    Node, Parenthesis, Return, Cast, Comparison, Ternary, BoolLiteral,
    Argument, Statement, AddressOf, Dereference, FuncType, BaseType, PropertyDecl,
    EnumDecl, OperatorDecl, InterfaceDecl, InterfaceImpl]

JSONGenerator: class extends Visitor {

    params: BuildParams
    outFile: File
    module: Module
    objects, root: HashBag

    init: func (=params, =module) {
        outFile = File new(params outPath getPath() + File separator + module getSourceFolderName(), module getPath(".json"))
        outFile parent() mkdirs()
        root = HashBag new()
        objects = HashBag new()
        /* build the structure! */
        root put("entities", objects)
        root put("name", module getPath())

        globalImports := Bag new()
        for(imp in module getGlobalImports())
            globalImports add(imp getModule() getPath())
        root put("globalImports", globalImports)

        namespacedImports := HashBag new()
        for(ns in module namespaces) {
            modules := Bag new()
            for(imp in ns getImports()) {
                modules add(imp getModule() getPath())
            }
            namespacedImports put(ns getName(), modules)
        }
        root put("namespacedImports", namespacedImports)

        uses := Bag new()
        for(uze in module getUses()) {
            uses add(uze identifier)
        }
        root put("uses", uses)
    }

    write: func {
        visitModule(module)
    }

    resolveType: func (type: Type) -> String {
        if(type instanceOf(FuncType)) {
            return "Func" /* TODO? */
        } else if(type instanceOf(PointerType)) {
            return "pointer(%s)" format(resolveType(type as PointerType inner))
        } else if(type instanceOf(ReferenceType)) {
            return "reference(%s)" format(resolveType(type as ReferenceType inner))
        } else {
            /* base type */
            return type as BaseType name /* TODO? */
        }
    }

    /** generate now, actually. */
    close: func {
        /* don't beautify, don't indent. */
        writer := FileWriter new(outFile)
        generate(writer, root)
        writer close()
    }

    visitClassDecl: func (node: ClassDecl) {
        if(node isMeta)
            return
        obj := HashBag new()
        /* `name` */
        obj put("name", node name as String)
        /* `type` */
        obj put("type", "class")
        /* `abstract` */
        obj put("abstract", node isAbstract)
        /* `final` */
        obj put("final", node isFinal)
         /* `fullName` */
        obj put("fullName", node underName())
        /* `tag` */
        obj put("tag", node name as String)
        /* `doc` */
        obj put("doc", node doc)
        /* `extends` */
        if(node getSuperRef() != null) {
            obj put("extends", node getSuperRef() name as String)
        } else {
            obj put("extends", null)
        }
        /* generic types */
        genericTypes := Bag new()
        for(typeArg in node typeArgs) {
            genericTypes add(typeArg name as String)
        }
        obj put("genericTypes", genericTypes)
        /* `members` */
        members := Bag new()
        /* methods */
        for(function in node meta functions) {
            member := Bag new()
            member add(function name) .add(buildFunctionDecl(function, "method"))
            members add(member)
        }
        /* variables */
        for(variable in node variables) {
            member := Bag new()
            member add(variable name) .add(buildVariableDecl(variable, "field"))
            members add(member)
        }
        /* static variables */
        for(variable in node meta variables) {
            member := Bag new()
            member add(variable name) .add(buildVariableDecl(variable, "field"))
            members add(member)
        }
        obj put("members", members)
        objects put(node name, obj)
        for(idecl in node getInterfaceDecls())
            visitInterfaceImpl(idecl)
    }

    visitCoverDecl: func (node: CoverDecl) {
        obj := HashBag new()
        /* `name` */
        obj put("name", node name as String)
        /* `type` */
        obj put("type", "cover")
        /* `doc` */
        obj put("doc", node doc)
        /* `tag` */
        obj put("tag", node name as String)
        /* `fullName` */
        obj put("fullName", node underName())
        /* `extends` */
        if(node getSuperRef() != null) {
            obj put("extends", node getSuperRef() name as String)
        } else {
            obj put("extends", null)
        }
        /* `from` */
        if(node fromType != null) {
            obj put("from", node fromType toString())
        } else {
            obj put("from", null)
        }
        /* `members` */
        members := Bag new()
        for(function in node functions) {
            member := Bag new()
            member add(function name) .add(buildFunctionDecl(function, "method"))
            members add(member)
        }
        for(variable: VariableDecl in node variables) {
            member := Bag new()
            member add(variable name) .add(buildVariableDecl(variable, "field"))
            members add(member)
        }
        obj put("members", members)
        objects put(node name, obj)
        for(idecl in node getInterfaceDecls())
            visitInterfaceImpl(idecl)
    }

    visitFunctionDecl: func (node: FunctionDecl) {
        /* add to the objects. */
        obj := buildFunctionDecl(node, "function")
        objects put(node name, obj)
    }

    buildFunctionDecl: func ~typed (node: FunctionDecl, type: String) -> HashBag {
        obj := HashBag new()
        /* `name` */
        obj put("name", node name)
        /* `doc` */
        obj put("doc", node doc)
        /* `tag` */
        if(type == "method") {
            obj put("tag", "method(%s, %s)" format(node owner name, node name))
        } else {
            obj put("tag", node name)
        }
        /* `type` */
        obj put("type", type)
        /* `extern` */
        if(node isExtern()) {
            if(!node isExternWithName())
                obj put("extern", true)
            else
                obj put("extern", node externName)
        } else {
            obj put("extern", false)
        }
        /* `unmangled` */
        if(node isUnmangled()) {
            if(!node isUnmangledWithName())
                obj put("unmangled", true)
            else
                obj put("unmangled", node getUnmangledName())
        }
        else {
            obj put("unmangled", false)
        }
        /* `fullName` */
        obj put("fullName", node getFullName())
        /* `modifiers` */
        modifiers := Bag new()
        if(node isAbstract())
            modifiers add("abstract")
        if(node isStatic())
            modifiers add("static")
        if(node isInline())
            modifiers add("inline")
        if(node isFinal())
            modifiers add("final")
        obj put("modifiers", modifiers)
        /* generic types */
        genericTypes := Bag new()
        for(typeArg in node typeArgs) {
            genericTypes add(typeArg name as String)
        }
        obj put("genericTypes", genericTypes)
        /* return type */
        if(node returnType != voidType) {
            obj put("returnType", resolveType(node getReturnType()))
        } else {
            obj put("returnType", null)
        }
        /* arguments */
        args := Bag new()
        for(arg in node args) {
            l := Bag new()
            l add(arg name as String) /* TODO: why is that needed? */
            if(arg instanceOf(VarArg))
                l add("")
            else
                l add(resolveType(arg type)) /* this handles generic types well. */
            if(arg isConst) {
                m := Bag new()
                m add("const")
                l add(m)
            } else {
                l add(null)
            }
            args add(l)
        }
        obj put("arguments", args)
        obj
    }

    visitVariableDecl: func (node: VariableDecl) {
        /* add to the objects */
        obj := buildVariableDecl(node, "globalVariable")
        objects put(node name, obj)
    }

    buildVariableDecl: func (node: VariableDecl, type: String) -> HashBag {
        obj := HashBag new()
        /* `name` */
        obj put("name", node name)
        /* `doc` */
        obj put("doc", node doc)
        /* `extern` */
        if(node isExtern()) {
            if(node externName isEmpty())
                obj put("extern", true)
            else
                obj put("extern", node externName)
        } else {
            obj put("extern", false)
        }
        /* `fullName` */
        obj put("fullName", node getFullName())
         /* `unmangled` */
        if(node isUnmangled()) {
            if(!node isUnmangledWithName())
                obj put("unmangled", true)
            else
                obj put("unmangled", node getUnmangledName())
        }
        else {
            obj put("unmangled", false)
        }
        /* `type` */
        obj put("type", type)
        /* `tag` */
        if(type == "field") {
            obj put("tag", "field(%s, %s)" format(node owner name, node name))
        } else {
            obj put("tag", node name)
        }
        /* `modifiers` */
        modifiers := Bag new()
        if(node isStatic)
            modifiers add("static")
        if(node isConst)
            modifiers add("const")
        obj put("modifiers", modifiers)
        /* `value` */
        if(node expr != null) {
            obj put("value", node expr toString())
        } else {
            obj put("value", null)
        }
        /* property data? */
        if(node instanceOf(PropertyDecl)) {
            data := HashBag new()
            pnode := node as PropertyDecl
            data put("hasGetter", pnode getter != null)
            data put("hasSetter", pnode setter != null)
            if(pnode getter != null) {
                data put("fullGetterName", pnode getter getFullName())
            } else {
                data put("fullGetterName", null)
            }
            if(pnode setter != null) {
                data put("fullSetterName", pnode setter getFullName())
            } else {
                data put("fullSetterName", null)
            }
            obj put("propertyData", data)
        } else {
            obj put("propertyData", null)
        }
        /* `varType` */
        obj put("varType", resolveType(node type))
        obj
    }

    visitEnumDecl: func (node: EnumDecl) {
        obj := HashBag new()
        /* `name` */
        obj put("name", node name)
        /* `type` */
        obj put("type", "enum")
        /* `tag` */
        obj put("tag", node name)
        /* `extern` */
        if(node isExtern()) {
            if(node externName isEmpty())
                obj put("extern", true)
            else
                obj put("extern", node externName)
        } else {
            obj put("extern", false)
        }
        /* `doc` */
        obj put("doc", node doc)
        /* `incrementOper` */
        obj put("incrementOper", String new(node incrementOper))
        /* `incrementStep` */
        obj put("incrementStep", node incrementStep)
        /* `elements` */
        elements := Bag new()
        obj put("elements", elements)
        for(var in node getMeta() getVariables()) {
            if(!var instanceOf(EnumElement))
                continue
            elem := var as EnumElement
            elemInfo := HashBag new()
            elemInfo put("name", elem name) \
                    .put("tag", "enumElement(%s, %s)" format(node name, elem name)) \
                    .put("type", "enumElement") \
                    .put("value", elem value)
            if(elem isExtern()) {
                // see `EnumDecl addElement`, elements always have an extern name if they are extern
                elemInfo put("extern", elem getExternName())
            } else {
                elemInfo put("extern", null)
            }
            /* add it, finally */
            elemBag := Bag new()
            elemBag add(elem name)
            elemBag add(elemInfo)
            elements add(elemBag)
        }
        objects put(node name, obj)
    }

    generateFuncTag: func (node: FunctionDecl, start: String) -> String {
        buf := Buffer new()
        buf append(start)
        first := true
        if(!node typeArgs isEmpty()) {
            first = false
            first_ := true
            buf append("generics(")
            for(typeArg in node typeArgs) {
                if(!first_)
                    buf append(',')
                else
                    first_ = false
                buf append(typeArg name)                
            }
            buf append(')')
        }
        if(!node args isEmpty()) {
            if(!first)
                buf append(',')
            else
                first = false
            buf append("arguments(")
            first_ := true
            for(arg in node args) {
                if(!first_)
                    buf append(',')
                else
                    first_ = false
                buf append(resolveType(arg type))
            }
            buf append(')')
        }
        if(node returnType != voidType) {
            if(!first)
                buf append(',')
            else
                first = false
            buf append("return(%s)" format(resolveType(node getReturnType())))
        }
        buf append(')')
        buf toString()
    }

    visitOperatorDecl: func (node: OperatorDecl) {
        obj := HashBag new()
        name := node getName()
        tag := generateFuncTag(node getFunctionDecl(), "operator(%s," format(name))
        obj put("symbol", node symbol) \
           .put("name", name) \
           .put("tag", tag) \
           .put("doc", "") \
           .put("type", "operator") \
           .put("function", buildFunctionDecl(node getFunctionDecl(), "function"))
        objects put(tag, obj)
    }

    visitInterfaceDecl: func (node: InterfaceDecl) {
        obj := HashBag new()
        obj put("tag", node name) .put("name", node name) .put("doc", node doc) .put("type", "interface")
        /* methods */
        members := Bag new()
        for(function in node meta functions) {
            member := Bag new()
            member add(function name) .add(buildFunctionDecl(function, "method"))
            members add(member)
        }
        obj put("members", members)
        objects put(node name, obj)
    }

    visitInterfaceImpl: func (node: InterfaceImpl) {
        obj := HashBag new()
        name := node getSuperType() getName()
        target := node impl getName()
        tag := "interfaceImpl(%s, %s)" format(name, target)
        obj put("tag", tag) \
           .put("type", "interfaceImpl") \
           .put("doc", "") \
           .put("interface", name) \
           .put("for", target)
        objects put(tag, obj)
    }

    visitType:               func (node: Type) {}

    visitModule:             func (node: Module) {
        for(function in node functions)
            function accept(this)
        for(type in node types)
            type accept(this)
        for(op in node operators) {
            visitOperatorDecl(op)
        }
        for(child in node body)
            if(child instanceOf(VariableDecl))
                child accept(this)
    }
}
<commit_msg>JSON backend: module has a path, not a name.<commit_after>import io/[File, FileWriter]
import structs/[Bag, HashBag]
import text/json/Generator
import text/Buffer

import ../../frontend/BuildParams

import ../../middle/[Visitor]

import ../../middle/[Module, FunctionDecl, FunctionCall, Expression, Type,
    BinaryOp, IntLiteral, CharLiteral, StringLiteral, RangeLiteral,
    VariableDecl, If, Else, While, Foreach, Conditional, ControlStatement,
    VariableAccess, Include, Import, Use, TypeDecl, ClassDecl, CoverDecl,
    Node, Parenthesis, Return, Cast, Comparison, Ternary, BoolLiteral,
    Argument, Statement, AddressOf, Dereference, FuncType, BaseType, PropertyDecl,
    EnumDecl, OperatorDecl, InterfaceDecl, InterfaceImpl]

JSONGenerator: class extends Visitor {

    params: BuildParams
    outFile: File
    module: Module
    objects, root: HashBag

    init: func (=params, =module) {
        outFile = File new(params outPath getPath() + File separator + module getSourceFolderName(), module getPath(".json"))
        outFile parent() mkdirs()
        root = HashBag new()
        objects = HashBag new()
        /* build the structure! */
        root put("entities", objects)
        root put("path", module getPath())

        globalImports := Bag new()
        for(imp in module getGlobalImports())
            globalImports add(imp getModule() getPath())
        root put("globalImports", globalImports)

        namespacedImports := HashBag new()
        for(ns in module namespaces) {
            modules := Bag new()
            for(imp in ns getImports()) {
                modules add(imp getModule() getPath())
            }
            namespacedImports put(ns getName(), modules)
        }
        root put("namespacedImports", namespacedImports)

        uses := Bag new()
        for(uze in module getUses()) {
            uses add(uze identifier)
        }
        root put("uses", uses)
    }

    write: func {
        visitModule(module)
    }

    resolveType: func (type: Type) -> String {
        if(type instanceOf(FuncType)) {
            return "Func" /* TODO? */
        } else if(type instanceOf(PointerType)) {
            return "pointer(%s)" format(resolveType(type as PointerType inner))
        } else if(type instanceOf(ReferenceType)) {
            return "reference(%s)" format(resolveType(type as ReferenceType inner))
        } else {
            /* base type */
            return type as BaseType name /* TODO? */
        }
    }

    /** generate now, actually. */
    close: func {
        /* don't beautify, don't indent. */
        writer := FileWriter new(outFile)
        generate(writer, root)
        writer close()
    }

    visitClassDecl: func (node: ClassDecl) {
        if(node isMeta)
            return
        obj := HashBag new()
        /* `name` */
        obj put("name", node name as String)
        /* `type` */
        obj put("type", "class")
        /* `abstract` */
        obj put("abstract", node isAbstract)
        /* `final` */
        obj put("final", node isFinal)
         /* `fullName` */
        obj put("fullName", node underName())
        /* `tag` */
        obj put("tag", node name as String)
        /* `doc` */
        obj put("doc", node doc)
        /* `extends` */
        if(node getSuperRef() != null) {
            obj put("extends", node getSuperRef() name as String)
        } else {
            obj put("extends", null)
        }
        /* generic types */
        genericTypes := Bag new()
        for(typeArg in node typeArgs) {
            genericTypes add(typeArg name as String)
        }
        obj put("genericTypes", genericTypes)
        /* `members` */
        members := Bag new()
        /* methods */
        for(function in node meta functions) {
            member := Bag new()
            member add(function name) .add(buildFunctionDecl(function, "method"))
            members add(member)
        }
        /* variables */
        for(variable in node variables) {
            member := Bag new()
            member add(variable name) .add(buildVariableDecl(variable, "field"))
            members add(member)
        }
        /* static variables */
        for(variable in node meta variables) {
            member := Bag new()
            member add(variable name) .add(buildVariableDecl(variable, "field"))
            members add(member)
        }
        obj put("members", members)
        objects put(node name, obj)
        for(idecl in node getInterfaceDecls())
            visitInterfaceImpl(idecl)
    }

    visitCoverDecl: func (node: CoverDecl) {
        obj := HashBag new()
        /* `name` */
        obj put("name", node name as String)
        /* `type` */
        obj put("type", "cover")
        /* `doc` */
        obj put("doc", node doc)
        /* `tag` */
        obj put("tag", node name as String)
        /* `fullName` */
        obj put("fullName", node underName())
        /* `extends` */
        if(node getSuperRef() != null) {
            obj put("extends", node getSuperRef() name as String)
        } else {
            obj put("extends", null)
        }
        /* `from` */
        if(node fromType != null) {
            obj put("from", node fromType toString())
        } else {
            obj put("from", null)
        }
        /* `members` */
        members := Bag new()
        for(function in node functions) {
            member := Bag new()
            member add(function name) .add(buildFunctionDecl(function, "method"))
            members add(member)
        }
        for(variable: VariableDecl in node variables) {
            member := Bag new()
            member add(variable name) .add(buildVariableDecl(variable, "field"))
            members add(member)
        }
        obj put("members", members)
        objects put(node name, obj)
        for(idecl in node getInterfaceDecls())
            visitInterfaceImpl(idecl)
    }

    visitFunctionDecl: func (node: FunctionDecl) {
        /* add to the objects. */
        obj := buildFunctionDecl(node, "function")
        objects put(node name, obj)
    }

    buildFunctionDecl: func ~typed (node: FunctionDecl, type: String) -> HashBag {
        obj := HashBag new()
        /* `name` */
        obj put("name", node name)
        /* `doc` */
        obj put("doc", node doc)
        /* `tag` */
        if(type == "method") {
            obj put("tag", "method(%s, %s)" format(node owner name, node name))
        } else {
            obj put("tag", node name)
        }
        /* `type` */
        obj put("type", type)
        /* `extern` */
        if(node isExtern()) {
            if(!node isExternWithName())
                obj put("extern", true)
            else
                obj put("extern", node externName)
        } else {
            obj put("extern", false)
        }
        /* `unmangled` */
        if(node isUnmangled()) {
            if(!node isUnmangledWithName())
                obj put("unmangled", true)
            else
                obj put("unmangled", node getUnmangledName())
        }
        else {
            obj put("unmangled", false)
        }
        /* `fullName` */
        obj put("fullName", node getFullName())
        /* `modifiers` */
        modifiers := Bag new()
        if(node isAbstract())
            modifiers add("abstract")
        if(node isStatic())
            modifiers add("static")
        if(node isInline())
            modifiers add("inline")
        if(node isFinal())
            modifiers add("final")
        obj put("modifiers", modifiers)
        /* generic types */
        genericTypes := Bag new()
        for(typeArg in node typeArgs) {
            genericTypes add(typeArg name as String)
        }
        obj put("genericTypes", genericTypes)
        /* return type */
        if(node returnType != voidType) {
            obj put("returnType", resolveType(node getReturnType()))
        } else {
            obj put("returnType", null)
        }
        /* arguments */
        args := Bag new()
        for(arg in node args) {
            l := Bag new()
            l add(arg name as String) /* TODO: why is that needed? */
            if(arg instanceOf(VarArg))
                l add("")
            else
                l add(resolveType(arg type)) /* this handles generic types well. */
            if(arg isConst) {
                m := Bag new()
                m add("const")
                l add(m)
            } else {
                l add(null)
            }
            args add(l)
        }
        obj put("arguments", args)
        obj
    }

    visitVariableDecl: func (node: VariableDecl) {
        /* add to the objects */
        obj := buildVariableDecl(node, "globalVariable")
        objects put(node name, obj)
    }

    buildVariableDecl: func (node: VariableDecl, type: String) -> HashBag {
        obj := HashBag new()
        /* `name` */
        obj put("name", node name)
        /* `doc` */
        obj put("doc", node doc)
        /* `extern` */
        if(node isExtern()) {
            if(node externName isEmpty())
                obj put("extern", true)
            else
                obj put("extern", node externName)
        } else {
            obj put("extern", false)
        }
        /* `fullName` */
        obj put("fullName", node getFullName())
         /* `unmangled` */
        if(node isUnmangled()) {
            if(!node isUnmangledWithName())
                obj put("unmangled", true)
            else
                obj put("unmangled", node getUnmangledName())
        }
        else {
            obj put("unmangled", false)
        }
        /* `type` */
        obj put("type", type)
        /* `tag` */
        if(type == "field") {
            obj put("tag", "field(%s, %s)" format(node owner name, node name))
        } else {
            obj put("tag", node name)
        }
        /* `modifiers` */
        modifiers := Bag new()
        if(node isStatic)
            modifiers add("static")
        if(node isConst)
            modifiers add("const")
        obj put("modifiers", modifiers)
        /* `value` */
        if(node expr != null) {
            obj put("value", node expr toString())
        } else {
            obj put("value", null)
        }
        /* property data? */
        if(node instanceOf(PropertyDecl)) {
            data := HashBag new()
            pnode := node as PropertyDecl
            data put("hasGetter", pnode getter != null)
            data put("hasSetter", pnode setter != null)
            if(pnode getter != null) {
                data put("fullGetterName", pnode getter getFullName())
            } else {
                data put("fullGetterName", null)
            }
            if(pnode setter != null) {
                data put("fullSetterName", pnode setter getFullName())
            } else {
                data put("fullSetterName", null)
            }
            obj put("propertyData", data)
        } else {
            obj put("propertyData", null)
        }
        /* `varType` */
        obj put("varType", resolveType(node type))
        obj
    }

    visitEnumDecl: func (node: EnumDecl) {
        obj := HashBag new()
        /* `name` */
        obj put("name", node name)
        /* `type` */
        obj put("type", "enum")
        /* `tag` */
        obj put("tag", node name)
        /* `extern` */
        if(node isExtern()) {
            if(node externName isEmpty())
                obj put("extern", true)
            else
                obj put("extern", node externName)
        } else {
            obj put("extern", false)
        }
        /* `doc` */
        obj put("doc", node doc)
        /* `incrementOper` */
        obj put("incrementOper", String new(node incrementOper))
        /* `incrementStep` */
        obj put("incrementStep", node incrementStep)
        /* `elements` */
        elements := Bag new()
        obj put("elements", elements)
        for(var in node getMeta() getVariables()) {
            if(!var instanceOf(EnumElement))
                continue
            elem := var as EnumElement
            elemInfo := HashBag new()
            elemInfo put("name", elem name) \
                    .put("tag", "enumElement(%s, %s)" format(node name, elem name)) \
                    .put("type", "enumElement") \
                    .put("value", elem value)
            if(elem isExtern()) {
                // see `EnumDecl addElement`, elements always have an extern name if they are extern
                elemInfo put("extern", elem getExternName())
            } else {
                elemInfo put("extern", null)
            }
            /* add it, finally */
            elemBag := Bag new()
            elemBag add(elem name)
            elemBag add(elemInfo)
            elements add(elemBag)
        }
        objects put(node name, obj)
    }

    generateFuncTag: func (node: FunctionDecl, start: String) -> String {
        buf := Buffer new()
        buf append(start)
        first := true
        if(!node typeArgs isEmpty()) {
            first = false
            first_ := true
            buf append("generics(")
            for(typeArg in node typeArgs) {
                if(!first_)
                    buf append(',')
                else
                    first_ = false
                buf append(typeArg name)                
            }
            buf append(')')
        }
        if(!node args isEmpty()) {
            if(!first)
                buf append(',')
            else
                first = false
            buf append("arguments(")
            first_ := true
            for(arg in node args) {
                if(!first_)
                    buf append(',')
                else
                    first_ = false
                buf append(resolveType(arg type))
            }
            buf append(')')
        }
        if(node returnType != voidType) {
            if(!first)
                buf append(',')
            else
                first = false
            buf append("return(%s)" format(resolveType(node getReturnType())))
        }
        buf append(')')
        buf toString()
    }

    visitOperatorDecl: func (node: OperatorDecl) {
        obj := HashBag new()
        name := node getName()
        tag := generateFuncTag(node getFunctionDecl(), "operator(%s," format(name))
        obj put("symbol", node symbol) \
           .put("name", name) \
           .put("tag", tag) \
           .put("doc", "") \
           .put("type", "operator") \
           .put("function", buildFunctionDecl(node getFunctionDecl(), "function"))
        objects put(tag, obj)
    }

    visitInterfaceDecl: func (node: InterfaceDecl) {
        obj := HashBag new()
        obj put("tag", node name) .put("name", node name) .put("doc", node doc) .put("type", "interface")
        /* methods */
        members := Bag new()
        for(function in node meta functions) {
            member := Bag new()
            member add(function name) .add(buildFunctionDecl(function, "method"))
            members add(member)
        }
        obj put("members", members)
        objects put(node name, obj)
    }

    visitInterfaceImpl: func (node: InterfaceImpl) {
        obj := HashBag new()
        name := node getSuperType() getName()
        target := node impl getName()
        tag := "interfaceImpl(%s, %s)" format(name, target)
        obj put("tag", tag) \
           .put("type", "interfaceImpl") \
           .put("doc", "") \
           .put("interface", name) \
           .put("for", target)
        objects put(tag, obj)
    }

    visitType:               func (node: Type) {}

    visitModule:             func (node: Module) {
        for(function in node functions)
            function accept(this)
        for(type in node types)
            type accept(this)
        for(op in node operators) {
            visitOperatorDecl(op)
        }
        for(child in node body)
            if(child instanceOf(VariableDecl))
                child accept(this)
    }
}

{'fraction_non_alphanumeric': 0.12651711335059748, 'fraction_numerical': 0.0, 'mean_word_length': 1.5806763285024155, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6635366', 'n_tokens_mistral': 8657, 'n_tokens_neox': 8444, 'n_words': 2422}