antlr | antlr tree visitor | Cell 7 | Search

The GenericVisitor class contains methods for extracting specific information from the abstract syntax tree (AST) of a programming language, such as identifiers, properties, types, and binary expressions, but three of the methods are currently empty and unused.

Cell 6


// TODO: move this in to CPP visitor above is our GenericVisitor
GenericVisitor.prototype.visitIdexpression = function (ctx) {
//    var id = getGenericContext.apply(this, [ctx])
    return {
        type: 'Identifier',
        name: this.visitChildren(ctx).flat().pop()
    }
}
GenericVisitor.prototype.visitMemberdeclaration = function (ctx) {
    var obj = getGenericContext.apply(this, [ctx])
    return {
        type: 'Property',
        kind: obj.declspecifierseq.accept(this).flat(4).pop(), // get, set, init
        key: obj.memberdeclaratorlist.accept(this).flat(6).pop(),
    //    computed: boolean,
        value: null, // TODO: default value
        method: false,
    //    shorthand: boolean,
    }
}
GenericVisitor.prototype.visitTrailingtypespecifier = function (ctx) {
    return {
        type: 'Type',
        name: this.visitChildren(ctx).flat(4).pop()
    }
}
GenericVisitor.prototype.visitTypespecifier = function (ctx) {
    return this.visitChildren(ctx).flat().pop()
}
GenericVisitor.prototype.visitEqualityexpression = function (ctx) {
    var obj = getGenericContext.apply(this, [ctx])
    if(!obj.NotEqual && !obj.Equal) return this.visitChildren(ctx)
    return {
        type: 'BinaryExpression',
        operator: (obj.NotEqual || obj.Equal).accept(this),
        left: obj.equalityexpression.accept(this),
        right: obj.relationalexpression.accept(this),
    }
}
GenericVisitor.prototype.visitClassspecifier = function (ctx) {
    var obj = getGenericContext.apply(this, [ctx])
    return {
        type: 'ClassExpression',
        id: null,
        superClass: obj.classhead.accept(this).flat(3).pop(),
        body: obj.memberspecification.accept(this),
    }
}
GenericVisitor.prototype.visitPtrdeclarator = function (ctx) {
    var obj = getGenericContext.apply(this, [ctx])
    return 
}
GenericVisitor.prototype.visitSimpledeclaration = function (ctx) {
    var obj = getGenericContext.apply(this, [ctx])
}
GenericVisitor.prototype.visitFunctiondefinition = function (ctx) {
    var obj = getGenericContext.apply(this, [ctx])
    var kind = getGenericContext.apply(this, [obj.declspecifierseq])
    console.log(kind)
    var declarator = obj.declarator.accept(this)
    return {
        type: 'Function',
    //    id: ,
    //    params: ,
        body: obj.functionbody.accept(this),
        kind: ,
    //    generator: boolean,
    //    async: boolean,
    //    expression: boolean,
        static: kind[0].flat(3).pop() === 'static',
    //    returnType: returnType ? returnType.accept(this) : returnType,
    //    void: ctx.declspecifierseq().accept(this) === 'void',
    }
}
GenericVisitor.prototype.visitParameterdeclaration = function (ctx) {

}
GenericVisitor.prototype.visitDeclspecifierseq = function (ctx) {

}
GenericVisitor.prototype.visit = function (ctx) {
    console.log(Object.getPrototypeOf(ctx))
    var visit = Object.getPrototypeOf(ctx).constructor.name
        .replace(/Context$/, '')
    if(typeof this['visit' + visit] === 'function') {
        return this['visit' + visit](ctx)
    }
    var obj = getGenericContext.apply(this, [ctx])
    return Object.keys(obj).reduce((ret, cur) => {
        ret[cur] = obj ? obj.accept(this) : obj
    }, {})
}
GenericVisitor.prototype.visitTranslationunit = function (ctx) {
    var obj = getGenericContext.apply(this, [ctx])
    return {
        type: 'Program',
        body: obj.declarationseq.accept(this)
    }
}
GenericVisitor.prototype.visitDeclarationseq = function (ctx) {
    var obj = getGenericContext.apply(this, [ctx])
    if(!obj.declarationseq) return obj.declaration.accept(this)
    return [...obj.declarationseq.accept(this), ...obj.declaration.accept(this)]
}
GenericVisitor.prototype.visitFunctiondefinition = function (ctx) {
    var obj = getGenericContext.apply(this, [ctx])
    var decl = getGenericContext.apply(this, [obj.declarator])
    var ptr = getGenericContext.apply(this, [decl.ptrdeclarator])
    var noptr = getGenericContext.apply(this, [ptr.noptrdeclarator])
    return {
        type: 'Function',
        id: getGenericContext.apply(this, [noptr.noptrdeclarator]).declaratorid.accept(this),
        params: noptr.parametersandqualifiers.accept(this),
        body: obj.functionbody.accept(this),
    //    returnType: returnType ? returnType.accept(this) : returnType,
    //    void: ctx.declspecifierseq().accept(this) === 'void',
    }
}
GenericVisitor.prototype.visitDeclaratorid = function (ctx) {
    return getGenericContext.apply(this, [ctx]).idexpression.accept(this)
}

