/*******************************************************************************
* Copyright (c) 2008, 2017 xored software, Inc. and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* xored software, Inc. - initial API and Implementation (Andrei Sobolev)
*******************************************************************************/
package org.eclipse.dltk.tcl.parser;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.dltk.tcl.ast.ISubstitution;
import org.eclipse.dltk.tcl.ast.StringArgument;
import org.eclipse.dltk.tcl.ast.TclArgument;
import org.eclipse.dltk.tcl.ast.TclCommand;
import org.eclipse.dltk.tcl.definitions.Argument;
import org.eclipse.dltk.tcl.definitions.ArgumentType;
import org.eclipse.dltk.tcl.definitions.Command;
import org.eclipse.dltk.tcl.definitions.ComplexArgument;
import org.eclipse.dltk.tcl.definitions.Constant;
import org.eclipse.dltk.tcl.definitions.Group;
import org.eclipse.dltk.tcl.definitions.Switch;
import org.eclipse.dltk.tcl.definitions.TypedArgument;
import org.eclipse.dltk.tcl.parser.definitions.DefinitionUtils;
import org.eclipse.dltk.tcl.parser.definitions.SynopsisBuilder;
import org.eclipse.emf.common.util.EList;
import org.eclipse.osgi.util.NLS;
public class TclArgumentMatcher {
public static final String SHORT_ARG = "short:";
public static final String SYNOPSIS_ARG = "synopsis:";
private List<Integer> codePositions;
private TclErrorCollector errors;
private TclCommand command;
// private Map<String, Boolean> options;
private List<ComplexArgumentResult> complexArguments;
private ISubstitutionManager substitutionManager;
private HashMap<Argument, int[]> mappings;
// private Command definition;
private SynopsisBuilder synopsisBuilder = new SynopsisBuilder(false);
private static interface ISinglePositionRule {
boolean check(TclArgument argument, Argument definition,
List<Integer> scriptPositions, int position,
TclErrorCollector collector,
List<ComplexArgumentResult> results);
}
public static class ComplexArgumentResult {
private List<Integer> blockArguments = new ArrayList<>();
private List<TclArgument> arguments = new ArrayList<>();
private int argumentNumber = -1;
private ComplexArgument definition;
private List<ComplexArgumentResult> complexArguments = new ArrayList<>();
public List<ComplexArgumentResult> getComplexArguments() {
return complexArguments;
}
public void setComplexArguments(
List<ComplexArgumentResult> complexArguments) {
this.complexArguments = complexArguments;
}
public int[] getBlockArguments() {
return getArrayFromList(this.blockArguments);
}
public List<Integer> getBlockArgumentsList() {
return this.blockArguments;
}
public void setBlockArguments(List<Integer> blockArguments) {
this.blockArguments = blockArguments;
}
public List<TclArgument> getArguments() {
return arguments;
}
public void setArguments(List<TclArgument> arguments) {
this.arguments = arguments;
}
public int getArgumentNumber() {
return argumentNumber;
}
public void setArgumentNumber(int argumentNumber) {
this.argumentNumber = argumentNumber;
}
public ComplexArgumentResult(int argumentNumber,
List<TclArgument> arguments, List<Integer> blockArguments) {
super();
this.argumentNumber = argumentNumber;
this.arguments = arguments;
this.blockArguments = blockArguments;
}
public ComplexArgument getDefinition() {
return definition;
}
public void setDefinition(ComplexArgument definition) {
this.definition = definition;
}
}
private static class MatchResult {
public static final int POSSIBLE = -1;
public static final int REGULAR = 0;
public static final int IMPLICIT = 1;
private int argumentsUsed = 0;
private TclErrorCollector errors = new TclErrorCollector();
private boolean matched = false;
private boolean matchWithErrors = false;
private int priority = REGULAR;
private List<Integer> blockArguments = new ArrayList<>();
private List<ComplexArgumentResult> complexArguments = new ArrayList<>();
// Map arguments to positions
private Map<Argument, int[]> mapping = new HashMap<>();
public int getArgumentsUsed() {
return this.argumentsUsed;
}
public void setArgumentsUsed(int argumentsUsed) {
this.argumentsUsed = argumentsUsed;
}
public boolean isMatched() {
return this.matched;
}
public void setMatched(boolean matched) {
this.matched = matched;
}
public boolean isImplicit() {
return priority >= IMPLICIT;
}
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
public void setSummaryPriorityOf(MatchResult r1, MatchResult r2) {
this.priority = r1.getPriority() + r2.getPriority();
}
public void incrPriority() {
this.priority++;
}
public void decrPriority() {
this.priority--;
}
public TclErrorCollector getErrors() {
return this.errors;
}
public boolean isMatchWithErrors() {
return this.matchWithErrors;
}
public void setMatchWithErrors(boolean matchWithErrors) {
this.matchWithErrors = matchWithErrors;
}
public List<Integer> getBlockArguments() {
return this.blockArguments;
}
public List<ComplexArgumentResult> getComplexArguments() {
return this.complexArguments;
}
public Map<Argument, int[]> getMapping() {
return this.mapping;
}
}
public TclArgumentMatcher(TclCommand command, Map<String, Boolean> map,
ISubstitutionManager substitutionManager) {
this.command = command;
// this.options = map;
this.substitutionManager = substitutionManager;
}
public boolean match(Command definition) {
// Initialize required fields
this.codePositions = new ArrayList<>();
this.errors = new TclErrorCollector();
this.complexArguments = new ArrayList<>();
this.mappings = new HashMap<>();
// this.definition = definition;
List<Argument> definitionArguments = definition.getArguments();
EList<TclArgument> arguments = command.getArguments();
int argSize = arguments.size();
if (argSize == 0 && definitionArguments.size() == 0) {
return true;
}
MatchResult result = matchArgument(arguments, 0, definitionArguments,
0);
this.errors.addAll(result.getErrors());
if (result.isMatched()) {
this.codePositions.addAll(result.getBlockArguments());
this.complexArguments.addAll(result.getComplexArguments());
this.mappings.putAll(result.getMapping());
if (result.getArgumentsUsed() == argSize) {
return true;
} else {
if (result.getArgumentsUsed() < argSize) {
reportExtraArguments(arguments, result.getArgumentsUsed(),
this.errors);
return true;
}
}
}
if (this.errors.getCount() == 0) {
reportInvalidArgumentCount(this.command.getStart(),
this.command.getEnd(), argSize, this.errors, definition);
}
return false;
}
private static class SubstitutedArgumentValue {
String value;
int offset = 0;
}
private SubstitutedArgumentValue getSubstitutedArgumentValue(
TclArgument arg) {
if (arg instanceof StringArgument) {
String value = ((StringArgument) arg).getValue();
if (value.startsWith("{") && value.endsWith("}")) {
SubstitutedArgumentValue val = new SubstitutedArgumentValue();
val.value = value.substring(1, value.length() - 1);
val.offset = 1;
return val;
} else if (value.startsWith("\"") && value.endsWith("\"")) {
SubstitutedArgumentValue val = new SubstitutedArgumentValue();
val.value = value.substring(1, value.length() - 1);
val.offset = 1;
return val;
}
SubstitutedArgumentValue val = new SubstitutedArgumentValue();
val.value = value;
val.offset = 0;
return val;
} else if (arg instanceof ISubstitution) {
if (substitutionManager != null) {
SubstitutedArgumentValue val = new SubstitutedArgumentValue();
val.value = substitutionManager.substitute((ISubstitution) arg);
val.offset = 0;
return val;
}
}
SubstitutedArgumentValue val = new SubstitutedArgumentValue();
val.value = null;
val.offset = 0;
return val;
}
/**
* Report all errors from previous match.
*
* @param reporter
*/
public void reportErrors(ITclErrorReporter reporter) {
if (this.errors != null && reporter != null) {
this.errors.reportAll(reporter);
}
}
/**
* Return code block argument indexes to parse.
*/
public int[] getBlockArguments() {
return getArrayFromList(this.codePositions);
}
private static int[] getArrayFromList(List<Integer> list) {
int lsize = list.size();
int[] positions = new int[lsize];
for (int i = 0; i < lsize; i++) {
positions[i] = list.get(i).intValue();
}
return positions;
}
public ComplexArgumentResult[] getComplexArguments() {
return this.complexArguments.toArray(
new ComplexArgumentResult[this.complexArguments.size()]);
}
// Chooses the best one match.
private MatchResult matchArgument(List<TclArgument> arguments, int pos,
List<Argument> definitionArguments, int defPos) {
List<MatchResult> results = matchArgumentList(arguments, pos,
definitionArguments, defPos);
MatchResult result = null;
for (MatchResult sr : results) {
if (result == null) {
result = sr;
} else {
int rpriority = result.getPriority();
int srPriority = sr.getPriority();
if (rpriority > srPriority) {
continue;
} else if (rpriority < srPriority) {
result = sr;
} else if (!result.isMatched() && sr.isMatched()) {
result = sr;
} else {
TclErrorCollector rErrors = result.getErrors();
TclErrorCollector srErrors = sr.getErrors();
int rArgsUsed = result.getArgumentsUsed();
if (rErrors.getCount() > srErrors.getCount()
&& rArgsUsed >= sr.getArgumentsUsed()) {
result = sr;
} else if (rErrors.getCount() >= srErrors.getCount()
&& rArgsUsed < sr.getArgumentsUsed()) {
result = sr;
}
}
} /*
* else if (result.getErrors().getCount() <= sr.getErrors()
* .getCount() && result.getArgumentsUsed() <= sr
* .getArgumentsUsed()) { result = sr; }
*/
}
return result;
}
private List<MatchResult> matchArgumentList(List<TclArgument> arguments,
int pos, List<Argument> definition, int defPos) {
List<MatchResult> results = new ArrayList<>();
if (definition.size() == defPos) {
MatchResult result = new MatchResult();
result.setMatched(true);
result.setMatchWithErrors(false);
result.setArgumentsUsed(0);
results.add(result);
return results; // End of match
}
Argument definitionArg = definition.get(defPos);
List<MatchResult> list = matchDefinition(arguments, pos, definitionArg);
int lsize = list.size();
if (lsize == 0 || (lsize == 1 && !list.get(0).isMatched()
&& !list.get(0).isImplicit())) {
if (arguments.size() > pos + 1) {
List<TclArgument> extraArgs = new ArrayList<>();
extraArgs.add(arguments.get(pos));
List<MatchResult> srl = matchArgumentList(arguments, pos + 1,
definition, defPos);
for (MatchResult sr : srl) {
if (sr.isMatched()) {
reportExtraArguments(extraArgs, 0, sr.getErrors());
// sr.setPriority(MatchResult.REGULAR);
sr.setArgumentsUsed(sr.getArgumentsUsed() + 1);
list.add(sr);
}
}
}
}
TclErrorCollector collector = new TclErrorCollector();
for (MatchResult r : list) {
if (r.isMatched() || r.isImplicit()) {
List<MatchResult> srl = matchArgumentList(arguments,
pos + r.getArgumentsUsed(), definition, defPos + 1);
boolean matched = false;
for (MatchResult sr : srl) {
if (sr.isMatched()) {
matched = true;
sr.setSummaryPriorityOf(r, sr);
sr.setMatchWithErrors(sr.isMatchWithErrors()
|| r.isMatchWithErrors());
sr.setArgumentsUsed(
sr.getArgumentsUsed() + r.getArgumentsUsed());
sr.getErrors().addAll(r.getErrors());
sr.getBlockArguments().addAll(r.getBlockArguments());
sr.getMapping().putAll(r.getMapping());
sr.getComplexArguments()
.addAll(r.getComplexArguments());
results.add(sr);
}
}
if (!matched && srl.size() == 1) {
for (MatchResult sr : srl) {
collector.addAll(sr.getErrors());
}
MatchResult sr = srl.get(0);
if (results.size() == 0) {
MatchResult result = new MatchResult();
result.setMatched(false);
result.setSummaryPriorityOf(r, sr);
result.setMatchWithErrors(true);
result.getErrors().addAll(sr.getErrors());
result.setArgumentsUsed(
r.getArgumentsUsed() + sr.getArgumentsUsed());
results.add(result);
}
}
} else {
collector.addAll(r.getErrors());
}
}
if (results.size() == 0) {
int last = arguments.size() - pos;
MatchResult result = new MatchResult();
result.setMatched(false);
result.setArgumentsUsed((last > 0) ? last : 0);
result.setMatchWithErrors(last <= 0);
result.getErrors().addAll(collector);
results.add(result);
}
return results;
}
// private Argument findNextRequiredArgument(
// List<Argument> definitionArguments, int defPos) {
// for (int i = defPos + 1; i < definitionArguments.size(); i++) {
// Argument a = definitionArguments.get(i);
// if (a.getLowerBound() > 0) {
// return a;
// }
// }
// return definitionArguments.get(defPos);
// }
private List<MatchResult> matchDefinition(List<TclArgument> arguments,
int pos, Argument definition) {
List<MatchResult> results = new ArrayList<>();
if (definition instanceof Constant) {
matchConstant(arguments, pos, definition, results);
for (MatchResult result : results)
if (result.isMatched() && result.getArgumentsUsed() > 0) {
if (arguments.get(pos) instanceof StringArgument) {
result.incrPriority();
result.incrPriority();
}
}
} else if (definition instanceof TypedArgument) {
matchTypedArgument(arguments, pos, definition, results);
} else if (definition instanceof Group) {
matchGroupArgument(arguments, pos, definition, results);
} else if (definition instanceof Switch) {
matchSwitchArgument(arguments, pos, definition, results);
} else if (definition instanceof ComplexArgument) {
matchComplexArgument(arguments, pos, definition, results);
}
return results;
}
private void matchComplexArgument(List<TclArgument> arguments, int pos,
Argument definition, List<MatchResult> results) {
matchSinglePositionArgument(results, arguments, pos, definition,
(argument, definition1, scriptPositions, position, collector,
results1) -> {
ComplexArgument complexArgument = (ComplexArgument) definition1;
SubstitutedArgumentValue substring = getSubstitutedArgumentValue(
argument);
if (substring.value == null) {
return true;
}
// List<Integer> blockArguments= new
// ArrayList<Integer>();
// TODO send blockArguments to
// TclParserUtils.parseCommandArguments()
List<TclArgument> subArguments = TclParserUtils
.parseCommandArguments(
argument.getStart() + substring.offset,
substring.value, null);
EList<Argument> definitionArguments = complexArgument
.getArguments();
MatchResult res = matchArgument(subArguments, 0,
definitionArguments, 0);
if (res.isMatched()) {
// if (res.getArgumentsUsed() ==
// subArguments.size()) {
ComplexArgumentResult cResult = new ComplexArgumentResult(
position, subArguments,
res.getBlockArguments());
cResult.getComplexArguments()
.addAll(res.getComplexArguments());
collector.reportAll(res.getErrors());
cResult.setDefinition(complexArgument);
results1.add(cResult);
if (res.getArgumentsUsed() < subArguments.size()) {
reportExtraArguments(subArguments,
res.getArgumentsUsed(), collector);
} /*
* else {
* reportInvalidArgumentCount(argument.getStart(),
* argument.getEnd(), res .getArgumentsUsed(),
* collector); }
*/
return true;
// } else {
// }
}
return false;
}, false);
}
private void matchExpression(List<TclArgument> arguments, int pos,
Argument definition, List<MatchResult> results) {
// TypedArgument typedArgument = (TypedArgument) definition;
// TclArgument argument = arguments.get(pos);
// SubstitutedArgumentValue substring =
// getSubstitutedArgumentValue(argument);
// if (substring.value == null) {
// return;
// }
// List<TclArgument> subArguments = TclParserUtils
// .parseCommandArguments(argument.getStart()
// + substring.offset, substring.value);
// TclArgumentList list = AstFactory.eINSTANCE.createTclArgumentList();
// list.getArguments().addAll(subArguments);
// arguments.set(pos, list);
matchSinglePositionArgument(results, arguments, pos, definition,
(argument, definition1, scriptPositions, position, collector,
results1) -> {
// TypedArgument typedArgument = (TypedArgument)
// definition;
SubstitutedArgumentValue substring = getSubstitutedArgumentValue(
argument);
if (substring.value == null) {
return true;
}
List<Integer> blockArguments = new ArrayList<>();
List<TclArgument> subArguments = TclParserUtils
.parseCommandArguments(
argument.getStart() + substring.offset,
substring.value, blockArguments);
// EList<Argument> definitionArguments = typedArgument
// .getArguments();
// MatchResult res = matchArgument(subArguments, 0,
// definitionArguments, 0);
// if (res.isMatched()) {
// if (res.getArgumentsUsed() ==
// subArguments.size()) {
ComplexArgumentResult cResult = new ComplexArgumentResult(
position, subArguments, blockArguments);
// cResult.getComplexArguments().addAll(
// res.getComplexArguments());
// collector.reportAll(res.getErrors());
// cResult.setDefinition(typedArgument);
results1.add(cResult);
// if (res.getArgumentsUsed() < subArguments.size()) {
// reportExtraArguments(subArguments, res
// .getArgumentsUsed(), collector);
// }
/*
* else { reportInvalidArgumentCount(argument.getStart(),
* argument.getEnd(), res .getArgumentsUsed(), collector); }
*/
return true;
// } else {
// }
// }
// return false;
}, false);
}
private void matchSwitchArgument(List<TclArgument> arguments, int pos,
Argument definition, List<MatchResult> results) {
Switch switchDef = (Switch) definition;
int lowerBound = switchDef.getLowerBound();
int upperBound = switchDef.getUpperBound();
if (upperBound == -1) {
upperBound = Integer.MAX_VALUE;
}
List<MatchResult> ress = new ArrayList<>();
Map<MatchResult, Integer> counts = new HashMap<>();
matchSwitch(arguments, pos, switchDef, ress, counts, 0, upperBound);
int ressSize = ress.size();
for (int i = 0; i < ressSize; i++) {
MatchResult r = ress.get(i);
Integer count = counts.get(r);
if (count != null && count.intValue() < lowerBound) {
r.setMatchWithErrors(true);
reportMissingArgument(definition, r, this.command.getStart(),
command.getEnd());
results.add(r);
}
if (count != null && count.intValue() >= lowerBound
&& count.intValue() <= upperBound) {
results.add(r);
}
// if (count != null && count.intValue() > upperBound) {
// r.setMatchWithErrors(true);
// reportInvalidArgumentCount(definition, r);
// results.add(r);
// }
}
int argSize = arguments.size();
if (ressSize == 0 && argSize > pos
&& DefinitionUtils.isMode(switchDef)) {
if (arguments.get(pos) instanceof ISubstitution) {
MatchResult r = new MatchResult();
r.setArgumentsUsed(1);
r.setMatched(true);
r.setMatchWithErrors(false);
results.add(r);
}
}
// Add empty variant if multiplicity support it.
if (ressSize == 0 && lowerBound > 0) {
// We should report error if not argument are specified, but
// required.
MatchResult r = new MatchResult();
r.setArgumentsUsed(0);
r.setMatched(false);
r.setMatchWithErrors(false);
TclArgument a = null;
if (argSize > pos) {
a = arguments.get(pos);
}
reportMissingSwitch(switchDef, r, a);
results.add(r);
} else if (lowerBound == 0) {
MatchResult r = new MatchResult();
r.setArgumentsUsed(0);
r.setMatched(true);
r.setMatchWithErrors(false);
results.add(r);
}
}
private void matchSwitch(List<TclArgument> arguments, int pos, Switch sw,
List<MatchResult> ress, Map<MatchResult, Integer> counts, int count,
int upperBound) {
TclErrorCollector errors = new TclErrorCollector();
if (pos >= arguments.size()) {
return;
}
List<MatchResult> list = new ArrayList<>();
Map<Group, Argument> fitGroupMap = getFitGroupMap(arguments, pos, sw);
Map<String, List<Group>> constFitGroupMap = new HashMap<>();
for (Group group : fitGroupMap.keySet()) {
Argument selector = fitGroupMap.get(group);
if (selector instanceof Constant) {
List<Group> groups = constFitGroupMap.get(selector);
if (groups == null) {
groups = new ArrayList<>();
}
groups.add(group);
constFitGroupMap.put(selector.getName(), groups);
fitGroupMap.remove(fitGroupMap.get(group));
}
}
if (constFitGroupMap.size() == 1) {
for (String string : constFitGroupMap.keySet()) {
((StringArgument) arguments.get(pos)).setValue(string);
for (Group group : constFitGroupMap.get(string)) {
matchGroupArgument(arguments, pos, group, list);
}
}
} else {
for (Group group : fitGroupMap.keySet()) {
matchGroupArgument(arguments, pos, group, list);
}
if (list.size() == 1) {
MatchResult r = list.get(0);
if (sw.getLowerBound() > 0) {
r.incrPriority();
}
}
}
int lSize = list.size();
for (int j = 0; j < lSize; j++) {
MatchResult r = list.get(j);
if (r.isMatched()
|| r.isImplicit()/* && !r.isMatchWithErrors() */) {
List<MatchResult> ress2 = new ArrayList<>();
counts.put(r, Integer.valueOf(count + 1));
ress.add(r);
if (count + 1 == upperBound) {
continue;
}
if (r.getArgumentsUsed() > 0) {
matchSwitch(arguments, pos + r.getArgumentsUsed(), sw,
ress2, counts, count + 1, upperBound);
for (int k = 0; k < ress2.size(); k++) {
MatchResult r2 = ress2.get(k);
if (r2.isMatched() && !r2.isMatchWithErrors()) {
r2.setArgumentsUsed(r.getArgumentsUsed()
+ r2.getArgumentsUsed());
r2.setSummaryPriorityOf(r, r2);
r2.getBlockArguments()
.addAll(r.getBlockArguments());
r2.getErrors().addAll(r.getErrors());
r2.getMapping().putAll(r.getMapping());
r2.getComplexArguments()
.addAll(r.getComplexArguments());
ress.add(r2);
}
}
}
} else {
errors.addAll(r.getErrors());
}
}
}
private void matchGroupArgument(List<TclArgument> arguments, int pos,
Argument definition, List<MatchResult> results) {
Group group = (Group) definition;
int lowerBound = group.getLowerBound();
int upperBound = group.getUpperBound();
if (upperBound == -1) {
upperBound = Integer.MAX_VALUE;
}
Constant constant = DefinitionUtils.extractGroupPseudoConstant(group);
List<Argument> groupArguments = group.getArguments();
if (constant != null) {
List<Argument> newArgs = new ArrayList<>();
newArgs.add(constant);
newArgs.addAll(group.getArguments());
groupArguments = newArgs;
}
List<MatchResult> ress = new ArrayList<>();
Map<MatchResult, Integer> counts = new HashMap<>();
matchGroup(arguments, pos, groupArguments, ress, counts, 0, upperBound);
int ressSize = ress.size();
for (int i = 0; i < ressSize; i++) {
MatchResult r = ress.get(i);
Integer count = counts.get(r);
if (count != null && count.intValue() < lowerBound) {
r.setMatchWithErrors(true);
reportMissingArgument(definition, r, this.command.getStart(),
command.getEnd());
results.add(r);
}
if (count != null && count.intValue() >= lowerBound
&& count.intValue() <= upperBound) {
results.add(r);
}
// if (count != null && count.intValue() > upperBound) {
// r.setMatchWithErrors(true);
// reportInvalidArgumentCount(definition, r);
// results.add(r);
// }
}
if (ressSize == 0 && lowerBound > 0) {
// We should report error if not argument are specified, but
// required.
MatchResult r = new MatchResult();
r.setArgumentsUsed(0);
r.setMatched(false);
r.setMatchWithErrors(false);
reportMissingGroup(group, r);
results.add(r);
} else if (/* ress.size() == 0 && */lowerBound == 0) {
// Add empty variant if multiplicity support it.
MatchResult r = new MatchResult();
r.setArgumentsUsed(0);
r.setMatched(true);
r.setMatchWithErrors(false);
results.add(r);
}
}
private void matchGroup(List<TclArgument> arguments, int pos,
List<Argument> groupArguments, List<MatchResult> ress,
Map<MatchResult, Integer> counts, int count, int upperBound) {
TclErrorCollector errors = new TclErrorCollector();
if (pos >= arguments.size()) {
return;
}
// TODO: Multiple results is very slow. Why?
// List<MatchResult> results = matchArgumentList(arguments, pos,
// groupArguments, 0);
//
// for (MatchResult r : results) {
for (int i = 0; i < 1; ++i) {
MatchResult r = matchArgument(arguments, pos, groupArguments, 0);
if (r.isMatched() || r.isImplicit() || r.isMatchWithErrors()) {
List<MatchResult> ress2 = new ArrayList<>();
counts.put(r, Integer.valueOf(count + 1));
ress.add(r);
if (count + 1 == upperBound) {
continue;
}
if (r.getArgumentsUsed() > 0) {
matchGroup(arguments, pos + r.getArgumentsUsed(),
groupArguments, ress2, counts, count + 1,
upperBound);
int ress2Size = ress2.size();
for (int k = 0; k < ress2Size; k++) {
MatchResult r2 = ress2.get(k);
if (r2.isMatched()/* && !r2.isMatchWithErrors() */) {
r2.setArgumentsUsed(r.getArgumentsUsed()
+ r2.getArgumentsUsed());
r2.setSummaryPriorityOf(r, r2);
r2.getBlockArguments()
.addAll(r.getBlockArguments());
r2.getErrors().addAll(r.getErrors());
r2.getMapping().putAll(r.getMapping());
r2.getComplexArguments()
.addAll(r.getComplexArguments());
ress.add(r2);
}
}
}
} else {
errors.addAll(r.getErrors());
}
}
}
private void matchConstant(List<TclArgument> arguments, int pos,
Argument definition, List<MatchResult> results) {
final Constant constDefinition = (Constant) definition;
final String value = constDefinition.getName();
matchSinglePositionArgument(results, arguments, pos, definition,
(argument, definition1, scriptPositions, position, collector,
complex) -> {
SubstitutedArgumentValue argumentValue = getSubstitutedArgumentValue(
argument);
// TODO: Add option to strictly or not check constants.
if (value.equals(argumentValue.value)) {
return true;
}
// else if (!constDefinition.isStrictMatch()
// && argumentValue.value == null) {
// return true;
// }
return false;
}, true);
}
private void matchTypedArgument(List<TclArgument> arguments, int pos,
Argument definition, List<MatchResult> results) {
TypedArgument arg = (TypedArgument) definition;
final ArgumentType type = arg.getType();
if (ArgumentType.EXPRESSION_VALUE == type.getValue()) {
matchExpression(arguments, pos, definition, results);
} else {
matchSinglePositionArgument(results, arguments, pos, definition,
(argument, definition1, scriptPositions, position,
collector, complex) -> {
if (checkType(argument, type, scriptPositions, position,
collector)) {
return true;
}
return false;
}, false);
}
}
private void matchSinglePositionArgument(List<MatchResult> results,
List<TclArgument> arguments, int pos, Argument definitionArg,
ISinglePositionRule rule, boolean returnMaxMatchedResult) {
int lowerBound = definitionArg.getLowerBound();
int upperBound = definitionArg.getUpperBound();
if (upperBound == -1) {
upperBound = Integer.MAX_VALUE;
}
int count = 0;
List<Integer> scriptPositions = new ArrayList<>();
List<ComplexArgumentResult> complexArguments = new ArrayList<>();
// We need to check for low
Map<Integer, TclErrorCollector> collectors = new HashMap<>();
int argsSize = arguments.size();
for (int i = 0; i < argsSize - pos; i++) {
TclErrorCollector collector = new TclErrorCollector();
TclArgument a = arguments.get(pos + i);
if (rule.check(a, definitionArg, scriptPositions, pos + i,
collector, complexArguments)) {
collectors.put(Integer.valueOf(i), collector);
count++;
} else {
if (i < lowerBound)
collectors.put(Integer.valueOf(i), collector);
break;
}
// Do not, check extra arguments
if (count == upperBound) {
break;
}
}
if (count < lowerBound) {
MatchResult r = new MatchResult();
r.setArgumentsUsed(count);
r.setMatched(count > 0);
r.setMatchWithErrors(true);
int start = this.command.getStart();
int end = this.command.getEnd();
if (argsSize > pos) {
TclArgument arg = arguments.get(pos);
start = arg.getStart();
end = arg.getEnd();
}
reportMissingArgument(definitionArg, r, start, end);
// Add argument errors
for (Integer integer : collectors.keySet()) {
if (integer.intValue() <= count) {
r.getErrors().addAll(collectors.get(integer));
}
}
r.getMapping().put(definitionArg, new int[] { pos, pos + count });
results.add(r);
}
if (count >= lowerBound) {
int up = count;
if (up > upperBound) {
up = upperBound;
}
int from = lowerBound;
if (returnMaxMatchedResult) {
from = count;
}
for (int i = from; i <= up; i++) {
MatchResult r = new MatchResult();
r.setArgumentsUsed(i);
r.setMatched(true);
r.setMatchWithErrors(false);
for (Integer integer : scriptPositions) {
if (integer.intValue() < pos + i) {
r.getBlockArguments().add(integer);
}
}
for (ComplexArgumentResult arg : complexArguments) {
if (arg.getArgumentNumber() < pos + i) {
r.getComplexArguments().add(arg);
}
}
// Add argument errors
for (Integer integer : collectors.keySet()) {
if (integer.intValue() <= i) {
r.getErrors().addAll(collectors.get(integer));
}
}
r.getMapping().put(definitionArg, new int[] { pos, pos + i });
results.add(r);
}
}
// if (lowerBound == 0) {
// // Add empty variant if multiplicity support it.
// MatchResult r = new MatchResult();
// r.setArgumentsUsed(0);
// r.setMatched(true);
// r.setMatchWithErrors(false);
// results.add(r);
// }
// if (count > upperBound) {
// for (int i = upperBound + 1; i <= count; i++) {
// MatchResult r = new MatchResult();
// r.setArgumentsUsed(i);
// r.setMatched(true);
// r.setMatchWithErrors(true);
// TclArgument begin = arguments.get(pos + upperBound);
// TclArgument end = begin;
// if (pos + count < arguments.size()) {
// end = arguments.get(pos + count);
// } else {
// end = arguments.get(arguments.size() - 1);
// }
// r.getErrors().report(
// ITclErrorReporter.INVALID_ARGUMENT_COUNT,
// "To many arguments:"
// + definitionToString(definitionArg),
// begin.getEnd(), end.getEnd(), ITclErrorReporter.ERROR);
// for (Integer integer : scriptPositions) {
// if (integer.intValue() < pos + i) {
// r.getBlockArguments().add(integer);
// }
// }
// for (ComplexArgumentResult arg : complexArguments) {
// if (arg.getArgumentNumber() < pos + i) {
// r.getComplexArguments().add(arg);
// }
// }
// // Add argument errors
// for (Integer integer : collectors.keySet()) {
// if (integer.intValue() <= i) {
// r.getErrors().addAll(collectors.get(integer));
// }
// }
// r.getMapping().put(definitionArg, new int[] { pos, pos + i });
// results.add(r);
// }
// }
}
protected boolean checkType(TclArgument argument, ArgumentType type,
List<Integer> scriptPositions, int position,
TclErrorCollector collector) {
boolean result = false;
boolean reported = false;
SubstitutedArgumentValue arg = getSubstitutedArgumentValue(argument);
String value = arg.value;
if (value == null) {
// This is not resolved substitution.
// Pass as checked true.
return true;
}
switch (type.getValue()) {
case ArgumentType.SCRIPT_VALUE: {
if (!value.startsWith("-")) {
scriptPositions.add(Integer.valueOf(position));
result = true;
}
break;
}
case ArgumentType.INTEGER_VALUE: {
try {
if (value.startsWith("+")) {
value = value.substring(1);
}
Integer.parseInt(value);
result = true;
} catch (NumberFormatException e) {
result = false;
// reported = true;
// reportInvalidArgumentValue(argument, type, collector, e
// .getMessage());
}
break;
}
case ArgumentType.NOT_NEGATIVE_VALUE: {
try {
int i = Integer.parseInt(value);
result = i >= 0;
if (!result) {
reported = true;
reportInvalidArgumentValue(argument, type, collector,
Messages.TclArgumentMatcher_Error_Number_is_negative);
}
} catch (NumberFormatException e) {
result = false;
}
break;
}
case ArgumentType.INDEX_VALUE: {
result = false;
try {
Integer.parseInt(value);
result = true;
} catch (NumberFormatException e) {
}
if (!result) {
if (value.startsWith("end")) {
String sub = value.substring(3);
if (sub.length() == 0) {
result = true;
} else {
char c = sub.charAt(0);
if (c == '+' || c == '-') {
String sub2 = sub.substring(1);
try {
int i = Integer.parseInt(sub2);
result = i >= 0;
} catch (NumberFormatException e) {
}
}
}
} else {
int pos = value.indexOf('+');
if (pos == -1) {
pos = value.indexOf('-');
}
if (pos != -1) {
String p1 = value.substring(0, pos);
String p2 = value.substring(pos + 1);
try {
int i = Integer.parseInt(p1);
result = i > 0;
} catch (NumberFormatException e) {
result = false;
}
if (result) {
try {
int i = Integer.parseInt(p2);
result = i >= 0;
} catch (NumberFormatException e) {
result = false;
}
}
}
}
}
break;
}
default:
result = true;
}
// Report generic error message
if (!result && !reported) {
reportInvalidArgumentValue(argument, type, collector, null);
}
return result;
}
private Map<Group, Argument> getFitGroupMap(List<TclArgument> arguments,
int pos, Switch sw) {
// List<Group> fit = new ArrayList<Group>();
Map<Group, Argument> fit = new HashMap<>();
String prefix = null;
TclArgument tclArgument = null;
if (arguments != null && pos < arguments.size()) {
tclArgument = arguments.get(pos);
if (tclArgument != null) {
if (tclArgument instanceof StringArgument) {
prefix = ((StringArgument) tclArgument).getValue();
}
} else
return fit;
}
Map<Group, Argument> selectorsMap = getSwitchSelectorMap(sw);
for (Group group : selectorsMap.keySet()) {
Argument selector = selectorsMap.get(group);
if (selector instanceof Constant) {
if (prefix != null) {
if (sw.isCheckPrefix()
&& selector.getName().startsWith(prefix)) {
fit.put(group, selector);
}
if (selector.getName().equals(prefix)) {
fit.clear();
fit.put(group, selector);
break;
}
}
} else if (selector instanceof TypedArgument) {
List<MatchResult> ress = matchDefinition(arguments, pos,
selector);
for (MatchResult r : ress) {
if (r.isMatched()) {
fit.put(group, selector);
break;
}
}
} else {
fit.put(group, selector);
}
}
return fit;
}
public Map<Group, Argument> getSwitchSelectorMap(Switch sw) {
Map<Group, Argument> map = new HashMap<>();
for (Group group : sw.getGroups()) {
map.put(group, getFirstSelector(group));
}
return map;
}
public Argument getFirstSelector(Argument definition) {
Argument selector = null;
if (definition instanceof Constant
|| definition instanceof TypedArgument
|| definition instanceof ComplexArgument) {
selector = DefinitionUtils.copyArgument(definition);
} else if (definition instanceof Group) {
Group group = (Group) definition;
Constant constant = DefinitionUtils
.extractGroupPseudoConstant(group);
if (constant != null) {
selector = constant;
} else if (group.getArguments().size() > 0) {
for (int i = 0; i < group.getArguments().size(); i++) {
if (group.getArguments().get(i).getLowerBound() > 0) {
selector = getFirstSelector(
group.getArguments().get(i));
break;
}
}
}
}
return selector;
}
public List<Argument> getFinalSelectors(Argument definition) {
List<Argument> selectors = new ArrayList<>();
if (definition instanceof Constant
|| definition instanceof TypedArgument
|| definition instanceof ComplexArgument) {
selectors.add(DefinitionUtils.copyArgument(definition));
} else if (definition instanceof Group) {
Group group = (Group) definition;
Constant constant = DefinitionUtils
.extractGroupPseudoConstant(group);
if (constant != null) {
selectors.add(constant);
} else {
EList<Argument> groupArgs = group.getArguments();
int groupArgsSize = groupArgs.size();
if (groupArgsSize > 0) {
for (int i = 0; i < groupArgsSize; i++) {
Argument gArg = groupArgs.get(i);
if (gArg.getLowerBound() > 0) {
selectors.addAll(getFinalSelectors(gArg));
break;
}
}
}
}
} else if (definition instanceof Switch) {
Switch sw = (Switch) definition;
for (Group group : sw.getGroups()) {
selectors.addAll(getFinalSelectors(group));
}
}
return selectors;
}
public TclErrorCollector getErrorReporter() {
return this.errors;
}
private String[] getExtraArgs() {
return null;
// return new String[] { IProblem.DESCRIPTION_ARGUMENT_PREFIX
// + "Synopsis:\n" + getSynopsis() };
// return null;// new String[] { SYNOPSIS_ARG + getSynopsis(),
// SHORT_ARG + getShortSynopsis() };
}
// Error reporting
private void reportInvalidArgumentCount(int start, int end, int count,
TclErrorCollector collector, Command definition) {
String message = Messages.TclArgumentMatcher_Invlid_Arguments;
collector.report(ITclErrorReporter.INVALID_ARGUMENT_COUNT, message,
getExtraArgs(), start, end, ITclErrorConstants.ERROR);
}
// private void reportInvalidComplexArgumentValue(TclArgument argument,
// TclErrorCollector collector) {
// String message = Messages.TclArgumentMatcher_Block_Argument_Expected;
// collector
// .report(ITclErrorReporter.INVALID_ARGUMENT_VALUE, message,
// argument.getStart(), argument.getEnd(),
// ITclErrorReporter.ERROR);
// }
private void reportInvalidArgumentValue(TclArgument argument,
ArgumentType type, TclErrorCollector collector, String additional) {
if (additional != null) {
collector.report(ITclErrorReporter.INVALID_ARGUMENT_VALUE, NLS.bind(
Messages.TclArgumentMatcher_Argument_Of_Type_ExpectedDetail,
new Object[] { synopsisBuilder.typeToString(type),
synopsisBuilder.argumentToString(argument),
additional }),
getExtraArgs(), argument.getStart(), argument.getEnd(),
ITclErrorReporter.ERROR);
} else {
collector.report(ITclErrorReporter.INVALID_ARGUMENT_VALUE, NLS.bind(
Messages.TclArgumentMatcher_Argument_Of_Type_Expected,
new Object[] { synopsisBuilder.typeToString(type),
synopsisBuilder.argumentToString(argument) }),
getExtraArgs(), argument.getStart(), argument.getEnd(),
ITclErrorReporter.ERROR);
}
}
private void reportExtraArguments(List<TclArgument> list, int argumentsUsed,
TclErrorCollector collector) {
if (list.size() > argumentsUsed) {
TclArgument begin = list.get(argumentsUsed);
TclArgument end = list.get(list.size() - 1);
String message = NLS.bind(
Messages.TclArgumentMatcher_Extra_Arguments, new Object[] {
Integer.valueOf(list.size() - argumentsUsed) });
collector.report(ITclErrorConstants.EXTRA_ARGUMENTS, message,
getExtraArgs(), begin.getStart(), end.getEnd(),
ITclErrorConstants.WARNING);
}
}
private void reportMissingArgument(Argument definitionArg, MatchResult r,
int start, int end) {
String value;
String arg = synopsisBuilder.definitionToString(
DefinitionUtils.minimizeBounds(definitionArg));
if (definitionArg instanceof TypedArgument) {
String type = synopsisBuilder
.typeToString(((TypedArgument) definitionArg).getType())
.toLowerCase();
value = NLS.bind(Messages.TclArgumentMatcher_Missing_TypedArgument,
new Object[] { type, arg });
} else {
value = NLS.bind(Messages.TclArgumentMatcher_Missing_Argument,
new Object[] { arg });
}
r.getErrors().report(ITclErrorReporter.MISSING_ARGUMENT, value,
getExtraArgs(), start, end, ITclErrorReporter.ERROR);
}
// private void reportMissingArguments(List<Argument> definitions,
// MatchResult r, int start, int end) {
// if (definitions.size() == 1) {
// reportMissingArgument(definitions.get(0), r, start, end);
// } else {
// String value = MessageFormat.format(
// Messages.TclArgumentMatcher_Missing_Argument,
// new Object[] { synopsisBuilder
// .definitionToString(definitions) });
// r.getErrors().report(ITclErrorReporter.MISSING_ARGUMENT, value,
// getExtraArgs(), start, end, ITclErrorReporter.ERROR);
// }
// }
private void reportMissingGroup(Group group, MatchResult r) {
Argument selector = getFirstSelector(group);
String message = NLS.bind(Messages.TclArgumentMatcher_Missing_Argument,
new Object[] { synopsisBuilder.definitionToString(selector) });
r.getErrors().report(ITclErrorReporter.MISSING_ARGUMENT, message,
getExtraArgs(), this.command.getStart(), this.command.getEnd(),
ITclErrorReporter.ERROR);
}
private void reportMissingSwitch(Switch sw, MatchResult r,
TclArgument tclArgument) {
List<TclArgument> arguments = new ArrayList<>();
arguments.add(tclArgument);
Map<Group, Argument> fitGroups = getFitGroupMap(arguments, 0, sw);
List<Argument> selectors = new ArrayList<>();
if (fitGroups.size() == 0) {
for (Group group : sw.getGroups()) {
selectors.addAll(getFinalSelectors(group));
}
} else {
for (Group group : sw.getGroups()) {
if (fitGroups.containsKey(group))
selectors.addAll(getFinalSelectors(group));
}
}
String expected = synopsisBuilder
.definitionToList(DefinitionUtils.minimizeBounds(selectors));
String message = "";
int code = 0;
if (tclArgument != null) {
message = NLS.bind(
Messages.TclArgumentMatcher_Invalid_Arguments_And_Expected,
new Object[] { expected,
synopsisBuilder.argumentToString(tclArgument) });
code = ITclErrorReporter.INVALID_ARGUMENT_VALUE;
r.getErrors().report(code, message, getExtraArgs(),
tclArgument.getStart(), tclArgument.getEnd(),
ITclErrorReporter.ERROR);
return;
} else {
message = NLS.bind(
Messages.TclArgumentMatcher_Missing_Switch_Argument,
new Object[] { expected });
code = ITclErrorReporter.MISSING_ARGUMENT;
}
if (expected.length() == 0) {
message = Messages.TclArgumentMatcher_Missing_Switch_Arg;
}
r.getErrors().report(code, message, getExtraArgs(),
this.command.getStart(), this.command.getEnd(),
ITclErrorReporter.ERROR);
}
public Map<Argument, int[]> getMappings() {
return new HashMap<>(this.mappings);
}
}