nl.lxtreme.libtdl.grammar.basic.BasicTdlSemanticAnalyzer.java Source code

Java tutorial

Introduction

Here is the source code for nl.lxtreme.libtdl.grammar.basic.BasicTdlSemanticAnalyzer.java

Source

/*
 * LibTDL - Library for parsing/handling the "Trigger Definition Language".
 *
 * (C) Copyright 2012-2013 - J.W. Janssen, <j.w.janssen@lxtreme.nl>
 *
 * Licensed under Apache Software License version 2.0, see <http://www.apache.org/licenses/LICENSE-2.0.html>.
 */
package nl.lxtreme.libtdl.grammar.basic;

import static nl.lxtreme.libtdl.grammar.Util.*;

import java.util.*;

import nl.lxtreme.libtdl.*;
import nl.lxtreme.libtdl.ProblemReporter.Category;
import nl.lxtreme.libtdl.ProblemReporter.Marker;
import nl.lxtreme.libtdl.ProblemReporter.MarkerBuilder;
import nl.lxtreme.libtdl.ProblemReporter.Type;
import nl.lxtreme.libtdl.grammar.*;
import nl.lxtreme.libtdl.grammar.basic.BasicTdlParser.ActiveClauseContext;
import nl.lxtreme.libtdl.grammar.basic.BasicTdlParser.ExprContext;
import nl.lxtreme.libtdl.grammar.basic.BasicTdlParser.StageDefContext;
import nl.lxtreme.libtdl.grammar.basic.BasicTdlParser.TermDeclContext;
import nl.lxtreme.libtdl.grammar.basic.BasicTdlParser.TermExprContext;
import nl.lxtreme.libtdl.grammar.basic.BasicTdlParser.WhenActionContext;

import org.antlr.v4.runtime.*;

/**
 * Verifies whether a parse tree produced by {@link BasicTdlParser} is
 * semantically valid.
 */
public class BasicTdlSemanticAnalyzer extends BasicTdlBaseVisitor<BasicTdlSemanticAnalyzer>
        implements SemanticAnalyzer<BasicTdlSemanticAnalyzer> {
    // CONSTANTS

    private static final long MAX_INT_VALUE = (1L << 32) - 1L;
    private static final long MAX_DELAY_VALUE = 0xFFFFL;

    // VARIABLES

    private final TdlConfig m_config;
    private final List<TermDefinitionListener> m_listeners;
    private final Map<String, Term> m_declarations;
    private final Map<Integer, Boolean> m_stages;
    private final ProblemReporter m_problemReporter;

    // CONSTRUCTORS

    /**
     * Creates a new {@link BasicTdlSemanticAnalyzer} instance.
     * 
     * @param config
     *            the configuration to use, cannot be <code>null</code>;
     * @param problemReporter
     *            the problem reporter to report any validation problems to,
     *            cannot be <code>null</code>.
     */
    public BasicTdlSemanticAnalyzer(TdlConfig config, ProblemReporter problemReporter) {
        if (config == null) {
            throw new IllegalArgumentException("Config cannot be null!");
        }
        m_config = config;

        if (problemReporter == null) {
            throw new IllegalArgumentException("Problem reporter cannot be null!");
        }
        m_problemReporter = problemReporter;

        m_listeners = new ArrayList<TermDefinitionListener>();

        m_declarations = new HashMap<String, Term>();
        m_stages = new HashMap<Integer, Boolean>();
    }

    // METHODS

    @Override
    public void addTermDefinitionListener(TermDefinitionListener listener) {
        if (!m_listeners.contains(listener)) {
            m_listeners.add(listener);
        }
    }

    @Override
    public void removeTermDefinitionListener(TermDefinitionListener listener) {
        m_listeners.remove(listener);
    }

    @Override
    public void reset() {
        m_declarations.clear();
        m_stages.clear();
    }

    @Override
    public BasicTdlSemanticAnalyzer visitActiveClause(ActiveClauseContext ctx) {
        if (ctx.n != null) {
            validateValue(ctx.n, 1, m_config.getMaxStages(), "invalid level ID");
        }
        return super.visitActiveClause(ctx);
    }

    @Override
    public BasicTdlSemanticAnalyzer visitExpr(ExprContext ctx) {
        if (ctx.term != null) {
            Token term = ctx.term;
            String name = normalizeName(term.getText());
            if (!m_declarations.containsKey(name)) {
                String msg = name + " is not declared";

                Marker marker = new MarkerBuilder().setCategory(Category.SEMANTIC).setType(Type.ERROR) //
                        .setLocation(ctx).setDescription(msg).build();

                m_problemReporter.report(marker);
            }
        }
        return super.visitExpr(ctx);
    }

    @Override
    public BasicTdlSemanticAnalyzer visitStageDef(StageDefContext ctx) {
        Long stageId = validateValue(ctx.n, 1, m_config.getMaxStages(), "invalid stage ID");
        if (stageId != null) {
            define(ctx, stageId.intValue());
        }
        return super.visitStageDef(ctx);
    }

    @Override
    public BasicTdlSemanticAnalyzer visitTermDecl(TermDeclContext ctx) {
        Long mask = validateValue(ctx.mask, 0, MAX_INT_VALUE, "invalid mask");
        Long value = validateValue(ctx.value, 0, MAX_INT_VALUE, "invalid value");

        if ((mask != null) && (value != null)) {
            declare(ctx, new Term(ctx.name.getText(), value.longValue(), mask.longValue()));
        }

        return super.visitTermDecl(ctx);
    }

    @Override
    public BasicTdlSemanticAnalyzer visitTermExpr(TermExprContext ctx) {
        if ((ctx.AT() != null) && (ctx.n != null)) {
            validateValue(ctx.n, 0, m_config.getMaxChannels() - 1, "invalid channel: " + ctx.n.getText());
        }
        return super.visitTermExpr(ctx);
    }

    @Override
    public BasicTdlSemanticAnalyzer visitWhenAction(WhenActionContext ctx) {
        if (ctx.n != null) {
            validateValue(ctx.n, 1, MAX_DELAY_VALUE, "invalid delay value");
        }
        return super.visitWhenAction(ctx);
    }

    private void declare(ParserRuleContext context, Term term) {
        if (m_declarations.put(term.getName(), term) != null) {
            String msg = "term " + term.getName() + " already declared";

            Marker marker = new MarkerBuilder().setCategory(Category.SEMANTIC).setType(Type.ERROR) //
                    .setLocation(context).setDescription(msg).build();

            m_problemReporter.report(marker);
        } else {
            for (TermDefinitionListener listener : m_listeners) {
                listener.termDeclared(term.getName(), term.toString());
            }
        }
    }

    private void define(ParserRuleContext context, int stageNo) {
        Integer stage = Integer.valueOf(stageNo);
        if (m_stages.put(stage, Boolean.TRUE) != null) {
            String msg = "stage " + stageNo + " already defined";

            Marker marker = new MarkerBuilder().setCategory(Category.SEMANTIC).setType(Type.ERROR) //
                    .setLocation(context).setDescription(msg).build();

            m_problemReporter.report(marker);
        }
    }

    private Long validateValue(ParserRuleContext ctx, long lower, long upper, String msg) {
        return Util.validateValue(ctx, lower, upper, msg, m_problemReporter);
    }
}