Changed license to Apache License 2.0
This commit is contained in:
parent
d3bf0be9d7
commit
9f36d71529
144 changed files with 32422 additions and 32544 deletions
|
@ -1,164 +1,163 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2017 Nuclex Development Labs
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the IBM Common Public License as
|
||||
published by the IBM Corporation; either version 1.0 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
IBM Common Public License for more details.
|
||||
|
||||
You should have received a copy of the IBM Common Public
|
||||
License along with this library
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace Nuclex.Support.Parsing {
|
||||
|
||||
partial class CommandLine {
|
||||
|
||||
/// <summary>Argument being specified on an application's command line</summary>
|
||||
public class Argument {
|
||||
|
||||
/// <summary>Initializes a new option with only a name</summary>
|
||||
/// <param name="raw">
|
||||
/// String segment with the entire argument as it was given on the command line
|
||||
/// </param>
|
||||
/// <param name="nameStart">Absolute index the argument name starts at</param>
|
||||
/// <param name="nameLength">Number of characters in the option name</param>
|
||||
/// <returns>The newly created option</returns>
|
||||
internal static Argument OptionOnly(
|
||||
StringSegment raw,
|
||||
int nameStart, int nameLength
|
||||
) {
|
||||
return new Argument(raw, nameStart, nameLength, -1, -1);
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new argument with only a value</summary>
|
||||
/// <param name="raw">
|
||||
/// String segment with the entire argument as it was given on the command line
|
||||
/// </param>
|
||||
/// <param name="valueStart">Absolute index the value starts at</param>
|
||||
/// <param name="valueLength">Number of characters in the value</param>
|
||||
/// <returns>The newly created option</returns>
|
||||
internal static Argument ValueOnly(
|
||||
StringSegment raw,
|
||||
int valueStart, int valueLength
|
||||
) {
|
||||
return new Argument(raw, -1, -1, valueStart, valueLength);
|
||||
}
|
||||
|
||||
/// <summary>Creates a new option with a name and an assigned value</summary>
|
||||
/// <param name="raw">
|
||||
/// String segment containing the entire option as it was given on the command line
|
||||
/// </param>
|
||||
/// <param name="nameStart">Absolute index the option name starts at</param>
|
||||
/// <param name="nameLength">Number of characters in the option name</param>
|
||||
/// <param name="valueStart">Absolute index the value starts at</param>
|
||||
/// <param name="valueLength">Number of characters in the value</param>
|
||||
/// <returns>The newly created option</returns>
|
||||
internal Argument(
|
||||
StringSegment raw,
|
||||
int nameStart, int nameLength,
|
||||
int valueStart, int valueLength
|
||||
) {
|
||||
this.raw = raw;
|
||||
this.nameStart = nameStart;
|
||||
this.nameLength = nameLength;
|
||||
this.valueStart = valueStart;
|
||||
this.valueLength = valueLength;
|
||||
}
|
||||
|
||||
/// <summary>Contains the raw string the command line argument was parsed from</summary>
|
||||
public string Raw {
|
||||
get { return this.raw.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>Characters used to initiate this option</summary>
|
||||
public string Initiator {
|
||||
get {
|
||||
if(this.nameStart == -1) {
|
||||
return null;
|
||||
} else {
|
||||
return this.raw.Text.Substring(
|
||||
this.raw.Offset, this.nameStart - this.raw.Offset
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Name of the command line option</summary>
|
||||
public string Name {
|
||||
get {
|
||||
if(this.nameStart == -1) {
|
||||
return null;
|
||||
} else {
|
||||
return this.raw.Text.Substring(this.nameStart, this.nameLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Characters used to associate a value to this option</summary>
|
||||
public string Associator {
|
||||
get {
|
||||
if(this.nameStart == -1) {
|
||||
return null;
|
||||
} else {
|
||||
int associatorStart = this.nameStart + this.nameLength;
|
||||
|
||||
if(this.valueStart == -1) {
|
||||
int characterCount = (this.raw.Offset + this.raw.Count) - associatorStart;
|
||||
if(characterCount == 0) {
|
||||
return null;
|
||||
}
|
||||
} else if(this.valueStart == associatorStart) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.raw.Text.Substring(associatorStart, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Name of the command line option</summary>
|
||||
public string Value {
|
||||
get {
|
||||
if(this.valueStart == -1) {
|
||||
return null;
|
||||
} else {
|
||||
return this.raw.Text.Substring(this.valueStart, this.valueLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The raw length of the command line argument</summary>
|
||||
internal int RawLength {
|
||||
get { return this.raw.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains the entire option as it was specified on the command line
|
||||
/// </summary>
|
||||
private StringSegment raw;
|
||||
|
||||
/// <summary>Absolute index in the raw string the option name starts at</summary>
|
||||
private int nameStart;
|
||||
/// <summary>Number of characters in the option name</summary>
|
||||
private int nameLength;
|
||||
/// <summary>Absolute index in the raw string the value starts at</summary>
|
||||
private int valueStart;
|
||||
/// <summary>Number of characters in the value</summary>
|
||||
private int valueLength;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Parsing
|
||||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
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.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
|
||||
namespace Nuclex.Support.Parsing {
|
||||
|
||||
partial class CommandLine {
|
||||
|
||||
/// <summary>Argument being specified on an application's command line</summary>
|
||||
public class Argument {
|
||||
|
||||
/// <summary>Initializes a new option with only a name</summary>
|
||||
/// <param name="raw">
|
||||
/// String segment with the entire argument as it was given on the command line
|
||||
/// </param>
|
||||
/// <param name="nameStart">Absolute index the argument name starts at</param>
|
||||
/// <param name="nameLength">Number of characters in the option name</param>
|
||||
/// <returns>The newly created option</returns>
|
||||
internal static Argument OptionOnly(
|
||||
StringSegment raw,
|
||||
int nameStart, int nameLength
|
||||
) {
|
||||
return new Argument(raw, nameStart, nameLength, -1, -1);
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new argument with only a value</summary>
|
||||
/// <param name="raw">
|
||||
/// String segment with the entire argument as it was given on the command line
|
||||
/// </param>
|
||||
/// <param name="valueStart">Absolute index the value starts at</param>
|
||||
/// <param name="valueLength">Number of characters in the value</param>
|
||||
/// <returns>The newly created option</returns>
|
||||
internal static Argument ValueOnly(
|
||||
StringSegment raw,
|
||||
int valueStart, int valueLength
|
||||
) {
|
||||
return new Argument(raw, -1, -1, valueStart, valueLength);
|
||||
}
|
||||
|
||||
/// <summary>Creates a new option with a name and an assigned value</summary>
|
||||
/// <param name="raw">
|
||||
/// String segment containing the entire option as it was given on the command line
|
||||
/// </param>
|
||||
/// <param name="nameStart">Absolute index the option name starts at</param>
|
||||
/// <param name="nameLength">Number of characters in the option name</param>
|
||||
/// <param name="valueStart">Absolute index the value starts at</param>
|
||||
/// <param name="valueLength">Number of characters in the value</param>
|
||||
/// <returns>The newly created option</returns>
|
||||
internal Argument(
|
||||
StringSegment raw,
|
||||
int nameStart, int nameLength,
|
||||
int valueStart, int valueLength
|
||||
) {
|
||||
this.raw = raw;
|
||||
this.nameStart = nameStart;
|
||||
this.nameLength = nameLength;
|
||||
this.valueStart = valueStart;
|
||||
this.valueLength = valueLength;
|
||||
}
|
||||
|
||||
/// <summary>Contains the raw string the command line argument was parsed from</summary>
|
||||
public string Raw {
|
||||
get { return this.raw.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>Characters used to initiate this option</summary>
|
||||
public string Initiator {
|
||||
get {
|
||||
if(this.nameStart == -1) {
|
||||
return null;
|
||||
} else {
|
||||
return this.raw.Text.Substring(
|
||||
this.raw.Offset, this.nameStart - this.raw.Offset
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Name of the command line option</summary>
|
||||
public string Name {
|
||||
get {
|
||||
if(this.nameStart == -1) {
|
||||
return null;
|
||||
} else {
|
||||
return this.raw.Text.Substring(this.nameStart, this.nameLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Characters used to associate a value to this option</summary>
|
||||
public string Associator {
|
||||
get {
|
||||
if(this.nameStart == -1) {
|
||||
return null;
|
||||
} else {
|
||||
int associatorStart = this.nameStart + this.nameLength;
|
||||
|
||||
if(this.valueStart == -1) {
|
||||
int characterCount = (this.raw.Offset + this.raw.Count) - associatorStart;
|
||||
if(characterCount == 0) {
|
||||
return null;
|
||||
}
|
||||
} else if(this.valueStart == associatorStart) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.raw.Text.Substring(associatorStart, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Name of the command line option</summary>
|
||||
public string Value {
|
||||
get {
|
||||
if(this.valueStart == -1) {
|
||||
return null;
|
||||
} else {
|
||||
return this.raw.Text.Substring(this.valueStart, this.valueLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The raw length of the command line argument</summary>
|
||||
internal int RawLength {
|
||||
get { return this.raw.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains the entire option as it was specified on the command line
|
||||
/// </summary>
|
||||
private StringSegment raw;
|
||||
|
||||
/// <summary>Absolute index in the raw string the option name starts at</summary>
|
||||
private int nameStart;
|
||||
/// <summary>Number of characters in the option name</summary>
|
||||
private int nameLength;
|
||||
/// <summary>Absolute index in the raw string the value starts at</summary>
|
||||
private int valueStart;
|
||||
/// <summary>Number of characters in the value</summary>
|
||||
private int valueLength;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Parsing
|
||||
|
|
|
@ -1,62 +1,61 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2017 Nuclex Development Labs
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the IBM Common Public License as
|
||||
published by the IBM Corporation; either version 1.0 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
IBM Common Public License for more details.
|
||||
|
||||
You should have received a copy of the IBM Common Public
|
||||
License along with this library
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Nuclex.Support.Parsing {
|
||||
|
||||
partial class CommandLine {
|
||||
|
||||
/// <summary>Formats a command line instance into a string</summary>
|
||||
internal static class Formatter {
|
||||
|
||||
/// <summary>
|
||||
/// Formats all arguments in the provided command line instance into a string
|
||||
/// </summary>
|
||||
/// <param name="commandLine">Command line instance that will be formatted</param>
|
||||
/// <returns>All arguments in the command line instance as a string</returns>
|
||||
public static string FormatCommandLine(CommandLine commandLine) {
|
||||
int totalLength = 0;
|
||||
for(int index = 0; index < commandLine.arguments.Count; ++index) {
|
||||
if(index != 0) {
|
||||
++totalLength; // For spacing between arguments
|
||||
}
|
||||
|
||||
totalLength += commandLine.arguments[index].RawLength;
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder(totalLength);
|
||||
for(int index = 0; index < commandLine.arguments.Count; ++index) {
|
||||
if(index != 0) {
|
||||
builder.Append(' ');
|
||||
}
|
||||
|
||||
builder.Append(commandLine.arguments[index].Raw);
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Parsing
|
||||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
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.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Nuclex.Support.Parsing {
|
||||
|
||||
partial class CommandLine {
|
||||
|
||||
/// <summary>Formats a command line instance into a string</summary>
|
||||
internal static class Formatter {
|
||||
|
||||
/// <summary>
|
||||
/// Formats all arguments in the provided command line instance into a string
|
||||
/// </summary>
|
||||
/// <param name="commandLine">Command line instance that will be formatted</param>
|
||||
/// <returns>All arguments in the command line instance as a string</returns>
|
||||
public static string FormatCommandLine(CommandLine commandLine) {
|
||||
int totalLength = 0;
|
||||
for(int index = 0; index < commandLine.arguments.Count; ++index) {
|
||||
if(index != 0) {
|
||||
++totalLength; // For spacing between arguments
|
||||
}
|
||||
|
||||
totalLength += commandLine.arguments[index].RawLength;
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder(totalLength);
|
||||
for(int index = 0; index < commandLine.arguments.Count; ++index) {
|
||||
if(index != 0) {
|
||||
builder.Append(' ');
|
||||
}
|
||||
|
||||
builder.Append(commandLine.arguments[index].Raw);
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Parsing
|
||||
|
|
|
@ -1,400 +1,399 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2017 Nuclex Development Labs
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the IBM Common Public License as
|
||||
published by the IBM Corporation; either version 1.0 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
IBM Common Public License for more details.
|
||||
|
||||
You should have received a copy of the IBM Common Public
|
||||
License along with this library
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Nuclex.Support.Parsing {
|
||||
|
||||
partial class CommandLine {
|
||||
|
||||
/// <summary>Parses command line strings</summary>
|
||||
private class Parser {
|
||||
|
||||
/// <summary>Initializes a new command line parser</summary>
|
||||
/// <param name="windowsMode">Whether the / character initiates an argument</param>
|
||||
private Parser(bool windowsMode) {
|
||||
this.windowsMode = windowsMode;
|
||||
this.arguments = new List<CommandLine.Argument>();
|
||||
}
|
||||
|
||||
/// <summary>Parses a string containing command line arguments</summary>
|
||||
/// <param name="commandLineString">String that will be parsed</param>
|
||||
/// <param name="windowsMode">Whether the / character initiates an argument</param>
|
||||
/// <returns>The parsed command line arguments from the string</returns>
|
||||
public static List<CommandLine.Argument> Parse(
|
||||
string commandLineString, bool windowsMode
|
||||
) {
|
||||
Parser theParser = new Parser(windowsMode);
|
||||
theParser.parseFullCommandLine(commandLineString);
|
||||
return theParser.arguments;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the provided string and adds the parameters found to
|
||||
/// the command line representation
|
||||
/// </summary>
|
||||
/// <param name="commandLineString">
|
||||
/// String containing the command line arguments that will be parsed
|
||||
/// </param>
|
||||
private void parseFullCommandLine(string commandLineString) {
|
||||
if(commandLineString == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Walk through the command line character by character and gather
|
||||
// the parameters and values to build the command line representation from
|
||||
for(int index = 0; index < commandLineString.Length; ) {
|
||||
|
||||
// Look for the next non-whitespace character
|
||||
index = StringHelper.IndexNotOfAny(
|
||||
commandLineString, WhitespaceCharacters, index
|
||||
);
|
||||
if(index == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Parse the chunk of characters at this location and advance the index
|
||||
// to the next location after the chunk of characters
|
||||
parseChunk(commandLineString, ref index);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a chunk of characters and adds it as an option or a loose value to
|
||||
/// the command line representation we're building
|
||||
/// </summary>
|
||||
/// <param name="commandLineString">
|
||||
/// String containing the chunk of characters that will be parsed
|
||||
/// </param>
|
||||
/// <param name="index">Index in the string at which to begin parsing</param>
|
||||
/// <returns>The number of characters that were consumed</returns>
|
||||
private void parseChunk(string commandLineString, ref int index) {
|
||||
int startIndex = index;
|
||||
|
||||
char currentCharacter = commandLineString[index];
|
||||
switch(currentCharacter) {
|
||||
|
||||
// Unix style argument using either '-' or "--" as its initiator
|
||||
case '-': {
|
||||
++index;
|
||||
|
||||
// Does the string end here? Stop parsing.
|
||||
if(index >= commandLineString.Length) {
|
||||
addValue(new StringSegment(commandLineString, startIndex, 1));
|
||||
break;
|
||||
}
|
||||
|
||||
// Does another '-' follow? Might be a unix style option or a loose "--"
|
||||
if(commandLineString[index] == '-') {
|
||||
++index;
|
||||
}
|
||||
|
||||
parsePotentialOption(commandLineString, startIndex, ref index);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Windows style argument using '/' as its initiator
|
||||
case '/': {
|
||||
// The '/ character is only used to initiate argument on windows and can be
|
||||
// toggled off. The application decides whether this is done depending on the
|
||||
// operating system or whether uniform behavior across platforms is desired.
|
||||
if(!this.windowsMode) {
|
||||
goto default;
|
||||
}
|
||||
|
||||
++index;
|
||||
parsePotentialOption(commandLineString, startIndex, ref index);
|
||||
break;
|
||||
}
|
||||
|
||||
// Quoted loose value
|
||||
case '"': {
|
||||
parseQuotedValue(commandLineString, ref index);
|
||||
break;
|
||||
}
|
||||
|
||||
// Unquoted loose value
|
||||
default: {
|
||||
parseNakedValue(commandLineString, ref index);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Parses a potential command line option</summary>
|
||||
/// <param name="commandLineString">String containing the command line arguments</param>
|
||||
/// <param name="initiatorStartIndex">
|
||||
/// Index of the option's initiator ('-' or '--' or '/')
|
||||
/// </param>
|
||||
/// <param name="index">
|
||||
/// Index at which the option name is supposed start (if it's an actual option)
|
||||
/// </param>
|
||||
/// <returns>The number of characters consumed</returns>
|
||||
private void parsePotentialOption(
|
||||
string commandLineString, int initiatorStartIndex, ref int index
|
||||
) {
|
||||
|
||||
// If the string ends here this can only be considered as a loose value
|
||||
if(index == commandLineString.Length) {
|
||||
addValue(
|
||||
new StringSegment(
|
||||
commandLineString,
|
||||
initiatorStartIndex,
|
||||
commandLineString.Length - initiatorStartIndex
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
int nameStartIndex = index;
|
||||
|
||||
// Look for the first character that ends the option. If it is not an actual option,
|
||||
// the very first character might be the end
|
||||
if(commandLineString[index] != commandLineString[initiatorStartIndex]) {
|
||||
index = commandLineString.IndexOfAny(NameEndingCharacters, nameStartIndex);
|
||||
if(index == -1) {
|
||||
index = commandLineString.Length;
|
||||
}
|
||||
}
|
||||
|
||||
// If the first character of the supposed option is not valid for an option name,
|
||||
// we have to consider this to be a loose value
|
||||
if((index == nameStartIndex)/* && !isAssignmentCharacter(commandLineString[index])*/) {
|
||||
index = commandLineString.IndexOfAny(WhitespaceCharacters, index);
|
||||
if(index == -1) {
|
||||
index = commandLineString.Length;
|
||||
}
|
||||
|
||||
addValue(
|
||||
new StringSegment(
|
||||
commandLineString, initiatorStartIndex, index - initiatorStartIndex
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
parsePotentialOptionAssignment(
|
||||
commandLineString, initiatorStartIndex, nameStartIndex, ref index
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Parses the value assignment in a command line option</summary>
|
||||
/// <param name="commandLineString">String containing the command line arguments</param>
|
||||
/// <param name="initiatorStartIndex">
|
||||
/// Position of the character that started the option
|
||||
/// </param>
|
||||
/// <param name="nameStartIndex">
|
||||
/// Position of the first character in the option's name
|
||||
/// </param>
|
||||
/// <param name="index">Index at which the option name ended</param>
|
||||
private void parsePotentialOptionAssignment(
|
||||
string commandLineString, int initiatorStartIndex, int nameStartIndex, ref int index
|
||||
) {
|
||||
int nameEndIndex = index;
|
||||
int valueStartIndex;
|
||||
int valueEndIndex;
|
||||
|
||||
// See if this is an assignment character. If it is, the assigned value
|
||||
// should follow to the right.
|
||||
bool isAssignment =
|
||||
(index < commandLineString.Length) &&
|
||||
isAssignmentCharacter(commandLineString[index]);
|
||||
|
||||
// If it's an assignment, we can proceed parsing the assigned value
|
||||
if(isAssignment) {
|
||||
++index;
|
||||
parseOptionValue(commandLineString, initiatorStartIndex, nameStartIndex, ref index);
|
||||
return;
|
||||
} else { // No, it's an option name without an assignment
|
||||
|
||||
bool isModifier =
|
||||
(commandLineString[index - 1] == '+') ||
|
||||
(commandLineString[index - 1] == '-');
|
||||
|
||||
if(isModifier) {
|
||||
valueStartIndex = index - 1;
|
||||
valueEndIndex = index;
|
||||
--nameEndIndex;
|
||||
} else {
|
||||
valueStartIndex = -1;
|
||||
valueEndIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int argumentLength = index - initiatorStartIndex;
|
||||
this.arguments.Add(
|
||||
new Argument(
|
||||
new StringSegment(commandLineString, initiatorStartIndex, argumentLength),
|
||||
nameStartIndex, nameEndIndex - nameStartIndex,
|
||||
valueStartIndex, valueEndIndex - valueStartIndex
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Parses the value assignment in a command line option</summary>
|
||||
/// <param name="commandLineString">String containing the command line arguments</param>
|
||||
/// <param name="initiatorStartIndex">
|
||||
/// Position of the character that started the option
|
||||
/// </param>
|
||||
/// <param name="nameStartIndex">
|
||||
/// Position of the first character in the option's name
|
||||
/// </param>
|
||||
/// <param name="index">Index at which the option name ended</param>
|
||||
private void parseOptionValue(
|
||||
string commandLineString, int initiatorStartIndex, int nameStartIndex, ref int index
|
||||
) {
|
||||
int nameEndIndex = index - 1;
|
||||
int valueStartIndex, valueEndIndex;
|
||||
|
||||
// Does the string end after the suspected assignment character?
|
||||
bool argumentEndReached = (index == commandLineString.Length);
|
||||
|
||||
if(argumentEndReached) {
|
||||
valueStartIndex = -1;
|
||||
valueEndIndex = -1;
|
||||
} else {
|
||||
char nextCharacter = commandLineString[index];
|
||||
|
||||
// Is this a quoted assignment
|
||||
if(nextCharacter == '"') {
|
||||
++index;
|
||||
valueStartIndex = index;
|
||||
index = commandLineString.IndexOf('"', index);
|
||||
if(index == -1) {
|
||||
index = commandLineString.Length;
|
||||
valueEndIndex = index;
|
||||
} else {
|
||||
valueEndIndex = index;
|
||||
++index;
|
||||
}
|
||||
} else { // Nope, assuming unquoted assignment or empty assignment
|
||||
valueStartIndex = index;
|
||||
index = commandLineString.IndexOfAny(WhitespaceCharacters, index);
|
||||
if(index == -1) {
|
||||
index = commandLineString.Length;
|
||||
valueEndIndex = index;
|
||||
} else {
|
||||
if(index == valueStartIndex) {
|
||||
valueStartIndex = -1;
|
||||
valueEndIndex = -1;
|
||||
} else {
|
||||
valueEndIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int argumentLength = index - initiatorStartIndex;
|
||||
this.arguments.Add(
|
||||
new Argument(
|
||||
new StringSegment(commandLineString, initiatorStartIndex, argumentLength),
|
||||
nameStartIndex, nameEndIndex - nameStartIndex,
|
||||
valueStartIndex, valueEndIndex - valueStartIndex
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Parses a quoted value from the input string</summary>
|
||||
/// <param name="commandLineString">String the quoted value is parsed from</param>
|
||||
/// <param name="index">Index at which the quoted value begins</param>
|
||||
private void parseQuotedValue(string commandLineString, ref int index) {
|
||||
int startIndex = index;
|
||||
char quoteCharacter = commandLineString[index];
|
||||
int valueIndex = startIndex + 1;
|
||||
|
||||
// Search for the closing quote
|
||||
index = commandLineString.IndexOf(quoteCharacter, valueIndex);
|
||||
if(index == -1) {
|
||||
index = commandLineString.Length; // value ends at string end
|
||||
this.arguments.Add(
|
||||
Argument.ValueOnly(
|
||||
new StringSegment(commandLineString, startIndex, index - startIndex),
|
||||
valueIndex, index - valueIndex
|
||||
)
|
||||
);
|
||||
} else { // A closing quote was found
|
||||
this.arguments.Add(
|
||||
Argument.ValueOnly(
|
||||
new StringSegment(commandLineString, startIndex, index - startIndex + 1),
|
||||
valueIndex, index - valueIndex
|
||||
)
|
||||
);
|
||||
++index; // Skip the closing quote
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Parses a plain, unquoted value from the input string</summary>
|
||||
/// <param name="commandLineString">String containing the value to be parsed</param>
|
||||
/// <param name="index">Index at which the value begins</param>
|
||||
private void parseNakedValue(string commandLineString, ref int index) {
|
||||
int startIndex = index;
|
||||
|
||||
index = commandLineString.IndexOfAny(WhitespaceCharacters, index);
|
||||
if(index == -1) {
|
||||
index = commandLineString.Length;
|
||||
}
|
||||
|
||||
addValue(new StringSegment(commandLineString, startIndex, index - startIndex));
|
||||
}
|
||||
|
||||
/// <summary>Adds a loose value to the command line</summary>
|
||||
/// <param name="value">Value taht will be added</param>
|
||||
private void addValue(StringSegment value) {
|
||||
this.arguments.Add(
|
||||
Argument.ValueOnly(value, value.Offset, value.Count)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified character indicates an assignment
|
||||
/// </summary>
|
||||
/// <param name="character">
|
||||
/// Character that will be checked for being an assignemnt
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// True if the specified character indicated an assignment, otherwise false
|
||||
/// </returns>
|
||||
private static bool isAssignmentCharacter(char character) {
|
||||
return (character == ':') || (character == '=');
|
||||
}
|
||||
|
||||
/// <summary>Characters which end an option name when they are encountered</summary>
|
||||
private static readonly char[] NameEndingCharacters = new char[] {
|
||||
' ', '\t', '=', ':', '"'
|
||||
};
|
||||
|
||||
/// <summary>Characters the parser considers to be whitespace</summary>
|
||||
private static readonly char[] WhitespaceCharacters = new char[] { ' ', '\t' };
|
||||
|
||||
/// <summary>Argument list being filled by the parser</summary>
|
||||
private List<CommandLine.Argument> arguments;
|
||||
/// <summary>Whether the '/' character initiates an argument</summary>
|
||||
private bool windowsMode;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Parsing
|
||||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
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.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Nuclex.Support.Parsing {
|
||||
|
||||
partial class CommandLine {
|
||||
|
||||
/// <summary>Parses command line strings</summary>
|
||||
private class Parser {
|
||||
|
||||
/// <summary>Initializes a new command line parser</summary>
|
||||
/// <param name="windowsMode">Whether the / character initiates an argument</param>
|
||||
private Parser(bool windowsMode) {
|
||||
this.windowsMode = windowsMode;
|
||||
this.arguments = new List<CommandLine.Argument>();
|
||||
}
|
||||
|
||||
/// <summary>Parses a string containing command line arguments</summary>
|
||||
/// <param name="commandLineString">String that will be parsed</param>
|
||||
/// <param name="windowsMode">Whether the / character initiates an argument</param>
|
||||
/// <returns>The parsed command line arguments from the string</returns>
|
||||
public static List<CommandLine.Argument> Parse(
|
||||
string commandLineString, bool windowsMode
|
||||
) {
|
||||
Parser theParser = new Parser(windowsMode);
|
||||
theParser.parseFullCommandLine(commandLineString);
|
||||
return theParser.arguments;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the provided string and adds the parameters found to
|
||||
/// the command line representation
|
||||
/// </summary>
|
||||
/// <param name="commandLineString">
|
||||
/// String containing the command line arguments that will be parsed
|
||||
/// </param>
|
||||
private void parseFullCommandLine(string commandLineString) {
|
||||
if(commandLineString == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Walk through the command line character by character and gather
|
||||
// the parameters and values to build the command line representation from
|
||||
for(int index = 0; index < commandLineString.Length; ) {
|
||||
|
||||
// Look for the next non-whitespace character
|
||||
index = StringHelper.IndexNotOfAny(
|
||||
commandLineString, WhitespaceCharacters, index
|
||||
);
|
||||
if(index == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Parse the chunk of characters at this location and advance the index
|
||||
// to the next location after the chunk of characters
|
||||
parseChunk(commandLineString, ref index);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a chunk of characters and adds it as an option or a loose value to
|
||||
/// the command line representation we're building
|
||||
/// </summary>
|
||||
/// <param name="commandLineString">
|
||||
/// String containing the chunk of characters that will be parsed
|
||||
/// </param>
|
||||
/// <param name="index">Index in the string at which to begin parsing</param>
|
||||
/// <returns>The number of characters that were consumed</returns>
|
||||
private void parseChunk(string commandLineString, ref int index) {
|
||||
int startIndex = index;
|
||||
|
||||
char currentCharacter = commandLineString[index];
|
||||
switch(currentCharacter) {
|
||||
|
||||
// Unix style argument using either '-' or "--" as its initiator
|
||||
case '-': {
|
||||
++index;
|
||||
|
||||
// Does the string end here? Stop parsing.
|
||||
if(index >= commandLineString.Length) {
|
||||
addValue(new StringSegment(commandLineString, startIndex, 1));
|
||||
break;
|
||||
}
|
||||
|
||||
// Does another '-' follow? Might be a unix style option or a loose "--"
|
||||
if(commandLineString[index] == '-') {
|
||||
++index;
|
||||
}
|
||||
|
||||
parsePotentialOption(commandLineString, startIndex, ref index);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Windows style argument using '/' as its initiator
|
||||
case '/': {
|
||||
// The '/ character is only used to initiate argument on windows and can be
|
||||
// toggled off. The application decides whether this is done depending on the
|
||||
// operating system or whether uniform behavior across platforms is desired.
|
||||
if(!this.windowsMode) {
|
||||
goto default;
|
||||
}
|
||||
|
||||
++index;
|
||||
parsePotentialOption(commandLineString, startIndex, ref index);
|
||||
break;
|
||||
}
|
||||
|
||||
// Quoted loose value
|
||||
case '"': {
|
||||
parseQuotedValue(commandLineString, ref index);
|
||||
break;
|
||||
}
|
||||
|
||||
// Unquoted loose value
|
||||
default: {
|
||||
parseNakedValue(commandLineString, ref index);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Parses a potential command line option</summary>
|
||||
/// <param name="commandLineString">String containing the command line arguments</param>
|
||||
/// <param name="initiatorStartIndex">
|
||||
/// Index of the option's initiator ('-' or '--' or '/')
|
||||
/// </param>
|
||||
/// <param name="index">
|
||||
/// Index at which the option name is supposed start (if it's an actual option)
|
||||
/// </param>
|
||||
/// <returns>The number of characters consumed</returns>
|
||||
private void parsePotentialOption(
|
||||
string commandLineString, int initiatorStartIndex, ref int index
|
||||
) {
|
||||
|
||||
// If the string ends here this can only be considered as a loose value
|
||||
if(index == commandLineString.Length) {
|
||||
addValue(
|
||||
new StringSegment(
|
||||
commandLineString,
|
||||
initiatorStartIndex,
|
||||
commandLineString.Length - initiatorStartIndex
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
int nameStartIndex = index;
|
||||
|
||||
// Look for the first character that ends the option. If it is not an actual option,
|
||||
// the very first character might be the end
|
||||
if(commandLineString[index] != commandLineString[initiatorStartIndex]) {
|
||||
index = commandLineString.IndexOfAny(NameEndingCharacters, nameStartIndex);
|
||||
if(index == -1) {
|
||||
index = commandLineString.Length;
|
||||
}
|
||||
}
|
||||
|
||||
// If the first character of the supposed option is not valid for an option name,
|
||||
// we have to consider this to be a loose value
|
||||
if((index == nameStartIndex)/* && !isAssignmentCharacter(commandLineString[index])*/) {
|
||||
index = commandLineString.IndexOfAny(WhitespaceCharacters, index);
|
||||
if(index == -1) {
|
||||
index = commandLineString.Length;
|
||||
}
|
||||
|
||||
addValue(
|
||||
new StringSegment(
|
||||
commandLineString, initiatorStartIndex, index - initiatorStartIndex
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
parsePotentialOptionAssignment(
|
||||
commandLineString, initiatorStartIndex, nameStartIndex, ref index
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Parses the value assignment in a command line option</summary>
|
||||
/// <param name="commandLineString">String containing the command line arguments</param>
|
||||
/// <param name="initiatorStartIndex">
|
||||
/// Position of the character that started the option
|
||||
/// </param>
|
||||
/// <param name="nameStartIndex">
|
||||
/// Position of the first character in the option's name
|
||||
/// </param>
|
||||
/// <param name="index">Index at which the option name ended</param>
|
||||
private void parsePotentialOptionAssignment(
|
||||
string commandLineString, int initiatorStartIndex, int nameStartIndex, ref int index
|
||||
) {
|
||||
int nameEndIndex = index;
|
||||
int valueStartIndex;
|
||||
int valueEndIndex;
|
||||
|
||||
// See if this is an assignment character. If it is, the assigned value
|
||||
// should follow to the right.
|
||||
bool isAssignment =
|
||||
(index < commandLineString.Length) &&
|
||||
isAssignmentCharacter(commandLineString[index]);
|
||||
|
||||
// If it's an assignment, we can proceed parsing the assigned value
|
||||
if(isAssignment) {
|
||||
++index;
|
||||
parseOptionValue(commandLineString, initiatorStartIndex, nameStartIndex, ref index);
|
||||
return;
|
||||
} else { // No, it's an option name without an assignment
|
||||
|
||||
bool isModifier =
|
||||
(commandLineString[index - 1] == '+') ||
|
||||
(commandLineString[index - 1] == '-');
|
||||
|
||||
if(isModifier) {
|
||||
valueStartIndex = index - 1;
|
||||
valueEndIndex = index;
|
||||
--nameEndIndex;
|
||||
} else {
|
||||
valueStartIndex = -1;
|
||||
valueEndIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int argumentLength = index - initiatorStartIndex;
|
||||
this.arguments.Add(
|
||||
new Argument(
|
||||
new StringSegment(commandLineString, initiatorStartIndex, argumentLength),
|
||||
nameStartIndex, nameEndIndex - nameStartIndex,
|
||||
valueStartIndex, valueEndIndex - valueStartIndex
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Parses the value assignment in a command line option</summary>
|
||||
/// <param name="commandLineString">String containing the command line arguments</param>
|
||||
/// <param name="initiatorStartIndex">
|
||||
/// Position of the character that started the option
|
||||
/// </param>
|
||||
/// <param name="nameStartIndex">
|
||||
/// Position of the first character in the option's name
|
||||
/// </param>
|
||||
/// <param name="index">Index at which the option name ended</param>
|
||||
private void parseOptionValue(
|
||||
string commandLineString, int initiatorStartIndex, int nameStartIndex, ref int index
|
||||
) {
|
||||
int nameEndIndex = index - 1;
|
||||
int valueStartIndex, valueEndIndex;
|
||||
|
||||
// Does the string end after the suspected assignment character?
|
||||
bool argumentEndReached = (index == commandLineString.Length);
|
||||
|
||||
if(argumentEndReached) {
|
||||
valueStartIndex = -1;
|
||||
valueEndIndex = -1;
|
||||
} else {
|
||||
char nextCharacter = commandLineString[index];
|
||||
|
||||
// Is this a quoted assignment
|
||||
if(nextCharacter == '"') {
|
||||
++index;
|
||||
valueStartIndex = index;
|
||||
index = commandLineString.IndexOf('"', index);
|
||||
if(index == -1) {
|
||||
index = commandLineString.Length;
|
||||
valueEndIndex = index;
|
||||
} else {
|
||||
valueEndIndex = index;
|
||||
++index;
|
||||
}
|
||||
} else { // Nope, assuming unquoted assignment or empty assignment
|
||||
valueStartIndex = index;
|
||||
index = commandLineString.IndexOfAny(WhitespaceCharacters, index);
|
||||
if(index == -1) {
|
||||
index = commandLineString.Length;
|
||||
valueEndIndex = index;
|
||||
} else {
|
||||
if(index == valueStartIndex) {
|
||||
valueStartIndex = -1;
|
||||
valueEndIndex = -1;
|
||||
} else {
|
||||
valueEndIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int argumentLength = index - initiatorStartIndex;
|
||||
this.arguments.Add(
|
||||
new Argument(
|
||||
new StringSegment(commandLineString, initiatorStartIndex, argumentLength),
|
||||
nameStartIndex, nameEndIndex - nameStartIndex,
|
||||
valueStartIndex, valueEndIndex - valueStartIndex
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Parses a quoted value from the input string</summary>
|
||||
/// <param name="commandLineString">String the quoted value is parsed from</param>
|
||||
/// <param name="index">Index at which the quoted value begins</param>
|
||||
private void parseQuotedValue(string commandLineString, ref int index) {
|
||||
int startIndex = index;
|
||||
char quoteCharacter = commandLineString[index];
|
||||
int valueIndex = startIndex + 1;
|
||||
|
||||
// Search for the closing quote
|
||||
index = commandLineString.IndexOf(quoteCharacter, valueIndex);
|
||||
if(index == -1) {
|
||||
index = commandLineString.Length; // value ends at string end
|
||||
this.arguments.Add(
|
||||
Argument.ValueOnly(
|
||||
new StringSegment(commandLineString, startIndex, index - startIndex),
|
||||
valueIndex, index - valueIndex
|
||||
)
|
||||
);
|
||||
} else { // A closing quote was found
|
||||
this.arguments.Add(
|
||||
Argument.ValueOnly(
|
||||
new StringSegment(commandLineString, startIndex, index - startIndex + 1),
|
||||
valueIndex, index - valueIndex
|
||||
)
|
||||
);
|
||||
++index; // Skip the closing quote
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Parses a plain, unquoted value from the input string</summary>
|
||||
/// <param name="commandLineString">String containing the value to be parsed</param>
|
||||
/// <param name="index">Index at which the value begins</param>
|
||||
private void parseNakedValue(string commandLineString, ref int index) {
|
||||
int startIndex = index;
|
||||
|
||||
index = commandLineString.IndexOfAny(WhitespaceCharacters, index);
|
||||
if(index == -1) {
|
||||
index = commandLineString.Length;
|
||||
}
|
||||
|
||||
addValue(new StringSegment(commandLineString, startIndex, index - startIndex));
|
||||
}
|
||||
|
||||
/// <summary>Adds a loose value to the command line</summary>
|
||||
/// <param name="value">Value taht will be added</param>
|
||||
private void addValue(StringSegment value) {
|
||||
this.arguments.Add(
|
||||
Argument.ValueOnly(value, value.Offset, value.Count)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified character indicates an assignment
|
||||
/// </summary>
|
||||
/// <param name="character">
|
||||
/// Character that will be checked for being an assignemnt
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// True if the specified character indicated an assignment, otherwise false
|
||||
/// </returns>
|
||||
private static bool isAssignmentCharacter(char character) {
|
||||
return (character == ':') || (character == '=');
|
||||
}
|
||||
|
||||
/// <summary>Characters which end an option name when they are encountered</summary>
|
||||
private static readonly char[] NameEndingCharacters = new char[] {
|
||||
' ', '\t', '=', ':', '"'
|
||||
};
|
||||
|
||||
/// <summary>Characters the parser considers to be whitespace</summary>
|
||||
private static readonly char[] WhitespaceCharacters = new char[] { ' ', '\t' };
|
||||
|
||||
/// <summary>Argument list being filled by the parser</summary>
|
||||
private List<CommandLine.Argument> arguments;
|
||||
/// <summary>Whether the '/' character initiates an argument</summary>
|
||||
private bool windowsMode;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Parsing
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,305 +1,304 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2017 Nuclex Development Labs
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the IBM Common Public License as
|
||||
published by the IBM Corporation; either version 1.0 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
IBM Common Public License for more details.
|
||||
|
||||
You should have received a copy of the IBM Common Public
|
||||
License along with this library
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Nuclex.Support.Parsing {
|
||||
|
||||
/// <summary>Parses and stores an application's command line parameters</summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// At the time of the creation of this component, there are already several command
|
||||
/// line parsing libraries out there. Most of them, however, do way too much at once
|
||||
/// or at the very least rely on huge, untested clutters of classes and methods to
|
||||
/// arrive at their results.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This command line parser does nothing more than represent the command line to
|
||||
/// the application through a convenient interface. It parses a command line and
|
||||
/// extracts the arguments, but doesn't interpret them and or check them for validity.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This design promotes simplicity and makes is an ideal building block to create
|
||||
/// actual command line interpreters that connect the parameters to program
|
||||
/// instructions and or fill structures in code.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Terminology
|
||||
/// <list type="table">
|
||||
/// <item>
|
||||
/// <term>Command line</term>
|
||||
/// <description>
|
||||
/// The entire command line either as a string or as
|
||||
/// an already parsed data structure
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Argument</term>
|
||||
/// <description>
|
||||
/// Either an option or a loose value (see below) being specified on
|
||||
/// the command line
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Option</term>
|
||||
/// <description>
|
||||
/// Can be specified on the command line and typically alters the behavior
|
||||
/// of the application or changes a setting. For example, '--normalize' or
|
||||
/// '/safemode'.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Value</term>
|
||||
/// <description>
|
||||
/// Can either sit loosely in the command line (eg. 'update' or 'textfile.txt')
|
||||
/// or as assignment to an option (eg. '--width=1280' or '/overwrite:always')
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public partial class CommandLine {
|
||||
|
||||
/// <summary>
|
||||
/// Whether the command line should use Windows mode by default
|
||||
/// </summary>
|
||||
public static readonly bool WindowsModeDefault =
|
||||
(Path.DirectorySeparatorChar == '\\');
|
||||
|
||||
/// <summary>Initializes a new command line</summary>
|
||||
public CommandLine() :
|
||||
this(new List<Argument>(), WindowsModeDefault) { }
|
||||
|
||||
/// <summary>Initializes a new command line</summary>
|
||||
/// <param name="windowsMode">Whether the / character initiates an argument</param>
|
||||
public CommandLine(bool windowsMode) :
|
||||
this(new List<Argument>(), windowsMode) { }
|
||||
|
||||
/// <summary>Initializes a new command line</summary>
|
||||
/// <param name="argumentList">List containing the parsed arguments</param>
|
||||
private CommandLine(IList<Argument> argumentList) :
|
||||
this(argumentList, WindowsModeDefault) { }
|
||||
|
||||
/// <summary>Initializes a new command line</summary>
|
||||
/// <param name="argumentList">List containing the parsed arguments</param>
|
||||
/// <param name="windowsMode">Whether the / character initiates an argument</param>
|
||||
private CommandLine(IList<Argument> argumentList, bool windowsMode) {
|
||||
this.arguments = argumentList;
|
||||
this.windowsMode = windowsMode;
|
||||
}
|
||||
|
||||
/// <summary>Parses the command line arguments from the provided string</summary>
|
||||
/// <param name="commandLineString">String containing the command line arguments</param>
|
||||
/// <returns>The parsed command line</returns>
|
||||
/// <remarks>
|
||||
/// You should always pass Environment.CommandLine to this method to avoid
|
||||
/// some problems with the built-in command line tokenizer in .NET
|
||||
/// (which splits '--test"hello world"/v' into '--testhello world/v')
|
||||
/// </remarks>
|
||||
public static CommandLine Parse(string commandLineString) {
|
||||
bool windowsMode = (Path.DirectorySeparatorChar != '/');
|
||||
return Parse(commandLineString, windowsMode);
|
||||
}
|
||||
|
||||
/// <summary>Parses the command line arguments from the provided string</summary>
|
||||
/// <param name="commandLineString">String containing the command line arguments</param>
|
||||
/// <param name="windowsMode">Whether the / character initiates an argument</param>
|
||||
/// <returns>The parsed command line</returns>
|
||||
/// <remarks>
|
||||
/// You should always pass Environment.CommandLine to this methods to avoid
|
||||
/// some problems with the built-in command line tokenizer in .NET
|
||||
/// (which splits '--test"hello world"/v' into '--testhello world/v')
|
||||
/// </remarks>
|
||||
public static CommandLine Parse(string commandLineString, bool windowsMode) {
|
||||
return new CommandLine(
|
||||
Parser.Parse(commandLineString, windowsMode)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Returns whether an argument with the specified name exists</summary>
|
||||
/// <param name="name">Name of the argument whose existence will be checked</param>
|
||||
/// <returns>True if an argument with the specified name exists</returns>
|
||||
public bool HasArgument(string name) {
|
||||
return (indexOfArgument(name) != -1);
|
||||
}
|
||||
|
||||
/// <summary>Adds a value to the command line</summary>
|
||||
/// <param name="value">Value that will be added</param>
|
||||
public void AddValue(string value) {
|
||||
int valueLength = (value != null) ? value.Length : 0;
|
||||
|
||||
if(requiresQuotes(value)) {
|
||||
StringBuilder builder = new StringBuilder(valueLength + 2);
|
||||
builder.Append('"');
|
||||
builder.Append(value);
|
||||
builder.Append('"');
|
||||
|
||||
this.arguments.Add(
|
||||
Argument.ValueOnly(
|
||||
new StringSegment(builder.ToString(), 0, valueLength + 2),
|
||||
1,
|
||||
valueLength
|
||||
)
|
||||
);
|
||||
} else {
|
||||
this.arguments.Add(
|
||||
Argument.ValueOnly(new StringSegment(value), 0, valueLength)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Adds an option to the command line</summary>
|
||||
/// <param name="name">Name of the option that will be added</param>
|
||||
public void AddOption(string name) {
|
||||
AddOption("-", name);
|
||||
}
|
||||
|
||||
/// <summary>Adds an option to the command line</summary>
|
||||
/// <param name="initiator">Initiator that will be used to start the option</param>
|
||||
/// <param name="name">Name of the option that will be added</param>
|
||||
public void AddOption(string initiator, string name) {
|
||||
StringBuilder builder = new StringBuilder(initiator.Length + name.Length);
|
||||
builder.Append(initiator);
|
||||
builder.Append(name);
|
||||
|
||||
this.arguments.Add(
|
||||
Argument.OptionOnly(
|
||||
new StringSegment(builder.ToString()),
|
||||
initiator.Length,
|
||||
name.Length
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Adds an option with an assignment to the command line</summary>
|
||||
/// <param name="name">Name of the option that will be added</param>
|
||||
/// <param name="value">Value that will be assigned to the option</param>
|
||||
public void AddAssignment(string name, string value) {
|
||||
AddAssignment("-", name, value);
|
||||
}
|
||||
|
||||
/// <summary>Adds an option with an assignment to the command line</summary>
|
||||
/// <param name="initiator">Initiator that will be used to start the option</param>
|
||||
/// <param name="name">Name of the option that will be added</param>
|
||||
/// <param name="value">Value that will be assigned to the option</param>
|
||||
public void AddAssignment(string initiator, string name, string value) {
|
||||
bool valueContainsSpaces = containsWhitespace(value);
|
||||
StringBuilder builder = new StringBuilder(
|
||||
initiator.Length + name.Length + 1 + value.Length + (valueContainsSpaces ? 2 : 0)
|
||||
);
|
||||
builder.Append(initiator);
|
||||
builder.Append(name);
|
||||
builder.Append('=');
|
||||
if(valueContainsSpaces) {
|
||||
builder.Append('"');
|
||||
builder.Append(value);
|
||||
builder.Append('"');
|
||||
} else {
|
||||
builder.Append(value);
|
||||
}
|
||||
|
||||
this.arguments.Add(
|
||||
new Argument(
|
||||
new StringSegment(builder.ToString()),
|
||||
initiator.Length,
|
||||
name.Length,
|
||||
initiator.Length + name.Length + 1 + (valueContainsSpaces ? 1 : 0),
|
||||
value.Length
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Returns a string that contains the entire command line</summary>
|
||||
/// <returns>The entire command line as a single string</returns>
|
||||
public override string ToString() {
|
||||
return Formatter.FormatCommandLine(this);
|
||||
}
|
||||
|
||||
/// <summary>Retrieves the index of the argument with the specified name</summary>
|
||||
/// <param name="name">Name of the argument whose index will be returned</param>
|
||||
/// <returns>
|
||||
/// The index of the indicated argument of -1 if no argument with that name exists
|
||||
/// </returns>
|
||||
private int indexOfArgument(string name) {
|
||||
for(int index = 0; index < this.arguments.Count; ++index) {
|
||||
if(this.arguments[index].Name == name) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>Options that were specified on the command line</summary>
|
||||
public IList<Argument> Arguments {
|
||||
get { return this.arguments; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the string requires quotes to survive the command line
|
||||
/// </summary>
|
||||
/// <param name="value">Value that will be checked for requiring quotes</param>
|
||||
/// <returns>True if the value requires quotes to survive the command line</returns>
|
||||
private bool requiresQuotes(string value) {
|
||||
|
||||
// If the value is empty, it needs quotes to become visible as an argument
|
||||
// (versus being intepreted as spacing between other arguments)
|
||||
if(string.IsNullOrEmpty(value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Any whitespace characters force us to use quotes, so does a minus sign
|
||||
// at the beginning of the value (otherwise, it would become an option argument)
|
||||
bool requiresQuotes =
|
||||
containsWhitespace(value) ||
|
||||
(value[0] == '-');
|
||||
|
||||
// On windows, option arguments can also be starten with the forward slash
|
||||
// character, so we require quotes as well if the value starts with one
|
||||
if(this.windowsMode) {
|
||||
requiresQuotes |= (value[0] == '/');
|
||||
}
|
||||
|
||||
return requiresQuotes;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the string contains any whitespace characters
|
||||
/// </summary>
|
||||
/// <param name="value">String that will be scanned for whitespace characters</param>
|
||||
/// <returns>True if the provided string contains whitespace characters</returns>
|
||||
private static bool containsWhitespace(string value) {
|
||||
return
|
||||
(value.IndexOf(' ') != -1) ||
|
||||
(value.IndexOf('\t') != -1);
|
||||
}
|
||||
|
||||
/// <summary>Options that were specified on the command line</summary>
|
||||
private IList<Argument> arguments;
|
||||
/// <summary>Whether the / character initiates an argument</summary>
|
||||
private bool windowsMode;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Parsing
|
||||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
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.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Nuclex.Support.Parsing {
|
||||
|
||||
/// <summary>Parses and stores an application's command line parameters</summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// At the time of the creation of this component, there are already several command
|
||||
/// line parsing libraries out there. Most of them, however, do way too much at once
|
||||
/// or at the very least rely on huge, untested clutters of classes and methods to
|
||||
/// arrive at their results.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This command line parser does nothing more than represent the command line to
|
||||
/// the application through a convenient interface. It parses a command line and
|
||||
/// extracts the arguments, but doesn't interpret them and or check them for validity.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This design promotes simplicity and makes is an ideal building block to create
|
||||
/// actual command line interpreters that connect the parameters to program
|
||||
/// instructions and or fill structures in code.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Terminology
|
||||
/// <list type="table">
|
||||
/// <item>
|
||||
/// <term>Command line</term>
|
||||
/// <description>
|
||||
/// The entire command line either as a string or as
|
||||
/// an already parsed data structure
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Argument</term>
|
||||
/// <description>
|
||||
/// Either an option or a loose value (see below) being specified on
|
||||
/// the command line
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Option</term>
|
||||
/// <description>
|
||||
/// Can be specified on the command line and typically alters the behavior
|
||||
/// of the application or changes a setting. For example, '--normalize' or
|
||||
/// '/safemode'.
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// <term>Value</term>
|
||||
/// <description>
|
||||
/// Can either sit loosely in the command line (eg. 'update' or 'textfile.txt')
|
||||
/// or as assignment to an option (eg. '--width=1280' or '/overwrite:always')
|
||||
/// </description>
|
||||
/// </item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public partial class CommandLine {
|
||||
|
||||
/// <summary>
|
||||
/// Whether the command line should use Windows mode by default
|
||||
/// </summary>
|
||||
public static readonly bool WindowsModeDefault =
|
||||
(Path.DirectorySeparatorChar == '\\');
|
||||
|
||||
/// <summary>Initializes a new command line</summary>
|
||||
public CommandLine() :
|
||||
this(new List<Argument>(), WindowsModeDefault) { }
|
||||
|
||||
/// <summary>Initializes a new command line</summary>
|
||||
/// <param name="windowsMode">Whether the / character initiates an argument</param>
|
||||
public CommandLine(bool windowsMode) :
|
||||
this(new List<Argument>(), windowsMode) { }
|
||||
|
||||
/// <summary>Initializes a new command line</summary>
|
||||
/// <param name="argumentList">List containing the parsed arguments</param>
|
||||
private CommandLine(IList<Argument> argumentList) :
|
||||
this(argumentList, WindowsModeDefault) { }
|
||||
|
||||
/// <summary>Initializes a new command line</summary>
|
||||
/// <param name="argumentList">List containing the parsed arguments</param>
|
||||
/// <param name="windowsMode">Whether the / character initiates an argument</param>
|
||||
private CommandLine(IList<Argument> argumentList, bool windowsMode) {
|
||||
this.arguments = argumentList;
|
||||
this.windowsMode = windowsMode;
|
||||
}
|
||||
|
||||
/// <summary>Parses the command line arguments from the provided string</summary>
|
||||
/// <param name="commandLineString">String containing the command line arguments</param>
|
||||
/// <returns>The parsed command line</returns>
|
||||
/// <remarks>
|
||||
/// You should always pass Environment.CommandLine to this method to avoid
|
||||
/// some problems with the built-in command line tokenizer in .NET
|
||||
/// (which splits '--test"hello world"/v' into '--testhello world/v')
|
||||
/// </remarks>
|
||||
public static CommandLine Parse(string commandLineString) {
|
||||
bool windowsMode = (Path.DirectorySeparatorChar != '/');
|
||||
return Parse(commandLineString, windowsMode);
|
||||
}
|
||||
|
||||
/// <summary>Parses the command line arguments from the provided string</summary>
|
||||
/// <param name="commandLineString">String containing the command line arguments</param>
|
||||
/// <param name="windowsMode">Whether the / character initiates an argument</param>
|
||||
/// <returns>The parsed command line</returns>
|
||||
/// <remarks>
|
||||
/// You should always pass Environment.CommandLine to this methods to avoid
|
||||
/// some problems with the built-in command line tokenizer in .NET
|
||||
/// (which splits '--test"hello world"/v' into '--testhello world/v')
|
||||
/// </remarks>
|
||||
public static CommandLine Parse(string commandLineString, bool windowsMode) {
|
||||
return new CommandLine(
|
||||
Parser.Parse(commandLineString, windowsMode)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Returns whether an argument with the specified name exists</summary>
|
||||
/// <param name="name">Name of the argument whose existence will be checked</param>
|
||||
/// <returns>True if an argument with the specified name exists</returns>
|
||||
public bool HasArgument(string name) {
|
||||
return (indexOfArgument(name) != -1);
|
||||
}
|
||||
|
||||
/// <summary>Adds a value to the command line</summary>
|
||||
/// <param name="value">Value that will be added</param>
|
||||
public void AddValue(string value) {
|
||||
int valueLength = (value != null) ? value.Length : 0;
|
||||
|
||||
if(requiresQuotes(value)) {
|
||||
StringBuilder builder = new StringBuilder(valueLength + 2);
|
||||
builder.Append('"');
|
||||
builder.Append(value);
|
||||
builder.Append('"');
|
||||
|
||||
this.arguments.Add(
|
||||
Argument.ValueOnly(
|
||||
new StringSegment(builder.ToString(), 0, valueLength + 2),
|
||||
1,
|
||||
valueLength
|
||||
)
|
||||
);
|
||||
} else {
|
||||
this.arguments.Add(
|
||||
Argument.ValueOnly(new StringSegment(value), 0, valueLength)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Adds an option to the command line</summary>
|
||||
/// <param name="name">Name of the option that will be added</param>
|
||||
public void AddOption(string name) {
|
||||
AddOption("-", name);
|
||||
}
|
||||
|
||||
/// <summary>Adds an option to the command line</summary>
|
||||
/// <param name="initiator">Initiator that will be used to start the option</param>
|
||||
/// <param name="name">Name of the option that will be added</param>
|
||||
public void AddOption(string initiator, string name) {
|
||||
StringBuilder builder = new StringBuilder(initiator.Length + name.Length);
|
||||
builder.Append(initiator);
|
||||
builder.Append(name);
|
||||
|
||||
this.arguments.Add(
|
||||
Argument.OptionOnly(
|
||||
new StringSegment(builder.ToString()),
|
||||
initiator.Length,
|
||||
name.Length
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Adds an option with an assignment to the command line</summary>
|
||||
/// <param name="name">Name of the option that will be added</param>
|
||||
/// <param name="value">Value that will be assigned to the option</param>
|
||||
public void AddAssignment(string name, string value) {
|
||||
AddAssignment("-", name, value);
|
||||
}
|
||||
|
||||
/// <summary>Adds an option with an assignment to the command line</summary>
|
||||
/// <param name="initiator">Initiator that will be used to start the option</param>
|
||||
/// <param name="name">Name of the option that will be added</param>
|
||||
/// <param name="value">Value that will be assigned to the option</param>
|
||||
public void AddAssignment(string initiator, string name, string value) {
|
||||
bool valueContainsSpaces = containsWhitespace(value);
|
||||
StringBuilder builder = new StringBuilder(
|
||||
initiator.Length + name.Length + 1 + value.Length + (valueContainsSpaces ? 2 : 0)
|
||||
);
|
||||
builder.Append(initiator);
|
||||
builder.Append(name);
|
||||
builder.Append('=');
|
||||
if(valueContainsSpaces) {
|
||||
builder.Append('"');
|
||||
builder.Append(value);
|
||||
builder.Append('"');
|
||||
} else {
|
||||
builder.Append(value);
|
||||
}
|
||||
|
||||
this.arguments.Add(
|
||||
new Argument(
|
||||
new StringSegment(builder.ToString()),
|
||||
initiator.Length,
|
||||
name.Length,
|
||||
initiator.Length + name.Length + 1 + (valueContainsSpaces ? 1 : 0),
|
||||
value.Length
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Returns a string that contains the entire command line</summary>
|
||||
/// <returns>The entire command line as a single string</returns>
|
||||
public override string ToString() {
|
||||
return Formatter.FormatCommandLine(this);
|
||||
}
|
||||
|
||||
/// <summary>Retrieves the index of the argument with the specified name</summary>
|
||||
/// <param name="name">Name of the argument whose index will be returned</param>
|
||||
/// <returns>
|
||||
/// The index of the indicated argument of -1 if no argument with that name exists
|
||||
/// </returns>
|
||||
private int indexOfArgument(string name) {
|
||||
for(int index = 0; index < this.arguments.Count; ++index) {
|
||||
if(this.arguments[index].Name == name) {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>Options that were specified on the command line</summary>
|
||||
public IList<Argument> Arguments {
|
||||
get { return this.arguments; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the string requires quotes to survive the command line
|
||||
/// </summary>
|
||||
/// <param name="value">Value that will be checked for requiring quotes</param>
|
||||
/// <returns>True if the value requires quotes to survive the command line</returns>
|
||||
private bool requiresQuotes(string value) {
|
||||
|
||||
// If the value is empty, it needs quotes to become visible as an argument
|
||||
// (versus being intepreted as spacing between other arguments)
|
||||
if(string.IsNullOrEmpty(value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Any whitespace characters force us to use quotes, so does a minus sign
|
||||
// at the beginning of the value (otherwise, it would become an option argument)
|
||||
bool requiresQuotes =
|
||||
containsWhitespace(value) ||
|
||||
(value[0] == '-');
|
||||
|
||||
// On windows, option arguments can also be starten with the forward slash
|
||||
// character, so we require quotes as well if the value starts with one
|
||||
if(this.windowsMode) {
|
||||
requiresQuotes |= (value[0] == '/');
|
||||
}
|
||||
|
||||
return requiresQuotes;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the string contains any whitespace characters
|
||||
/// </summary>
|
||||
/// <param name="value">String that will be scanned for whitespace characters</param>
|
||||
/// <returns>True if the provided string contains whitespace characters</returns>
|
||||
private static bool containsWhitespace(string value) {
|
||||
return
|
||||
(value.IndexOf(' ') != -1) ||
|
||||
(value.IndexOf('\t') != -1);
|
||||
}
|
||||
|
||||
/// <summary>Options that were specified on the command line</summary>
|
||||
private IList<Argument> arguments;
|
||||
/// <summary>Whether the / character initiates an argument</summary>
|
||||
private bool windowsMode;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Parsing
|
||||
|
|
|
@ -1,233 +1,232 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2017 Nuclex Development Labs
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the IBM Common Public License as
|
||||
published by the IBM Corporation; either version 1.0 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
IBM Common Public License for more details.
|
||||
|
||||
You should have received a copy of the IBM Common Public
|
||||
License along with this library
|
||||
*/
|
||||
#endregion
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Parsing {
|
||||
|
||||
/// <summary>Verifies that the parser helper methods are correct</summary>
|
||||
[TestFixture]
|
||||
internal class ParserHelperTest {
|
||||
|
||||
/// <summary>Ensures that the SkipSpaces() method can handle null strings</summary>
|
||||
[Test]
|
||||
public void CanSkipSpacesInNullString() {
|
||||
int index = 0;
|
||||
Assert.DoesNotThrow(
|
||||
delegate() { ParserHelper.SkipSpaces((string)null, ref index); }
|
||||
);
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipSpaces() method can handle empty strings</summary>
|
||||
[Test]
|
||||
public void CanSkipSpacesInEmptyString() {
|
||||
int index = 0;
|
||||
Assert.DoesNotThrow(
|
||||
delegate() { ParserHelper.SkipSpaces(string.Empty, ref index); }
|
||||
);
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipSpaces() method can skip spaces</summary>
|
||||
[Test]
|
||||
public void SpacesCanBeSkipped() {
|
||||
int index = 7;
|
||||
ParserHelper.SkipSpaces(" Test Test ", ref index);
|
||||
Assert.AreEqual(10, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNonSpaces() method can handle null strings</summary>
|
||||
[Test]
|
||||
public void CanSkipNonSpacesInNullString() {
|
||||
int index = 0;
|
||||
Assert.DoesNotThrow(
|
||||
delegate() { ParserHelper.SkipNonSpaces((string)null, ref index); }
|
||||
);
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNonSpaces() method can handle empty strings</summary>
|
||||
[Test]
|
||||
public void CanSkipNonSpacesInEmptyString() {
|
||||
int index = 0;
|
||||
Assert.DoesNotThrow(
|
||||
delegate() { ParserHelper.SkipNonSpaces(string.Empty, ref index); }
|
||||
);
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNonSpaces() method can skip non-space characters</summary>
|
||||
[Test]
|
||||
public void NonSpacesCanBeSkipped() {
|
||||
int index = 7;
|
||||
ParserHelper.SkipNonSpaces("Test Test Test", ref index);
|
||||
Assert.AreEqual(11, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNumbers() method can handle null strings</summary>
|
||||
[Test]
|
||||
public void CanSkipNumbersInNullString() {
|
||||
int index = 0;
|
||||
Assert.DoesNotThrow(
|
||||
delegate() { ParserHelper.SkipNumericals((string)null, ref index); }
|
||||
);
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNumbers() method can handle empty strings</summary>
|
||||
[Test]
|
||||
public void CanSkipNumbersInEmptyString() {
|
||||
int index = 0;
|
||||
Assert.DoesNotThrow(
|
||||
delegate() { ParserHelper.SkipNumericals(string.Empty, ref index); }
|
||||
);
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNumbers() method can skip numbers</summary>
|
||||
[Test]
|
||||
public void NumbersCanBeSkipped() {
|
||||
int index = 6;
|
||||
ParserHelper.SkipNumericals("123abc456def789", ref index);
|
||||
Assert.AreEqual(9, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipIntegers() method can handle null strings</summary>
|
||||
[Test]
|
||||
public void CanSkipIntegersInNullString() {
|
||||
int index = 0;
|
||||
Assert.IsFalse(ParserHelper.SkipInteger((string)null, ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNumbers() method can handle empty strings</summary>
|
||||
[Test]
|
||||
public void CanSkipIntegersInEmptyString() {
|
||||
int index = 0;
|
||||
Assert.IsFalse(ParserHelper.SkipInteger(string.Empty, ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a prefix alone can not be skipped as an integer</summary>
|
||||
[Test]
|
||||
public void PrefixAloneIsNotAnInteger() {
|
||||
int index = 0;
|
||||
Assert.IsFalse(ParserHelper.SkipInteger("+Test", ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
Assert.IsFalse(ParserHelper.SkipInteger("-", ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a prefixed integer can be skipped</summary>
|
||||
[Test]
|
||||
public void PrefixedIntegersCanBeSkipped() {
|
||||
int index = 0;
|
||||
Assert.IsTrue(ParserHelper.SkipInteger("+123", ref index));
|
||||
Assert.AreEqual(4, index);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an integer without a prefix can be skipped</summary>
|
||||
[Test]
|
||||
public void PlainIntegersCanBeSkipped() {
|
||||
int index = 0;
|
||||
Assert.IsTrue(ParserHelper.SkipInteger("12345", ref index));
|
||||
Assert.AreEqual(5, index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that trying to skip text as if it was an integer skips nothing
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SkippingTextAsIntegerReturnsFalse() {
|
||||
int index = 0;
|
||||
Assert.IsFalse(ParserHelper.SkipInteger("hello", ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipIntegers() method can handle null strings</summary>
|
||||
[Test]
|
||||
public void CanSkipStringInNullString() {
|
||||
int index = 0;
|
||||
Assert.IsFalse(ParserHelper.SkipString((string)null, ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNumbers() method can handle empty strings</summary>
|
||||
[Test]
|
||||
public void CanSkipStringInEmptyString() {
|
||||
int index = 0;
|
||||
Assert.IsFalse(ParserHelper.SkipString(string.Empty, ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a string consisting of a single word can be skipped</summary>
|
||||
[Test]
|
||||
public void SingleWordStringsCanBeSkipped() {
|
||||
int index = 0;
|
||||
Assert.IsTrue(ParserHelper.SkipString("hello", ref index));
|
||||
Assert.AreEqual(5, index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a space character is not skipped over when skipping a string
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SpaceTerminatesUnquotedStrings() {
|
||||
int index = 0;
|
||||
Assert.IsTrue(ParserHelper.SkipString("hello world", ref index));
|
||||
Assert.AreEqual(5, index);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a string in quotes continues until the closing quote</summary>
|
||||
[Test]
|
||||
public void QuotedStringsCanBeSkipped() {
|
||||
int index = 0;
|
||||
Assert.IsTrue(ParserHelper.SkipString("\"This is a test\"", ref index));
|
||||
Assert.AreEqual(16, index);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a string in quotes continues until the closing quote</summary>
|
||||
[Test]
|
||||
public void QuotedStringsStopAtClosingQuote() {
|
||||
int index = 0;
|
||||
Assert.IsTrue(ParserHelper.SkipString("\"This is a test\" but this not.", ref index));
|
||||
Assert.AreEqual(16, index);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a string in quotes continues until the closing quote</summary>
|
||||
[Test]
|
||||
public void QuotedStringRequiresClosingQuote() {
|
||||
int index = 0;
|
||||
Assert.IsFalse(ParserHelper.SkipString("\"This is missing the closing quote", ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Parsing
|
||||
|
||||
#endif // UNITTEST
|
||||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
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.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Parsing {
|
||||
|
||||
/// <summary>Verifies that the parser helper methods are correct</summary>
|
||||
[TestFixture]
|
||||
internal class ParserHelperTest {
|
||||
|
||||
/// <summary>Ensures that the SkipSpaces() method can handle null strings</summary>
|
||||
[Test]
|
||||
public void CanSkipSpacesInNullString() {
|
||||
int index = 0;
|
||||
Assert.DoesNotThrow(
|
||||
delegate() { ParserHelper.SkipSpaces((string)null, ref index); }
|
||||
);
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipSpaces() method can handle empty strings</summary>
|
||||
[Test]
|
||||
public void CanSkipSpacesInEmptyString() {
|
||||
int index = 0;
|
||||
Assert.DoesNotThrow(
|
||||
delegate() { ParserHelper.SkipSpaces(string.Empty, ref index); }
|
||||
);
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipSpaces() method can skip spaces</summary>
|
||||
[Test]
|
||||
public void SpacesCanBeSkipped() {
|
||||
int index = 7;
|
||||
ParserHelper.SkipSpaces(" Test Test ", ref index);
|
||||
Assert.AreEqual(10, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNonSpaces() method can handle null strings</summary>
|
||||
[Test]
|
||||
public void CanSkipNonSpacesInNullString() {
|
||||
int index = 0;
|
||||
Assert.DoesNotThrow(
|
||||
delegate() { ParserHelper.SkipNonSpaces((string)null, ref index); }
|
||||
);
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNonSpaces() method can handle empty strings</summary>
|
||||
[Test]
|
||||
public void CanSkipNonSpacesInEmptyString() {
|
||||
int index = 0;
|
||||
Assert.DoesNotThrow(
|
||||
delegate() { ParserHelper.SkipNonSpaces(string.Empty, ref index); }
|
||||
);
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNonSpaces() method can skip non-space characters</summary>
|
||||
[Test]
|
||||
public void NonSpacesCanBeSkipped() {
|
||||
int index = 7;
|
||||
ParserHelper.SkipNonSpaces("Test Test Test", ref index);
|
||||
Assert.AreEqual(11, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNumbers() method can handle null strings</summary>
|
||||
[Test]
|
||||
public void CanSkipNumbersInNullString() {
|
||||
int index = 0;
|
||||
Assert.DoesNotThrow(
|
||||
delegate() { ParserHelper.SkipNumericals((string)null, ref index); }
|
||||
);
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNumbers() method can handle empty strings</summary>
|
||||
[Test]
|
||||
public void CanSkipNumbersInEmptyString() {
|
||||
int index = 0;
|
||||
Assert.DoesNotThrow(
|
||||
delegate() { ParserHelper.SkipNumericals(string.Empty, ref index); }
|
||||
);
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNumbers() method can skip numbers</summary>
|
||||
[Test]
|
||||
public void NumbersCanBeSkipped() {
|
||||
int index = 6;
|
||||
ParserHelper.SkipNumericals("123abc456def789", ref index);
|
||||
Assert.AreEqual(9, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipIntegers() method can handle null strings</summary>
|
||||
[Test]
|
||||
public void CanSkipIntegersInNullString() {
|
||||
int index = 0;
|
||||
Assert.IsFalse(ParserHelper.SkipInteger((string)null, ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNumbers() method can handle empty strings</summary>
|
||||
[Test]
|
||||
public void CanSkipIntegersInEmptyString() {
|
||||
int index = 0;
|
||||
Assert.IsFalse(ParserHelper.SkipInteger(string.Empty, ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a prefix alone can not be skipped as an integer</summary>
|
||||
[Test]
|
||||
public void PrefixAloneIsNotAnInteger() {
|
||||
int index = 0;
|
||||
Assert.IsFalse(ParserHelper.SkipInteger("+Test", ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
Assert.IsFalse(ParserHelper.SkipInteger("-", ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a prefixed integer can be skipped</summary>
|
||||
[Test]
|
||||
public void PrefixedIntegersCanBeSkipped() {
|
||||
int index = 0;
|
||||
Assert.IsTrue(ParserHelper.SkipInteger("+123", ref index));
|
||||
Assert.AreEqual(4, index);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an integer without a prefix can be skipped</summary>
|
||||
[Test]
|
||||
public void PlainIntegersCanBeSkipped() {
|
||||
int index = 0;
|
||||
Assert.IsTrue(ParserHelper.SkipInteger("12345", ref index));
|
||||
Assert.AreEqual(5, index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that trying to skip text as if it was an integer skips nothing
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SkippingTextAsIntegerReturnsFalse() {
|
||||
int index = 0;
|
||||
Assert.IsFalse(ParserHelper.SkipInteger("hello", ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipIntegers() method can handle null strings</summary>
|
||||
[Test]
|
||||
public void CanSkipStringInNullString() {
|
||||
int index = 0;
|
||||
Assert.IsFalse(ParserHelper.SkipString((string)null, ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNumbers() method can handle empty strings</summary>
|
||||
[Test]
|
||||
public void CanSkipStringInEmptyString() {
|
||||
int index = 0;
|
||||
Assert.IsFalse(ParserHelper.SkipString(string.Empty, ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a string consisting of a single word can be skipped</summary>
|
||||
[Test]
|
||||
public void SingleWordStringsCanBeSkipped() {
|
||||
int index = 0;
|
||||
Assert.IsTrue(ParserHelper.SkipString("hello", ref index));
|
||||
Assert.AreEqual(5, index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a space character is not skipped over when skipping a string
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SpaceTerminatesUnquotedStrings() {
|
||||
int index = 0;
|
||||
Assert.IsTrue(ParserHelper.SkipString("hello world", ref index));
|
||||
Assert.AreEqual(5, index);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a string in quotes continues until the closing quote</summary>
|
||||
[Test]
|
||||
public void QuotedStringsCanBeSkipped() {
|
||||
int index = 0;
|
||||
Assert.IsTrue(ParserHelper.SkipString("\"This is a test\"", ref index));
|
||||
Assert.AreEqual(16, index);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a string in quotes continues until the closing quote</summary>
|
||||
[Test]
|
||||
public void QuotedStringsStopAtClosingQuote() {
|
||||
int index = 0;
|
||||
Assert.IsTrue(ParserHelper.SkipString("\"This is a test\" but this not.", ref index));
|
||||
Assert.AreEqual(16, index);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a string in quotes continues until the closing quote</summary>
|
||||
[Test]
|
||||
public void QuotedStringRequiresClosingQuote() {
|
||||
int index = 0;
|
||||
Assert.IsFalse(ParserHelper.SkipString("\"This is missing the closing quote", ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Parsing
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
|
@ -1,181 +1,180 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2017 Nuclex Development Labs
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the IBM Common Public License as
|
||||
published by the IBM Corporation; either version 1.0 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
IBM Common Public License for more details.
|
||||
|
||||
You should have received a copy of the IBM Common Public
|
||||
License along with this library
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
|
||||
namespace Nuclex.Support.Parsing {
|
||||
|
||||
/// <summary>Provides helper methods for parsers</summary>
|
||||
public class ParserHelper {
|
||||
|
||||
/// <summary>Advances the index past any whitespace in the string</summary>
|
||||
/// <param name="text">String which is being indexed</param>
|
||||
/// <param name="index">Index that will be advanced</param>
|
||||
public static void SkipSpaces(string text, ref int index) {
|
||||
if(text == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int length = text.Length;
|
||||
while(index < length) {
|
||||
if(!char.IsWhiteSpace(text, index)) {
|
||||
break;
|
||||
}
|
||||
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Advances the index to the next whitespace in the string</summary>
|
||||
/// <param name="text">String which is being indexed</param>
|
||||
/// <param name="index">Index that will be advanced</param>
|
||||
public static void SkipNonSpaces(string text, ref int index) {
|
||||
if(text == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int length = text.Length;
|
||||
while(index < length) {
|
||||
if(char.IsWhiteSpace(text, index)) {
|
||||
break;
|
||||
}
|
||||
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Advances the index to the next character that isn't numeric</summary>
|
||||
/// <param name="text">String which is being indexed</param>
|
||||
/// <param name="index">Index that will be advanced</param>
|
||||
/// <remarks>
|
||||
/// This skips only numeric characters, but not complete numbers -- if the number
|
||||
/// begins with a minus or plus sign, for example, this function will not skip it.
|
||||
/// </remarks>
|
||||
public static void SkipNumericals(string text, ref int index) {
|
||||
if(text == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int length = text.Length;
|
||||
while(index < length) {
|
||||
if(!char.IsNumber(text, index)) {
|
||||
break;
|
||||
}
|
||||
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Skips an integer in the provided string</summary>
|
||||
/// <param name="text">String in which an integer will be skipped</param>
|
||||
/// <param name="index">Index at which the integer begins</param>
|
||||
/// <returns>True if an integer was found and skipped, otherwise false</returns>
|
||||
public static bool SkipInteger(string text, ref int index) {
|
||||
if(text == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int length = text.Length;
|
||||
if(index >= length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the number begins with a minus or plus sign, skip over the sign
|
||||
int nextIndex;
|
||||
if((text[index] == '-') || (text[index] == '+')) {
|
||||
nextIndex = index + 1;
|
||||
|
||||
SkipNumericals(text, ref nextIndex);
|
||||
if(nextIndex == (index + 1)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
nextIndex = index;
|
||||
|
||||
SkipNumericals(text, ref nextIndex);
|
||||
if(nextIndex == index) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
index = nextIndex;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Skips a string appearing in the input text</summary>
|
||||
/// <param name="text">Text in which a string will be skipped</param>
|
||||
/// <param name="index">Index at which the string begins</param>
|
||||
/// <returns>True if a string was found and skipped, otherwise false</returns>
|
||||
public static bool SkipString(string text, ref int index) {
|
||||
if(text == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int length = text.Length;
|
||||
if(index >= length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the string begins with an opening quote, look for the closing quote
|
||||
if(text[index] == '"') {
|
||||
|
||||
int endIndex = text.IndexOf('"', index + 1);
|
||||
if(endIndex == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
index = endIndex + 1;
|
||||
return true;
|
||||
|
||||
} else { // Normal strings end with the first whitespace
|
||||
|
||||
int startIndex = index;
|
||||
SkipNonSpaces(text, ref index);
|
||||
|
||||
return (index != startIndex);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Skips a floating point value appearing in the input text</summary>
|
||||
/// <param name="text">Text in which a floating point value will be skipped</param>
|
||||
/// <param name="index">Index at which the floating point value begins</param>
|
||||
/// <returns>True if the floating point value was skipped, otherwise false</returns>
|
||||
public static bool SkipFloat(string text, ref int index) {
|
||||
if(SkipInteger(text, ref index)) {
|
||||
if(index < text.Length) {
|
||||
if(text[index] == '.') {
|
||||
++index;
|
||||
SkipNumericals(text, ref index);
|
||||
}
|
||||
if((text[index] == 'e') || (text[index] == 'E')) {
|
||||
throw new NotImplementedException("Exponential format not supported yet");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Parsing
|
||||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
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.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
|
||||
namespace Nuclex.Support.Parsing {
|
||||
|
||||
/// <summary>Provides helper methods for parsers</summary>
|
||||
public class ParserHelper {
|
||||
|
||||
/// <summary>Advances the index past any whitespace in the string</summary>
|
||||
/// <param name="text">String which is being indexed</param>
|
||||
/// <param name="index">Index that will be advanced</param>
|
||||
public static void SkipSpaces(string text, ref int index) {
|
||||
if(text == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int length = text.Length;
|
||||
while(index < length) {
|
||||
if(!char.IsWhiteSpace(text, index)) {
|
||||
break;
|
||||
}
|
||||
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Advances the index to the next whitespace in the string</summary>
|
||||
/// <param name="text">String which is being indexed</param>
|
||||
/// <param name="index">Index that will be advanced</param>
|
||||
public static void SkipNonSpaces(string text, ref int index) {
|
||||
if(text == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int length = text.Length;
|
||||
while(index < length) {
|
||||
if(char.IsWhiteSpace(text, index)) {
|
||||
break;
|
||||
}
|
||||
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Advances the index to the next character that isn't numeric</summary>
|
||||
/// <param name="text">String which is being indexed</param>
|
||||
/// <param name="index">Index that will be advanced</param>
|
||||
/// <remarks>
|
||||
/// This skips only numeric characters, but not complete numbers -- if the number
|
||||
/// begins with a minus or plus sign, for example, this function will not skip it.
|
||||
/// </remarks>
|
||||
public static void SkipNumericals(string text, ref int index) {
|
||||
if(text == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int length = text.Length;
|
||||
while(index < length) {
|
||||
if(!char.IsNumber(text, index)) {
|
||||
break;
|
||||
}
|
||||
|
||||
++index;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Skips an integer in the provided string</summary>
|
||||
/// <param name="text">String in which an integer will be skipped</param>
|
||||
/// <param name="index">Index at which the integer begins</param>
|
||||
/// <returns>True if an integer was found and skipped, otherwise false</returns>
|
||||
public static bool SkipInteger(string text, ref int index) {
|
||||
if(text == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int length = text.Length;
|
||||
if(index >= length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the number begins with a minus or plus sign, skip over the sign
|
||||
int nextIndex;
|
||||
if((text[index] == '-') || (text[index] == '+')) {
|
||||
nextIndex = index + 1;
|
||||
|
||||
SkipNumericals(text, ref nextIndex);
|
||||
if(nextIndex == (index + 1)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
nextIndex = index;
|
||||
|
||||
SkipNumericals(text, ref nextIndex);
|
||||
if(nextIndex == index) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
index = nextIndex;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Skips a string appearing in the input text</summary>
|
||||
/// <param name="text">Text in which a string will be skipped</param>
|
||||
/// <param name="index">Index at which the string begins</param>
|
||||
/// <returns>True if a string was found and skipped, otherwise false</returns>
|
||||
public static bool SkipString(string text, ref int index) {
|
||||
if(text == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int length = text.Length;
|
||||
if(index >= length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the string begins with an opening quote, look for the closing quote
|
||||
if(text[index] == '"') {
|
||||
|
||||
int endIndex = text.IndexOf('"', index + 1);
|
||||
if(endIndex == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
index = endIndex + 1;
|
||||
return true;
|
||||
|
||||
} else { // Normal strings end with the first whitespace
|
||||
|
||||
int startIndex = index;
|
||||
SkipNonSpaces(text, ref index);
|
||||
|
||||
return (index != startIndex);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Skips a floating point value appearing in the input text</summary>
|
||||
/// <param name="text">Text in which a floating point value will be skipped</param>
|
||||
/// <param name="index">Index at which the floating point value begins</param>
|
||||
/// <returns>True if the floating point value was skipped, otherwise false</returns>
|
||||
public static bool SkipFloat(string text, ref int index) {
|
||||
if(SkipInteger(text, ref index)) {
|
||||
if(index < text.Length) {
|
||||
if(text[index] == '.') {
|
||||
++index;
|
||||
SkipNumericals(text, ref index);
|
||||
}
|
||||
if((text[index] == 'e') || (text[index] == 'E')) {
|
||||
throw new NotImplementedException("Exponential format not supported yet");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Parsing
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue