All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.textmapper.lapg.builder.LiSetResolver Maven / Gradle / Ivy

/**
 * Copyright 2002-2016 Evgeny Gryaznov
 *
 * 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 org.textmapper.lapg.builder;

import org.textmapper.lapg.api.*;
import org.textmapper.lapg.api.rule.*;
import org.textmapper.lapg.api.rule.RhsPart.Kind;
import org.textmapper.lapg.api.rule.RhsSet.Operation;
import org.textmapper.lapg.util.ArrayIterable;
import org.textmapper.lapg.util.RhsUtil;

import java.util.*;
import java.util.stream.Collectors;

class LiSetResolver {
	private static final Descriptor SENTINEL = new Descriptor(-1, SetsClosure.EMPTY_ARRAY);

	private static class Descriptor {
		private int set;                // positive
		private int[] dependencies;        // contains indices in "sets" field

		private Descriptor(int set, int[] dependencies) {
			this.set = set;
			this.dependencies = dependencies;
		}
	}

	private LiSetIndex index;
	private SetsClosure closure;
	private Descriptor[] sets;
	private NamedSet[] namedSets;

	private SetBuilder terminalsSet;
	private SetBuilder dependenciesSet;
	private Map> usages;

	public LiSetResolver(Symbol[] symbols, int terminals, NamedSet[] namedSets) {
		index = new LiSetIndex(symbols, terminals, namedSets);
		closure = new SetsClosure();
		sets = new Descriptor[index.size()];
		terminalsSet = new SetBuilder(terminals);
		dependenciesSet = new SetBuilder(index.size());
		this.namedSets = namedSets;
	}

	private void scheduleDependencies(Descriptor d, Queue queue,
									  List postProcess) {
		for (int dep : d.dependencies) {
			if (sets[dep] == null) {
				sets[dep] = SENTINEL;
				queue.add(dep);
			}
		}
		postProcess.add(d);
	}

	public void resolve(ExpansionContext expansionContext, List problems) {
		Queue queue = new LinkedList<>();
		List postProcess = new ArrayList<>();

		for (RhsSet set : index.sortedSets()) {
			int i = index.set(set);
			assert sets[i] == null;
			sets[i] = extractSet(set);
			scheduleDependencies(sets[i], queue, postProcess);
		}

		Integer item;
		while ((item = queue.poll()) != null) {
			int i = item;
			assert sets[i] == SENTINEL;
			Operation op = index.operation(i);
			switch (op) {
				case Any:
				case First:
				case Last: {
					RhsPart part = index.nonterminal(i).getDefinition();
					collectTerminals(part, op);
					sets[i] = new Descriptor(closure.addSet(terminalsSet.create(), part),
							dependenciesSet.create());
					scheduleDependencies(sets[i], queue, postProcess);
					break;
				}
				case Follow:
				case Precede: {
					Symbol symbol = index.symbol(i);
					boolean reverse = (op == Operation.Precede);
					for (LiNonterminal left : getUsages(symbol)) {
						if (collectAdjacent(symbol, left.getDefinition(), reverse, false)) {
							dependenciesSet.add(index.index(op, left));
						}
					}
					sets[i] = new Descriptor(closure.addSet(terminalsSet.create(), symbol),
							dependenciesSet.create());
					scheduleDependencies(sets[i], queue, postProcess);
					break;
				}
			}
		}

		for (Descriptor d : postProcess) {
			if (d.dependencies.length == 0) continue;

			int[] converted = new int[d.dependencies.length];
			for (int i = 0; i < converted.length; i++) {
				int dep = d.dependencies[i];
				assert sets[dep] != null;
				converted[i] = sets[dep].set;
			}
			closure.addDependencies(d.set, converted);
		}

		if (!closure.compute()) {
			Set problemSets = new HashSet<>();
			List errors = new ArrayList<>();
			for (Object errorNode : closure.getErrorNodes()) {
				if (errorNode instanceof RhsSet) {
					problemSets.add((RhsSet) errorNode);
				} else if (errorNode instanceof RhsPart) {
					errors.add((RhsPart) errorNode);
				}
			}
			for (RhsSet set : index.topLevelSets()) {
				traverseProblemSets(set, problemSets, errors);
			}
			problems.addAll(errors.stream()
					.map(error -> new LiProblem(error,
							"Cannot resolve set, since it recursively depends on itself."))
					.collect(Collectors.toList()));
			return;
		}

		for (RhsSet set : index.topLevelSets()) {
			Terminal[] result = getResolvedElements(set);
			if (index.isRhs(set)) {
				if (result.length == 0) {
					problems.add(new LiProblem(set, "Set is empty."));
					continue;
				}
				expansionContext.addSet(set, result);
			}
		}
		for (NamedSet namedSet : namedSets) {
			Terminal[] resolvedElements = getResolvedElements(namedSet.getSet());
			int[] res = new int[resolvedElements.length];
			for (int i = 0; i < resolvedElements.length; i++) {
				res[i] = resolvedElements[i].getIndex();
			}
			((LiNamedSet) namedSet).setElements(res);
		}
	}

	private Iterable getUsages(Symbol s) {
		if (usages == null) {
			usages = new HashMap<>();
			for (Symbol left : index.getSymbols()) {
				if (!(left instanceof Nonterminal)) continue;
				for (RhsSymbol ref : RhsUtil.getRhsSymbols(((Nonterminal) left).getDefinition())) {
					Symbol target = ref.getTarget();
					assert target != null;
					Set nonterminals = usages.get(target);
					if (nonterminals == null) {
						nonterminals = new LinkedHashSet<>();
						usages.put(target, nonterminals);
					}
					nonterminals.add((LiNonterminal) left);
				}
			}
		}

		Set result = usages.get(s);
		if (result == null) result = Collections.emptySet();
		return result;
	}

	private Terminal[] getResolvedElements(RhsSet set) {
		int i = index.set(set);
		assert sets[i] != null;
		int[] resultSet = closure.getSet(sets[i].set);
		boolean complement = closure.isComplement(sets[i].set);
		Terminal[] result;
		if (complement) {
			result = new Terminal[index.terminals() - resultSet.length];
			int k = 0;
			for (int e = 0; e < index.terminals(); e++) {
				if (k < resultSet.length && e == resultSet[k]) {
					k++;
					continue;
				}
				result[e - k] = index.terminal(e);
			}
		} else {
			result = new Terminal[resultSet.length];
			for (int e = 0; e < resultSet.length; e++) {
				result[e] = index.terminal(resultSet[e]);
			}
		}
		return result;
	}

	private Descriptor extractSet(RhsSet set) {
		switch (set.getOperation()) {
			case Any:
			case First:
			case Last: {
				Symbol target = set.getSymbol();
				if (target.isTerm()) {
					terminalsSet.add(target.getIndex());
				} else {
					dependenciesSet.add(index.index(set.getOperation(), target));
				}
				return new Descriptor(closure.addSet(terminalsSet.create(), set),
						dependenciesSet.create());
			}
			case Precede:
			case Follow:
				dependenciesSet.add(index.index(set.getOperation(), set.getSymbol()));
				return new Descriptor(closure.addSet(terminalsSet.create(), set),
						dependenciesSet.create());
			case Complement: {
				assert set.getSets().length == 1;
				int targetIndex = index.set(set.getSets()[0]);
				assert sets[targetIndex] != null;
				return new Descriptor(closure.complement(sets[targetIndex].set, set),
						SetsClosure.EMPTY_ARRAY);
			}
			case Union:
				for (RhsSet child : set.getSets()) {
					int targetIndex = index.set(child);
					assert sets[targetIndex] != null;
					dependenciesSet.add(targetIndex);
				}
				return new Descriptor(closure.addSet(SetsClosure.EMPTY_ARRAY, set),
						dependenciesSet.create());
			case Intersection: {
				RhsSet[] children = set.getSets();
				int[] childSets = new int[children.length];
				for (int i = 0; i < children.length; i++) {
					int targetIndex = index.set(children[i]);
					assert sets[targetIndex] != null;
					childSets[i] = sets[targetIndex].set;
				}
				return new Descriptor(closure.addIntersection(childSets, set),
						SetsClosure.EMPTY_ARRAY);
			}
			default:
				throw new IllegalStateException();
		}
	}

	/**
	 * Only first, last and all are supported.
	 */
	private void collectTerminals(RhsPart p, Operation op) {
		if (p == null) throw new IllegalStateException();

		switch (p.getKind()) {
			case Assignment:
				collectTerminals(((RhsAssignment) p).getPart(), op);
				break;
			case Cast:
				collectTerminals(((RhsCast) p).getPart(), op);
				break;
			case Ignored:
				collectTerminals(((RhsIgnored) p).getInner(), op);
				break;
			case List:
				RhsList list = (RhsList) p;
				for (RhsSequence s : list.asRules()) {
					collectTerminals(s, op);
				}
				break;
			case Optional:
				collectTerminals(((RhsOptional) p).getPart(), op);
				break;
			case Unordered:
				for (RhsPart inner : ((RhsUnordered) p).getParts()) {
					collectTerminals(inner, op);
				}
				break;
			case Choice:
				for (RhsPart inner : ((RhsChoice) p).getParts()) {
					collectTerminals(inner, op);
				}
				break;
			case Sequence: {
				RhsPart[] parts = ((RhsSequence) p).getParts();
				for (RhsPart inner : new ArrayIterable<>(parts, op == Operation.Last)) {
					collectTerminals(inner, op);

					if (op != Operation.Any && !RhsUtil.isNullable(inner, null)) break;
				}
				break;
			}
			case Set:
				dependenciesSet.add(index.set((RhsSet) p));
				break;
			case Symbol:
				Symbol target = ((RhsSymbol) p).getTarget();
				if (target.isTerm()) {
					terminalsSet.add(target.getIndex());
				} else {
					dependenciesSet.add(index.index(op, target));
				}
				break;
			case StateMarker:
				break;
			case Conditional:
				throw new UnsupportedOperationException();
			default:
				throw new IllegalStateException();
		}
	}

	/**
	 * Only precede and follow are supported.
	 */
	private boolean collectAdjacent(Symbol symbol, RhsPart p, boolean reverse, boolean isAfter) {
		if (p == null) throw new IllegalStateException();

		switch (p.getKind()) {
			case Assignment:
				return collectAdjacent(symbol, ((RhsAssignment) p).getPart(), reverse, isAfter);
			case Cast:
				return collectAdjacent(symbol, ((RhsCast) p).getPart(), reverse, isAfter);
			case Ignored:
				return collectAdjacent(symbol, ((RhsIgnored) p).getInner(), reverse, isAfter);
			case Optional:
				return collectAdjacent(symbol, ((RhsOptional) p).getPart(), reverse, isAfter)
						|| isAfter;
			case List: {
				RhsList list = (RhsList) p;
				boolean result = false;
				for (RhsSequence s : list.asRules()) {
					result |= collectAdjacent(symbol, s, reverse, isAfter);
				}
				return result;
			}
			case Choice: {
				boolean result = false;
				for (RhsPart inner : ((RhsChoice) p).getParts()) {
					result |= collectAdjacent(symbol, inner, reverse, isAfter);
				}
				return result;
			}
			case Unordered: {
//				boolean result = false;
//				for (RhsPart inner : ((RhsUnordered) p).getParts()) {
//					result |= collectAdjacent(symbol, inner, reverse, isAfter);
//				}
//				if (result && !isAfter) {
//					for (RhsPart inner : ((RhsUnordered) p).getParts()) {
//						collectAdjacent(symbol, inner, reverse, true);
//					}
//				}
//				return result;
				// TODO!!!
				throw new UnsupportedOperationException();
			}
			case Sequence: {
				RhsPart[] parts = ((RhsSequence) p).getParts();
				for (RhsPart inner : new ArrayIterable<>(parts, reverse)) {
					if (inner.getKind() == Kind.StateMarker) continue;
					isAfter = collectAdjacent(symbol, inner, reverse, isAfter);
				}
				return isAfter;
			}
			case Set:
				if (isAfter) {
					RhsSet set = (RhsSet) p;
					dependenciesSet.add(index.set(set));
				}
				if (symbol.isTerm()) {
					// TODO!!!
					throw new UnsupportedOperationException();
				}
				return false;
			case Symbol:
				Symbol target = ((RhsSymbol) p).getTarget();
				if (isAfter) {
					if (target.isTerm()) {
						terminalsSet.add(target.getIndex());
					} else {
						dependenciesSet.add(
								index.index(reverse ? Operation.Last : Operation.First, target));
						if (((Nonterminal) target).isNullable()) {
							return true;
						}
					}
				}
				return target == symbol;
			case Conditional:
				throw new UnsupportedOperationException();
			default:
				throw new IllegalStateException();
		}
	}

	/**
	 * Copies to errors only topmost problem sets.
	 */
	private static void traverseProblemSets(RhsSet set, Set problemSets,
											List errors) {
		if (problemSets.contains(set)) {
			errors.add(set);
			return;
		}

		RhsSet[] children = set.getSets();
		if (children == null) return;

		for (RhsSet child : children) {
			traverseProblemSets(child, problemSets, errors);
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy