Enhance parser and add a few functions, fix hex parsing #4

Merged
dominic merged 1 commits from fix-parser into master 2026-04-26 17:42:52 +00:00
2 changed files with 57 additions and 7 deletions

View File

@ -78,15 +78,22 @@ namespace XNeedle.Parser
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 =
from prefix in Parse.String("0x").Token()
from number in Parse.Char(char.IsAsciiHexDigit, "hexadecimal digit").AtLeastOnce().Text()
select number;
Parse.Regex(@"0x[0-9a-fA-F]+").Token()
.Select(s => s.Substring(2));
public static readonly Parser<string> BooleanLiteral =
from value in Parse.String("true").XOr(Parse.String("false")).Text()
select value;
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))))
.Named("number");
private static readonly Parser<Expression> StringConstant =
from open in Parse.Char('"')

View File

@ -1,4 +1,5 @@
using System;
using System.Globalization;
using System.Linq;
using System.Text;
using Sprache;
@ -12,12 +13,54 @@ namespace XNeedle.Transformation
{
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;
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)
{
return text.ToLower();
@ -70,7 +113,7 @@ namespace XNeedle.Transformation
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)
return 0;
@ -88,7 +131,7 @@ namespace XNeedle.Transformation
byte[] fragBytes = Encoding.UTF8.GetBytes(data);
// 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)