GenericVisitor.prototype.visitFunctionbody = function (ctx) {
    return getGenericContext.apply(this, [ctx]).compoundstatement.accept(this)
}
GenericVisitor.prototype.visitCompoundstatement = function (ctx) {
    return {
        type: 'BlockStatement',
        body: getGenericContext.apply(this, [ctx]).statementseq.accept(this)
    }
}
GenericVisitor.prototype.visitStatementseq = function (ctx) {
    var obj = getGenericContext.apply(this, [ctx])
    if(!obj.statementseq) return obj.statement.accept(this)
    return [...obj.statementseq.accept(this), ...obj.statement.accept(this)]
}
GenericVisitor.prototype.visitStatement = function (ctx) {
    return this.visitChildren(ctx)
}
GenericVisitor.prototype.visitExpressionstatement = function (ctx) {
    var obj = getGenericContext.apply(this, [ctx])
    return obj.expression.accept(this)
}
GenericVisitor.prototype.visitExpression = function (ctx) {
    var obj = getGenericContext.apply(this, [ctx])
    return this.visitChildren(ctx)[0]
}
GenericVisitor.prototype.visitAssignmentexpression = function (ctx) {
//    var obj = getGenericContext.apply(this, [ctx])
    return {
        type: 'AssignmentExpression',
        operator: '=' | '*=' | '**=' | '/=' | '%=' | '+=' | '-=' |
            '<<=' | '>>=' | '>>>=' | '&=' | '^=' | '|=',
        left: 0,
        right: this.visitChildren(ctx)[0],
    }
}
GenericVisitor.prototype.visitConditionalexpression = function (ctx) {
//    var obj = getGenericContext.apply(this, [ctx])
    return {
        type: 'ConditionalExpression',
        test: this.visitChildren(ctx)[0],
        consequent: 0,
        alternate: 0,
    }
}
GenericVisitor.prototype.visitInclusiveorexpression = function (ctx) {
//    var obj = getGenericContext.apply(this, [ctx])
    return {
        type: 'LogicalExpression',
        operator: '||',
        left: this.visitChildren(ctx),
        right: 0,
    }
}
GenericVisitor.prototype.visitExclusiveorexpression = function (ctx) {
    return {
        type: 'LogicalExpression',
        operator: '||',
        left: this.visitChildren(ctx),
        right: 0,
    }
}
GenericVisitor.prototype.visitRelationalexpression = function (ctx) {
    return this.visitChildren(ctx)[0]
}
GenericVisitor.prototype.visitAdditiveexpression = function (ctx) {
    return this.visitChildren(ctx)[0]
}
GenericVisitor.prototype.visitDeclarator = function (ctx) {
    var params = ctx.parametersandqualifiers()
    if(params) return params.accept(this)
    return {
        type: 'Identifier',
        name: getGenericToken(parser, ctx._start).value,
        body: this.visitChildren(ctx)
    }
}
GenericVisitor.prototype.visitDeclspecifierseq = function (ctx) {
//    console.log(ctx.attributespecifierseq())
//    console.log(ctx.declspecifierseq())
//    console.log(ctx.declspecifier())
    var seq = ctx.declspecifierseq()
    if(!seq) return ctx.declspecifier().accept(this)
    return [ctx.declspecifier().accept(this), seq.accept(this)]
}
GenericVisitor.prototype.visitDeclspecifier = function (ctx) {
    return {
        storage: ctx.storageclassspecifier(),
        type: ctx.typespecifier(),
        function: ctx.functionspecifier(),
        typedef: getGenericToken(ctx.Typedef()),
        friend: getGenericToken(ctx.Friend()),
        const: getGenericToken(ctx.Constexpr())
    }
}
GenericVisitor.prototype.visitPseudodestructorname = function (ctx) {
    //console.log('hit')
    return {
        id: ctx.decltypespecifier().accept(this)
    }
}
GenericVisitor.prototype.visitSimpledeclaration = function (ctx) {
    return {
        type: 'Declarator',
        value: getGenericToken(parser, ctx._start).value,
        body: this.visitChildren(ctx)
    }
}

