diff --git a/Parser/XnParser.cs b/Parser/XnParser.cs index 57c282e..e226488 100644 --- a/Parser/XnParser.cs +++ b/Parser/XnParser.cs @@ -35,7 +35,7 @@ namespace XNeedle.Parser static Parser Function(Context ctx) { return - from name in Parse.Letter.AtLeastOnce().Text() + from name in Identifier from lparen in Parse.Char('(') from expr in Parse.Ref(() => Expr(ctx)).DelimitedBy(Parse.Char(',').Token()).Optional() from rparen in Parse.Char(')') @@ -46,23 +46,44 @@ namespace XNeedle.Parser { var mathMethodInfo = typeof(Math).GetTypeInfo().GetMethod(name, parameters.Select(e => e.Type).ToArray()); if (mathMethodInfo != null) - { return Expression.Call(mathMethodInfo, parameters); - } - var ctxMethodInfo = typeof(ContextFunctions).GetTypeInfo().GetMethod(name, parameters.Select(e => e.Type).Prepend(typeof(Context)).ToArray()); + + // Find by name and fill in optional parameters with their default values + var ctxMethodInfo = typeof(ContextFunctions).GetTypeInfo() + .GetMethods() + .FirstOrDefault(m => + { + if (m.Name != name) return false; + var mp = m.GetParameters(); + if (mp.Length == 0 || mp[0].ParameterType != typeof(Context)) return false; + int required = mp.Skip(1).Count(p => !p.HasDefaultValue); + return parameters.Length >= required && parameters.Length <= mp.Length - 1; + }); + if (ctxMethodInfo != null) { - return Expression.Call(ctxMethodInfo, parameters.Prepend(CtxParam).ToArray()); + var mp = ctxMethodInfo.GetParameters(); + var args = mp.Skip(1).Select((p, i) => + i < parameters.Length + ? parameters[i] + : Expression.Constant(p.DefaultValue, p.ParameterType) + ).ToArray(); + return Expression.Call(ctxMethodInfo, args.Prepend(CtxParam).ToArray()); } throw new ParseException(string.Format("Function '{0}({1})' does not exist.", name, string.Join(",", parameters.Select(e => e.Type.Name)))); } + public static readonly Parser HexNumber = + from prefix in Parse.String("0x").Token() + from number in Parse.Char(char.IsAsciiHexDigit, "hexadecimal digit").AtLeastOnce().Text() + select number; + static readonly Parser NumberConstant = - Parse.Decimal - .Select(x => Expression.Constant(double.Parse(x))) - .Named("number"); + HexNumber.Select(x => Expression.Constant(int.Parse(x, System.Globalization.NumberStyles.HexNumber))) + .XOr(Parse.Decimal.Select(x => Expression.Constant(double.Parse(x)))) + .Named("number"); private static readonly Parser StringConstant = from open in Parse.Char('"') @@ -183,10 +204,6 @@ namespace XNeedle.Parser from arguments in Argument.DelimitedBy(Parse.Char(',').Token()) from close in Parse.Char(')') select new Call{Identifier = new string(identifier.ToArray()), Arguments = arguments}; - - /*public static readonly Parser ExpressionObject = - from expression in CallObject - */ public static CommentParser Comment = new CommentParser { Single = "#", NewLine = Environment.NewLine }; diff --git a/Transformation/ContextFunctions.cs b/Transformation/ContextFunctions.cs index 8bf926d..e2aa770 100644 --- a/Transformation/ContextFunctions.cs +++ b/Transformation/ContextFunctions.cs @@ -26,7 +26,41 @@ namespace XNeedle.Transformation level++; return level; } - public static double crc(Context ctx) + + private static uint ComputeCrc32(byte[] data, uint poly, uint init, uint xorOut, bool reflected) + { + uint crc = init; + uint polyReflected = ReflectBits(poly, 32); + foreach (byte b in data) + { + if (reflected) + { + crc ^= b; + for (int i = 0; i < 8; i++) + crc = (crc & 1) != 0 ? (crc >> 1) ^ polyReflected : crc >> 1; + } + else + { + crc ^= (uint)b << 24; + for (int i = 0; i < 8; i++) + crc = (crc & 0x80000000u) != 0 ? (crc << 1) ^ poly : crc << 1; + } + } + return crc ^ xorOut; + } + + private static uint ReflectBits(uint value, int bits) + { + uint reflected = 0; + for (int i = 0; i < bits; i++) + { + if ((value & (1u << i)) != 0) + reflected |= 1u << (bits - 1 - i); + } + return reflected; + } + + public static double crc32(Context ctx, int poly = 0x04C11DB7, int init = 0, int xorOut = 0, bool reflected = false) { if (ctx.Node is null) return 0; @@ -36,17 +70,15 @@ namespace XNeedle.Transformation ctx.Node.WriteTo(writer); writer.Flush(); string xml = buffer.ToString(); - + // Step 2: Extract all text after the opening tag through and including the closing tag string tagName = ctx.Node.Name.LocalName; string data = XmlFragParser.ChecksumData(tagName).Parse(xml); + //byte[] fragBytes = ctx.SLoc.Encoding.GetBytes(data); + byte[] fragBytes = Encoding.UTF8.GetBytes(data); - // Step 3: Digest into CRC32 using the source encoding - var crc32 = new System.IO.Hashing.Crc32(); - crc32.Append(ctx.SLoc.Encoding.GetBytes(data)); - uint result = BitConverter.ToUInt32(crc32.GetCurrentHash()); - - return (double)result; + // Step 3: Digest into CRC32 using parameterized CRC32 - by default MPEG-2 + return (double)ComputeCrc32(fragBytes, (uint)poly, (uint)init, (uint)xorOut, reflected); } public static string text(Context ctx)