All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.textmapper.lapg.builder.ListsRewriter Maven / Gradle / Ivy
/**
* Copyright 2002-2018 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.Nonterminal;
import org.textmapper.lapg.api.Terminal;
import org.textmapper.lapg.api.rule.*;
import org.textmapper.lapg.util.RhsUtil;
import java.util.*;
/**
* evgeny, 2/1/13
*/
public class ListsRewriter {
private enum ListKind {
NONE,
LEFT_RECURSIVE,
RIGHT_RECURSIVE,
}
private LiSymbol symbol;
public ListsRewriter(LiSymbol symbol) {
this.symbol = symbol;
}
/**
* Replaces the definition of a list nonterminal with RhsList.
* (see rewrite.tm)
*/
public boolean rewrite() {
if (!(symbol instanceof Nonterminal)) return false;
Nonterminal nonterm = (Nonterminal) symbol;
if (!(nonterm.getDefinition() instanceof RhsChoice)) return false;
// TODO check if any parts of the current definition are mapped
RhsChoice def = (RhsChoice) nonterm.getDefinition();
ListKind kind = getListKind(def);
if (kind != ListKind.NONE) {
// TODO respect RhsMapping ?
RhsList list = createList(def, kind == ListKind.RIGHT_RECURSIVE);
// debug("Rewriting\n" + def.toString() + "\n" + "with\n" + list.toString() + "\n");
((LiRhsRoot) def).rewrite(list);
return true;
}
return false;
}
private static ListKind getListKind(RhsChoice def) {
boolean immLeft = false;
boolean immRight = false;
final Nonterminal list = def.getLeft();
for (RhsPart rule : def.getParts()) {
if (!(rule instanceof RhsSequence)) {
if (!RhsUtil.containsRef(rule, list)) {
continue;
}
// too complex to analyze, i.e. not a list
return ListKind.NONE;
}
final RhsPart[] ruleSeq = ((RhsSequence) rule).getParts();
for (RhsPart s : ruleSeq) {
if (s instanceof RhsSymbol) {
if (((RhsSymbol) s).getTarget() != list) continue;
if (s == ruleSeq[0]) {
immLeft = true;
} else if (s == ruleSeq[ruleSeq.length - 1]) {
immRight = true;
} else {
return ListKind.NONE;
}
} else if (RhsUtil.containsRef(s, list)) {
return ListKind.NONE;
}
}
}
if (immLeft && immRight) {
return ListKind.NONE;
}
return immLeft ? ListKind.LEFT_RECURSIVE
: immRight ? ListKind.RIGHT_RECURSIVE
: ListKind.NONE;
}
private static RhsList createList(RhsChoice def, boolean rightRecursive) {
List initialElements = new ArrayList<>();
List listRules = new ArrayList<>();
LiRhsPart emptyRule = null;
final Nonterminal list = def.getLeft();
for (RhsPart rule : def.getParts()) {
if (rule instanceof RhsSequence) {
RhsPart[] ruleSeq = ((RhsSequence) rule).getParts();
RhsPart p = ruleSeq.length > 0
? ruleSeq[rightRecursive ? ruleSeq.length - 1 : 0] : null;
if (p instanceof RhsSymbol && ((RhsSymbol) p).getTarget() == list) {
listRules.add((LiRhsSequence) rule);
continue;
}
if (ruleSeq.length == 0) {
emptyRule = (LiRhsPart) rule;
continue;
}
}
initialElements.add((LiRhsPart) RhsUtil.unwrap(rule));
}
assert !listRules.isEmpty();
List separator = getCommonSeparator(listRules, rightRecursive);
int skipParts = 1 + (separator != null ? separator.size() : 0);
List elements = extractListElements(listRules, skipParts, rightRecursive);
final LiRhsSequence element = asSequence(merge(elements, MergeStrategy.CHOICE));
LiRhsSequence customInitialElement =
asSequence(merge(initialElements, MergeStrategy.CHOICE));
LiRhsSequence rewritten = null;
if (customInitialElement != null && element.structurallyEquals(customInitialElement)) {
rewritten = customInitialElement;
customInitialElement = null;
}
// Note: lists with separator should have at least one element
if (emptyRule != null && (customInitialElement != null || separator != null)) {
rewritten = null;
initialElements.add(emptyRule);
customInitialElement = asSequence(merge(initialElements, MergeStrategy.CHOICE));
if (element.structurallyEquals(customInitialElement)) {
rewritten = customInitialElement;
customInitialElement = null;
}
emptyRule = null;
}
if (rewritten != null) {
registerRewrite(rewritten, element);
}
return new LiRhsList(element, merge(separator, MergeStrategy.SEQUENCE), emptyRule == null,
customInitialElement, rightRecursive, true, def);
}
private static void registerRewrite(RhsSymbol from, RhsSymbol to) {
assert from.getUserData(RhsSymbol.UD_REWRITTEN) == null;
from.putUserData(RhsSymbol.UD_REWRITTEN, to);
}
private static void registerRewrite(RhsPart from, RhsPart to) {
final RhsSymbol[] fromArr = RhsUtil.getRhsSymbols(from);
final RhsSymbol[] toArr = RhsUtil.getRhsSymbols(to);
assert fromArr.length == toArr.length;
for (int i = 0; i < fromArr.length; i++) {
assert ((LiRhsPart) fromArr[i]).structurallyEquals((LiRhsPart) toArr[i]);
registerRewrite(fromArr[i], toArr[i]);
}
}
private static List extractListElements(List listRules,
int skipParts, boolean rightRecursive) {
List result = new ArrayList<>();
for (LiRhsSequence part : listRules) {
RhsPart[] ruleSeq = part.getParts();
if (ruleSeq.length == skipParts + 1) {
result.add((LiRhsPart) ruleSeq[rightRecursive ? 0 : ruleSeq.length - 1]);
} else {
LiRhsPart[] newArr = new LiRhsPart[ruleSeq.length - skipParts];
//noinspection SuspiciousSystemArraycopy
System.arraycopy(ruleSeq, rightRecursive ? 0 : skipParts,
newArr, 0, newArr.length);
result.add(new LiRhsSequence(null, newArr, true, part));
}
}
return result;
}
private static List getCommonSeparator(Collection listRules,
boolean rightRecursive) {
if (listRules.isEmpty()) return null;
int maxLen = Integer.MAX_VALUE;
for (RhsSequence seq : listRules) {
maxLen = Math.min(seq.getParts().length - 2, maxLen);
}
List result = new ArrayList<>(maxLen);
for (int i = 0; i < maxLen; i++) {
Terminal sep = null;
RhsSymbol first = null;
LinkedList rewriteCandidates = new LinkedList<>();
for (LiRhsSequence rule : listRules) {
RhsPart[] parts = rule.getParts();
RhsPart part = parts[rightRecursive ? parts.length - 2 - i : 1 + i];
Terminal curr = asConstantTerminal(part);
if (curr == null || sep != null && sep != curr) {
sep = null;
break;
}
if (first == null) {
first = (RhsSymbol) part;
sep = curr;
} else {
rewriteCandidates.add((RhsSymbol) part);
}
}
if (sep == null) {
break;
}
result.add((LiRhsSymbol) first);
for (RhsSymbol c : rewriteCandidates) {
registerRewrite(c, first);
}
}
if (rightRecursive) {
Collections.reverse(result);
}
return result.isEmpty() ? null : result;
}
private enum MergeStrategy {
CHOICE,
SEQUENCE
}
private static LiRhsPart merge(List parts, MergeStrategy strategy) {
if (parts == null || parts.isEmpty()) {
return null;
}
final LiRhsPart first = parts.get(0);
if (parts.size() == 1) {
return first;
}
final LiRhsPart[] partsArray = parts.toArray(new LiRhsPart[parts.size()]);
return strategy == MergeStrategy.CHOICE
? new LiRhsChoice(partsArray, true, first)
: new LiRhsSequence(null, partsArray, true, first);
}
private static LiRhsSequence asSequence(LiRhsPart part) {
if (part instanceof RhsSequence) {
return (LiRhsSequence) part;
}
return new LiRhsSequence(null, new LiRhsPart[]{part}, true, part);
}
private static Terminal asConstantTerminal(RhsPart part) {
if (part instanceof RhsSymbol && ((RhsSymbol) part).getTarget() instanceof Terminal) {
Terminal term = (Terminal) ((RhsSymbol) part).getTarget();
if (term.isConstant()) {
return term;
}
}
return null;
}
}