var types = selectDom(['//SimpletypespecifierContext'], dom)
types.forEach(t => {
    var node = selectDom(['.//TerminalNode'], t)
    assert(node.length === 1)
    t.replaceWith(selectDom('./*', treeToHtml([{
        type: 'Type',
        id: node[0].getAttribute('value')
    }])))
})

var ids = selectDom(['//IdexpressionContext'], dom)
ids.forEach(i => {
    var node = selectDom(['.//TerminalNode'], i)
    // unary operator parses incorrectly
    var ctx = selectDom(['.//ClassnameContext'], i)
    if(ctx.length > 0 && node[0].getAttribute('value') === '~') {
        i.replaceWith(selectDom('./*', treeToHtml([{
            type: 'UnaryExpression',
            operator: '~',
            argument: {
                type: 'Identifier',
                name: node[1].getAttribute('value')
            },
            prefix: true
        }])))
    } else {
        assert(node.length === 1)
        i.replaceWith(selectDom('./*', treeToHtml([{
            type: 'Identifier',
            name: node[0].getAttribute('value')
        }])))
    }
})

// TODO: use transpile to get even more succinct
selectDom([
    '//MemberdeclarationContext',
    replace(ctx => selectDom('./*', treeToHtml([{
        type: 'Property',
        kind: htmlToTree(selectDom('.//Type', ctx)),
        key: htmlToTree(selectDom('.//Identifier', ctx)),
        method: false,
        computed: false,
        value: null,
        shorthand: false,
    }])))
], dom)

selectDom([
    // TODO: use parent:: method instead especially since it supports it
    '//ClassspecifierContext',
    replace(ctx => selectDom('./*', treeToHtml([{
        type: 'ClassExpression',
        id: null,
        superClass: selectDom('.//ClasskeyContext//@value', ctx),
        body: {
            type: 'ClassBody',
            body: htmlToTree(selectDom('.//Property', ctx))
        }
    }])))
], dom)

selectDom([
    '//DeclspecifierseqContext[not(.//DeclspecifierseqContext)]',
    replace(ctx => {
        var result = selectDom([
            `.//Type[not(.//parent::Identifier)]
            |.//Identifier[not(.//parent::ClassExpression)]
            |.//ClassExpression`
        ], ctx)
        console.log(result)
        return result
    })
], dom)

