Changed license to Apache License 2.0

This commit is contained in:
Markus Ewald 2024-06-13 18:36:21 +02:00 committed by cygon
parent d3bf0be9d7
commit 9f36d71529
144 changed files with 32422 additions and 32544 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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