blob: 6e9581f477510cce3121dc262716d73455e7aee6 [file] [log] [blame]
/*******************************************************************************
* 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.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.dltk.tcl.ast.ArgumentMatch;
import org.eclipse.dltk.tcl.ast.AstFactory;
import org.eclipse.dltk.tcl.ast.ComplexString;
import org.eclipse.dltk.tcl.ast.Node;
import org.eclipse.dltk.tcl.ast.Script;
import org.eclipse.dltk.tcl.ast.StringArgument;
import org.eclipse.dltk.tcl.ast.Substitution;
import org.eclipse.dltk.tcl.ast.TclArgument;
import org.eclipse.dltk.tcl.ast.TclArgumentList;
import org.eclipse.dltk.tcl.ast.TclCommand;
import org.eclipse.dltk.tcl.ast.VariableReference;
import org.eclipse.dltk.tcl.definitions.Argument;
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.internal.parser.raw.BracesSubstitution;
import org.eclipse.dltk.tcl.internal.parser.raw.CommandSubstitution;
import org.eclipse.dltk.tcl.internal.parser.raw.QuotesSubstitution;
import org.eclipse.dltk.tcl.internal.parser.raw.SimpleTclParser;
import org.eclipse.dltk.tcl.internal.parser.raw.TclParseException;
import org.eclipse.dltk.tcl.internal.parser.raw.TclScript;
import org.eclipse.dltk.tcl.internal.parser.raw.TclWord;
import org.eclipse.emf.common.util.EList;
public class TclParserUtils implements ITclParserOptions {
public static final Pattern VERSION_PATTERN = Pattern
.compile("([\\(\\[][^\\(\\)]*[\\)\\]])"); //$NON-NLS-1$
public static final Pattern INTERVAL_PATTERN = Pattern
.compile("([\\(\\[])(.*)[:;](.*)([\\)\\]])"); //$NON-NLS-1$
public static final Pattern VALID_VERSION_PATTERN = Pattern.compile(
"([\\(\\[](([0-9]+(\\.[0-9]+)*)|-)[;:](([0-9]+(\\.[0-9]+)*)|-)[\\)\\]]\\s*)*"); //$NON-NLS-1$
public static boolean isVersionValid(String version) {
if (version == null || version.length() == 0)
return true;
version = version.trim();
Matcher matcher = VALID_VERSION_PATTERN.matcher(version);
return matcher.matches();
}
public static boolean parseVersion(String version, String currentVersion) {
if (version == null || version.length() == 0)
return false;
Matcher versionMatcher = VERSION_PATTERN.matcher(version);
boolean isValid = false;
while (versionMatcher.find()) {
boolean isIntervalValid = true;
String interval = versionMatcher.group(1);
Matcher intervalMatcher = INTERVAL_PATTERN.matcher(interval);
while (intervalMatcher.find()) {
String lowerType = intervalMatcher.group(1);
String lowerVersion = intervalMatcher.group(2);
String upperVersion = intervalMatcher.group(3);
String upperType = intervalMatcher.group(4);
if (!lowerVersion.equals("-")) { //$NON-NLS-1$
if (lowerType.equals("(") //$NON-NLS-1$
&& compareVersions(currentVersion,
lowerVersion) <= 0) {
isIntervalValid = false;
continue;
}
if (lowerType.equals("[") //$NON-NLS-1$
&& compareVersions(currentVersion,
lowerVersion) < 0) {
isIntervalValid = false;
continue;
}
}
if (!upperVersion.equals("-")) { //$NON-NLS-1$
if (upperType.equals(")") //$NON-NLS-1$
&& compareVersions(currentVersion,
upperVersion) >= 0) {
isIntervalValid = false;
continue;
}
if (upperType.equals("]") //$NON-NLS-1$
&& compareVersions(currentVersion,
upperVersion) > 0) {
isIntervalValid = false;
continue;
}
}
}
if (isIntervalValid) {
isValid = true;
break;
}
}
return isValid;
}
public static int compareVersions(String v1, String v2) {
String[] splited1 = v1.split("\\."); //$NON-NLS-1$
String[] splited2 = v2.split("\\."); //$NON-NLS-1$
int res = splited1.length - splited2.length;
int min = (splited1.length < splited2.length) ? splited1.length
: splited2.length;
for (int i = 0; i < min; i++) {
if (Integer.parseInt(splited1[i]) > Integer.parseInt(splited2[i]))
return 1;
if (Integer.parseInt(splited1[i]) < Integer.parseInt(splited2[i]))
return -1;
}
return res;
}
public static List<TclArgument> parseCommandArguments(int offset,
String content, List<Integer> blockArguments) {
List<TclArgument> results = new ArrayList<>();
try {
AstFactory factory = AstFactory.eINSTANCE;
SimpleTclParser parser = new SimpleTclParser();
parser.setSkipComments(false);
TclScript script;
try {
script = parser.parse(content);
} catch (TclParseException e) {
e.printStackTrace();
return results;
}
List<org.eclipse.dltk.tcl.internal.parser.raw.TclCommand> commands = script
.getCommands();
for (int i = 0; i < commands.size(); i++) {
org.eclipse.dltk.tcl.internal.parser.raw.TclCommand command = commands
.get(i);
for (final TclWord word : command.getWords()) {
final TclArgument exp;
Object o = word.getContents().get(0);
if (o instanceof QuotesSubstitution) {
QuotesSubstitution qs = (QuotesSubstitution) o;
StringArgument literal = factory.createStringArgument();
literal.setStart(offset + qs.getStart());
literal.setEnd(offset + qs.getEnd() + 1);
final String value = content.substring(word.getStart(),
word.getEnd() + 1);
literal.setValue(value);
literal.setRawValue(value);
exp = literal;
} else if (o instanceof BracesSubstitution) {
BracesSubstitution bs = (BracesSubstitution) o;
StringArgument block = factory.createStringArgument();
block.setStart(offset + bs.getStart());
block.setEnd(offset + bs.getEnd() + 1);
final String value = content.substring(word.getStart(),
word.getEnd() + 1);
block.setValue(value);
block.setRawValue(value);
exp = block;
} else if (o instanceof CommandSubstitution
&& word.getContents().size() == 1) {
CommandSubstitution bs = (CommandSubstitution) o;
StringArgument bl = factory.createStringArgument();
bl.setStart(offset + bs.getStart());
bl.setEnd(offset + bs.getEnd() + 1);
final String value = content.substring(word.getStart(),
word.getEnd() + 1);
bl.setValue(value);
bl.setRawValue(value);
if (blockArguments != null) {
blockArguments.add(results.size());
}
exp = bl;
} else {
StringArgument reference = factory
.createStringArgument();
reference.setStart(offset + word.getStart());
reference.setEnd(offset + word.getEnd() + 1);
final String value = content.substring(word.getStart(),
word.getEnd() + 1);
reference.setValue(value);
reference.setRawValue(value);
exp = reference;
}
results.add(exp);
}
}
return results;
} catch (StringIndexOutOfBoundsException bounds) {
return results;
}
}
public static <T> void traverse(List<T> nodes, TclVisitor visitor) {
for (int i = 0; i < nodes.size(); i++) {
Node nde = (Node) nodes.get(i);
if (nde instanceof Script) {
Script script = (Script) nde;
if (visitor.visit(script)) {
traverse(script.getCommands(), visitor);
visitor.endVisit(script);
}
} else if (nde instanceof Substitution) {
Substitution substitution = (Substitution) nde;
if (visitor.visit(substitution)) {
traverse(substitution.getCommands(), visitor);
visitor.endVisit(substitution);
}
} else if (nde instanceof TclCommand) {
TclCommand command = (TclCommand) nde;
if (visitor.visit(command)) {
traverse(Collections.singletonList(command.getName()),
visitor);
traverse(command.getArguments(), visitor);
visitor.endVisit(command);
}
} else if (nde instanceof StringArgument) {
StringArgument argument = (StringArgument) nde;
if (visitor.visit(argument)) {
visitor.endVisit(argument);
}
} else if (nde instanceof TclArgumentList) {
TclArgumentList list = (TclArgumentList) nde;
if (visitor.visit(list)) {
traverse(list.getArguments(), visitor);
visitor.endVisit(list);
}
} else if (nde instanceof ComplexString) {
ComplexString list = (ComplexString) nde;
if (visitor.visit(list)) {
traverse(list.getArguments(), visitor);
visitor.endVisit(list);
}
} else if (nde instanceof VariableReference) {
VariableReference list = (VariableReference) nde;
if (visitor.visit(list)) {
TclArgument index = list.getIndex();
if (index != null) {
traverse(Collections.singletonList(index), visitor);
}
visitor.endVisit(list);
}
}
}
}
public static String getSynopsis(Command command) {
if (command == null)
return null;
List<StringBuilder> list = new ArrayList<>();
StringBuilder synopsis = new StringBuilder();
String name = command.getName();
if (name != null && name.length() != 0) {
list.add(new StringBuilder(name));
} else {
// TODO error : bad definition
}
for (int i = 0; i < command.getArguments().size(); i++) {
Argument arg = command.getArguments().get(i);
list = concatSynopsises(list, getSynopsisArgInfo(arg, i));
}
boolean first = true;
for (StringBuilder str : list) {
if (!first)
synopsis.append("\n"); //$NON-NLS-1$
else
first = false;
synopsis.append(str.toString());
}
return synopsis.toString();
}
static final int SUBCOMMAND = 0;
static final int OPTIONS = 1;
static final int MODE = 2;
static final int REGULAR = 3;
private static List<StringBuilder> getSynopsisArgInfo(Argument arg,
int pos) {
List<StringBuilder> list = new ArrayList<>();
if (arg instanceof Constant) {
list.add(new StringBuilder(((Constant) arg).getName()));
} else if (arg instanceof TypedArgument) {
list.add(new StringBuilder(((TypedArgument) arg).getName()));
} else if (arg instanceof Group) {
String constant = ((Group) arg).getConstant();
if (constant != null && constant.length() != 0)
list.add(new StringBuilder(constant));
for (Argument sub : ((Group) arg).getArguments()) {
list = concatSynopsises(list, getSynopsisArgInfo(sub, pos + 1));
}
} else if (arg instanceof ComplexArgument) {
for (Argument sub : ((ComplexArgument) arg).getArguments()) {
list = concatSynopsises(list, getSynopsisArgInfo(sub, pos + 1));
}
for (StringBuilder sub : list) {
sub.insert(0, "{"); //$NON-NLS-1$
sub.append("}"); //$NON-NLS-1$
}
} else if (arg instanceof Switch) {
int type = REGULAR;
String constant = null;
if (((Switch) arg).getGroups() != null) {
constant = ((Switch) arg).getGroups().get(0).getConstant();
if (constant != null && constant.length() != 0) {
if (constant.startsWith("-")) { //$NON-NLS-1$
type = OPTIONS;
} else {
if (pos == 0)
type = SUBCOMMAND;
else
type = MODE;
}
}
} else {
// TODO bad definition
}
switch (type) {
case REGULAR:
if (arg.getLowerBound() == 0) {
list.add(new StringBuilder());
}
case SUBCOMMAND:
for (Group group : ((Switch) arg).getGroups()) {
list.addAll(getSynopsisArgInfo(group, pos + 1));
}
return list;
case OPTIONS:
case MODE:
boolean first = true;
StringBuilder options = new StringBuilder();
options.append("<"); //$NON-NLS-1$
for (Group group : ((Switch) arg).getGroups()) {
for (StringBuilder str : getSynopsisArgInfo(group,
pos + 1)) {
if (!first)
options.append("|"); //$NON-NLS-1$
else
first = false;
options.append(str);
}
}
options.append(">"); //$NON-NLS-1$
list.add(options);
}
}
for (StringBuilder res : list) {
if (arg.getUpperBound() == 0) {
// TODO error : bad definition
} else if (arg.getUpperBound() == -1) {
if (arg.getLowerBound() == 0) {
res.append(" ..."); //$NON-NLS-1$
res.insert(0, "?"); //$NON-NLS-1$
res.append("?"); //$NON-NLS-1$
} else {
String value = res.toString();
for (int i = 0; i < arg.getUpperBound() - 1; i++)
res.append(" ").append(value); //$NON-NLS-1$
res.append(" ?").append(value).append(" ...?"); //$NON-NLS-1$ //$NON-NLS-2$
}
} else if (arg.getUpperBound() == 1) {
if (arg.getLowerBound() == 0) {
res.insert(0, "?"); //$NON-NLS-1$
res.append("?"); //$NON-NLS-1$
}
} else {
String value = res.toString();
for (int i = 0; i < arg.getUpperBound(); i++)
res.append(" ").append(value); //$NON-NLS-1$
}
}
return list;
}
private static List<StringBuilder> concatSynopsises(
List<StringBuilder> prefixes, List<StringBuilder> ss) {
List<StringBuilder> newList = new ArrayList<>();
if (prefixes.size() == 0)
return ss;
for (StringBuilder prefix : prefixes) {
for (StringBuilder s : ss) {
newList.add(new StringBuilder(prefix.length() + s.length() + 1)
.append(prefix).append(" ").append(s)); //$NON-NLS-1$
}
}
return newList;
}
/*
* static DefinitionsFactory factory = DefinitionsFactory.eINSTANCE; public
* Command getDefinition(String synopsis) { Command command =
* factory.createCommand(); StringTokenizer tokenizer = new
* StringTokenizer(synopsis); boolean first = true; while
* (tokenizer.hasMoreTokens()){ String token = tokenizer.nextToken(" "); if
* (first) { command.setName(token); first = false; } else { Argument arg =
* null; if (token.startsWith("?")){ if (token.endsWith("?")){ arg =
* factory.createTypedArgument(); arg.setLowerBound(0);
* ((TypedArgument)arg).setName(token.substring(1,token.length()-1)); } else
* { arg = factory.createGroup(); boolean isOver = false; while
* (tokenizer.hasMoreTokens() && !isOver){ String subToken =
* tokenizer.nextToken(" "); if (subToken.endsWith("?")){ subToken =
* subToken.substring(0,subToken.length()-1); if (subToken.equals("...")){
* arg.setUpperBound(-1); break; } } } } } command.getArguments().add(arg);
* }
*
* } return command; }
*/
/**
* Return empty list if not matched.
*
* @param command
* @param name
* @return
*/
public static TclArgument[] getTypedMatch(TclCommand command, String name) {
EList<ArgumentMatch> matches = command.getMatches();
List<TclArgument> results = new ArrayList<>();
for (ArgumentMatch argumentMatch : matches) {
Argument definition = argumentMatch.getDefinition();
if (definition instanceof TypedArgument) {
TypedArgument arg = (TypedArgument) definition;
if (name.equals(arg.getName())) {
results.addAll(argumentMatch.getArguments());
}
}
}
return results.toArray(new TclArgument[results.size()]);
}
}