selectDom([
    '//SimpledeclarationContext',
    replace(ctx => selectDom('./*', treeToHtml([{
        type: 'VariableDeclaration',
        declarations: [{
            type: 'VariableDeclarator',
            id: htmlToTree(selectDom('.//Identifier', ctx)),
            init: htmlToTree(selectDom('.//ClassExpression', ctx)),
            kind: htmlToTree(selectDom('.//Type', ctx)),
            static: selectDom('.//DeclspecifierContext//@value', ctx) === 'static'
        }],
        kind: selectDom('.//CvqualifierContext//@value', ctx) === 'const'
            ? 'const'
            : selectDom('.//DeclspecifierContext//@value', ctx) === 'typedef'
                ? 'typedef'
                : 'let'
    }])))
], dom)

What the code could have been:

class GenericVisitor {
    visitIdexpression(ctx) {
        return {
            type: 'Identifier',
            name: this.visitChildren(ctx).flat().pop()
        }
    }

    visitMemberdeclaration(ctx) {
        const obj = this.getGenericContext(ctx);
        const [kind, key] = this.visitChildren(ctx);
        return {
            type: 'Property',
            kind: kind.flat(4).pop(),
            key,
            method: false,
            computed: false,
            value: null,
            shorthand: false,
        }
    }

    visitTrailingtypespecifier(ctx) {
        return {
            type: 'Type',
            name: this.visitChildren(ctx).flat(4).pop()
        }
    }

    visitTypespecifier(ctx) {
        return this.visitChildren(ctx).flat().pop()
    }

    visitEqualityexpression(ctx) {
        const obj = this.getGenericContext(ctx);
        if (!obj.NotEqual &&!obj.Equal) return this.visitChildren(ctx);
        return {
            type: 'BinaryExpression',
            operator: obj.NotEqual? obj.NotEqual.accept(this) : obj.Equal.accept(this),
            left: obj.equalityexpression.accept(this),
            right: obj.relationalexpression.accept(this),
        }
    }

    visitClassspecifier(ctx) {
        const obj = this.getGenericContext(ctx);
        const superClass = obj.classhead.accept(this).flat(3).pop();
        return {
            type: 'ClassExpression',
            id: null,
            superClass,
            body: obj.memberspecification.accept(this),
        }
    }

    visitPtrdeclarator(ctx) {
        // TODO: implement
        return null;
    }

    visitSimpledeclaration(ctx) {
        // TODO: implement
    }

    visitFunctiondefinition(ctx) {
        const obj = this.getGenericContext(ctx);
        const kind = this.getGenericContext(obj.declspecifierseq);
        const declarator = obj.declarator.accept(this);
        return {
            type: 'Function',
            id: declarator,
            params: obj.parametersandqualifiers.accept(this),
            body: obj.functionbody.accept(this),
            kind: kind.flat(3).pop(),
            static: kind[0].flat(3).pop() ==='static',
            generator: false,
            async: false,
            expression: false,
        }
    }

    visitParameterdeclaration(ctx) {
        // TODO: implement
    }

    visitDeclspecifierseq(ctx) {
        // TODO: implement
    }

    visitDeclspecifier(ctx) {
        return {
            storage: ctx.storageclassspecifier(),
            type: ctx.typespecifier(),
            function: ctx.functionspecifier(),
            typedef: ctx.Typedef(),
            friend: ctx.Friend(),
            const: ctx.Constexpr(),
        }
    }

    visitPseudodestructorname(ctx) {
        return {
            id: ctx.decltypespecifier().accept(this),
        }
    }

    visitSimpledeclaration(ctx) {
        return {
            type: 'Declarator',
            value: this.getGenericToken(ctx._start).value,
            body: this.visitChildren(ctx),
        }
    }

    visit() {
        const visit = Object.getPrototypeOf(this).constructor.name
           .replace(/Context$/, '');
        return this['visit' + visit](...arguments);
    }

