| /******************************************************************************* |
| * 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()]); |
| } |
| } |