KennethanCeyer/metric-parser

View on GitHub
src/token/token.analyzer.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { TokenHelper } from './token.helper';
import { Token } from './token';
import { AbstractSyntaxTree } from '../ast/ast';
import { TokenEnumerable } from './token.enumerable';
import { ParserError } from '../error';
import { TokenError } from './token.error';
import { TokenValidator } from './token.validator';
import { GeneralError } from '../error.value';
import { AbstractSyntaxTreeValidator } from '../ast/ast.validator';

export class TokenAnalyzer extends TokenEnumerable {
    private ast: AbstractSyntaxTree;
    private currentTree: AbstractSyntaxTree;

    constructor(token: Token.Token[]) {
        super(token);
    }

    public parse(): AbstractSyntaxTree {
        this.try(() => this.preValidate());
        this.initialize();
        this.try(() => this.makeAst());
        this.try(() => this.postValidate());
        return this.ast;
    }

    private initialize() {
        this.ast = new AbstractSyntaxTree(Token.literal.BracketOpen);
        this.ast.leftNode = new AbstractSyntaxTree();
        this.currentTree = this.ast.leftNode;
        this.rewind();
    }

    public getAst(): AbstractSyntaxTree {
        return this.ast;
    }

    private makeAst() {
        let token: Token.Token;

        while (token = this.next()) {
            this.try(() => this.doAnalyzeToken(token));
        }
        this.finalizeStack();
        this.ast = this.ast.removeRootBracket().findRoot();
    }

    private try<T>(tryFunction: Function): T {
        try {
            return tryFunction();
        } catch (error) {
            this.handleError(error);
        }
    }

    private preValidate() {
        if (!this.token || !this.token.length)
            throw new ParserError(TokenError.emptyToken);
    }

    private postValidate() {
        const error = AbstractSyntaxTreeValidator.validate(this.ast);

        if (error)
            throw error;
    }

    private handleError(error: ParserError) {
        if (error instanceof  ParserError)
            throw error.withStack(this.stack);

        throw new ParserError(GeneralError.unknownError).withStack(this.stack);
    }

    private doAnalyzeToken(token: Token.Token) {
        this.analyzeToken(token);
        this.addStack(token);
    }

    private analyzeToken(token: Token.Token) {
        const lastToken = this.popStack();
        if (TokenHelper.isBracket(token)) {
            this.analyzeBracketToken(token);
            return;
        }

        if (TokenHelper.isOperator(token)) {
            this.analyzeOperatorToken(token);
            return;
        }

        const error = TokenValidator.validateValueToken(token, lastToken);
        if (error)
            throw error;

        this.currentTree.insertNode(token);
    }

    private analyzeBracketToken(token: Token.Token): void {
        if (TokenHelper.isBracketOpen(token)) {
            this.analyzeImplicitToken();
            this.currentTree = this.currentTree.insertNode(token);
            return;
        }

        if (TokenHelper.isBracketClose(token)) {
            this.currentTree = this.currentTree.removeClosestBracket();
            this.ast = this.currentTree.findRoot();
            return;
        }
    }

    private analyzeOperatorToken(token: Token.Token): void {
        const lastToken = this.popStack();

        if (TokenHelper.isOperator(lastToken))
            throw new ParserError(TokenError.invalidTwoOperator, lastToken, token);

        if (!this.currentTree.value) {
            this.currentTree.value = token;
            return;
        }

        const error = AbstractSyntaxTreeValidator.validateInvalidTwoOperator(this.currentTree, token, lastToken);
        if (error)
            throw error;

        this.currentTree = this.currentTree.insertNode(token);
        this.ast = this.ast.findRoot();
    }

    private analyzeImplicitToken() {
        const lastToken = this.popStack();
        if (lastToken && !TokenHelper.isSymbol(lastToken) || TokenHelper.isBracketClose(lastToken))
            this.insertImplicitMultiplication();
    }

    private insertImplicitMultiplication() {
        this.analyzeToken(Token.literal.Multiplication);
        this.addStack(Token.literal.Multiplication);
    }
}