    visitTranslationunit(ctx) {
        const obj = this.getGenericContext(ctx);
        return {
            type: 'Program',
            body: obj.declarationseq.accept(this),
        }
    }

    visitDeclarationseq(ctx) {
        const obj = this.getGenericContext(ctx);
        return obj.declarationseq? [...obj.declarationseq.accept(this),...obj.declaration.accept(this)] : obj.declaration.accept(this);
    }

    visitFunctionbody(ctx) {
        return ctx.compoundstatement.accept(this);
    }

    visitCompoundstatement(ctx) {
        return {
            type: 'BlockStatement',
            body: ctx.statementseq.accept(this),
        }
    }

    visitStatementseq(ctx) {
        const obj = this.getGenericContext(ctx);
        return obj.statementseq? [...obj.statementseq.accept(this),...obj.statement.accept(this)] : obj.statement.accept(this);
    }

    visitStatement(ctx) {
        return this.visitChildren(ctx);
    }

    visitExpressionstatement(ctx) {
        return ctx.expression.accept(this);
    }

    visitExpression(ctx) {
        return this.visitChildren(ctx)[0];
    }

    visitAssignmentexpression(ctx) {
        return {
            type: 'AssignmentExpression',
            operator: ctx.op[0].text,
            left: 0,
            right: this.visitChildren(ctx)[0],
        }
    }

    visitConditionalexpression(ctx) {
        return {
            type: 'ConditionalExpression',
            test: this.visitChildren(ctx)[0],
            consequent: 0,
            alternate: 0,
        }
    }

    visitInclusiveorexpression(ctx) {
        return {
            type: 'LogicalExpression',
            operator: '||',
            left: this.visitChildren(ctx),
            right: 0,
        }
    }

    visitExclusiveorexpression(ctx) {
        return {
            type: 'LogicalExpression',
            operator: '||',
            left: this.visitChildren(ctx),
            right: 0,
        }
    }

    visitRelationalexpression(ctx) {
        return this.visitChildren(ctx)[0];
    }

    visitAdditiveexpression(ctx) {
        return this.visitChildren(ctx)[0];
    }

    visitDeclarator(ctx) {
        const params = ctx.parametersandqualifiers();
        return params? params.accept(this) : {
            type: 'Identifier',
            name: this.getGenericToken(ctx._start).value,
            body: this.visitChildren(ctx),
        }
    }

    visitDeclspecifier(ctx) {
        return {
            storage: ctx.storageclassspecifier(),
            type: ctx.typespecifier(),
            function: ctx.functionspecifier(),
            typedef: ctx.Typedef(),
            friend: ctx.Friend(),
            const: ctx.Constexpr(),
        }
    }

    getGenericContext(ctx) {
        return ctx;
    }

    getGenericToken(ctx, tokenName) {
        return ctx[tokenName];
    }

    visitChildren(ctx) {
        return this.visit(ctx);
    }
}

//...

const selectDom = (selectors, dom) => {
    //...
};

const replace = (callback, ctx) => {
    //...
};

const treeToHtml = (ast) => {
    //...
};

const htmlToTree = (html) => {
    //...
};

Overview

This code defines a set of methods for a class GenericVisitor that appears to be used for parsing and analyzing a programming language, likely C++. The methods are designed to extract specific information from the abstract syntax tree (AST) of a program.

Method Breakdown

  1. visitIdexpression:
  2. visitMemberdeclaration:
  3. visitTrailingtypespecifier:
  4. visitTypespecifier:
  5. visitEqualityexpression:
  6. visitClassspecifier:
  7. visitPtrdeclarator:
  8. visitSimpledeclaration:
  9. visitFunctiondefinition:

Unused Methods

Methods visitPtrdeclarator, visitSimpledeclaration, and visitFunctiondefinition are currently empty and do not extract any information from the AST. These methods may have been left incomplete or are not currently being used in the code.