Enhance parser and add a few functions, fix hex parsing (#4)
1. Unify all numbers under double type A bit lazy, but is easily maintainable. Precision is enough for most use cases. 2. Add more functions Add `num`, `parselit`, `str`. 3. Fix hex parser error Previously the "0x" was greedy and matches single zeros, which then raised a parsing error because it couldn't match a hex number. Reviewed-on: #4
This commit is contained in:
parent
103b9dbb98
commit
4629002ef0
@ -78,15 +78,22 @@ namespace XNeedle.Parser
|
|||||||
string.Join(",", parameters.Select(e => e.Type.Name))));
|
string.Join(",", parameters.Select(e => e.Type.Name))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This needs to match as one atomic token, else with a separate String("0x") the 0 is consumed,
|
||||||
|
// which triggers a parsing error on single zero numbers
|
||||||
public static readonly Parser<string> HexNumber =
|
public static readonly Parser<string> HexNumber =
|
||||||
from prefix in Parse.String("0x").Token()
|
Parse.Regex(@"0x[0-9a-fA-F]+").Token()
|
||||||
from number in Parse.Char(char.IsAsciiHexDigit, "hexadecimal digit").AtLeastOnce().Text()
|
.Select(s => s.Substring(2));
|
||||||
select number;
|
|
||||||
|
public static readonly Parser<string> BooleanLiteral =
|
||||||
|
from value in Parse.String("true").XOr(Parse.String("false")).Text()
|
||||||
|
select value;
|
||||||
|
|
||||||
static readonly Parser<Expression> NumberConstant =
|
static readonly Parser<Expression> NumberConstant =
|
||||||
HexNumber.Select(x => Expression.Constant(int.Parse(x, System.Globalization.NumberStyles.HexNumber)))
|
HexNumber.Select(x => Expression.Constant((double)int.Parse(x, System.Globalization.NumberStyles.HexNumber)))
|
||||||
|
.XOr(BooleanLiteral.Select(x => Expression.Constant(x == "true" ? 1.0 : 0.0)))
|
||||||
.XOr(Parse.Decimal.Select(x => Expression.Constant(double.Parse(x))))
|
.XOr(Parse.Decimal.Select(x => Expression.Constant(double.Parse(x))))
|
||||||
.Named("number");
|
.Named("number");
|
||||||
|
|
||||||
|
|
||||||
private static readonly Parser<Expression> StringConstant =
|
private static readonly Parser<Expression> StringConstant =
|
||||||
from open in Parse.Char('"')
|
from open in Parse.Char('"')
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Sprache;
|
using Sprache;
|
||||||
@ -12,12 +13,54 @@ namespace XNeedle.Transformation
|
|||||||
{
|
{
|
||||||
public class ContextFunctions
|
public class ContextFunctions
|
||||||
{
|
{
|
||||||
public static string hexlit(Context ctx, int value)
|
|
||||||
|
public static double num(Context ctx, string value, double numbase = 10)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
string cleanedValue = value.Trim();
|
||||||
|
|
||||||
|
if (numbase != 10)
|
||||||
|
{
|
||||||
|
cleanedValue = cleanedValue.TrimStart('0');
|
||||||
|
cleanedValue = cleanedValue.TrimStart('x');
|
||||||
|
|
||||||
|
// Convert to Int64 to not lose any fidelity
|
||||||
|
return Convert.ToInt64(cleanedValue, (int)numbase);
|
||||||
|
}
|
||||||
|
|
||||||
|
return double.Parse(cleanedValue, CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
public static string str(Context ctx, double value, double numbase = 10)
|
||||||
|
{
|
||||||
|
if (numbase == 10)
|
||||||
|
{
|
||||||
|
return $"{value}";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore fraction for other bases by casting to long
|
||||||
|
return Convert.ToString((long)value, (int)numbase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string hexlit(Context ctx, double value)
|
||||||
{
|
{
|
||||||
uint v = (uint)value;
|
uint v = (uint)value;
|
||||||
return $"#x{v:x8}";
|
return $"#x{v:x8}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static double parselit(Context ctx, string literal, double numbase = 16)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(literal) || literal.Length <= 2)
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Cannot parse literal \"{literal}\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (double)Convert.ToInt32(literal.Substring(2), (int)numbase);
|
||||||
|
}
|
||||||
|
|
||||||
public static string lower(Context ctx, string text)
|
public static string lower(Context ctx, string text)
|
||||||
{
|
{
|
||||||
return text.ToLower();
|
return text.ToLower();
|
||||||
@ -70,7 +113,7 @@ namespace XNeedle.Transformation
|
|||||||
return reflected;
|
return reflected;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int crc32(Context ctx, int poly = 0x04C11DB7, int init = 0, int xorOut = 0, bool reflected = false)
|
public static double crc32(Context ctx, double poly = 0x04C11DB7, double init = 0, double xorOut = 0, double reflected = 0.0)
|
||||||
{
|
{
|
||||||
if (ctx.Node is null)
|
if (ctx.Node is null)
|
||||||
return 0;
|
return 0;
|
||||||
@ -88,7 +131,7 @@ namespace XNeedle.Transformation
|
|||||||
byte[] fragBytes = Encoding.UTF8.GetBytes(data);
|
byte[] fragBytes = Encoding.UTF8.GetBytes(data);
|
||||||
|
|
||||||
// Step 3: Digest into CRC32 using parameterized CRC32 - by default MPEG-2
|
// Step 3: Digest into CRC32 using parameterized CRC32 - by default MPEG-2
|
||||||
return (int)ComputeCrc32(fragBytes, (uint)poly, (uint)init, (uint)xorOut, reflected);
|
return (double)ComputeCrc32(fragBytes, (uint)poly, (uint)init, (uint)xorOut, reflected == 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string text(Context ctx)
|
public static string text(Context ctx)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user