InternalMatch.java

/*
 * Copyright 2017 Michael Mackenzie High
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mackenziehigh.sexpr.internal.schema;

import com.mackenziehigh.sexpr.Schema.Match;
import com.mackenziehigh.sexpr.Sexpr;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;

/**
 *
 * @author mackenzie
 */
final class InternalMatch
        implements Match
{
    private final InternalMatchNode root;

    private final boolean success;

    private final Sexpr<?> lastSuccess;

    /**
     * These are the names of the user-defined translation passes.
     */
    private final List<String> passes;

    /**
     * This map maps the name of a translation pass (P) to a map that maps the name
     * of a rule (R) in the schema to a list of user-defined actions (A1 ... AN)
     * that will be performed for each successful match of (R) during pass (P).
     */
    private final Map<String, Map<String, List<Consumer<Sexpr<?>>>>> beforeActions;

    /**
     * This map maps the name of a translation pass (P) to a map that maps the name
     * of a rule (R) in the schema to a list of user-defined actions (A1 ... AN)
     * that will be performed for each successful match of (R) during pass (P).
     */
    private final Map<String, Map<String, List<Consumer<Sexpr<?>>>>> afterActions;

    public InternalMatch (final boolean success,
                          final InternalMatchNode root,
                          final Sexpr<?> lastSuccess,
                          final List<String> passes,
                          final Map<String, Map<String, List<Consumer<Sexpr<?>>>>> beforeActions,
                          final Map<String, Map<String, List<Consumer<Sexpr<?>>>>> afterActions)
    {
        this.success = success;
        this.root = root;
        this.lastSuccess = lastSuccess;
        this.passes = List.copyOf(passes);
        this.beforeActions = Map.copyOf(beforeActions);
        this.afterActions = Map.copyOf(afterActions);
    }

    @Override
    public boolean isSuccess ()
    {
        return success;
    }

    @Override
    public boolean isFailure ()
    {
        return !isSuccess();
    }

    @Override
    public Sexpr<?> input ()
    {
        return root.node();
    }

    @Override
    public Optional<Sexpr<?>> lastSuccess ()
    {
        return Optional.ofNullable(lastSuccess);
    }

    @Override
    public Match execute ()
    {
        executeActions(root);
        return this;
    }

    /**
     * On successful matches, this method transverses the match-tree
     * in multiple "translation passes" executing user-defined actions.
     *
     * @param tree describes a successful match attempt.
     */
    private void executeActions (final InternalMatchNode tree)
    {
        passes.forEach(pass -> executeActions(pass, tree));
    }

    private void executeActions (final String pass,
                                 final InternalMatchNode node)
    {
        if (beforeActions.containsKey(pass) && beforeActions.get(pass).containsKey(node.rule().name()))
        {
            final List<Consumer<Sexpr<?>>> actions = beforeActions.get(pass).get(node.rule().name());
            actions.forEach(action -> action.accept(node.node()));
        }

        node.children().forEach(child -> executeActions(pass, child));

        if (afterActions.containsKey(pass) && afterActions.get(pass).containsKey(node.rule().name()))
        {
            final List<Consumer<Sexpr<?>>> actions = afterActions.get(pass).get(node.rule().name());
            actions.forEach(action -> action.accept(node.node()));
        }
    }

}