/**
* Copyright 2002-2017 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.SourceElement;
import org.textmapper.lapg.api.Symbol;
import org.textmapper.lapg.api.Terminal;
import org.textmapper.lapg.api.rule.RhsCFPart;
import org.textmapper.lapg.api.rule.RhsList;
import org.textmapper.lapg.api.rule.RhsSequence;
import org.textmapper.lapg.api.rule.RhsSymbol;
import org.textmapper.lapg.common.FormatUtil;
import org.textmapper.lapg.util.RhsUtil;
import java.util.ArrayList;
import java.util.List;
/**
* evgeny, 1/3/13
*/
class LiRhsList extends LiRhsRoot implements RhsList {
private final LiRhsSequence element;
private final LiRhsPart separator;
private final boolean nonEmpty;
private final LiRhsSequence customInitialElement;
private final boolean rightRecursive;
private RhsSequence[] preprocessed;
LiRhsList(LiRhsSequence element, LiRhsPart separator, boolean nonEmpty,
LiRhsSequence customInitialElement, boolean rightRecursive,
boolean isRewrite, SourceElement origin) {
super(null, origin);
if (element == null) {
throw new NullPointerException();
}
if (separator != null && !nonEmpty) {
throw new IllegalArgumentException("lists with separator should have at least one element");
}
if (customInitialElement != null && !nonEmpty) {
throw new IllegalArgumentException("custom initial element is allowed only in non-empty lists");
}
this.element = element;
this.separator = separator;
this.nonEmpty = nonEmpty;
this.customInitialElement = customInitialElement;
this.rightRecursive = rightRecursive;
register(isRewrite, element, separator, customInitialElement);
}
@Override
public LiRhsSequence getElement() {
return element;
}
@Override
public LiRhsPart getSeparator() {
return separator;
}
@Override
public boolean isNonEmpty() {
return nonEmpty;
}
@Override
public boolean isRightRecursive() {
return rightRecursive;
}
@Override
public LiRhsSequence getCustomInitialElement() {
return customInitialElement;
}
@Override
List<RhsCFPart[]> expand(ExpansionContext context) {
throw new UnsupportedOperationException();
}
@Override
public boolean structurallyEquals(LiRhsPart o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
LiRhsList that = (LiRhsList) o;
if (separator != null ? !separator.structurallyEquals(that.separator) : that.separator != null) return false;
if (customInitialElement != null ? !customInitialElement.structurallyEquals(that.customInitialElement) :
that.customInitialElement != null)
return false;
if (nonEmpty != that.nonEmpty) return false;
if (rightRecursive != that.rightRecursive) return false;
return element.structurallyEquals(that.element);
}
@Override
public int structuralHashCode() {
int result = element.structuralHashCode();
result = 31 * result + (separator != null ? separator.structuralHashCode() : 0);
result = 31 * result + (customInitialElement != null ? customInitialElement.structuralHashCode() : 0);
result = 31 * result + (nonEmpty ? 1 : 0);
result = 31 * result + (rightRecursive ? 1 : 0);
return result;
}
@Override
public Kind getKind() {
return Kind.List;
}
@Override
protected void toString(StringBuilder sb) {
if (customInitialElement != null) {
boolean nonEmptyInitial = customInitialElement.getParts().length != 0;
if (nonEmptyInitial) {
sb.append("(");
}
if (rightRecursive) {
sb.append("(");
element.toString(sb);
if (separator != null) {
sb.append(" ");
separator.toString(sb);
}
sb.append(" /rr)*");
if (nonEmptyInitial) {
sb.append(' ');
customInitialElement.toString(sb);
}
} else {
if (nonEmptyInitial) {
customInitialElement.toString(sb);
sb.append(" ");
}
sb.append("(");
if (separator != null) {
separator.toString(sb);
sb.append(" ");
}
element.toString(sb);
sb.append(")*");
}
if (nonEmptyInitial) {
sb.append(")");
}
} else {
sb.append("(");
element.toString(sb);
if (separator != null) {
sb.append(" separator ");
separator.toString(sb);
}
if (rightRecursive) {
sb.append(" /rr");
}
sb.append(")").append(nonEmpty ? "+" : "*");
}
}
@Override
public RhsSequence[] asRules() {
return preprocess();
}
@Override
protected RhsSequence[] preprocess() {
if (preprocessed != null) return preprocessed;
LiRhsSymbol selfRef = new LiRhsSymbol(getLeft(), null, true, this);
List<LiRhsPart> listRule = new ArrayList<>(3);
listRule.add(rightRecursive ? element : selfRef);
if (separator != null) {
listRule.add(separator);
}
listRule.add(rightRecursive ? selfRef : element);
LiRhsSequence rule1 = new LiRhsSequence(null, listRule.toArray(new LiRhsPart[listRule.size()]), true, this);
LiRhsSequence rule2;
if (nonEmpty) {
rule2 = customInitialElement != null ? customInitialElement : element;
} else {
rule2 = new LiRhsSequence(null, new LiRhsPart[0], false, this);
}
register(true, rule1, rule2, customInitialElement, element, separator);
return preprocessed = new RhsSequence[]{rule1, rule2};
}
@Override
public String getProvisionalName() {
StringBuilder sb = new StringBuilder();
Symbol representative = RhsUtil.getRepresentative(element);
if (representative != null) {
sb.append(LiUtil.getSymbolName(representative));
sb.append(nonEmpty || separator != null ? "_list" : "_optlist");
} else {
RhsSymbol[] rhsSymbols = RhsUtil.getRhsSymbols(element);
sb.append("list_of_");
if (rhsSymbols.length > 0) {
sb.append(LiUtil.getSymbolName(rhsSymbols[0]));
if (rhsSymbols.length > 1) {
sb.append("_and_").append(rhsSymbols.length - 1).append("_elements");
}
} else {
sb.append("unknown");
}
}
if (separator != null) {
Symbol separatorTerminal = RhsUtil.getRepresentative(separator);
if (separatorTerminal instanceof Terminal && ((Terminal) separatorTerminal).isConstant()) {
String val = ((Terminal) separatorTerminal).getConstantValue();
sb.append("_").append(FormatUtil.toIdentifier(val)).append("_separated");
} else {
sb.append("_withsep");
}
}
if (rightRecursive) {
sb.append("_rr");
}
return sb.toString();
}
}