Package peg

Source Code of peg.GraphHelper$Distance

/*
* Copyright 2011-2011 Gregory Shrago
*
* 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 peg;

import org.antlr.runtime.tree.Tree;
import org.jetbrains.annotations.Nullable;

import java.util.*;

import static peg.GeneratorUtil.Rule;
import static peg.GeneratorUtil.getAttribute;
import static peg.GrammarParser.*;
import static peg.GraphHelper.Cardinality.*;

/**
* @author gregory
*         Date: 15.07.11 18:19
*/
public class GraphHelper {
    public enum Cardinality{NONE, OPTIONAL, REQUIRED, AT_LEAST_ONE, ANY_NUMBER;
        boolean optional() { return this == OPTIONAL || this == ANY_NUMBER || this == NONE; }
        boolean many() { return this == AT_LEAST_ONE || this == ANY_NUMBER; }
        public static Cardinality fromNodeType(int type) {
            if (type == OPT) return OPTIONAL;
            else if (type == SEQ || type == ID) return REQUIRED;
            else if (type == CHOICE) return Cardinality.OPTIONAL;
            else if (type == ONEMORE) return AT_LEAST_ONE;
            else if (type == ZEROMORE) return ANY_NUMBER;
            else throw new AssertionError(GrammarParser.tokenNames[type]);
        }
        public Cardinality and(Cardinality c) {
            if (this == NONE || c == NONE) return NONE;
            if (optional() || c.optional()) {
                return many() || c.many() ? ANY_NUMBER : OPTIONAL;
            } else {
                return many() || c.many() ? AT_LEAST_ONE : REQUIRED;
            }
        }
        public Cardinality or(Cardinality c) {
            if (c == null) c = NONE;
            if (this == NONE && c == NONE) return NONE;
            if (this == NONE) return c;
            if (c == NONE) return this;
            return optional() && c.optional()? ANY_NUMBER : AT_LEAST_ONE;
        }
    }


    public static Distance NO_WAY = new Distance(0, NONE);
    public static class Distance {

        int num = 0;
        Cardinality card = NONE;

        public Distance(int num, Cardinality card) {
            this.num = num;
            this.card = card;
        }

        public Distance update(Tree iNode, Distance d1, Distance d2, Tree kNode) {
            if (d1 == NO_WAY || d2 == NO_WAY) return this;
            // calc d1 + d2
            Cardinality c = d1.card.and(fromNodeType(kNode.getType())).and(d2.card);
            return or(iNode, c);
        }

        public Distance or(Tree iNode, Cardinality c) {
            if (this == NO_WAY) {
                return new Distance(1, c);
            }
            int type = iNode.getType();
            if (type == CHOICE && c != NONE) {
                num++;
                if (num == iNode.getChildCount()) {
                    assert card == OPTIONAL;
                    card = REQUIRED;
                }
            }
            else if (type == SEQ && c != NONE) {
                num ++;
                card = AT_LEAST_ONE;
            }
            else {
                card = card.or(c);
            }
            return this;
        }

        public String toString() { return card+":"+num; }
    }


    private Map<String, Tree> ruleMap;

    public GraphHelper(Map<String, Tree> ruleMap) {
        this.ruleMap = ruleMap;
    }

    @Nullable
    private static Tree resolveRule(Map<String, Tree> ruleMap, String text) {
        Tree rule = ruleMap.get(text);
        if (rule == null) return null;
        String superRuleName = getAttribute(rule, "extends", null);
        if (superRuleName == null) return null;
        Tree superRule = ruleMap.get(superRuleName);
        return superRule == null? rule : superRule;
    }


    public Map<Tree, Cardinality> getFor(Tree rule) {
        return collectMembers(Rule.body(rule), new HashSet<Tree>());
    }


    private Map<Tree, Cardinality> collectMembers(Tree tree, HashSet<Tree> visited) {
        int type = tree.getType();
        if (type == AND || type == NOT) return Collections.emptyMap();
        if (type == STRING || type == NUMBER) return Collections.singletonMap(tree, REQUIRED);

        if (!visited.add(tree)) return Collections.singletonMap(tree, REQUIRED);
        if (type == ID) {
            Tree targetRule = resolveRule(ruleMap, tree.getText());
            if (targetRule != null) {
                if (Rule.isPrivate(targetRule)) {
                    Tree body = Rule.body(targetRule);
                    Map<Tree, Cardinality> map = collectMembers(body, visited);
                    return map.containsKey(body) ? joinMaps(ZEROMORE, Arrays.asList(map, map)) : map;
                }
                else {
                    return Collections.singletonMap(targetRule, REQUIRED);
                }
            }
            return Collections.singletonMap(tree, REQUIRED);
        }

        List<Map<Tree, Cardinality>> list = new ArrayList<Map<Tree, Cardinality>>();
        for (int i=0, len = tree.getChildCount(); i<len; i++) {
            list.add(collectMembers(tree.getChild(i), visited));
        }
        return joinMaps(tree.getType(), list);
    }

    private Map<Tree, Cardinality> joinMaps(int type, List<Map<Tree, Cardinality>> list) {
        if (list.isEmpty()) return Collections.emptyMap();
        if (type == OPT || type == ONEMORE || type == ZEROMORE) {
            HashMap<Tree, Cardinality> map = new HashMap<Tree, Cardinality>();
            assert list.size() == 1;
            Map<Tree, Cardinality> m = list.get(0);
            for (Tree t : m.keySet()) {
                map.put(t, fromNodeType(type).and(m.get(t)));
            }
            return map;
        }
        else if (type == SEQ) {
            if (list.size() == 1) return list.get(0);
            HashMap<Tree, Cardinality> map = new HashMap<Tree, Cardinality>();
            for (Map<Tree, Cardinality> m : list) {
                for (Tree t : m.keySet()) {
                    map.put(t, m.get(t).or(map.get(t)));
                }
            }
            return map;
        }
        else if (type == CHOICE) {
            HashMap<Tree, Cardinality> map = new HashMap<Tree, Cardinality>();
            Map<Tree, Cardinality> m0 = list.get(0);
            map.putAll(m0);
            for (Map<Tree, Cardinality> m : list) {
                map.keySet().retainAll(m.keySet());
            }
            for (Tree t : map.keySet()) {
                map.put(t, REQUIRED.and(m0.get(t)));
                for (Map<Tree, Cardinality> m : list) {
                    if (m == list.get(0)) continue;
                    map.put(t, map.get(t).and(m.get(t)));
                }
            }
            for (Map<Tree, Cardinality> m : list) {
                for (Tree t : m.keySet()) {
                    if (map.containsKey(t)) continue;
                    map.put(t, OPTIONAL.and(m.get(t)));
                }
            }
            return map;
        }
        else throw new AssertionError(GrammarParser.tokenNames[type]);
    }


}
TOP

Related Classes of peg.GraphHelper$Distance

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.