diff --git a/src/Parlot/Fluent/DecimalLiteral.cs b/src/Parlot/Fluent/DecimalLiteral.cs index cbdeb4b..d0b005e 100644 --- a/src/Parlot/Fluent/DecimalLiteral.cs +++ b/src/Parlot/Fluent/DecimalLiteral.cs @@ -41,7 +41,7 @@ public override bool Parse(ParseContext context, ref ParseResult result if (decimal.TryParse(sourceToParse, NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var value)) { - result.Set(start, end, value); + result.Set(start, end, value); return true; } } diff --git a/src/Parlot/Fluent/DoubleLiteral.cs b/src/Parlot/Fluent/DoubleLiteral.cs new file mode 100644 index 0000000..e0c78d7 --- /dev/null +++ b/src/Parlot/Fluent/DoubleLiteral.cs @@ -0,0 +1,138 @@ +using Parlot.Compilation; +using System; +using System.Globalization; +using System.Linq.Expressions; + +namespace Parlot.Fluent +{ + public sealed class DoubleLiteral : Parser, ICompilable + { + private readonly NumberOptions _numberOptions; + + public DoubleLiteral(NumberOptions numberOptions = NumberOptions.Default) + { + _numberOptions = numberOptions; + } + + public override bool Parse(ParseContext context, ref ParseResult result) + { + context.EnterParser(this); + + var reset = context.Scanner.Cursor.Position; + var start = reset.Offset; + + if ((_numberOptions & NumberOptions.AllowSign) == NumberOptions.AllowSign) + { + if (!context.Scanner.ReadChar('-')) + { + // If there is no '-' try to read a '+' but don't read both. + context.Scanner.ReadChar('+'); + } + } + + if (context.Scanner.ReadDecimal()) + { + var end = context.Scanner.Cursor.Offset; +#if !SUPPORTS_SPAN_PARSE + var sourceToParse = context.Scanner.Buffer.Substring(start, end - start); +#else + var sourceToParse = context.Scanner.Buffer.AsSpan(start, end - start); +#endif + + if (double.TryParse(sourceToParse, NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var value)) + { + result.Set(start, end, value); + return true; + } + } + + context.Scanner.Cursor.ResetPosition(reset); + + return false; + } + + public CompilationResult Compile(CompilationContext context) + { + var result = new CompilationResult(); + + var success = context.DeclareSuccessVariable(result, false); + var value = context.DeclareValueVariable(result, Expression.Default(typeof(double))); + + // var start = context.Scanner.Cursor.Offset; + // var reset = context.Scanner.Cursor.Position; + + var start = context.DeclareOffsetVariable(result); + var reset = context.DeclarePositionVariable(result); + + if ((_numberOptions & NumberOptions.AllowSign) == NumberOptions.AllowSign) + { + // if (!context.Scanner.ReadChar('-')) + // { + // context.Scanner.ReadChar('+'); + // } + + result.Body.Add( + Expression.IfThen( + Expression.Not(context.ReadChar('-')), + context.ReadChar('+') + ) + ); + } + + // if (context.Scanner.ReadDecimal()) + // { + // var end = context.Scanner.Cursor.Offset; + // NETSTANDARD2_0 var sourceToParse = context.Scanner.Buffer.Substring(start, end - start); + // NETSTANDARD2_1 var sourceToParse = context.Scanner.Buffer.AsSpan(start, end - start); + // success = double.TryParse(sourceToParse, NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var value)) + // } + // + // if (!success) + // { + // context.Scanner.Cursor.ResetPosition(begin); + // } + // + + var end = Expression.Variable(typeof(int), $"end{context.NextNumber}"); +#if NETSTANDARD2_0 + var sourceToParse = Expression.Variable(typeof(string), $"sourceToParse{context.NextNumber}"); + var sliceExpression = Expression.Assign(sourceToParse, Expression.Call(context.Buffer(), typeof(string).GetMethod("Substring", new[] { typeof(int), typeof(int) }), start, Expression.Subtract(end, start))); + var tryParseMethodInfo = typeof(double).GetMethod(nameof(double.TryParse), new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(double).MakeByRefType()}); +#else + var sourceToParse = Expression.Variable(typeof(ReadOnlySpan), $"sourceToParse{context.NextNumber}"); + var sliceExpression = Expression.Assign(sourceToParse, Expression.Call(typeof(MemoryExtensions).GetMethod("AsSpan", new[] { typeof(string), typeof(int), typeof(int) }), context.Buffer(), start, Expression.Subtract(end, start))); + var tryParseMethodInfo = typeof(double).GetMethod(nameof(double.TryParse), new[] { typeof(ReadOnlySpan), typeof(NumberStyles), typeof(IFormatProvider), typeof(double).MakeByRefType()}); +#endif + + // TODO: NETSTANDARD2_1 code path + var block = + Expression.IfThen( + context.ReadDecimal(), + Expression.Block( + new[] { end, sourceToParse }, + Expression.Assign(end, context.Offset()), + sliceExpression, + Expression.Assign(success, + Expression.Call( + tryParseMethodInfo, + sourceToParse, + Expression.Constant(NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint), + Expression.Constant(CultureInfo.InvariantCulture), + value) + ) + ) + ); + + result.Body.Add(block); + + result.Body.Add( + Expression.IfThen( + Expression.Not(success), + context.ResetPosition(reset) + ) + ); + + return result; + } + } +} diff --git a/src/Parlot/Fluent/FloatLiteral.cs b/src/Parlot/Fluent/FloatLiteral.cs new file mode 100644 index 0000000..933e18a --- /dev/null +++ b/src/Parlot/Fluent/FloatLiteral.cs @@ -0,0 +1,138 @@ +using Parlot.Compilation; +using System; +using System.Globalization; +using System.Linq.Expressions; + +namespace Parlot.Fluent +{ + public sealed class FloatLiteral : Parser, ICompilable + { + private readonly NumberOptions _numberOptions; + + public FloatLiteral(NumberOptions numberOptions = NumberOptions.Default) + { + _numberOptions = numberOptions; + } + + public override bool Parse(ParseContext context, ref ParseResult result) + { + context.EnterParser(this); + + var reset = context.Scanner.Cursor.Position; + var start = reset.Offset; + + if ((_numberOptions & NumberOptions.AllowSign) == NumberOptions.AllowSign) + { + if (!context.Scanner.ReadChar('-')) + { + // If there is no '-' try to read a '+' but don't read both. + context.Scanner.ReadChar('+'); + } + } + + if (context.Scanner.ReadDecimal()) + { + var end = context.Scanner.Cursor.Offset; +#if !SUPPORTS_SPAN_PARSE + var sourceToParse = context.Scanner.Buffer.Substring(start, end - start); +#else + var sourceToParse = context.Scanner.Buffer.AsSpan(start, end - start); +#endif + + if (float.TryParse(sourceToParse, NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var value)) + { + result.Set(start, end, value); + return true; + } + } + + context.Scanner.Cursor.ResetPosition(reset); + + return false; + } + + public CompilationResult Compile(CompilationContext context) + { + var result = new CompilationResult(); + + var success = context.DeclareSuccessVariable(result, false); + var value = context.DeclareValueVariable(result, Expression.Default(typeof(float))); + + // var start = context.Scanner.Cursor.Offset; + // var reset = context.Scanner.Cursor.Position; + + var start = context.DeclareOffsetVariable(result); + var reset = context.DeclarePositionVariable(result); + + if ((_numberOptions & NumberOptions.AllowSign) == NumberOptions.AllowSign) + { + // if (!context.Scanner.ReadChar('-')) + // { + // context.Scanner.ReadChar('+'); + // } + + result.Body.Add( + Expression.IfThen( + Expression.Not(context.ReadChar('-')), + context.ReadChar('+') + ) + ); + } + + // if (context.Scanner.ReadDecimal()) + // { + // var end = context.Scanner.Cursor.Offset; + // NETSTANDARD2_0 var sourceToParse = context.Scanner.Buffer.Substring(start, end - start); + // NETSTANDARD2_1 var sourceToParse = context.Scanner.Buffer.AsSpan(start, end - start); + // success = float.TryParse(sourceToParse, NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var value)) + // } + // + // if (!success) + // { + // context.Scanner.Cursor.ResetPosition(begin); + // } + // + + var end = Expression.Variable(typeof(int), $"end{context.NextNumber}"); +#if NETSTANDARD2_0 + var sourceToParse = Expression.Variable(typeof(string), $"sourceToParse{context.NextNumber}"); + var sliceExpression = Expression.Assign(sourceToParse, Expression.Call(context.Buffer(), typeof(string).GetMethod("Substring", new[] { typeof(int), typeof(int) }), start, Expression.Subtract(end, start))); + var tryParseMethodInfo = typeof(float).GetMethod(nameof(float.TryParse), new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(float).MakeByRefType()}); +#else + var sourceToParse = Expression.Variable(typeof(ReadOnlySpan), $"sourceToParse{context.NextNumber}"); + var sliceExpression = Expression.Assign(sourceToParse, Expression.Call(typeof(MemoryExtensions).GetMethod("AsSpan", new[] { typeof(string), typeof(int), typeof(int) }), context.Buffer(), start, Expression.Subtract(end, start))); + var tryParseMethodInfo = typeof(float).GetMethod(nameof(float.TryParse), new[] { typeof(ReadOnlySpan), typeof(NumberStyles), typeof(IFormatProvider), typeof(float).MakeByRefType()}); +#endif + + // TODO: NETSTANDARD2_1 code path + var block = + Expression.IfThen( + context.ReadDecimal(), + Expression.Block( + new[] { end, sourceToParse }, + Expression.Assign(end, context.Offset()), + sliceExpression, + Expression.Assign(success, + Expression.Call( + tryParseMethodInfo, + sourceToParse, + Expression.Constant(NumberStyles.AllowLeadingSign | NumberStyles.AllowDecimalPoint), + Expression.Constant(CultureInfo.InvariantCulture), + value) + ) + ) + ); + + result.Body.Add(block); + + result.Body.Add( + Expression.IfThen( + Expression.Not(success), + context.ResetPosition(reset) + ) + ); + + return result; + } + } +} diff --git a/src/Parlot/Fluent/Parsers.cs b/src/Parlot/Fluent/Parsers.cs index 152738a..7719881 100644 --- a/src/Parlot/Fluent/Parsers.cs +++ b/src/Parlot/Fluent/Parsers.cs @@ -117,10 +117,20 @@ public class LiteralBuilder public Parser Integer() => new IntegerLiteral(); /// - /// Builds a parser that matches a floating point number. + /// Builds a parser that matches a floating point number represented as a value. /// public Parser Decimal() => new DecimalLiteral(); + /// + /// Builds a parser that matches a floating point number represented as a value. + /// + public Parser Float() => new FloatLiteral(); + + /// + /// Builds a parser that matches a floating point number represented as a value. + /// + public Parser Double() => new DoubleLiteral(); + /// /// Builds a parser that matches an quoted string that can be escaped. /// @@ -163,10 +173,20 @@ public class TermBuilder public Parser Integer(NumberOptions numberOptions = NumberOptions.Default) => Parsers.SkipWhiteSpace(new IntegerLiteral(numberOptions)); /// - /// Builds a parser that matches a floating point number. + /// Builds a parser that matches a floating point number represented as a value. /// public Parser Decimal(NumberOptions numberOptions = NumberOptions.Default) => Parsers.SkipWhiteSpace(new DecimalLiteral(numberOptions)); + /// + /// Builds a parser that matches a floating point number represented as a value. + /// + public Parser Float(NumberOptions numberOptions = NumberOptions.Default) => Parsers.SkipWhiteSpace(new FloatLiteral(numberOptions)); + + /// + /// Builds a parser that matches a floating point number represented as a value. + /// + public Parser Double(NumberOptions numberOptions = NumberOptions.Default) => Parsers.SkipWhiteSpace(new DoubleLiteral(numberOptions)); + /// /// Builds a parser that matches an quoted string that can be escaped. ///