From f09a51689fe84dcc9f840ba0b292b5086e48aaad Mon Sep 17 00:00:00 2001 From: hnvvv Date: Sun, 22 Oct 2023 10:25:27 +0800 Subject: [PATCH] ver2.4.0 --- src/JinianNet.JNTemplate.Test/TestMore.cs | 30 +- src/JinianNet.JNTemplate.Test/TestTag.cs | 3 + .../CodeCompilation/CompileContext.cs | 11 - .../CompileContextExtensions.cs | 34 +- .../CodeCompilation/ILGeneratorExtensions.cs | 115 +-- .../CodeCompilation/TypeGuesser.cs | 74 +- .../Configuration/EngineConfig.cs | 4 +- .../Configuration/IConfig.cs | 9 +- src/JinianNet.JNTemplate/Context.cs | 4 + .../Dynamic/ReflectionExtensions.cs | 84 +- .../Hosting/DefaultHostEnvironment.cs | 28 +- src/JinianNet.JNTemplate/Hosting/IHost.cs | 33 +- .../Hosting/IHostEnvironment.cs | 23 +- src/JinianNet.JNTemplate/IOutputFormatter.cs | 29 + src/JinianNet.JNTemplate/InterpretResult.cs | 4 +- .../JinianNet.JNTemplate.csproj | 6 +- src/JinianNet.JNTemplate/Nodes/BasisTag.cs | 5 +- src/JinianNet.JNTemplate/Nodes/ComplexTag.cs | 4 +- src/JinianNet.JNTemplate/Nodes/EndTag.cs | 2 + .../Nodes/FunctaionTag.cs | 2 +- src/JinianNet.JNTemplate/Nodes/ITag.cs | 17 +- src/JinianNet.JNTemplate/Nodes/JsonTag.cs | 2 +- src/JinianNet.JNTemplate/Nodes/NullTag.cs | 2 + src/JinianNet.JNTemplate/Nodes/Tag.cs | 8 +- .../Nodes/TagCollection.cs | 32 +- .../Nodes/TagExtensions.cs | 57 +- src/JinianNet.JNTemplate/Nodes/TextTag.cs | 19 +- .../Nodes/TokenCollection.cs | 11 + .../Parsers/ArithmeticVisitor.cs | 305 ++++++ .../Parsers/ArrayRegistrar.cs | 136 --- .../Parsers/ArrayVisitor.cs | 130 +++ .../Parsers/BodyRegistrar.cs | 81 -- .../Parsers/BodyVisitor.cs | 69 ++ ...{BooleanRegistrar.cs => BooleanVisitor.cs} | 65 +- ...{CommentRegistrar.cs => CommentVisitor.cs} | 53 +- .../Parsers/ComplexRegistrar.cs | 905 ------------------ .../Parsers/ComplexVisitor.cs | 18 + .../Parsers/DynamicVisitor.cs | 80 ++ .../{TextRegistrar.cs => ElseVisitor.cs} | 62 +- .../Parsers/ElseifRegistrar.cs | 86 -- .../{ElseRegistrar.cs => ElseifVisitor.cs} | 63 +- .../{EndRegistrar.cs => EndVisitor.cs} | 48 +- .../Parsers/ForRegistrar.cs | 287 ------ .../Parsers/ForVisitor.cs | 276 ++++++ ...{ForeachRegistrar.cs => ForeachVisitor.cs} | 95 +- .../Parsers/FunctionRegistrar.cs | 359 ------- .../Parsers/FunctionVisitor.cs | 345 +++++++ .../Parsers/ITagVisitor.cs | 43 + .../Parsers/IfRegistrar.cs | 310 ------ .../{ArithmeticRegistrar.cs => IfVisitor.cs} | 382 ++++---- .../Parsers/IncludeRegistrar.cs | 153 --- .../Parsers/IncludeVisitor.cs | 141 +++ ...ValueRegistrar.cs => IndexValueVisitor.cs} | 184 ++-- .../Parsers/JsonVisitor.cs | 126 +++ .../Parsers/LayoutRegistrar.cs | 134 --- .../Parsers/LayoutVisitor.cs | 120 +++ .../Parsers/LoadRegistrar.cs | 169 ---- .../Parsers/LoadVisitor.cs | 160 ++++ .../{LogicRegistrar.cs => LogicVisitor.cs} | 502 +++++----- .../Parsers/NullVisitor.cs | 51 + .../{NumberRegistrar.cs => NumberVisitor.cs} | 93 +- ...peratorRegistrar.cs => OperatorVisitor.cs} | 19 +- .../Parsers/ReferenceRegistrar.cs | 99 -- .../{JsonRegistrar.cs => ReferenceVisitor.cs} | 93 +- .../Parsers/SetRegistrar.cs | 162 ---- .../Parsers/SetVisitor.cs | 147 +++ .../{StringRegistrar.cs => StringVisitor.cs} | 61 +- src/JinianNet.JNTemplate/Parsers/TagParser.cs | 12 +- .../Parsers/TagRegistrar.cs | 54 +- .../Parsers/TagVisitor.cs | 29 + .../{NullRegistrar.cs => TextVisitor.cs} | 54 +- .../Parsers/VariableRegistrar.cs | 260 ----- .../Parsers/VariableVisitor.cs | 247 +++++ src/JinianNet.JNTemplate/Runtime/IOptions.cs | 13 +- .../Runtime/RuntimeOptions.cs | 9 +- src/JinianNet.JNTemplate/TagResolver.cs | 422 ++++++++ src/JinianNet.JNTemplate/TemplateContext.cs | 5 - .../TemplateContextExtensions.cs | 220 +++-- src/JinianNet.JNTemplate/TemplateParser.cs | 44 +- src/JinianNet.JNTemplate/TemplatingEngine.cs | 134 ++- 80 files changed, 4172 insertions(+), 4595 deletions(-) create mode 100644 src/JinianNet.JNTemplate/IOutputFormatter.cs create mode 100644 src/JinianNet.JNTemplate/Parsers/ArithmeticVisitor.cs delete mode 100644 src/JinianNet.JNTemplate/Parsers/ArrayRegistrar.cs create mode 100644 src/JinianNet.JNTemplate/Parsers/ArrayVisitor.cs delete mode 100644 src/JinianNet.JNTemplate/Parsers/BodyRegistrar.cs create mode 100644 src/JinianNet.JNTemplate/Parsers/BodyVisitor.cs rename src/JinianNet.JNTemplate/Parsers/{BooleanRegistrar.cs => BooleanVisitor.cs} (35%) rename src/JinianNet.JNTemplate/Parsers/{CommentRegistrar.cs => CommentVisitor.cs} (39%) delete mode 100644 src/JinianNet.JNTemplate/Parsers/ComplexRegistrar.cs create mode 100644 src/JinianNet.JNTemplate/Parsers/ComplexVisitor.cs create mode 100644 src/JinianNet.JNTemplate/Parsers/DynamicVisitor.cs rename src/JinianNet.JNTemplate/Parsers/{TextRegistrar.cs => ElseVisitor.cs} (41%) delete mode 100644 src/JinianNet.JNTemplate/Parsers/ElseifRegistrar.cs rename src/JinianNet.JNTemplate/Parsers/{ElseRegistrar.cs => ElseifVisitor.cs} (47%) rename src/JinianNet.JNTemplate/Parsers/{EndRegistrar.cs => EndVisitor.cs} (41%) delete mode 100644 src/JinianNet.JNTemplate/Parsers/ForRegistrar.cs create mode 100644 src/JinianNet.JNTemplate/Parsers/ForVisitor.cs rename src/JinianNet.JNTemplate/Parsers/{ForeachRegistrar.cs => ForeachVisitor.cs} (86%) delete mode 100644 src/JinianNet.JNTemplate/Parsers/FunctionRegistrar.cs create mode 100644 src/JinianNet.JNTemplate/Parsers/FunctionVisitor.cs create mode 100644 src/JinianNet.JNTemplate/Parsers/ITagVisitor.cs delete mode 100644 src/JinianNet.JNTemplate/Parsers/IfRegistrar.cs rename src/JinianNet.JNTemplate/Parsers/{ArithmeticRegistrar.cs => IfVisitor.cs} (32%) delete mode 100644 src/JinianNet.JNTemplate/Parsers/IncludeRegistrar.cs create mode 100644 src/JinianNet.JNTemplate/Parsers/IncludeVisitor.cs rename src/JinianNet.JNTemplate/Parsers/{IndexValueRegistrar.cs => IndexValueVisitor.cs} (38%) create mode 100644 src/JinianNet.JNTemplate/Parsers/JsonVisitor.cs delete mode 100644 src/JinianNet.JNTemplate/Parsers/LayoutRegistrar.cs create mode 100644 src/JinianNet.JNTemplate/Parsers/LayoutVisitor.cs delete mode 100644 src/JinianNet.JNTemplate/Parsers/LoadRegistrar.cs create mode 100644 src/JinianNet.JNTemplate/Parsers/LoadVisitor.cs rename src/JinianNet.JNTemplate/Parsers/{LogicRegistrar.cs => LogicVisitor.cs} (36%) create mode 100644 src/JinianNet.JNTemplate/Parsers/NullVisitor.cs rename src/JinianNet.JNTemplate/Parsers/{NumberRegistrar.cs => NumberVisitor.cs} (34%) rename src/JinianNet.JNTemplate/Parsers/{OperatorRegistrar.cs => OperatorVisitor.cs} (58%) delete mode 100644 src/JinianNet.JNTemplate/Parsers/ReferenceRegistrar.cs rename src/JinianNet.JNTemplate/Parsers/{JsonRegistrar.cs => ReferenceVisitor.cs} (33%) delete mode 100644 src/JinianNet.JNTemplate/Parsers/SetRegistrar.cs create mode 100644 src/JinianNet.JNTemplate/Parsers/SetVisitor.cs rename src/JinianNet.JNTemplate/Parsers/{StringRegistrar.cs => StringVisitor.cs} (34%) create mode 100644 src/JinianNet.JNTemplate/Parsers/TagVisitor.cs rename src/JinianNet.JNTemplate/Parsers/{NullRegistrar.cs => TextVisitor.cs} (43%) delete mode 100644 src/JinianNet.JNTemplate/Parsers/VariableRegistrar.cs create mode 100644 src/JinianNet.JNTemplate/Parsers/VariableVisitor.cs create mode 100644 src/JinianNet.JNTemplate/TagResolver.cs diff --git a/src/JinianNet.JNTemplate.Test/TestMore.cs b/src/JinianNet.JNTemplate.Test/TestMore.cs index 9c7d580..888f7d8 100644 --- a/src/JinianNet.JNTemplate.Test/TestMore.cs +++ b/src/JinianNet.JNTemplate.Test/TestMore.cs @@ -48,21 +48,21 @@ namespace JinianNet.JNTemplate.Test } else { - Engine.Current.RegisterParseFunc((p, tc) => - { - if (tc.Count == 2 && tc.First.Text == ":") - { - return new TestTag - { - Document = tc[1].Text - }; - } - return null; - }); - Engine.Current.RegisterExecuteFunc((tag, c) => - { - return $"say {(tag as TestTag).Document}"; - }); + //Engine.Current.RegisterParseFunc((p, tc) => + //{ + // if (tc.Count == 2 && tc.First.Text == ":") + // { + // return new TestTag + // { + // Document = tc[1].Text + // }; + // } + // return null; + //}); + //Engine.Current.RegisterExecuteFunc((tag, c) => + //{ + // return $"say {(tag as TestTag).Document}"; + //}); } var templateContent = "${:hello}"; diff --git a/src/JinianNet.JNTemplate.Test/TestTag.cs b/src/JinianNet.JNTemplate.Test/TestTag.cs index 4a08f2c..8fd5368 100644 --- a/src/JinianNet.JNTemplate.Test/TestTag.cs +++ b/src/JinianNet.JNTemplate.Test/TestTag.cs @@ -12,6 +12,9 @@ namespace JinianNet.JNTemplate.Test public class TestTag : Tag, ITag { public string Document { get; set; } + + public override bool IsSimple => true; + } diff --git a/src/JinianNet.JNTemplate/CodeCompilation/CompileContext.cs b/src/JinianNet.JNTemplate/CodeCompilation/CompileContext.cs index 209c564..2285ec0 100644 --- a/src/JinianNet.JNTemplate/CodeCompilation/CompileContext.cs +++ b/src/JinianNet.JNTemplate/CodeCompilation/CompileContext.cs @@ -45,17 +45,6 @@ namespace JinianNet.JNTemplate.CodeCompilation /// Used to cache some compiled methods . /// public Dictionary Methods { get; set; } - - /// - /// Gets the - /// - public CompileBuilder CompileBuilder => Environment.Builder; - - /// - /// Gets the - /// - public TypeGuesser TypeGuesser => Environment.Guesser; - /// /// Set the type of compilation parameters /// diff --git a/src/JinianNet.JNTemplate/CodeCompilation/CompileContextExtensions.cs b/src/JinianNet.JNTemplate/CodeCompilation/CompileContextExtensions.cs index 8703e9a..43f933a 100644 --- a/src/JinianNet.JNTemplate/CodeCompilation/CompileContextExtensions.cs +++ b/src/JinianNet.JNTemplate/CodeCompilation/CompileContextExtensions.cs @@ -92,7 +92,7 @@ namespace JinianNet.JNTemplate.CodeCompilation /// public static Type GuessType(this CompileContext ctx, ITag tag) { - return ctx.TypeGuesser.GetType(tag, ctx); + return ctx.Resolver.GetType(tag, ctx); } @@ -115,14 +115,14 @@ namespace JinianNet.JNTemplate.CodeCompilation /// /// /// - internal static void BlockCompile(this CompileContext ctx, ILGenerator il, ITag[] tags) + internal static void BlockCompile(this CompileContext ctx, ILGenerator il, TagCollection tags) { var stringBuilderType = typeof(StringBuilder); il.DeclareLocal(stringBuilderType); il.DeclareLocal(typeof(string)); il.Emit(OpCodes.Newobj, stringBuilderType.GetConstructor(Type.EmptyTypes)); il.Emit(OpCodes.Stloc_0); - for (var i = 0; i < tags.Length; i++) + for (var i = 0; i < tags.Count; i++) { il.CallTag(ctx, tags[i], (nil, hasReturn, needCall) => { @@ -264,13 +264,17 @@ namespace JinianNet.JNTemplate.CodeCompilation /// The array of the tag. /// The . /// - private static ICompilerResult Compile(this CompileContext ctx, ITag[] tags) + private static ICompilerResult Compile(this CompileContext ctx, TagCollection tags) { var baseType = typeof(CompilerResult); + //var fileName = "233.dll"; + //var builder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName($"{baseType.Namespace}.Template{ctx.Name.GetHashCode()}"), AssemblyBuilderAccess.RunAndSave); + //ctx.TypeBuilder = ObjectBuilder.DefineType(builder, baseType.GetInterface(nameof(ICompilerResult)), baseType, $"{baseType.Namespace}.Template{ctx.Name.GetHashCode()}", "DynamicMocule", fileName); + ctx.TypeBuilder = ObjectBuilder.DefineType(baseType.GetInterface(nameof(ICompilerResult)), baseType, $"{baseType.Namespace}.Template{ctx.Name.GetHashCode()}"); var methods = new List(); - for (var i = 0; i < tags.Length; i++) + for (var i = 0; i < tags.Count; i++) { if (tags[i] == null || tags[i] is EndTag _ @@ -321,7 +325,7 @@ namespace JinianNet.JNTemplate.CodeCompilation { return null; } - + //builder.Save(fileName); var instance = type.CreateInstance(); return instance; @@ -384,8 +388,7 @@ namespace JinianNet.JNTemplate.CodeCompilation { return mi; } - var func = context.CompileBuilder.Build(name); - var method = func(tag, context); + var method = context.Resolver.Compile(tag, context); if (tagKey != null) { context.Methods[tagKey] = method; @@ -517,19 +520,8 @@ namespace JinianNet.JNTemplate.CodeCompilation if (method.ReturnType.IsMatchType(taskType)) { - if (method.ReturnType.IsGenericType) - { - if (method.ReturnType.GenericTypeArguments.Length == 1 && method.ReturnType.GenericTypeArguments[0] != typeof(string)) - { - var callMethod = typeof(Utility).GetGenericMethod(method.ReturnType.GenericTypeArguments, "ExcuteTaskAsync", new Type[] { method.ReturnType }); - context.Generator.Emit(OpCodes.Call, callMethod); - } - } - else - { - var callMethod = typeof(Utility).GetMethod("ExcuteTaskAsync", new Type[] { method.ReturnType }); - context.Generator.Emit(OpCodes.Call, callMethod); - } + var callMethod = typeof(Utility).GetMethod("ExcuteTaskAsync", new Type[] { method.ReturnType }); + context.Generator.Emit(OpCodes.Call, callMethod); } else { diff --git a/src/JinianNet.JNTemplate/CodeCompilation/ILGeneratorExtensions.cs b/src/JinianNet.JNTemplate/CodeCompilation/ILGeneratorExtensions.cs index eb3e978..d627bb0 100644 --- a/src/JinianNet.JNTemplate/CodeCompilation/ILGeneratorExtensions.cs +++ b/src/JinianNet.JNTemplate/CodeCompilation/ILGeneratorExtensions.cs @@ -24,50 +24,22 @@ namespace JinianNet.JNTemplate.CodeCompilation /// The target type. public static void ObjectTo(this ILGenerator il, Type type) { - var toString = typeof(object).GetMethodInfo("ToString", Type.EmptyTypes); - switch (type.FullName) + var m = type.GetMethod("Parse", new[] { typeof(string) }); + if (m != null) { - case "System.Int32": - il.Emit(OpCodes.Callvirt, toString); - il.Emit(OpCodes.Call, typeof(Int32).GetMethod("Parse", new[] { typeof(string) })); - break; - case "System.Int64": - il.Emit(OpCodes.Callvirt, toString); - il.Emit(OpCodes.Call, typeof(Int64).GetMethod("Parse", new[] { typeof(string) })); - break; - case "System.Int16": - il.Emit(OpCodes.Callvirt, toString); - il.Emit(OpCodes.Call, typeof(Int16).GetMethod("Parse", new[] { typeof(string) })); - break; - case "System.Decimal": - il.Emit(OpCodes.Callvirt, toString); - il.Emit(OpCodes.Call, typeof(Decimal).GetMethod("Parse", new[] { typeof(string) })); - break; - case "System.Single": - il.Emit(OpCodes.Callvirt, toString); - il.Emit(OpCodes.Call, typeof(Single).GetMethod("Parse", new[] { typeof(string) })); - break; - case "System.Double": - il.Emit(OpCodes.Callvirt, toString); - il.Emit(OpCodes.Call, typeof(Double).GetMethod("Parse", new[] { typeof(string) })); - break; - case "System.Boolean": - il.Emit(OpCodes.Callvirt, toString); - il.Emit(OpCodes.Call, typeof(Boolean).GetMethod("Parse", new[] { typeof(string) })); - break; - case "System.String": - il.Emit(OpCodes.Callvirt, toString); - break; - default: - if (type.IsValueType) - { - il.Emit(OpCodes.Unbox_Any, type); - } - else - { - il.Emit(OpCodes.Castclass, type); - } - break; + var toString = typeof(object).GetMethodInfo("ToString", Type.EmptyTypes); + il.Emit(OpCodes.Callvirt, toString); + il.Emit(OpCodes.Call, m); + return; + } + + if (type.IsValueType) + { + il.Emit(OpCodes.Unbox_Any, type); + } + else + { + il.Emit(OpCodes.Castclass, type); } } @@ -309,7 +281,7 @@ namespace JinianNet.JNTemplate.CodeCompilation /// The /// The index on which resides. /// The index on which resides. - public static void StringAppend(this ILGenerator il, CompileContext context, IList tags, int stringBuildIndex, int contextIndex) + public static void StringAppend(this ILGenerator il, CompileContext context, TagCollection tags, int stringBuildIndex, int contextIndex) { for (var i = 0; i < tags.Count; i++) { @@ -347,40 +319,31 @@ namespace JinianNet.JNTemplate.CodeCompilation public static void StringAppend(this ILGenerator il, CompileContext c, Type returnType) { var stringBuilderType = typeof(StringBuilder); - MethodInfo appendMethod; - switch (returnType.FullName) + MethodInfo appendMethod = null; + var ms = stringBuilderType.GetCacheMethods("Append"); + foreach (var m in ms) { - case "System.Object": - case "System.String": - case "System.Decimal": - case "System.Single": - case "System.UInt64": - case "System.Int64": - case "System.UInt32": - case "System.Boolean": - case "System.Double": - case "System.Char": - case "System.UInt16": - case "System.Int16": - case "System.Byte": - case "System.SByte": - case "System.Int32": - appendMethod = stringBuilderType.GetMethodInfo("Append", new Type[] { returnType }); - break; - default: - if (returnType.IsValueType) - { - var p = il.DeclareLocal(returnType); - il.Emit(OpCodes.Stloc, p.LocalIndex); - LoadVariable(il, returnType, p.LocalIndex); - il.Call(returnType, typeof(object).GetMethodInfo("ToString", Type.EmptyTypes)); - appendMethod = stringBuilderType.GetMethodInfo("Append", new Type[] { typeof(string) }); - } - else - { - appendMethod = stringBuilderType.GetMethodInfo("Append", new Type[] { typeof(object) }); - } + var ps = m.GetParameters(); + if (ps.Length == 1 && returnType.FullName == ps[0].ParameterType.FullName && m.IsPublic) + { + appendMethod = m; break; + } + } + if (appendMethod == null) + { + if (returnType.IsValueType) + { + var p = il.DeclareLocal(returnType); + il.Emit(OpCodes.Stloc, p.LocalIndex); + LoadVariable(il, returnType, p.LocalIndex); + il.Call(returnType, typeof(object).GetMethodInfo("ToString", Type.EmptyTypes)); + appendMethod = stringBuilderType.GetMethodInfo("Append", new Type[] { typeof(string) }); + } + else + { + appendMethod = stringBuilderType.GetMethodInfo("Append", new Type[] { typeof(object) }); + } } il.Emit(OpCodes.Callvirt, appendMethod); } diff --git a/src/JinianNet.JNTemplate/CodeCompilation/TypeGuesser.cs b/src/JinianNet.JNTemplate/CodeCompilation/TypeGuesser.cs index 7356dd3..364f131 100644 --- a/src/JinianNet.JNTemplate/CodeCompilation/TypeGuesser.cs +++ b/src/JinianNet.JNTemplate/CodeCompilation/TypeGuesser.cs @@ -14,69 +14,7 @@ namespace JinianNet.JNTemplate.CodeCompilation /// Type Guess /// public class TypeGuesser - { - Dictionary> dict; - /// - /// Initializes a new instance of the class. - /// - public TypeGuesser() - { - dict = new Dictionary>(StringComparer.OrdinalIgnoreCase); - } - - /// - /// Register a guess mehtod for the tag. - /// - /// The type of tag. - /// The guess method. - public void Register(Func func) where T : ITag - { - var name = typeof(T).Name; - Register(name, func); - } - - - /// - /// Register a guess mehtod for the tag. - /// - /// The name of the tag. - /// The guess method. - public void Register(string name, Func func) - { - dict[name] = func; - } - - /// - /// Gets the with the specified tag. - /// - /// The tag of the type to get. - /// The . - /// - public Type GetType(ITag tag, CompileContext ctx) - { - return GetType(tag.GetType().Name, tag, ctx); - } - - /// - /// Gets the with the specified tag. - /// - /// The tag name of the type to get. - /// The tag of the type to get. - /// The . - /// The type with the specified tag, if found; otherwise, null. - public Type GetType(string name, ITag tag, CompileContext ctx) - { - if (dict.TryGetValue(name, out var func)) - { - var type = func(tag, ctx); - if (type != null) - { - return type; - } - } - throw new CompileException(tag, $"[{name}]:\"{tag.ToSource()}\" is not defined!"); - } - + { /// /// Gets the child with the specified . /// @@ -313,14 +251,6 @@ namespace JinianNet.JNTemplate.CodeCompilation } return false; } - - - /// - /// Removes all elements from - /// - public void Clear() - { - dict.Clear(); - } + } } diff --git a/src/JinianNet.JNTemplate/Configuration/EngineConfig.cs b/src/JinianNet.JNTemplate/Configuration/EngineConfig.cs index 74a4771..3f33709 100644 --- a/src/JinianNet.JNTemplate/Configuration/EngineConfig.cs +++ b/src/JinianNet.JNTemplate/Configuration/EngineConfig.cs @@ -60,8 +60,8 @@ namespace JinianNet.JNTemplate.Configuration /// public bool ThrowExceptions { get; set; } = true; - /// - public TypeDetect TypeDetectPattern { get; set; } = TypeDetect.Standard; + // /// + //public TypeDetect TypeDetectPattern { get; set; } = TypeDetect.Standard; /// public OutMode OutMode { get; set; } = OutMode.None; } diff --git a/src/JinianNet.JNTemplate/Configuration/IConfig.cs b/src/JinianNet.JNTemplate/Configuration/IConfig.cs index 63a93ac..4e49143 100644 --- a/src/JinianNet.JNTemplate/Configuration/IConfig.cs +++ b/src/JinianNet.JNTemplate/Configuration/IConfig.cs @@ -62,10 +62,11 @@ namespace JinianNet.JNTemplate.Configuration /// bool ThrowExceptions { get; set; } - /// - /// Gets or sets the detect patterns. - /// - TypeDetect TypeDetectPattern { get; set; } + // /// + // /// Gets or sets the detect patterns. + // /// + //TypeDetect TypeDetectPattern { get; set; } + /// /// Gets or sets the output mode. /// diff --git a/src/JinianNet.JNTemplate/Context.cs b/src/JinianNet.JNTemplate/Context.cs index feb282d..b99f147 100644 --- a/src/JinianNet.JNTemplate/Context.cs +++ b/src/JinianNet.JNTemplate/Context.cs @@ -30,6 +30,10 @@ namespace JinianNet.JNTemplate this.Debug = System.Diagnostics.Debugger.IsAttached; } + /// + /// + /// + public Resolver Resolver => Environment.Resolver; /// /// Gets or sets the render mode. diff --git a/src/JinianNet.JNTemplate/Dynamic/ReflectionExtensions.cs b/src/JinianNet.JNTemplate/Dynamic/ReflectionExtensions.cs index 272bde0..06b4d34 100644 --- a/src/JinianNet.JNTemplate/Dynamic/ReflectionExtensions.cs +++ b/src/JinianNet.JNTemplate/Dynamic/ReflectionExtensions.cs @@ -12,6 +12,7 @@ using System.Reflection; using System.ComponentModel; using System.Linq.Expressions; using System.Linq; +using JinianNet.JNTemplate.Exceptions; namespace JinianNet.JNTemplate.Dynamic { @@ -134,8 +135,8 @@ namespace JinianNet.JNTemplate.Dynamic /// public static MethodInfo[] GetMethodInfos(this Type type, string name) { - IEnumerable ms = GetMethodInfos(type); - List result = new List(); + var ms = GetMethodInfos(type); + var result = new List(); foreach (MethodInfo m in ms) { if (m.Name.Equals(name, StringComparison.OrdinalIgnoreCase)) @@ -164,13 +165,13 @@ namespace JinianNet.JNTemplate.Dynamic { if (m.Name.Equals("op_Explicit", StringComparison.OrdinalIgnoreCase) || m.Name.Equals("op_Implicit", StringComparison.OrdinalIgnoreCase)) { - if (m.ReturnType == target) + if (m.ReturnType != target) + continue; + + var ps = m.GetParameters(); + if (ps.Length == 1 && ps[0].ParameterType == source) { - var ps = m.GetParameters(); - if (ps.Length == 1 && ps[0].ParameterType == source) - { - return m; - } + return m; } } } @@ -473,13 +474,12 @@ namespace JinianNet.JNTemplate.Dynamic { return p.GetValue(container, null); } - FieldInfo f = type.GetField(name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase); - if (f != null) - { - return f.GetValue(container); - } } - + FieldInfo f = type.GetField(name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase); + if (f != null) + { + return f.GetValue(container); + } return null; #else var key = $"PI${type.GetHashCode()}.{name}"; @@ -608,6 +608,57 @@ namespace JinianNet.JNTemplate.Dynamic return null; } + /// + /// + /// + /// + /// + /// + public static object[] ChangeArguments(this object[] args, ParameterInfo[] pis) + { + var newArgs = new object[pis.Length]; + for (var i = 0; i < pis.Length; i++) + { + if (args.Length <= i || args[i] == null) + { + if (pis[i].IsOptional) + newArgs[i] = (pis[i].DefaultValue != null && pis[i].DefaultValue != DBNull.Value) ? pis[i].DefaultValue : DefaultForType(pis[i].ParameterType); + continue; + } + newArgs[i] = args[i]; + var type = args[i].GetType(); + if (type == pis[i].ParameterType) + continue; + if (pis[i].ParameterType.Name == "Nullable`1") + { + var genericType = +#if NF40 || NF35 || NF20 + pis[i].ParameterType.GetGenericArguments()[0] +#else + pis[i].ParameterType.GenericTypeArguments[0] +#endif + ; + + var genericValue = args[i]; + if (genericType != type) + { + genericValue = Convert.ChangeType(args[i], genericType); + } + newArgs[i] = Activator.CreateInstance(pis[i].ParameterType, new object[] { genericValue }); + continue; + } + var changeObj = Convert.ChangeType(args[i], pis[i].ParameterType); + if (changeObj != null) + { + newArgs[i] = changeObj; + continue; + } + throw new TemplateException($"[FunctaionTag]: parameter error. Expected \"{pis[i].ParameterType.Name}\" but got \"{type.Name}\""); + } + + return newArgs; + } + /// /// Get the index value. /// @@ -719,10 +770,13 @@ namespace JinianNet.JNTemplate.Dynamic /// public static object Call(this Type type, MethodInfo method, object container, object[] args) { + ParameterInfo[] pi = method.GetParameters(); + args = args.ChangeArguments(pi); #if NF20 || !USEEXPRESSION + return method.Invoke(container, args); #else - ParameterInfo[] pi = method.GetParameters(); + var keys = pi.Select(m => m.ParameterType.GetHashCode()).ToArray(); var name = $"D${type.GetHashCode()}.{method.Name}({string.Join(",", keys)})"; diff --git a/src/JinianNet.JNTemplate/Hosting/DefaultHostEnvironment.cs b/src/JinianNet.JNTemplate/Hosting/DefaultHostEnvironment.cs index ac910eb..cf6b9f6 100644 --- a/src/JinianNet.JNTemplate/Hosting/DefaultHostEnvironment.cs +++ b/src/JinianNet.JNTemplate/Hosting/DefaultHostEnvironment.cs @@ -43,14 +43,12 @@ namespace JinianNet.JNTemplate.Hosting this.EnvironmentVariable = new Dictionary(System.StringComparer.OrdinalIgnoreCase); this.Options = options ?? new RuntimeOptions(); this.RootPath = System.IO.Directory.GetCurrentDirectory(); - this.Parser = parser ?? new TagParser(); - this.Builder = compileBuilder ?? new CompileBuilder(); - this.Guesser = typeGuesser ?? new TypeGuesser(); - this.ExecutorBuilder = executorBuilder ?? new ExecutorBuilder(); this.ScopeProvider = scopeProvider ?? new DefaultScopeProvider(); this.Cache = cache ?? new MemoryCache(); this.Loader = resourceLoader ?? new FileLoader(); - this.ApplicationName = Guid.NewGuid().ToString("N"); + this.ApplicationName = Guid.NewGuid().ToString("N"); + this.OutputFormatters = new List(); + this.Resolver = new Resolver(); this.EnvironmentName = #if DEBUG "DEBUG" @@ -77,18 +75,6 @@ namespace JinianNet.JNTemplate.Hosting /// public ResultCollection Results { get; } - /// - public TagParser Parser { set; get; } - - /// - public CompileBuilder Builder { set; get; } - - /// - public TypeGuesser Guesser { set; get; } - - /// - public ExecutorBuilder ExecutorBuilder { set; get; } - /// public IScopeProvider ScopeProvider { set; get; } @@ -102,9 +88,15 @@ namespace JinianNet.JNTemplate.Hosting public IResourceLoader Loader { set; get; } /// - public Dictionary EnvironmentVariable { set; get; } + public IDictionary EnvironmentVariable { set; get; } /// public IOptions Options { get; set; } + + /// + public IList OutputFormatters { get; private set; } + + /// + public Resolver Resolver { get; private set; } } } diff --git a/src/JinianNet.JNTemplate/Hosting/IHost.cs b/src/JinianNet.JNTemplate/Hosting/IHost.cs index d504916..e96c75d 100644 --- a/src/JinianNet.JNTemplate/Hosting/IHost.cs +++ b/src/JinianNet.JNTemplate/Hosting/IHost.cs @@ -3,7 +3,8 @@ Licensed under the MIT license. See licence.txt file in the project root for full license information. ********************************************************************************/ using JinianNet.JNTemplate.CodeCompilation; -using JinianNet.JNTemplate.Nodes; +using JinianNet.JNTemplate.Nodes; +using JinianNet.JNTemplate.Parsers; using System; using System.Reflection; @@ -19,37 +20,11 @@ namespace JinianNet.JNTemplate.Hosting /// IHostEnvironment HostEnvironment { get; } - /// - /// Register an new parsing method. - /// - /// parser of the new tag. - /// The zero-based index. - void RegisterParseFunc(Func func, - int index = 0); - - /// - /// Register an new compile method. - /// - /// Type of the new tag. - /// compile method of the new tag. - void RegisterCompileFunc(Func func) - where T : ITag; - - /// - /// Register an new guess method. - /// - /// Type of the new tag. - /// guess method of the new tag. - void RegisterGuessFunc(Func func) - where T : ITag; - /// /// Register an new excute method. /// - /// Type of the new tag. - /// excute method of the new tag. - void RegisterExecuteFunc(Func func) - where T : ITag; + /// + void Register(ITagVisitor visitor); /// /// / diff --git a/src/JinianNet.JNTemplate/Hosting/IHostEnvironment.cs b/src/JinianNet.JNTemplate/Hosting/IHostEnvironment.cs index fa4de98..3fe8c09 100644 --- a/src/JinianNet.JNTemplate/Hosting/IHostEnvironment.cs +++ b/src/JinianNet.JNTemplate/Hosting/IHostEnvironment.cs @@ -43,21 +43,7 @@ namespace JinianNet.JNTemplate.Hosting /// /// Gets or sets the tag parser of the engine. /// - TagParser Parser { set; get; } - /// - /// Gets or sets the tag of the engine. - /// - CompileBuilder Builder { set; get; } - - /// - /// Gets or sets the tag of the engine. - /// - TypeGuesser Guesser { set; get; } - - /// - /// Gets or sets the tag of the engine. - /// - ExecutorBuilder ExecutorBuilder { set; get; } + Resolver Resolver { get; } /// /// Gets or sets the of the engine. @@ -78,6 +64,11 @@ namespace JinianNet.JNTemplate.Hosting /// /// Gets or sets the environment variable of the engine. /// - Dictionary EnvironmentVariable { get; } + IDictionary EnvironmentVariable { get; } + + /// + /// Gets the of the engine. + /// + IList OutputFormatters { get; } } } diff --git a/src/JinianNet.JNTemplate/IOutputFormatter.cs b/src/JinianNet.JNTemplate/IOutputFormatter.cs new file mode 100644 index 0000000..e821d48 --- /dev/null +++ b/src/JinianNet.JNTemplate/IOutputFormatter.cs @@ -0,0 +1,29 @@ +/******************************************************************************** + Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. + Licensed under the MIT license. See licence.txt file in the project root for full license information. + ********************************************************************************/ +using JinianNet.JNTemplate.Hosting; +using System; +using System.IO; + +namespace JinianNet.JNTemplate +{ + /// + /// + /// + public interface IOutputFormatter + { + /// + /// + /// + /// + /// + bool CanWriteType(Type type); + /// + /// + /// + /// + /// + string Format(object input); + } +} diff --git a/src/JinianNet.JNTemplate/InterpretResult.cs b/src/JinianNet.JNTemplate/InterpretResult.cs index 02b0486..f3deb14 100644 --- a/src/JinianNet.JNTemplate/InterpretResult.cs +++ b/src/JinianNet.JNTemplate/InterpretResult.cs @@ -19,7 +19,7 @@ namespace JinianNet.JNTemplate /// /// /// - public InterpretResult(ITag[] tags) + public InterpretResult(TagCollection tags) { this.Tags = tags; } @@ -27,7 +27,7 @@ namespace JinianNet.JNTemplate /// /// /// - public ITag[] Tags { get; set; } + public TagCollection Tags { get; set; } /// public void Render(TextWriter writer, TemplateContext context) diff --git a/src/JinianNet.JNTemplate/JinianNet.JNTemplate.csproj b/src/JinianNet.JNTemplate/JinianNet.JNTemplate.csproj index 8eb23dd..5522584 100644 --- a/src/JinianNet.JNTemplate/JinianNet.JNTemplate.csproj +++ b/src/JinianNet.JNTemplate/JinianNet.JNTemplate.csproj @@ -6,7 +6,7 @@ JinianNet.JNTemplate false True - 2.3.3 + 2.4.0 JinianNet.com JinianNet.com JNTemplate @@ -25,8 +25,8 @@ jiniannet.snk License.txt - 2.3.3.0 - 2.3.3.0 + 2.4.0.0 + 2.4.0.0 false diff --git a/src/JinianNet.JNTemplate/Nodes/BasisTag.cs b/src/JinianNet.JNTemplate/Nodes/BasisTag.cs index 9cf6e7c..6be71a4 100644 --- a/src/JinianNet.JNTemplate/Nodes/BasisTag.cs +++ b/src/JinianNet.JNTemplate/Nodes/BasisTag.cs @@ -10,8 +10,9 @@ namespace JinianNet.JNTemplate.Nodes /// BasisTag /// [Serializable] - public abstract class BasisTag : Tag,ITag + public abstract class BasisTag : Tag, ITag { - + /// + public override bool IsSimple => true; } } diff --git a/src/JinianNet.JNTemplate/Nodes/ComplexTag.cs b/src/JinianNet.JNTemplate/Nodes/ComplexTag.cs index bd59c72..b22ec19 100644 --- a/src/JinianNet.JNTemplate/Nodes/ComplexTag.cs +++ b/src/JinianNet.JNTemplate/Nodes/ComplexTag.cs @@ -11,6 +11,8 @@ namespace JinianNet.JNTemplate.Nodes /// [Serializable] public abstract class ComplexTag : Tag, ITag - { + { + /// + public override bool IsSimple => false; } } diff --git a/src/JinianNet.JNTemplate/Nodes/EndTag.cs b/src/JinianNet.JNTemplate/Nodes/EndTag.cs index 803a87e..16c5506 100644 --- a/src/JinianNet.JNTemplate/Nodes/EndTag.cs +++ b/src/JinianNet.JNTemplate/Nodes/EndTag.cs @@ -14,5 +14,7 @@ namespace JinianNet.JNTemplate.Nodes { /// public override bool Out => false; + /// + public override bool IsPrimitive => true; } } \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Nodes/FunctaionTag.cs b/src/JinianNet.JNTemplate/Nodes/FunctaionTag.cs index 30d799b..5ce28d5 100644 --- a/src/JinianNet.JNTemplate/Nodes/FunctaionTag.cs +++ b/src/JinianNet.JNTemplate/Nodes/FunctaionTag.cs @@ -10,7 +10,7 @@ namespace JinianNet.JNTemplate.Nodes /// FunctaionTag /// [Serializable] - public class FunctaionTag : ChildrenTag + public class FunctionTag : ChildrenTag { /// /// The method name of the tag. diff --git a/src/JinianNet.JNTemplate/Nodes/ITag.cs b/src/JinianNet.JNTemplate/Nodes/ITag.cs index c89f4ab..15d6124 100644 --- a/src/JinianNet.JNTemplate/Nodes/ITag.cs +++ b/src/JinianNet.JNTemplate/Nodes/ITag.cs @@ -36,8 +36,21 @@ namespace JinianNet.JNTemplate.Nodes /// bool Out { get; } /// - /// Gets or sets the output mode + /// Gets or sets the previous tag output mode + /// + bool Previous { get; set; } + + /// + /// + /// + bool Next { get; set; } + /// + /// + /// + bool IsPrimitive { get; } + /// + /// Gets a Boolean value indicating whether this Tag is simple type. /// - ITag Previous { get; set; } + bool IsSimple { get; } } } diff --git a/src/JinianNet.JNTemplate/Nodes/JsonTag.cs b/src/JinianNet.JNTemplate/Nodes/JsonTag.cs index 50a1347..6daf235 100644 --- a/src/JinianNet.JNTemplate/Nodes/JsonTag.cs +++ b/src/JinianNet.JNTemplate/Nodes/JsonTag.cs @@ -11,7 +11,7 @@ namespace JinianNet.JNTemplate.Nodes /// JsonTag /// [Serializable] - public class JsonTag : ComplexTag + public class JsonTag : BasisTag { /// /// Gets or sets the data of the tag. diff --git a/src/JinianNet.JNTemplate/Nodes/NullTag.cs b/src/JinianNet.JNTemplate/Nodes/NullTag.cs index 1c6b5ae..2fe253e 100644 --- a/src/JinianNet.JNTemplate/Nodes/NullTag.cs +++ b/src/JinianNet.JNTemplate/Nodes/NullTag.cs @@ -12,6 +12,8 @@ namespace JinianNet.JNTemplate.Nodes [Serializable] public class NullTag : SpecialTag { + /// + public override bool IsPrimitive => true; /// public override string ToString() { diff --git a/src/JinianNet.JNTemplate/Nodes/Tag.cs b/src/JinianNet.JNTemplate/Nodes/Tag.cs index 11a7090..8ba9618 100644 --- a/src/JinianNet.JNTemplate/Nodes/Tag.cs +++ b/src/JinianNet.JNTemplate/Nodes/Tag.cs @@ -21,7 +21,13 @@ namespace JinianNet.JNTemplate.Nodes /// public virtual bool Out => true; /// - public ITag Previous { get; set; } + public bool Previous { get; set; } + /// + public bool Next { get; set; } + /// + public virtual bool IsPrimitive => false; + /// + public abstract bool IsSimple { get; } /// public virtual void AddChild(ITag node) { diff --git a/src/JinianNet.JNTemplate/Nodes/TagCollection.cs b/src/JinianNet.JNTemplate/Nodes/TagCollection.cs index 12313c5..4f6832a 100644 --- a/src/JinianNet.JNTemplate/Nodes/TagCollection.cs +++ b/src/JinianNet.JNTemplate/Nodes/TagCollection.cs @@ -34,7 +34,23 @@ namespace JinianNet.JNTemplate.Nodes { if (Count > 0) { - item.Previous = list[list.Count - 1]; + //if (list[list.Count - 1] is TextTag t) + //{ + // var text = t.OriginalText.TrimEnd(' '); + // if (text.Length > 0 && text[text.Length - 1] == '\n') + // t.Text = text; + //} + //else + //{ + // if(item is TextTag c) + // { + // var text = c.OriginalText.TrimStart(' '); + // if ((text.Length > 0 && text[0] == '\n') || (text.Length > 1 && text[0] == '\r' && text[1] == '\n')) + // c.Text = text; + // } + //} + item.Previous = list[list.Count - 1].Out; + list[list.Count - 1].Next = item.Out; } this.list.Add(item); } @@ -70,12 +86,14 @@ namespace JinianNet.JNTemplate.Nodes list[index] = value; if (index > 0) { - list[index].Previous = list[index - 1]; + value.Previous = list[index - 1].Out; + list[index - 1].Next = value.Out; } var next = index + 1; if (next < Count) { - this[next].Previous = this[index]; + this[next].Previous = value.Out; + value.Next = this[next].Out; } } } @@ -104,8 +122,12 @@ namespace JinianNet.JNTemplate.Nodes var next = index + 1; if (next < Count) { - this[next].Previous = this[index].Previous; - this[index].Previous = null; + this[next].Previous = this[index].Previous; + } + var prev = index - 1; + if(prev > 0) + { + this[prev].Next = this[index].Next; } } diff --git a/src/JinianNet.JNTemplate/Nodes/TagExtensions.cs b/src/JinianNet.JNTemplate/Nodes/TagExtensions.cs index 3bd3977..56c2eaa 100644 --- a/src/JinianNet.JNTemplate/Nodes/TagExtensions.cs +++ b/src/JinianNet.JNTemplate/Nodes/TagExtensions.cs @@ -10,6 +10,11 @@ namespace JinianNet.JNTemplate.Nodes /// public static class TagExtensions { + static char[][] newLines = new char[][] { + new char[]{ '\r','\n' }, + new char[]{ '\n' }, + }; + /// /// Returns a source code that represents the current tag. /// @@ -47,6 +52,27 @@ namespace JinianNet.JNTemplate.Nodes return sb.ToString(); } + private static int StartsWithNewLine(string text) + { + for (var i = 0; i < newLines.Length; i++) + { + if (text.Length < newLines[i].Length) + continue; + var find = true; + for (var j = 0; j < newLines[i].Length; j++) + { + if (text[j] != newLines[i][j]) + { + find = false; + break; + } + } + if(find) + return newLines[i].Length; + } + return 0; + } + /// /// Returns tag instance of /// @@ -55,26 +81,35 @@ namespace JinianNet.JNTemplate.Nodes /// public static string ToString(this TextTag tag, OutMode mode) { + var text = tag?.FirstToken?.Text; + if (string.IsNullOrEmpty(text)) + return text; switch (mode) { case OutMode.Auto: - if ((tag.Previous == null || !tag.Previous.Out) && !string.IsNullOrEmpty(tag.Text)) + if (!tag.Previous) { - if (tag.Text.Length > 1 && tag.Text[0] == '\r' && tag.Text[1] == '\n') + int len = StartsWithNewLine(text); + if (len > 0) { - return tag.Text.Substring(2); - } - - if (tag.Text.Length > 0 && tag.Text[0] == '\n') - { - return tag.Text.Substring(1); + if (string.IsNullOrEmpty(text.Trim())) + { + return string.Empty; + } + text = text.Substring(len); } } - return tag.Text; + if (!tag.Next && text.Length > 0 && text[text.Length - 1] == ' ') + { + var preText = text.TrimEnd(' '); + if (preText.Length > 0 && preText[preText.Length - 1] == '\n') + text = preText; + } + return text; case OutMode.StripWhiteSpace: - return tag.Text?.Trim(); + return text.Trim(); default: - return tag.Text; + return text; } } } diff --git a/src/JinianNet.JNTemplate/Nodes/TextTag.cs b/src/JinianNet.JNTemplate/Nodes/TextTag.cs index c010ca7..ff2c9ca 100644 --- a/src/JinianNet.JNTemplate/Nodes/TextTag.cs +++ b/src/JinianNet.JNTemplate/Nodes/TextTag.cs @@ -12,6 +12,7 @@ namespace JinianNet.JNTemplate.Nodes [Serializable] public class TextTag : SpecialTag { + private string _text; /// /// Gets the text of the tag. /// @@ -19,13 +20,27 @@ namespace JinianNet.JNTemplate.Nodes { get { + if (_text != null) + { + return _text; + } if (this.FirstToken != null) { - return this.FirstToken.ToString(); + return _text = OriginalText; } - return null; + return _text = ""; + } + set + { + _text = value; } } + + /// + /// + /// + public string OriginalText => this.FirstToken.ToString(); + /// public override string ToString() { diff --git a/src/JinianNet.JNTemplate/Nodes/TokenCollection.cs b/src/JinianNet.JNTemplate/Nodes/TokenCollection.cs index f9f5d3c..372f7c1 100644 --- a/src/JinianNet.JNTemplate/Nodes/TokenCollection.cs +++ b/src/JinianNet.JNTemplate/Nodes/TokenCollection.cs @@ -198,6 +198,16 @@ namespace JinianNet.JNTemplate.Nodes return this.list.Contains(item); } + /// + /// + /// + /// + /// + public bool Any(TokenKind kind) + { + return list.FindIndex(m => m.TokenKind == kind) != -1; + } + /// public void CopyTo(Token[] array, int arrayIndex) { @@ -281,5 +291,6 @@ namespace JinianNet.JNTemplate.Nodes { return base.GetHashCode(); } + } } \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/ArithmeticVisitor.cs b/src/JinianNet.JNTemplate/Parsers/ArithmeticVisitor.cs new file mode 100644 index 0000000..e6498a0 --- /dev/null +++ b/src/JinianNet.JNTemplate/Parsers/ArithmeticVisitor.cs @@ -0,0 +1,305 @@ +/******************************************************************************** + Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. + Licensed under the MIT license. See licence.txt file in the project root for full license information. + ********************************************************************************/ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; +using JinianNet.JNTemplate.CodeCompilation; +using JinianNet.JNTemplate.Dynamic; +using JinianNet.JNTemplate.Exceptions; +using JinianNet.JNTemplate.Nodes; + +namespace JinianNet.JNTemplate.Parsers +{ + /// + /// The registrar + /// + public class ArithmeticVisitor : TagVisitor, ITagVisitor + { + /// + public ITag Parse(TemplateParser parser, TokenCollection tc) + { + if (tc.Count > 2) + { + var tcs = tc.Split(TokenKind.Arithmetic); + if (tcs.Length <= 1) + { + return null; + } + + var tags = new List(); + for (int i = 0; i < tcs.Length; i++) + { + if (tcs[i].Count == 1 && tcs[i][0].TokenKind == TokenKind.Arithmetic) + { + tags.Add(new OperatorTag(tcs[i][0])); + } + else + { + var t = parser.ReadSimple(tcs[i]); + if (t == null) + return null; + tags.Add(t); + } + } + + if (tags.Count == 1) + { + return tags[0]; + } + + if (tags.Count > 1) + { + ITag t = new ArithmeticTag(); + for (int i = 0; i < tags.Count; i++) + { + t.AddChild(tags[i]); + } + tags.Clear(); + return t; + } + + } + + return null; + } + + /// + public MethodInfo Compile(ITag tag, CompileContext c) + { + var stringBuilderType = typeof(StringBuilder); + var t = tag as ArithmeticTag; + var type = c.GuessType(t); + var mb = c.CreateReutrnMethod(type); + var il = mb.GetILGenerator(); + Label labelEnd = il.DefineLabel(); + il.DeclareLocal(type); + var array = new object[t.Children.Count]; + var types = new List(); + var opts = new List(); + var message = new List(); + for (int i = 0; i < t.Children.Count; i++) + { + var opt = t.Children[i] as OperatorTag; + if (opt != null) + { + if (!opts.Contains(opt.Value)) + { + opts.Add(opt.Value); + } + array[i] = opt.Value; + message.Add(OperatorConvert.ToString(opt.Value)); + } + else + { + array[i] = t.Children[i]; + types.Add(c.GuessType(t.Children[i])); + message.Add(types[types.Count - 1].Name); + } + } + if (types.Contains(typeof(string)) && opts.Count == 1 && opts[0] == Operator.Add) + { + il.DeclareLocal(stringBuilderType); + il.Emit(OpCodes.Newobj, stringBuilderType.GetConstructor(Type.EmptyTypes)); + il.Emit(OpCodes.Stloc_1); + for (int i = 0; i < t.Children.Count; i++) + { + var opt = t.Children[i] as OperatorTag; + if (opt != null) + { + continue; + } + il.Emit(OpCodes.Ldloc_1); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + var m = c.CompileTag(t.Children[i]); + il.Emit(OpCodes.Call, m); + il.StringAppend(c, m.ReturnType); + il.Emit(OpCodes.Pop); + } + + il.Emit(OpCodes.Ldloc_1); + il.Call(stringBuilderType, typeof(object).GetMethodInfo("ToString", Type.EmptyTypes)); + il.Emit(OpCodes.Stloc_0); + } + else + { + var bestType = TypeGuesser.FindBestType(types.ToArray()); + var stack = ExpressionEvaluator.ProcessExpression(array); + var arr = stack.ToArray(); + for (var i = arr.Length - 1; i >= 0; i--) + { + var obj = arr[i]; + var childTag = obj as ITag; + if (childTag != null) + { + var m = c.CompileTag(childTag); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, m); + if (m.ReturnType.FullName != bestType.FullName) + { + switch (bestType.FullName) + { + case "System.Decimal": + Type cType = m.ReturnType; + switch (cType.FullName) + { + case "System.Int16": + case "System.UInt16": + case "System.Byte": + il.Emit(OpCodes.Conv_I4); + break; + } + il.Emit(OpCodes.Call, typeof(decimal).GetConstructor(new Type[] { cType })); + break; + case "System.Double": + il.Emit(OpCodes.Conv_R8); + break; + case "System.Single": + il.Emit(OpCodes.Conv_R4); + break; + case "System.Int64": + il.Emit(OpCodes.Conv_I8); + break; + case "System.UInt64": + il.Emit(OpCodes.Conv_U8); + break; + case "System.Int32": + il.Emit(OpCodes.Conv_I4); + break; + case "System.UInt32": + il.Emit(OpCodes.Conv_U4); + break; + case "System.Int16": + il.Emit(OpCodes.Conv_I2); + break; + case "System.UInt16": + il.Emit(OpCodes.Conv_U2); + break; + case "System.Byte": + il.Emit(OpCodes.Conv_U1); + break; + default: + throw new CompileException(tag, $"[ExpressionTag] : The type \"{bestType.FullName}\" is not supported ."); + } + } + } + else + { + switch ((Operator)obj) + { + case Operator.Add: + il.Emit(OpCodes.Add); + break; + case Operator.Subtract: + il.Emit(OpCodes.Sub); + break; + case Operator.Multiply: + il.Emit(OpCodes.Mul); + break; + case Operator.Divided: + il.Emit(OpCodes.Div); + break; + case Operator.Remainder: + il.Emit(OpCodes.Rem); + break; + case Operator.GreaterThan: + il.Emit(OpCodes.Cgt); + break; + case Operator.GreaterThanOrEqual: + il.Emit(OpCodes.Clt_Un); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Ceq); + break; + case Operator.LessThan: + il.Emit(OpCodes.Clt); + break; + case Operator.LessThanOrEqual: + il.Emit(OpCodes.Cgt_Un); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Ceq); + break; + case Operator.Equal: + il.Emit(OpCodes.Ceq); + break; + case Operator.NotEqual: + il.Emit(OpCodes.Ceq); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Ceq); + break; + default: + throw new CompileException(tag, $"[ExpressionTag] : The expression \"{string.Concat(message)}\" is not supported ."); + //throw new CompileException($"The operator \"{obj}\" is not supported on type \"{bestType.FullName}\" ."); + //case Operator.Or: + // il.Emit(OpCodes.Blt); + // break; + } + } + } + il.Emit(OpCodes.Stloc_0); + } + il.MarkLabel(labelEnd); + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Ret); + return mb.GetBaseDefinition(); + } + + /// + public Type GuessType(ITag tag, CompileContext c) + { + var t = tag as ArithmeticTag; + var types = new List(); + var opts = new List(); + for (var i = 0; i < t.Children.Count; i++) + { + var opt = t.Children[i] as OperatorTag; + if (opt == null) + { + types.Add(c.GuessType(t.Children[i])); + } + } + if (types.Count == 1) + { + return types[0]; + } + if (types.Contains(typeof(string))) + { + return typeof(string); + } + if (types.Count > 0) + { + return TypeGuesser.FindBestType(types.ToArray()); + } + + return typeof(int); + + } + /// + public object Excute(ITag tag, TemplateContext context) + { + var t = tag as ArithmeticTag; + var parameters = new List(); + + for (int i = 0; i < t.Children.Count; i++) + { + var opt = t.Children[i] as OperatorTag; + if (opt != null) + { + parameters.Add(opt.Value); + } + else + { + parameters.Add(context.Execute(t.Children[i])); + } + } + + var stack = ExpressionEvaluator.ProcessExpression(parameters.ToArray()); + return ExpressionEvaluator.Calculate(stack); + } + } +} diff --git a/src/JinianNet.JNTemplate/Parsers/ArrayRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/ArrayRegistrar.cs deleted file mode 100644 index c2fe799..0000000 --- a/src/JinianNet.JNTemplate/Parsers/ArrayRegistrar.cs +++ /dev/null @@ -1,136 +0,0 @@ -/******************************************************************************** - Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. - Licensed under the MIT license. See licence.txt file in the project root for full license information. - ********************************************************************************/ -using JinianNet.JNTemplate.CodeCompilation; -using JinianNet.JNTemplate.Nodes; -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Reflection.Emit; - -namespace JinianNet.JNTemplate.Parsers -{ - /// - /// The registrar - /// - public class ArrayRegistrar : TagRegistrar, IRegistrar - { - /// - public override Func BuildParseMethod() - { - return (parser, tc) => - { - if (tc.Count > 2 - && (tc[0].TokenKind == TokenKind.LeftBrace) - && tc.Last.TokenKind == TokenKind.RightBrace) - { - var list = new List(); - var tcs = tc.Split(1, tc.Count - 1, TokenKind.Comma); - for (int i = 0; i < tcs.Length; i++) - { - if (tcs[i].Count == 1 && tcs[i][0].TokenKind == TokenKind.Comma) - { - continue; - } - var itemTag = parser.Read(tcs[i]); - if (itemTag != null && itemTag is ITypeTag t) - { - list.Add(t.Value); - continue; - } - return null; - } - var tag = new ArrayTag(); - tag.Value = list.ToArray(); - return tag; - } - - return null; - }; - } - /// - public override Func BuildCompileMethod() - { - return (tag, c) => - { - var t = tag as ArrayTag; - var type = t.Value.GetType(); - var mb = c.CreateReutrnMethod(type); - var il = mb.GetILGenerator(); - il.DeclareLocal(type); - il.DeclareLocal(type); - il.Emit(OpCodes.Ldc_I4, t.Value.Length); - il.Emit(OpCodes.Newarr, typeof(object)); - il.Emit(OpCodes.Stloc_0); - - for (var i = 0; i < t.Value.Length; i++) - { - if (t.Value[i] == null) - { - continue; - } - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Ldc_I4, i); - var itemType = t.Value[i].GetType(); - switch (itemType.Name) - { - case "Int32": - il.Emit(OpCodes.Ldc_I4, (int)t.Value[i]); - il.Emit(OpCodes.Box, itemType); - break; - case "Int64": - il.Emit(OpCodes.Ldc_I8, (long)t.Value[i]); - il.Emit(OpCodes.Box, itemType); - break; - case "Single": - il.Emit(OpCodes.Ldc_R4, (float)t.Value[i]); - il.Emit(OpCodes.Box, itemType); - break; - case "Double": - il.Emit(OpCodes.Ldc_R8, (double)t.Value[i]); - il.Emit(OpCodes.Box, itemType); - break; - case "Int16": - il.Emit(OpCodes.Ldc_I4, (short)t.Value[i]); - il.Emit(OpCodes.Box, itemType); - break; - case "Boolean": - il.Emit(OpCodes.Ldc_I4, (bool)t.Value[i] ? 1 : 0); - il.Emit(OpCodes.Box, itemType); - break; - case "String": - il.Emit(OpCodes.Ldstr, t.Value[i].ToString()); - break; - default: - throw new NotSupportedException($"[ArrayTag] : [{t.Value[i]}] is not supported"); - } - il.Emit(OpCodes.Stelem_Ref); - } - - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Stloc_1); - il.Emit(OpCodes.Ldloc_1); - il.Emit(OpCodes.Ret); - return mb.GetBaseDefinition(); - }; - } - /// - public override Func BuildGuessMethod() - { - return (tag, c) => - { - return typeof(object[]); - }; - } - /// - public override Func BuildExcuteMethod() - { - return (tag, context) => - { - var t = tag as ArrayTag; - return t.Value; - }; - } - } -} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/ArrayVisitor.cs b/src/JinianNet.JNTemplate/Parsers/ArrayVisitor.cs new file mode 100644 index 0000000..3270f08 --- /dev/null +++ b/src/JinianNet.JNTemplate/Parsers/ArrayVisitor.cs @@ -0,0 +1,130 @@ +/******************************************************************************** + Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. + Licensed under the MIT license. See licence.txt file in the project root for full license information. + ********************************************************************************/ +using JinianNet.JNTemplate.CodeCompilation; +using JinianNet.JNTemplate.Nodes; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; + +namespace JinianNet.JNTemplate.Parsers +{ + /// + /// The registrar + /// + public class ArrayVisitor : TagVisitor, ITagVisitor + { + /// + public ITag Parse(TemplateParser parser, TokenCollection tc) + { + if (tc.Count > 2 + && (tc[0].TokenKind == TokenKind.LeftBrace) + && tc.Last.TokenKind == TokenKind.RightBrace + && !tc.Any(TokenKind.Colon)) + { + var list = new List(); + var tcs = tc.Split(1, tc.Count - 1, TokenKind.Comma); + for (int i = 0; i < tcs.Length; i++) + { + if (tcs[i].Count == 1 && tcs[i][0].TokenKind == TokenKind.Comma) + { + continue; + } + + var itemTag = parser.ReadSimple(tcs[i]); + if (itemTag != null && itemTag is ITypeTag t) + { + list.Add(t.Value); + continue; + } + return null; + } + var tag = new ArrayTag(); + tag.Value = list.ToArray(); + return tag; + } + + return null; + } + + /// + public MethodInfo Compile(ITag tag, CompileContext c) + { + + var t = tag as ArrayTag; + var type = t.Value.GetType(); + var mb = c.CreateReutrnMethod(type); + var il = mb.GetILGenerator(); + il.DeclareLocal(type); + il.DeclareLocal(type); + il.Emit(OpCodes.Ldc_I4, t.Value.Length); + il.Emit(OpCodes.Newarr, typeof(object)); + il.Emit(OpCodes.Stloc_0); + + for (var i = 0; i < t.Value.Length; i++) + { + if (t.Value[i] == null) + { + continue; + } + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Ldc_I4, i); + var itemType = t.Value[i].GetType(); + switch (itemType.Name) + { + case "Int32": + il.Emit(OpCodes.Ldc_I4, (int)t.Value[i]); + il.Emit(OpCodes.Box, itemType); + break; + case "Int64": + il.Emit(OpCodes.Ldc_I8, (long)t.Value[i]); + il.Emit(OpCodes.Box, itemType); + break; + case "Single": + il.Emit(OpCodes.Ldc_R4, (float)t.Value[i]); + il.Emit(OpCodes.Box, itemType); + break; + case "Double": + il.Emit(OpCodes.Ldc_R8, (double)t.Value[i]); + il.Emit(OpCodes.Box, itemType); + break; + case "Int16": + il.Emit(OpCodes.Ldc_I4, (short)t.Value[i]); + il.Emit(OpCodes.Box, itemType); + break; + case "Boolean": + il.Emit(OpCodes.Ldc_I4, (bool)t.Value[i] ? 1 : 0); + il.Emit(OpCodes.Box, itemType); + break; + case "String": + il.Emit(OpCodes.Ldstr, t.Value[i].ToString()); + break; + default: + throw new NotSupportedException($"[ArrayTag] : [{t.Value[i]}] is not supported"); + } + il.Emit(OpCodes.Stelem_Ref); + } + + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Stloc_1); + il.Emit(OpCodes.Ldloc_1); + il.Emit(OpCodes.Ret); + return mb.GetBaseDefinition(); + + } + + /// + public Type GuessType(ITag tag, CompileContext c) + { + return typeof(object[]); + } + /// + public object Excute(ITag tag, TemplateContext context) + { + var t = tag as ArrayTag; + return t.Value; + } + } +} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/BodyRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/BodyRegistrar.cs deleted file mode 100644 index 11fba93..0000000 --- a/src/JinianNet.JNTemplate/Parsers/BodyRegistrar.cs +++ /dev/null @@ -1,81 +0,0 @@ -/******************************************************************************** - Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. - Licensed under the MIT license. See licence.txt file in the project root for full license information. - ********************************************************************************/ -using JinianNet.JNTemplate.CodeCompilation; -using JinianNet.JNTemplate.Dynamic; -using JinianNet.JNTemplate.Nodes; -using System; -using System.Linq; -using System.Reflection; -using System.Reflection.Emit; - -namespace JinianNet.JNTemplate.Parsers -{ - /// - /// The registrar - /// - public class BodyRegistrar : TagRegistrar, IRegistrar - { - /// - public override Func BuildParseMethod() - { - return (parser, tc) => - { - if (tc != null - && tc.Count == 1 - && Utility.IsEqual(tc.First.Text, Const.KEY_BODY)) - { - return new BodyTag(); - } - - return null; - }; - } - /// - public override Func BuildCompileMethod() - { - return (tag, c) => - { - var t = tag as BodyTag; - var type = typeof(string); - var mb = c.CreateReutrnMethod(type); - var il = mb.GetILGenerator(); - c.BlockCompile(il, t.Children.ToArray()); - il.Emit(OpCodes.Ret); - return mb.GetBaseDefinition(); - }; - } - /// - public override Func BuildGuessMethod() - { - return (tag, c) => - { - return typeof(string); - }; - } - - /// - public override Func BuildExcuteMethod() - { - return (tag, context) => - { - var t = tag as BodyTag; - if (t.Children.Count == 0) - { - return null; - } - if (t.Children.Count == 1) - { - return context.Execute(t.Children[0]); - } - var sb = new System.Text.StringBuilder(); - for (int i = 0; i < t.Children.Count; i++) - { - sb.Append(context.Execute(t.Children[i])); - } - return sb.ToString(); - }; - } - } -} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/BodyVisitor.cs b/src/JinianNet.JNTemplate/Parsers/BodyVisitor.cs new file mode 100644 index 0000000..b820cff --- /dev/null +++ b/src/JinianNet.JNTemplate/Parsers/BodyVisitor.cs @@ -0,0 +1,69 @@ +/******************************************************************************** + Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. + Licensed under the MIT license. See licence.txt file in the project root for full license information. + ********************************************************************************/ +using JinianNet.JNTemplate.CodeCompilation; +using JinianNet.JNTemplate.Dynamic; +using JinianNet.JNTemplate.Nodes; +using System; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; + +namespace JinianNet.JNTemplate.Parsers +{ + /// + /// The registrar + /// + public class BodyVisitor : TagVisitor, ITagVisitor + { + /// + public ITag Parse(TemplateParser parser, TokenCollection tc) + { + if (tc.Count == 1 + && Utility.IsEqual(tc.First.Text, Const.KEY_BODY)) + { + return new BodyTag(); + } + + return null; + } + + /// + public MethodInfo Compile(ITag tag, CompileContext c) + { + var t = tag as BodyTag; + var type = typeof(string); + var mb = c.CreateReutrnMethod(type); + var il = mb.GetILGenerator(); + c.BlockCompile(il, t.Children); + il.Emit(OpCodes.Ret); + return mb.GetBaseDefinition(); + } + + /// + public Type GuessType(ITag tag, CompileContext c) + { + return typeof(string); + } + /// + public object Excute(ITag tag, TemplateContext context) + { + var t = tag as BodyTag; + if (t.Children.Count == 0) + { + return null; + } + if (t.Children.Count == 1) + { + return context.Execute(t.Children[0]); + } + var sb = new System.Text.StringBuilder(); + for (int i = 0; i < t.Children.Count; i++) + { + sb.Append(context.Execute(t.Children[i])); + } + return sb.ToString(); + } + } +} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/BooleanRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/BooleanVisitor.cs similarity index 35% rename from src/JinianNet.JNTemplate/Parsers/BooleanRegistrar.cs rename to src/JinianNet.JNTemplate/Parsers/BooleanVisitor.cs index c56cf71..c18015d 100644 --- a/src/JinianNet.JNTemplate/Parsers/BooleanRegistrar.cs +++ b/src/JinianNet.JNTemplate/Parsers/BooleanVisitor.cs @@ -14,59 +14,48 @@ namespace JinianNet.JNTemplate.Parsers /// /// The registrar /// - public class BooleanRegistrar : TagRegistrar, IRegistrar + public class BooleanVisitor : TagVisitor, ITagVisitor { /// - public override Func BuildParseMethod() + public ITag Parse(TemplateParser parser, TokenCollection tc) { - return (parser, tc) => + if (tc.Count == 1 + && (tc.First.Text == "true" || tc.First.Text == "false")) { - if (tc != null - && tc.Count == 1 - && (tc.First.Text == "true" || tc.First.Text == "false")) - { - var tag = new BooleanTag(); - tag.Value = Utility.StringToBoolean(tc.First.Text); - return tag; - } + var tag = new BooleanTag(); + tag.Value = Utility.StringToBoolean(tc.First.Text); + return tag; + } + + return null; - return null; - }; } /// - public override Func BuildCompileMethod() + public MethodInfo Compile(ITag tag, CompileContext c) { - return (tag, c) => - { - var t = tag as BooleanTag; - var type = t.Value.GetType(); - var mb = c.CreateReutrnMethod(type); - var il = mb.GetILGenerator(); - il.DeclareLocal(type); - il.CallTypeTag(t); - il.Emit(OpCodes.Stloc_0); - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Ret); - return mb.GetBaseDefinition(); - }; + var t = tag as BooleanTag; + var type = t.Value.GetType(); + var mb = c.CreateReutrnMethod(type); + var il = mb.GetILGenerator(); + il.DeclareLocal(type); + il.CallTypeTag(t); + il.Emit(OpCodes.Stloc_0); + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Ret); + return mb.GetBaseDefinition(); } /// - public override Func BuildGuessMethod() + public Type GuessType(ITag tag, CompileContext c) { - return (tag, c) => - { - return typeof(bool); - }; + return typeof(bool); + } /// - public override Func BuildExcuteMethod() + public object Excute(ITag tag, TemplateContext context) { - return (tag, context) => - { - var t = tag as BooleanTag; - return t.Value; - }; + var t = tag as BooleanTag; + return t.Value; } } } \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/CommentRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/CommentVisitor.cs similarity index 39% rename from src/JinianNet.JNTemplate/Parsers/CommentRegistrar.cs rename to src/JinianNet.JNTemplate/Parsers/CommentVisitor.cs index ee4f1a1..63c6d93 100644 --- a/src/JinianNet.JNTemplate/Parsers/CommentRegistrar.cs +++ b/src/JinianNet.JNTemplate/Parsers/CommentVisitor.cs @@ -13,54 +13,41 @@ namespace JinianNet.JNTemplate.Parsers /// /// The registrar /// - public class CommentRegistrar : TagRegistrar, IRegistrar + public class CommentVisitor : TagVisitor, ITagVisitor { /// - public override Func BuildParseMethod() + public ITag Parse(TemplateParser parser, TokenCollection tc) { - return (parser, tc) => + if (tc.Count == 1 + && tc[0].TokenKind == TokenKind.Comment + ) { - if (tc != null - && tc.Count == 1 - && tc[0].TokenKind == TokenKind.Comment - ) - { - var tag = new CommentTag(); - tag.FirstToken = tc[0]; - return tag; - } - return null; - }; + var tag = new CommentTag(); + tag.FirstToken = tc[0]; + return tag; + } + return null; } /// - public override Func BuildCompileMethod() + public MethodInfo Compile(ITag tag, CompileContext c) { - return (tag, c) => - { - var type = typeof(void); - var mb = c.CreateReutrnMethod(type); - var il = mb.GetILGenerator(); - il.Emit(OpCodes.Ret); - return mb.GetBaseDefinition(); - }; + var type = typeof(void); + var mb = c.CreateReutrnMethod(type); + var il = mb.GetILGenerator(); + il.Emit(OpCodes.Ret); + return mb.GetBaseDefinition(); } /// - public override Func BuildGuessMethod() + public Type GuessType(ITag tag, CompileContext c) { - return (tag, c) => - { - return typeof(void); - }; + return typeof(void); } /// - public override Func BuildExcuteMethod() + public object Excute(ITag tag, TemplateContext context) { - return (tag, context) => - { - return null; - }; + return null; } } } \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/ComplexRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/ComplexRegistrar.cs deleted file mode 100644 index c4f2786..0000000 --- a/src/JinianNet.JNTemplate/Parsers/ComplexRegistrar.cs +++ /dev/null @@ -1,905 +0,0 @@ -/******************************************************************************** - Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. - Licensed under the MIT license. See licence.txt file in the project root for full license information. - ********************************************************************************/ -using JinianNet.JNTemplate.CodeCompilation; -using JinianNet.JNTemplate.Dynamic; -using JinianNet.JNTemplate.Nodes; -using JinianNet.JNTemplate.Exceptions; -using System; -using System.Collections.Generic; -using System.Reflection.Emit; -using System.Text; -using JinianNet.JNTemplate.Hosting; - -namespace JinianNet.JNTemplate.Parsers -{ - /// - /// The complex tags registrar - /// - public class ComplexRegistrar : IRegistrar - { - #region - private static List operators = new List(new Operator[] { Operator.Equal, Operator.NotEqual, Operator.LessThan, Operator.LessThanOrEqual, Operator.GreaterThan, Operator.GreaterThanOrEqual }); - #endregion - - #region - /// - public void Regiser(IHost host) - { - host.RegisterParseFunc((parser, tc) => - { - if (tc != null - && parser != null - && tc.Count > 2) - { - - var tags = new List(); - var tcs = tc.Split(0, tc.Count, TokenKind.Dot, TokenKind.Operator); - bool isLogical = false; - if (tcs.Length == 1) - { - return null; - } - for (int i = 0; i < tcs.Length; i++) - { - if (tcs[i].Count == 1 && tcs[i][0].TokenKind == TokenKind.Dot) - { - if (tags.Count == 0 || i == tcs[i].Count - 1 || (tcs[i + 1].Count == 1 && (tcs[i + 1][0].TokenKind == TokenKind.Dot || tcs[i + 1][0].TokenKind == TokenKind.Operator))) - { - throw new ParseException($"syntax error near '.': {tc.ToString()}", tcs[i][0].BeginLine, tcs[i][0].BeginColumn); - } - if (tags[tags.Count - 1] is ReferenceTag) - { - tags[tags.Count - 1].AddChild(parser.Read(tcs[i + 1])); - } - else - { - var t = new ReferenceTag(); - t.AddChild(tags[tags.Count - 1]); - t.AddChild(parser.Read(tcs[i + 1])); - tags[tags.Count - 1] = t; - } - i++; - } - else if (tcs[i].Count == 1 && tcs[i][0].TokenKind == TokenKind.Operator) - { - tags.Add(new OperatorTag(tcs[i][0])); - - Operator op = Dynamic.OperatorConvert.Parse(tcs[i][0].Text); - switch (op) - { - case Operator.Or: - case Operator.And: - case Operator.LessThan: - case Operator.LessThanOrEqual: - case Operator.Equal: - case Operator.GreaterThan: - case Operator.GreaterThanOrEqual: - case Operator.NotEqual: - isLogical = true; - break; - } - } - else if (tcs[i][0].TokenKind == TokenKind.LeftBracket) - { - if (tags[tags.Count - 1] is ReferenceTag) - { - tags[tags.Count - 1].AddChild(parser.Read(tcs[i])); - } - else - { - if (tags.Count == 0) - { - throw new ParseException($"syntax error near '[': {tc.ToString()}", tcs[i][0].BeginLine, tcs[i][0].BeginColumn); - } - var t = new ReferenceTag(); - t.AddChild(tags[tags.Count - 1]); - t.AddChild(parser.Read(tcs[i])); - tags[tags.Count - 1] = t; - } - } - else if (tcs[i].Count > 0) - { - if (tcs[i].First.TokenKind == TokenKind.LeftParentheses && tcs[i].Last.TokenKind == TokenKind.RightParentheses) - { - tcs[i].RemoveAt(0); - tcs[i].RemoveAt(tcs[i].Count - 1); - } - tags.Add(parser.Read(tcs[i])); - } - } - - if (tags.Count == 1) - { - return tags[0]; - } - - if (tags.Count > 1) - { - var list = new List>(); - ITag t; - if (isLogical) - { - t = new LogicTag(); - var arr = Analysis(tags, new List(new Operator[] { Operator.And, Operator.Or })); - if (arr.Length == 1) - { - return arr[0]; - } - AddRange(t, arr); - } - else - { - t = new ArithmeticTag(); - for (int i = 0; i < tags.Count; i++) - { - t.AddChild(tags[i]); - } - } - tags.Clear(); - return t; - } - } - return null; - }, -1); - var options = host.HostEnvironment.Options; - if (options.Mode == EngineMode.Compiled) - { - RegiserCompile(host); - } - else - { - RegiserExcutor(host); - } - - - } - - private void RegiserCompile(IHost host) - { - host.RegisterCompileFunc((tag, c) => - { - return c.CompileTag(((ReferenceTag)tag).Child); - }); - - host.RegisterCompileFunc((tag, c) => - { - var t = tag as LogicTag; - var type = c.GuessType(t); - var mb = c.CreateReutrnMethod(type); - var il = mb.GetILGenerator(); - Label labelEnd = il.DefineLabel(); - il.DeclareLocal(type); - var array = new object[t.Children.Count]; - var types = new List(); - var opts = new List(); - var message = new List(); - for (int i = 0; i < t.Children.Count; i++) - { - var opt = t.Children[i] as OperatorTag; - if (opt != null) - { - if (!opts.Contains(opt.Value)) - { - opts.Add(opt.Value); - } - array[i] = opt.Value; - message.Add(OperatorConvert.ToString(opt.Value)); - } - else - { - array[i] = t.Children[i]; - types.Add(c.GuessType(t.Children[i])); - message.Add(types[types.Count - 1].Name); - } - } - - if (opts.Contains(Operator.Or) || opts.Contains(Operator.And)) - { - - Label labelTrue = il.DefineLabel(); - Label labelFalse = il.DefineLabel(); - Operator pre = Operator.None; - for (int i = 0; i < t.Children.Count; i++) - { - var opt = t.Children[i] as OperatorTag; - if (opt != null) - { - pre = opt.Value; - continue; - } - - var m = c.CompileTag(t.Children[i]); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Call, m); - if (m.ReturnType.Name != "Boolean") - { - var cm = typeof(Utility).GetMethodInfo("ToBoolean", new Type[] { m.ReturnType }); - if (cm == null) - { - cm = typeof(Utility).GetMethodInfo("ToBoolean", new Type[] { typeof(object) }); - if (m.ReturnType.IsValueType) - { - il.Emit(OpCodes.Box, m.ReturnType); - } - else - { - il.Emit(OpCodes.Castclass, typeof(object)); - } - } - il.Emit(OpCodes.Call, cm); - } - //il.Emit(OpCodes.Stloc_0); - if (pre == Operator.None) - { - pre = (t.Children[i + 1] as OperatorTag).Value; - } - - if (pre == Operator.Or) - { - il.Emit(OpCodes.Brtrue, labelTrue); - } - if (pre == Operator.And) - { - il.Emit(OpCodes.Brfalse, labelFalse); - } - } - - if (pre == Operator.Or) - { - il.Emit(OpCodes.Br, labelEnd); - } - - if (pre == Operator.And) - { - il.Emit(OpCodes.Br, labelTrue); - } - il.MarkLabel(labelTrue); - il.Emit(OpCodes.Ldc_I4_1); - il.Emit(OpCodes.Stloc_0); - il.Emit(OpCodes.Br, labelEnd); - - - il.MarkLabel(labelFalse); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Stloc_0); - } - else - { - if (t.Children.Count == 1) - { - var m = c.CompileTag(t.Children[0]); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Call, m); - il.Emit(OpCodes.Stloc_0); - } - else if (t.Children.Count == 3) - { - - var bestType = TypeGuesser.FindBestType(types.ToArray()); - var stack = ExpressionEvaluator.ProcessExpression(array); - var arr = stack.ToArray(); - for (var i = arr.Length - 1; i >= 0; i--) - { - var obj = arr[i]; - var childTag = obj as ITag; - if (childTag != null) - { - var m = c.CompileTag(childTag); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Call, m); - if (m.ReturnType.FullName != bestType.FullName) - { - switch (bestType.FullName) - { - case "System.Decimal": - Type cType = m.ReturnType; - switch (cType.FullName) - { - case "System.Int16": - case "System.UInt16": - case "System.Byte": - il.Emit(OpCodes.Conv_I4); - break; - } - il.Emit(OpCodes.Call, typeof(decimal).GetConstructor(new Type[] { cType })); - break; - case "System.Double": - il.Emit(OpCodes.Conv_R8); - break; - case "System.Single": - il.Emit(OpCodes.Conv_R4); - break; - case "System.Int64": - il.Emit(OpCodes.Conv_I8); - break; - case "System.UInt64": - il.Emit(OpCodes.Conv_U8); - break; - case "System.Int32": - il.Emit(OpCodes.Conv_I4); - break; - case "System.UInt32": - il.Emit(OpCodes.Conv_U4); - break; - case "System.Int16": - il.Emit(OpCodes.Conv_I2); - break; - case "System.UInt16": - il.Emit(OpCodes.Conv_U2); - break; - case "System.Byte": - il.Emit(OpCodes.Conv_U1); - break; - case "System.String": - if (m.ReturnType.IsValueType) - { - var p = il.DeclareLocal(m.ReturnType); - il.Emit(OpCodes.Stloc, p.LocalIndex); - il.Emit(OpCodes.Ldloca, p.LocalIndex); - } - il.Call(m.ReturnType, typeof(object).GetMethodInfo("ToString", Type.EmptyTypes)); - break; - default: - if (m.ReturnType.IsValueType) - { - if (bestType.IsValueType) - { - il.Emit(OpCodes.Isinst, bestType); - } - else - { - il.Emit(OpCodes.Box, m.ReturnType); - } - } - else - { - if (bestType.IsValueType) - { - il.Emit(OpCodes.Unbox, bestType); - } - else - { - il.Emit(OpCodes.Castclass, bestType); - } - } - break; - } - } - } - else - { - switch ((Operator)obj) - { - case Operator.GreaterThan: - if (TypeGuesser.CanUseEqual(bestType)) - { - il.Emit(OpCodes.Cgt); - } - else - { - var m = bestType.GetMethodInfo("op_GreaterThan", new Type[] { bestType, bestType }); - if (m == null) - { - throw new TemplateException($"Operator \">\" can not be applied operand \"{bestType.FullName}\" and \"{bestType.FullName}\""); - } - il.Emit(OpCodes.Call, m); - } - break; - case Operator.GreaterThanOrEqual: - if (TypeGuesser.CanUseEqual(bestType)) - { - il.Emit(OpCodes.Clt_Un); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - } - else - { - var m = bestType.GetMethodInfo("op_GreaterThanOrEqual", new Type[] { bestType, bestType }); - if (m == null) - { - throw new TemplateException($"Operator \">=\" can not be applied operand \"{bestType.FullName}\" and \"{bestType.FullName}\""); - } - il.Emit(OpCodes.Call, m); - } - break; - case Operator.LessThan: - if (TypeGuesser.CanUseEqual(bestType)) - { - il.Emit(OpCodes.Clt); - } - else - { - var m = bestType.GetMethodInfo("op_LessThan", new Type[] { bestType, bestType }); - if (m == null) - { - throw new TemplateException($"Operator \"<\" can not be applied operand \"{bestType.FullName}\" and \"{bestType.FullName}\""); - } - il.Emit(OpCodes.Call, m); - } - break; - case Operator.LessThanOrEqual: - if (TypeGuesser.CanUseEqual(bestType)) - { - il.Emit(OpCodes.Cgt_Un); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - } - else - { - var m = bestType.GetMethodInfo("op_LessThanOrEqual", new Type[] { bestType, bestType }); - if (m == null) - { - throw new TemplateException($"Operator \"<=\" can not be applied operand \"{bestType.FullName}\" and \"{bestType.FullName}\""); - } - il.Emit(OpCodes.Call, m); - } - break; - case Operator.Equal: - if (TypeGuesser.CanUseEqual(bestType)) - { - il.Emit(OpCodes.Ceq); - } - else - { - il.EmitEquals(bestType); - //il.Emit(OpCodes.Call, DynamicHelpers.GetMethodInfo(bestType, "Equals", new Type[] { bestType })); - } - break; - case Operator.NotEqual: - if (TypeGuesser.CanUseEqual(bestType)) - { - il.Emit(OpCodes.Ceq); - } - else - { - il.EmitEquals(bestType); - //il.Emit(OpCodes.Call, DynamicHelpers.GetMethodInfo(bestType, "Equals", new Type[] { bestType })); - } - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - break; - default: - throw new CompileException(tag, $"The operator \"{obj}\" is not supported on type \"{bestType.FullName}\" ."); - } - } - } - il.Emit(OpCodes.Stloc_0); - } - else - { - throw new CompileException(tag, $"[LogicExpressionTag] : The expression \"{string.Concat(message)}\" is not supported ."); - } - } - - il.MarkLabel(labelEnd); - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Ret); - return mb.GetBaseDefinition(); - }); - - host.RegisterCompileFunc((tag, c) => - { - var stringBuilderType = typeof(StringBuilder); - var t = tag as ArithmeticTag; - var type = c.GuessType(t); - var mb = c.CreateReutrnMethod(type); - var il = mb.GetILGenerator(); - Label labelEnd = il.DefineLabel(); - il.DeclareLocal(type); - var array = new object[t.Children.Count]; - var types = new List(); - var opts = new List(); - var message = new List(); - for (int i = 0; i < t.Children.Count; i++) - { - var opt = t.Children[i] as OperatorTag; - if (opt != null) - { - if (!opts.Contains(opt.Value)) - { - opts.Add(opt.Value); - } - array[i] = opt.Value; - message.Add(OperatorConvert.ToString(opt.Value)); - } - else - { - array[i] = t.Children[i]; - types.Add(c.GuessType(t.Children[i])); - message.Add(types[types.Count - 1].Name); - } - } - if (types.Contains(typeof(string)) && opts.Count == 1 && opts[0] == Operator.Add) - { - il.DeclareLocal(stringBuilderType); - il.Emit(OpCodes.Newobj, stringBuilderType.GetConstructor(Type.EmptyTypes)); - il.Emit(OpCodes.Stloc_1); - for (int i = 0; i < t.Children.Count; i++) - { - var opt = t.Children[i] as OperatorTag; - if (opt != null) - { - continue; - } - il.Emit(OpCodes.Ldloc_1); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - var m = c.CompileTag(t.Children[i]); - il.Emit(OpCodes.Call, m); - il.StringAppend(c, m.ReturnType); - il.Emit(OpCodes.Pop); - } - - il.Emit(OpCodes.Ldloc_1); - il.Call(stringBuilderType, typeof(object).GetMethodInfo("ToString", Type.EmptyTypes)); - il.Emit(OpCodes.Stloc_0); - } - else - { - var bestType = TypeGuesser.FindBestType(types.ToArray()); - var stack = ExpressionEvaluator.ProcessExpression(array); - var arr = stack.ToArray(); - for (var i = arr.Length - 1; i >= 0; i--) - { - var obj = arr[i]; - var childTag = obj as ITag; - if (childTag != null) - { - var m = c.CompileTag(childTag); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Call, m); - if (m.ReturnType.FullName != bestType.FullName) - { - switch (bestType.FullName) - { - case "System.Decimal": - Type cType = m.ReturnType; - switch (cType.FullName) - { - case "System.Int16": - case "System.UInt16": - case "System.Byte": - il.Emit(OpCodes.Conv_I4); - break; - } - il.Emit(OpCodes.Call, typeof(decimal).GetConstructor(new Type[] { cType })); - break; - case "System.Double": - il.Emit(OpCodes.Conv_R8); - break; - case "System.Single": - il.Emit(OpCodes.Conv_R4); - break; - case "System.Int64": - il.Emit(OpCodes.Conv_I8); - break; - case "System.UInt64": - il.Emit(OpCodes.Conv_U8); - break; - case "System.Int32": - il.Emit(OpCodes.Conv_I4); - break; - case "System.UInt32": - il.Emit(OpCodes.Conv_U4); - break; - case "System.Int16": - il.Emit(OpCodes.Conv_I2); - break; - case "System.UInt16": - il.Emit(OpCodes.Conv_U2); - break; - case "System.Byte": - il.Emit(OpCodes.Conv_U1); - break; - default: - throw new CompileException(tag, $"[ExpressionTag] : The type \"{bestType.FullName}\" is not supported ."); - } - } - } - else - { - switch ((Operator)obj) - { - case Operator.Add: - il.Emit(OpCodes.Add); - break; - case Operator.Subtract: - il.Emit(OpCodes.Sub); - break; - case Operator.Multiply: - il.Emit(OpCodes.Mul); - break; - case Operator.Divided: - il.Emit(OpCodes.Div); - break; - case Operator.Remainder: - il.Emit(OpCodes.Rem); - break; - case Operator.GreaterThan: - il.Emit(OpCodes.Cgt); - break; - case Operator.GreaterThanOrEqual: - il.Emit(OpCodes.Clt_Un); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - break; - case Operator.LessThan: - il.Emit(OpCodes.Clt); - break; - case Operator.LessThanOrEqual: - il.Emit(OpCodes.Cgt_Un); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - break; - case Operator.Equal: - il.Emit(OpCodes.Ceq); - break; - case Operator.NotEqual: - il.Emit(OpCodes.Ceq); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - break; - default: - throw new CompileException(tag, $"[ExpressionTag] : The expression \"{string.Concat(message)}\" is not supported ."); - //throw new CompileException($"The operator \"{obj}\" is not supported on type \"{bestType.FullName}\" ."); - //case Operator.Or: - // il.Emit(OpCodes.Blt); - // break; - } - } - } - il.Emit(OpCodes.Stloc_0); - } - il.MarkLabel(labelEnd); - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Ret); - return mb.GetBaseDefinition(); - }); - - host.RegisterGuessFunc((tag, c) => - { - return c.GuessType(((ReferenceTag)tag).Child); - }); - - host.RegisterGuessFunc((tag, ctx) => - { - return typeof(bool); - }); - - host.RegisterGuessFunc((tag, ctx) => - { - var t = tag as ArithmeticTag; - var types = new List(); - var opts = new List(); - for (var i = 0; i < t.Children.Count; i++) - { - var opt = t.Children[i] as OperatorTag; - if (opt == null) - { - types.Add(ctx.GuessType(t.Children[i])); - } - } - if (types.Count == 1) - { - return types[0]; - } - if (types.Contains(typeof(string))) - { - return typeof(string); - } - if (types.Count > 0) - { - return TypeGuesser.FindBestType(types.ToArray()); - } - - return typeof(int); - }); - } - - private void RegiserExcutor(IHost host) - { - host.RegisterExecuteFunc((tag, context) => - { - var t = tag as ArithmeticTag; - var parameters = new List(); - - for (int i = 0; i < t.Children.Count; i++) - { - var opt = t.Children[i] as OperatorTag; - if (opt != null) - { - parameters.Add(opt.Value); - } - else - { - parameters.Add(context.Execute(t.Children[i])); - } - } - var stack = ExpressionEvaluator.ProcessExpression(parameters.ToArray()); - return ExpressionEvaluator.Calculate(stack); - }); - - host.RegisterExecuteFunc((tag, context) => - { - var t = tag as LogicTag; - List parameters = new List(); - - for (int i = 0; i < t.Children.Count; i++) - { - bool isOperator = t.Children[i] is OperatorTag; - object result = context.Execute(t.Children[i]); - if (Eval(parameters, isOperator, result)) - { - return parameters[parameters.Count - 1]; - } - } - - var stack = ExpressionEvaluator.ProcessExpression(parameters.ToArray()); - return ExpressionEvaluator.Calculate(stack); - }); - - host.RegisterExecuteFunc((tag, context) => - { - var t = tag as ReferenceTag; - if (t.Child != null) - { - return context.Execute(t.Child); - } - return null; - }); - } - /// - /// - /// - /// - /// - /// - public static ITag[] Analysis(IList tags, IList opt) - { - var result = new List(); - var temp = new List(); - - var isLogical = false; - for (var i = 0; i < tags.Count; i++) - { - var o = tags[i] as OperatorTag; - if (o != null) - { - if (opt.Contains(o.Value)) - { - result.Add(Analysis(temp, isLogical)); - result.Add(tags[i]); - isLogical = false; - temp.Clear(); - } - else - { - if (operators.Contains(o.Value)) - { - isLogical = true; - } - temp.Add(tags[i]); - } - //result.Add(new Tem); - } - else - { - temp.Add(tags[i]); - } - } - - if (temp.Count > 0) - { - result.Add(Analysis(temp, isLogical)); - } - - return result.ToArray(); - } - /// - /// - /// - /// - /// - /// - public static ITag Analysis(IList tags, bool isLogical) - { - if (tags.Count > 1) - { - ITag t; - if (isLogical) - { - t = new LogicTag(); - AddRange(t, Analysis(tags, operators)); - } - else - { - t = new ArithmeticTag(); - AddRange(t, tags); - } - return t; - } - else - { - return tags[0]; - } - } - - /// - /// - /// - /// - /// - public static void AddRange(ITag tag, IList list) - { - for (var i = 0; i < list.Count; i++) - { - tag.AddChild(list[i]); - } - } - - - - /// - /// eval expression - /// - /// list - /// - /// - /// - public static bool Eval(List list, bool isOperator, object value) - { - if (isOperator) - { - Operator op = (Operator)value; - if (op == Operator.Or || op == Operator.And) - { - object result; - bool isTrue; - if (list.Count > 1) - { - var stack = ExpressionEvaluator.ProcessExpression(list.ToArray()); - result = ExpressionEvaluator.Calculate(stack); - } - else - { - result = list[0]; - } - if (result is bool) - { - isTrue = (bool)result; - } - else - { - isTrue = ExpressionEvaluator.CalculateBoolean(result); - } - if (op == Operator.Or && isTrue) - { - list.Add(true); - return true; - } - if (op == Operator.And && !isTrue) - { - list.Add(false); - return true; - } - list.Clear(); - list.Add(isTrue); - } - list.Add(op); - } - else - { - list.Add(value); - } - return false; - } - - #endregion - } -} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/ComplexVisitor.cs b/src/JinianNet.JNTemplate/Parsers/ComplexVisitor.cs new file mode 100644 index 0000000..87f7a00 --- /dev/null +++ b/src/JinianNet.JNTemplate/Parsers/ComplexVisitor.cs @@ -0,0 +1,18 @@ +/******************************************************************************** + Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. + Licensed under the MIT license. See licence.txt file in the project root for full license information. + ********************************************************************************/ +using JinianNet.JNTemplate.CodeCompilation; +using JinianNet.JNTemplate.Dynamic; +using JinianNet.JNTemplate.Nodes; +using JinianNet.JNTemplate.Exceptions; +using System; +using System.Collections.Generic; +using System.Reflection.Emit; +using System.Text; +using JinianNet.JNTemplate.Hosting; + +namespace JinianNet.JNTemplate.Parsers +{ + +} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/DynamicVisitor.cs b/src/JinianNet.JNTemplate/Parsers/DynamicVisitor.cs new file mode 100644 index 0000000..8ee789b --- /dev/null +++ b/src/JinianNet.JNTemplate/Parsers/DynamicVisitor.cs @@ -0,0 +1,80 @@ +/******************************************************************************** + Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. + Licensed under the MIT license. See licence.txt file in the project root for full license information. + ********************************************************************************/ +using JinianNet.JNTemplate.CodeCompilation; +using JinianNet.JNTemplate.Nodes; +using System; +using System.Reflection; + +namespace JinianNet.JNTemplate.Parsers +{ + /// + /// + /// + public class DynamicVisitor : ITagVisitor + { + + Func parseMethod; + Func compileMethod; + Func guessMethod; + Func excuteMethod; + + /// + /// + /// + /// + /// + /// + /// + public DynamicVisitor(Type type, + Func parse, + Func compile, + Func guess) + { + Name = type.Name; + parseMethod = parse; + compileMethod = compile; + guessMethod = guess; + } + + /// + /// + /// + /// + /// + public DynamicVisitor(Type type, + Func func) + { + Name = type.Name; + excuteMethod = func; + } + + /// + public string Name { get; set; } + + /// + public MethodInfo Compile(ITag tag, CompileContext context) + { + return compileMethod?.Invoke(tag, context); + } + + /// + public object Excute(ITag tag, TemplateContext context) + { + return excuteMethod?.Invoke(tag, context); + } + + /// + public Type GuessType(ITag tag, CompileContext context) + { + return guessMethod?.Invoke(tag, context); + } + + /// + public ITag Parse(TemplateParser parser, TokenCollection tc) + { + return parseMethod?.Invoke(parser, tc); + } + } +} diff --git a/src/JinianNet.JNTemplate/Parsers/TextRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/ElseVisitor.cs similarity index 41% rename from src/JinianNet.JNTemplate/Parsers/TextRegistrar.cs rename to src/JinianNet.JNTemplate/Parsers/ElseVisitor.cs index 7de1df2..42babca 100644 --- a/src/JinianNet.JNTemplate/Parsers/TextRegistrar.cs +++ b/src/JinianNet.JNTemplate/Parsers/ElseVisitor.cs @@ -4,57 +4,57 @@ ********************************************************************************/ using JinianNet.JNTemplate.CodeCompilation; using JinianNet.JNTemplate.Nodes; -using System; -using System.Linq; using System.Reflection; -using System.Reflection.Emit; +using System; +using JinianNet.JNTemplate.Dynamic; namespace JinianNet.JNTemplate.Parsers { /// - /// The registrar + /// The registrar /// - public class TextRegistrar : TagRegistrar, IRegistrar + public class ElseVisitor : TagVisitor, ITagVisitor { /// - public override Func BuildParseMethod() + public ITag Parse(TemplateParser parser, TokenCollection tc) { + if (tc.Count == 1 + && Utility.IsEqual(tc.First.Text, Const.KEY_ELSE)) + { + return new ElseTag(); + } + return null; } + /// - public override Func BuildCompileMethod() + public MethodInfo Compile(ITag tag, CompileContext c) { - return (tag, c) => - { - var t = tag as TextTag; - if (!string.IsNullOrEmpty(t.Text)) - { - var type = typeof(string); - var mb = c.CreateReutrnMethod(type); - var il = mb.GetILGenerator(); - il.Emit(OpCodes.Ldstr, t.Text); - il.Emit(OpCodes.Ret); - return mb.GetBaseDefinition(); - } - return null; - }; + return c.IfCompile((ElseTag)tag); } /// - public override Func BuildGuessMethod() + public Type GuessType(ITag tag, CompileContext c) { - return (tag, c) => - { - return typeof(string); - }; + return c.GuessIfType((ElseTag)tag); } /// - public override Func BuildExcuteMethod() + public object Excute(ITag tag, TemplateContext context) { - return (tag, context) => + var t = tag as ElseTag; + if (t.Children.Count == 0) + { + return null; + } + if (t.Children.Count == 1) + { + return context.Execute(t.Children[0]); + } + var sb = new System.Text.StringBuilder(); + for (int i = 0; i < t.Children.Count; i++) { - var t = tag as TextTag; - return t.ToString(context.OutMode); - }; + sb.Append(context.Execute(t.Children[i])); + } + return sb.ToString(); } } } \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/ElseifRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/ElseifRegistrar.cs deleted file mode 100644 index 4096f09..0000000 --- a/src/JinianNet.JNTemplate/Parsers/ElseifRegistrar.cs +++ /dev/null @@ -1,86 +0,0 @@ -/******************************************************************************** - Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. - Licensed under the MIT license. See licence.txt file in the project root for full license information. - ********************************************************************************/ -using JinianNet.JNTemplate.CodeCompilation; -using JinianNet.JNTemplate.Nodes; -using System.Reflection; -using System; -using JinianNet.JNTemplate.Dynamic; - -namespace JinianNet.JNTemplate.Parsers -{ - /// - /// The registrar - /// - public class ElseifRegistrar : TagRegistrar, IRegistrar - { - /// - public override Func BuildParseMethod() - { - return (parser, tc) => - { - if (tc != null - && parser != null - && tc.Count > 3 - && (Utility.IsEqual(tc.First.Text, Const.KEY_ELSEIF) || Utility.IsEqual(tc.First.Text, Const.KEY_ELIF)) - && tc[1].TokenKind == TokenKind.LeftParentheses - && tc.Last.TokenKind == TokenKind.RightParentheses) - { - var tag = new ElseifTag(); - - var coll = new TokenCollection(); - tag.Condition = parser.Read(tc[2, -1]); - - return tag; - } - - return null; - }; - } - - /// - public override Func BuildCompileMethod() - { - return (tag, c) => - { - return c.IfCompile((ElseifTag)tag); - }; - } - /// - public override Func BuildGuessMethod() - { - return (tag, c) => - { - return c.GuessIfType((ElseifTag)tag); - }; - } - /// - public override Func BuildExcuteMethod() - { - return (tag, context) => - { - var t = tag as ElseifTag; - var condition = context.Execute(t.Condition); - if (Utility.ToBoolean(condition)) - { - if (t.Children.Count == 0) - { - return null; - } - if (t.Children.Count == 1) - { - return context.Execute(t.Children[0]); - } - var sb = new System.Text.StringBuilder(); - for (int i = 0; i < t.Children.Count; i++) - { - sb.Append(context.Execute(t.Children[i])); - } - return sb.ToString(); - } - return null; - }; - } - } -} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/ElseRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/ElseifVisitor.cs similarity index 47% rename from src/JinianNet.JNTemplate/Parsers/ElseRegistrar.cs rename to src/JinianNet.JNTemplate/Parsers/ElseifVisitor.cs index 13280a4..28b265d 100644 --- a/src/JinianNet.JNTemplate/Parsers/ElseRegistrar.cs +++ b/src/JinianNet.JNTemplate/Parsers/ElseifVisitor.cs @@ -1,4 +1,4 @@ -/******************************************************************************** +/******************************************************************************** Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. Licensed under the MIT license. See licence.txt file in the project root for full license information. ********************************************************************************/ @@ -11,49 +11,52 @@ using JinianNet.JNTemplate.Dynamic; namespace JinianNet.JNTemplate.Parsers { /// - /// The registrar + /// The registrar /// - public class ElseRegistrar : TagRegistrar, IRegistrar + public class ElseifVisitor : TagVisitor, ITagVisitor { /// - public override Func BuildParseMethod() + public ITag Parse(TemplateParser parser, TokenCollection tc) { - return (parser, tc) => + if (tc.Count > 3 + && (Utility.IsEqual(tc.First.Text, Const.KEY_ELSEIF) || Utility.IsEqual(tc.First.Text, Const.KEY_ELIF)) + && tc[1].TokenKind == TokenKind.LeftParentheses + && tc.Last.TokenKind == TokenKind.RightParentheses) { - if (tc != null - && parser != null - && tc.Count == 1 - && Utility.IsEqual(tc.First.Text, Const.KEY_ELSE)) - { - return new ElseTag(); - } + var tag = new ElseifTag(); + + var coll = new TokenCollection(); + tag.Condition = parser.Read(tc[2, -1]); + if (!tag.Condition.IsSimple) + return null; + return tag; + } - return null; - }; + return null; } /// - public override Func BuildCompileMethod() + public MethodInfo Compile(ITag tag, CompileContext c) { - return (tag, c) => - { - return c.IfCompile((ElseTag)tag); - }; + + return c.IfCompile((ElseifTag)tag); + } /// - public override Func BuildGuessMethod() + public Type GuessType(ITag tag, CompileContext c) { - return (tag, c) => - { - return c.GuessIfType((ElseTag)tag); - }; + + + return c.GuessIfType((ElseifTag)tag); + } /// - public override Func BuildExcuteMethod() - { - return (tag, context) => + public object Excute(ITag tag, TemplateContext context) + { + var t = tag as ElseifTag; + var condition = context.Execute(t.Condition); + if (Utility.ToBoolean(condition)) { - var t = tag as ElseTag; if (t.Children.Count == 0) { return null; @@ -68,7 +71,9 @@ namespace JinianNet.JNTemplate.Parsers sb.Append(context.Execute(t.Children[i])); } return sb.ToString(); - }; + } + return null; + } } } \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/EndRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/EndVisitor.cs similarity index 41% rename from src/JinianNet.JNTemplate/Parsers/EndRegistrar.cs rename to src/JinianNet.JNTemplate/Parsers/EndVisitor.cs index e693cd5..8d8b8c8 100644 --- a/src/JinianNet.JNTemplate/Parsers/EndRegistrar.cs +++ b/src/JinianNet.JNTemplate/Parsers/EndVisitor.cs @@ -13,51 +13,39 @@ namespace JinianNet.JNTemplate.Parsers /// /// The registrar /// - public class EndRegistrar : TagRegistrar, IRegistrar + public class EndVisitor : TagVisitor, ITagVisitor { /// - public override Func BuildParseMethod() + public ITag Parse(TemplateParser parser, TokenCollection tc) { - return (parser, tc) => + + if (tc.Count == 1 + && Utility.IsEqual(tc.First.Text, Const.KEY_END)) { - if (tc != null - && tc.Count == 1 - && Utility.IsEqual(tc.First.Text, Const.KEY_END)) - { - return new EndTag(); - } + return new EndTag(); + } - return null; - }; + return null; } /// - public override Func BuildCompileMethod() + public MethodInfo Compile(ITag tag, CompileContext c) { - return (tag, c) => - { - var type = typeof(void); - var mb = c.CreateReutrnMethod(type); - var il = mb.GetILGenerator(); - il.Emit(OpCodes.Ret); - return mb.GetBaseDefinition(); - }; + var type = typeof(void); + var mb = c.CreateReutrnMethod(type); + var il = mb.GetILGenerator(); + il.Emit(OpCodes.Ret); + return mb.GetBaseDefinition(); } /// - public override Func BuildGuessMethod() + public Type GuessType(ITag tag, CompileContext c) { - return (tag, c) => - { - return typeof(void); - }; + return typeof(void); } /// - public override Func BuildExcuteMethod() + public object Excute(ITag tag, TemplateContext context) { - return (tag, context) => - { - return null; - }; + return null; } } } \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/ForRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/ForRegistrar.cs deleted file mode 100644 index 99dc72d..0000000 --- a/src/JinianNet.JNTemplate/Parsers/ForRegistrar.cs +++ /dev/null @@ -1,287 +0,0 @@ -/******************************************************************************** - Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. - Licensed under the MIT license. See licence.txt file in the project root for full license information. - ********************************************************************************/ -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Reflection.Emit; -using System.Text; -using JinianNet.JNTemplate.CodeCompilation; -using JinianNet.JNTemplate.Dynamic; -using JinianNet.JNTemplate.Exceptions; -using JinianNet.JNTemplate.Nodes; - -namespace JinianNet.JNTemplate.Parsers -{ - /// - /// The registrar - /// - public class ForRegistrar : TagRegistrar, IRegistrar - { - /// - public override Func BuildParseMethod() - { - return (parser, tc) => - { - if (tc != null - && parser != null - && tc.Count > 3 - && Utility.IsEqual(Const.KEY_FOR, tc.First.Text)) - { - - if (tc[1].TokenKind == TokenKind.LeftParentheses - && tc.Last.TokenKind == TokenKind.RightParentheses) - { - int pos = 0, - start = 2, - end; - - var ts = new List(3); - - var tag = new ForTag(); - for (int i = 2; i < tc.Count - 1; i++) - { - end = i; - if (tc[i].TokenKind == TokenKind.Punctuation && tc[i].Text == ";") - { - if (pos == 0) - { - var coll = tc[start, end]; - if (coll.Count > 0) - { - ts.Add(parser.Read(coll)); - } - else - { - ts.Add(null); - } - start = i + 1; - continue; - } - } - - if (tc[i].TokenKind == TokenKind.LeftParentheses) - { - pos++; - } - else if (tc[i].TokenKind == TokenKind.RightParentheses) - { - pos--; - } - if (i == tc.Count - 2) - { - var coll = tc[start, end + 1]; - if (coll.Count > 0) - { - ts.Add(parser.Read(coll)); - } - else - { - ts.Add(null); - } - } - } - - if (ts.Count != 3) - { - throw new ParseException($"syntax error near for on {tc.ToString()}", tc.First.BeginLine, tc.First.BeginColumn); - } - - tag.Initial = ts[0]; - tag.Condition = ts[1]; - tag.Do = ts[2]; - - while (parser.MoveNext()) - { - tag.Children.Add(parser.Current); - if (parser.Current is EndTag) - { - return tag; - } - } - - throw new ParseException($"for is not properly closed by a end tag: {tc.ToString()}", tc.First.BeginLine, tc.First.BeginColumn); - } - else - { - throw new ParseException($"syntax error near for: {tc.ToString()}",tc.First.BeginLine, tc.First.BeginColumn); - } - } - - return null; - }; - } - - /// - public override Func BuildCompileMethod() - { - return (tag, c) => - { - var stringBuilderType = typeof(StringBuilder); - var t = tag as ForTag; - var type = c.GuessType(t); - var templateContextType = typeof(TemplateContext); - var mb = c.CreateReutrnMethod(type); - var il = mb.GetILGenerator(); - //Label labelEnd = il.DefineLabel(); - Label labelNext = il.DefineLabel(); - Label labelStart = il.DefineLabel(); - il.DeclareLocal(stringBuilderType); - il.DeclareLocal(templateContextType); - il.DeclareLocal(typeof(bool)); - il.DeclareLocal(typeof(string)); - var index = 4; - il.Emit(OpCodes.Nop); - il.Emit(OpCodes.Newobj, stringBuilderType.GetConstructor(Type.EmptyTypes)); - il.Emit(OpCodes.Stloc_0); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Call, templateContextType.GetMethodInfo("CreateContext", new Type[] { templateContextType })); - il.Emit(OpCodes.Stloc_1); - - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldloc_1); - var m = c.CompileTag(t.Initial); - il.Emit(OpCodes.Call, m); - if (m.ReturnType.FullName != "System.Void") - { - il.DeclareLocal(m.ReturnType); - il.Emit(OpCodes.Stloc, index); - index++; - } - - il.Emit(OpCodes.Br, labelNext); - - il.MarkLabel(labelStart); - for (var i = 0; i < t.Children.Count; i++) - { - il.CallTag(c, t.Children[i], (nil, hasReturn, needCall) => - { - if (hasReturn) - { - nil.Emit(OpCodes.Ldloc_0); - } - if (needCall) - { - nil.Emit(OpCodes.Ldarg_0); - nil.Emit(OpCodes.Ldloc_1); - } - }, (nil, returnType) => - { - if (returnType == null) - { - return; - } - nil.StringAppend(c, returnType); - nil.Emit(OpCodes.Pop); - }); - } - - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldloc_1); - m = c.CompileTag(t.Do); - il.Emit(OpCodes.Call, m); - - if (m.ReturnType.FullName != "System.Void") - { - il.DeclareLocal(m.ReturnType); - il.Emit(OpCodes.Stloc, index); - index++; - } - - - il.MarkLabel(labelNext); - - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldloc_1); - - m = c.CompileTag(t.Condition); - il.Emit(OpCodes.Call, m); - - if (m.ReturnType.Name != "Boolean") - { - var cm = typeof(Utility).GetMethodInfo("ToBoolean", new Type[] { m.ReturnType }); - if (cm == null) - { - cm = typeof(Utility).GetMethodInfo("ToBoolean", new Type[] { typeof(object) }); - if (m.ReturnType.IsValueType) - { - il.Emit(OpCodes.Box, m.ReturnType); - } - else - { - il.Emit(OpCodes.Castclass, typeof(object)); - } - } - il.Emit(OpCodes.Call, cm); - } - - il.Emit(OpCodes.Stloc, 2); - il.Emit(OpCodes.Ldloc, 2); - il.Emit(OpCodes.Brtrue, labelStart); - - - il.Emit(OpCodes.Ldloc, 0); - il.Call(stringBuilderType, stringBuilderType.GetMethodInfo("ToString", Type.EmptyTypes)); - - - il.Emit(OpCodes.Stloc, 3); - il.Emit(OpCodes.Ldloc, 3); - il.Emit(OpCodes.Ret); - return mb.GetBaseDefinition(); - }; - } - /// - public override Func BuildGuessMethod() - { - return (tag, c) => - { - return typeof(string); - }; - } - /// - public override Func BuildExcuteMethod() - { - return (tag, context) => - { - var t = tag as ForTag; - context.Execute(t.Initial); - //ǩΪգֱΪfalse,ѭڴ - bool run; - - if (t.Condition == null) - { - run = false; - } - else - { - run = Utility.ToBoolean(context.Execute(t.Condition)); - } - using (var writer = new StringWriter()) - { - while (run) - { - for (int i = 0; i < t.Children.Count; i++) - { - var obj = context.Execute(t.Children[i]); - if (obj != null) - { - writer.Write(obj.ToString()); - } - } - - if (t.Do != null) - { - //ִм㣬Ҫi++ - context.Execute(t.Do); - } - run = Utility.ToBoolean(context.Execute(t.Condition)); - } - return writer.ToString(); - } - }; - } - } - -} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/ForVisitor.cs b/src/JinianNet.JNTemplate/Parsers/ForVisitor.cs new file mode 100644 index 0000000..7c7ba93 --- /dev/null +++ b/src/JinianNet.JNTemplate/Parsers/ForVisitor.cs @@ -0,0 +1,276 @@ +/******************************************************************************** + Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. + Licensed under the MIT license. See licence.txt file in the project root for full license information. + ********************************************************************************/ +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; +using JinianNet.JNTemplate.CodeCompilation; +using JinianNet.JNTemplate.Dynamic; +using JinianNet.JNTemplate.Exceptions; +using JinianNet.JNTemplate.Nodes; + +namespace JinianNet.JNTemplate.Parsers +{ + /// + /// The registrar + /// + public class ForVisitor : TagVisitor, ITagVisitor + { + /// + public ITag Parse(TemplateParser parser, TokenCollection tc) + { + + if (tc.Count > 3 + && Utility.IsEqual(Const.KEY_FOR, tc.First.Text)) + { + + if (tc[1].TokenKind == TokenKind.LeftParentheses + && tc.Last.TokenKind == TokenKind.RightParentheses) + { + int pos = 0, + start = 2, + end; + + var ts = new List(3); + + var tag = new ForTag(); + for (int i = 2; i < tc.Count - 1; i++) + { + end = i; + if (tc[i].TokenKind == TokenKind.Punctuation && tc[i].Text == ";") + { + if (pos == 0) + { + var coll = tc[start, end]; + if (coll.Count > 0) + { + ts.Add(parser.Read(coll)); + } + else + { + ts.Add(null); + } + start = i + 1; + continue; + } + } + + if (tc[i].TokenKind == TokenKind.LeftParentheses) + { + pos++; + } + else if (tc[i].TokenKind == TokenKind.RightParentheses) + { + pos--; + } + if (i == tc.Count - 2) + { + var coll = tc[start, end + 1]; + if (coll.Count > 0) + { + ts.Add(parser.Read(coll)); + } + else + { + ts.Add(null); + } + } + } + + if (ts.Count != 3) + { + throw new ParseException($"syntax error near for on {tc.ToString()}", tc.First.BeginLine, tc.First.BeginColumn); + } + + tag.Initial = ts[0]; + tag.Condition = ts[1]; + tag.Do = ts[2]; + + while (parser.MoveNext()) + { + tag.Children.Add(parser.Current); + if (parser.Current is EndTag) + { + return tag; + } + } + + throw new ParseException($"for is not properly closed by a end tag: {tc.ToString()}", tc.First.BeginLine, tc.First.BeginColumn); + } + else + { + throw new ParseException($"syntax error near for: {tc.ToString()}", tc.First.BeginLine, tc.First.BeginColumn); + } + } + + return null; + + } + + /// + public MethodInfo Compile(ITag tag, CompileContext c) + { + + var stringBuilderType = typeof(StringBuilder); + var t = tag as ForTag; + var type = c.GuessType(t); + var templateContextType = typeof(TemplateContext); + var mb = c.CreateReutrnMethod(type); + var il = mb.GetILGenerator(); + //Label labelEnd = il.DefineLabel(); + Label labelNext = il.DefineLabel(); + Label labelStart = il.DefineLabel(); + il.DeclareLocal(stringBuilderType); + il.DeclareLocal(templateContextType); + il.DeclareLocal(typeof(bool)); + il.DeclareLocal(typeof(string)); + var index = 4; + il.Emit(OpCodes.Nop); + il.Emit(OpCodes.Newobj, stringBuilderType.GetConstructor(Type.EmptyTypes)); + il.Emit(OpCodes.Stloc_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, templateContextType.GetMethodInfo("CreateContext", new Type[] { templateContextType })); + il.Emit(OpCodes.Stloc_1); + + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldloc_1); + var m = c.CompileTag(t.Initial); + il.Emit(OpCodes.Call, m); + if (m.ReturnType.FullName != "System.Void") + { + il.DeclareLocal(m.ReturnType); + il.Emit(OpCodes.Stloc, index); + index++; + } + + il.Emit(OpCodes.Br, labelNext); + + il.MarkLabel(labelStart); + for (var i = 0; i < t.Children.Count; i++) + { + il.CallTag(c, t.Children[i], (nil, hasReturn, needCall) => + { + if (hasReturn) + { + nil.Emit(OpCodes.Ldloc_0); + } + if (needCall) + { + nil.Emit(OpCodes.Ldarg_0); + nil.Emit(OpCodes.Ldloc_1); + } + }, (nil, returnType) => + { + if (returnType == null) + { + return; + } + nil.StringAppend(c, returnType); + nil.Emit(OpCodes.Pop); + }); + } + + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldloc_1); + m = c.CompileTag(t.Do); + il.Emit(OpCodes.Call, m); + + if (m.ReturnType.FullName != "System.Void") + { + il.DeclareLocal(m.ReturnType); + il.Emit(OpCodes.Stloc, index); + index++; + } + + + il.MarkLabel(labelNext); + + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldloc_1); + + m = c.CompileTag(t.Condition); + il.Emit(OpCodes.Call, m); + + if (m.ReturnType.Name != "Boolean") + { + var cm = typeof(Utility).GetMethodInfo("ToBoolean", new Type[] { m.ReturnType }); + if (cm == null) + { + cm = typeof(Utility).GetMethodInfo("ToBoolean", new Type[] { typeof(object) }); + if (m.ReturnType.IsValueType) + { + il.Emit(OpCodes.Box, m.ReturnType); + } + else + { + il.Emit(OpCodes.Castclass, typeof(object)); + } + } + il.Emit(OpCodes.Call, cm); + } + + il.Emit(OpCodes.Stloc, 2); + il.Emit(OpCodes.Ldloc, 2); + il.Emit(OpCodes.Brtrue, labelStart); + + + il.Emit(OpCodes.Ldloc, 0); + il.Call(stringBuilderType, stringBuilderType.GetMethodInfo("ToString", Type.EmptyTypes)); + + + il.Emit(OpCodes.Stloc, 3); + il.Emit(OpCodes.Ldloc, 3); + il.Emit(OpCodes.Ret); + return mb.GetBaseDefinition(); + + } + /// + public Type GuessType(ITag tag, CompileContext c) + { + return typeof(string); + } + /// + public object Excute(ITag tag, TemplateContext context) + { + var t = tag as ForTag; + context.Execute(t.Initial); + //ǩΪգֱΪfalse,ѭڴ + bool run; + + if (t.Condition == null) + { + run = false; + } + else + { + run = Utility.ToBoolean(context.Execute(t.Condition)); + } + using (var writer = new StringWriter()) + { + while (run) + { + for (int i = 0; i < t.Children.Count; i++) + { + var obj = context.Execute(t.Children[i]); + if (obj != null) + { + writer.Write(obj.ToString()); + } + } + + if (t.Do != null) + { + //ִм㣬Ҫi++ + context.Execute(t.Do); + } + run = Utility.ToBoolean(context.Execute(t.Condition)); + } + return writer.ToString(); + } + } + } +} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/ForeachRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/ForeachVisitor.cs similarity index 86% rename from src/JinianNet.JNTemplate/Parsers/ForeachRegistrar.cs rename to src/JinianNet.JNTemplate/Parsers/ForeachVisitor.cs index 3046dd1..11a5df1 100644 --- a/src/JinianNet.JNTemplate/Parsers/ForeachRegistrar.cs +++ b/src/JinianNet.JNTemplate/Parsers/ForeachVisitor.cs @@ -21,73 +21,66 @@ namespace JinianNet.JNTemplate.Parsers /// /// The registrar /// - public class ForeachRegistrar : TagRegistrar, IRegistrar + public class ForeachVisitor : TagVisitor, ITagVisitor { /// - public override Func BuildParseMethod() + public ITag Parse(TemplateParser parser, TokenCollection tc) { - return (parser, tc) => - { - if (tc != null - && parser != null - && tc.Count > 5 - && (Utility.IsEqual(Const.KEY_FOREACH, tc.First.Text) || Utility.IsEqual(Const.KEY_FOR, tc.First.Text)) - && tc[1].TokenKind == TokenKind.LeftParentheses - && tc[2].TokenKind == TokenKind.TextData - && Utility.IsEqual(tc[3].Text, Const.KEY_IN) - && tc.Last.TokenKind == TokenKind.RightParentheses) - { - var tag = new ForeachTag(); - tag.Name = tc[2].Text; - tag.Source = parser.Read(tc[4, -1]); + if (tc.Count > 5 + && (Utility.IsEqual(Const.KEY_FOREACH, tc.First.Text) || Utility.IsEqual(Const.KEY_FOR, tc.First.Text)) + && tc[1].TokenKind == TokenKind.LeftParentheses + && tc[2].TokenKind == TokenKind.TextData + && Utility.IsEqual(tc[3].Text, Const.KEY_IN) + && tc.Last.TokenKind == TokenKind.RightParentheses) + { - while (parser.MoveNext()) + var tag = new ForeachTag(); + tag.Name = tc[2].Text; + tag.Source = parser.ReadSimple(tc[4, -1]); + if (tag.Source == null) + return null; + while (parser.MoveNext()) + { + tag.Children.Add(parser.Current); + if (parser.Current is EndTag) { - tag.Children.Add(parser.Current); - if (parser.Current is EndTag) - { - return tag; - } + return tag; } + } - throw new ParseException($"foreach is not properly closed by a end tag: {tc.ToString()}", tc.First.BeginLine, tc.First.BeginColumn); + throw new ParseException($"foreach is not properly closed by a end tag: {tc.ToString()}", tc.First.BeginLine, tc.First.BeginColumn); + + } + return null; - } - return null; - }; } /// - public override Func BuildCompileMethod() + public MethodInfo Compile(ITag tag, CompileContext c) { - return (tag, c) => - { - var t = tag as ForeachTag; - var sourceType = c.GuessType(t.Source); - var isAsync = false; + var t = tag as ForeachTag; + var sourceType = c.GuessType(t.Source); + var isAsync = false; #if !NF35 && !NF20 - if (sourceType.IsGenericType - && sourceType.GetGenericTypeDefinition() == typeof(Task<>)) - { - sourceType = sourceType.GetGenericArguments()[0]; - isAsync = true; - } + if (sourceType.IsGenericType + && sourceType.GetGenericTypeDefinition() == typeof(Task<>)) + { + sourceType = sourceType.GetGenericArguments()[0]; + isAsync = true; + } #endif - if (sourceType.IsArray) - { - return ArrayForeachCompile(c, t, sourceType, isAsync); - } - return EnumerableForeachCompile(c, t, sourceType, isAsync); - }; + if (sourceType.IsArray) + { + return ArrayForeachCompile(c, t, sourceType, isAsync); + } + return EnumerableForeachCompile(c, t, sourceType, isAsync); + } /// - public override Func BuildGuessMethod() + public Type GuessType(ITag tag, CompileContext c) { - return (tag, c) => - { - return typeof(string); - }; + return typeof(string); } /// @@ -395,9 +388,9 @@ namespace JinianNet.JNTemplate.Parsers } /// - public override Func BuildExcuteMethod() + public object Excute(ITag tag, TemplateContext context) { - return (tag, context) => + { var t = tag as ForeachTag; if (t.Source != null) diff --git a/src/JinianNet.JNTemplate/Parsers/FunctionRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/FunctionRegistrar.cs deleted file mode 100644 index 10670da..0000000 --- a/src/JinianNet.JNTemplate/Parsers/FunctionRegistrar.cs +++ /dev/null @@ -1,359 +0,0 @@ -/******************************************************************************** - Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. - Licensed under the MIT license. See licence.txt file in the project root for full license information. - ********************************************************************************/ -using System; -using System.Reflection; -using System.Reflection.Emit; -using System.Text; -using JinianNet.JNTemplate.CodeCompilation; -using JinianNet.JNTemplate.Dynamic; -using JinianNet.JNTemplate.Exceptions; -using JinianNet.JNTemplate.Nodes; - -namespace JinianNet.JNTemplate.Parsers -{ - /// - /// The registrar - /// - public class FunctionRegistrar : TagRegistrar, IRegistrar - { - /// - public override Func BuildCompileMethod() - { - return (tag, c) => - { - var getVariableScope = typeof(TemplateContext).GetPropertyGetMethod("TempData"); - var getVariableValue = typeof(IVariableScope).GetMethodInfo("get_Item", new[] { typeof(string) }); - var t = tag as FunctaionTag; - Type baseType; - MethodInfo method; - Type[] paramType = new Type[t.Children.Count]; - for (int i = 0; i < t.Children.Count; i++) - { - paramType[i] = c.GuessType(t.Children[i]); - if (paramType[i].FullName == "System.Void") - { - throw new CompileException(tag, "[FunctaionTag]:parameter error"); - } - } - - MethodInfo childMethd = null; - FieldInfo field = null; - if (t.Parent != null) - { - baseType = c.GuessType(t.Parent); - method = baseType.GetMethodInfo(t.Name, paramType, false); - if (method == null) - { - var property = baseType.GetPropertyInfo(t.Name); - Type funcType = null; - if (property == null) - { - field = baseType.GetFieldInfo(t.Name); - if (field != null) - { - funcType = field.FieldType; - } - } - else - { - funcType = property.PropertyType; -#if NF40 || NF20 || NF35 - childMethd = property.GetGetMethod(); -#else - childMethd = property.GetMethod; -#endif - } - if (funcType != null) - { - if (funcType.BaseType.Name != "MulticastDelegate") - { - throw new ArgumentException($"[FunctaionTag]:\"{t.Name}\" must be delegate"); - } - method = funcType.GetMethod("Invoke"); - } - if (method == null) - { - throw new CompileException(tag, $"[FunctaionTag]:method \"{t.Name}\" cannot be found!"); - } - } - } - else - { - baseType = c.Data.GetType(t.Name); - if (baseType.BaseType.Name != "MulticastDelegate") - { - throw new ArgumentException("[FunctaionTag]:functaion must be delegate"); // Delegate’s actions must be addressed ! - } - method = baseType.GetMethod("Invoke"); - } - var isVoid = method.ReturnType.FullName == "System.Void"; - var mb = c.CreateReutrnMethod(method.ReturnType); - var il = mb.GetILGenerator(); - il.DeclareLocal(baseType); - for (int i = 0; i < paramType.Length; i++) - { - il.DeclareLocal(paramType[i]); - } - if (t.Parent != null) - { - if (!method.IsStatic - && (childMethd == null || !childMethd.IsStatic) - && (field == null || !field.IsStatic)) - { - il.Emit(OpCodes.Nop); - var parentMethod = c.CompileTag(t.Parent); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Call, parentMethod); - il.Emit(OpCodes.Stloc_0); - } - - } - else - { - var localVar = il.DeclareLocal(typeof(object)); - il.Emit(OpCodes.Nop); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Callvirt, getVariableScope); - il.Emit(OpCodes.Ldstr, t.Name); - il.Emit(OpCodes.Callvirt, getVariableValue); - il.Emit(OpCodes.Stloc, localVar.LocalIndex); - il.Emit(OpCodes.Ldloc, localVar.LocalIndex); - il.Emit(OpCodes.Isinst, baseType); - il.Emit(OpCodes.Stloc_0); - } - - for (int i = 0; i < t.Children.Count; i++) - { - - il.CallTag(c, t.Children[i], (nil, hasReturn, needCall) => - { - if (!hasReturn) - { - throw new ArgumentNullException($"[FunctaionTag]:cannot compile functaion \"{t.Name}\" when the parameter is void."); - } - if (needCall) - { - nil.Emit(OpCodes.Ldarg_0); - nil.Emit(OpCodes.Ldarg_1); - } - }, (nil, returnType) => - { - if (returnType == null) - { - return; - } - il.Emit(OpCodes.Stloc, i + 1); - }); - } - if (!method.IsStatic - && (childMethd == null || !childMethd.IsStatic) - && (field == null || !field.IsStatic)) - { - il.LoadVariable(baseType, 0); - } - if (childMethd != null) - { - il.Emit(OpCodes.Call, childMethd); - } - if (field != null) - { - if (field.IsStatic) - { - il.Emit(OpCodes.Ldsfld, field); - } - else - { - il.Emit(OpCodes.Ldfld, field); - } - } - var ps = method.GetParameters(); - for (int i = 0; i < paramType.Length; i++) - { - il.Emit(OpCodes.Ldloc, i + 1); - if (i < ps.Length && paramType[i] != ps[i].ParameterType) - { - if (ps[i].ParameterType.Name == "Nullable`1") - { - var genericType = -#if NF40 || NF35 || NF20 - ps[i].ParameterType.GetGenericArguments()[0] -#else - ps[i].ParameterType.GenericTypeArguments[0] -#endif - ; - - if (genericType == paramType[i]) - { - var nullableType = typeof(Nullable<>).MakeGenericType(paramType[i]); - il.Emit(OpCodes.Newobj, nullableType.GetConstructor(new Type[] { paramType[i] })); - } - else - { - throw new CompileException(tag, $"[FunctaionTag]: \"{t.Name}\" parameter error. Expected \"{ps[i].ParameterType.Name}\" but got \"{ paramType[i].Name}\""); - } - } - else - { - il.ConvertTo(paramType[i], ps[i].ParameterType); - } - } - - } - il.Call(baseType, method); - //il.Emit(OpCodes.Callvirt, method); - if (!isVoid) - { - var localVar = il.DeclareLocal(method.ReturnType); - il.Emit(OpCodes.Stloc, localVar.LocalIndex); - il.Emit(OpCodes.Ldloc, localVar.LocalIndex); - } - il.Emit(OpCodes.Ret); - return mb.GetBaseDefinition(); - }; - } - /// - public override Func BuildGuessMethod() - { - return (tag, c) => - { - var t = tag as FunctaionTag; - if (t.Parent == null) - { - var bodyType = c.Data.GetType(t.Name); - if (bodyType.BaseType.FullName != "System.MulticastDelegate") - { - throw new CompileException(tag, $"[FunctaionTag]: \"{bodyType.BaseType}\" is not supported."); - } - var invokeMethod = bodyType.GetMethod("Invoke"); - return invokeMethod.ReturnType; - - } - var parentType = c.GuessType(t.Parent); - Type[] types = new Type[t.Children.Count]; - for (int i = 0; i < types.Length; i++) - { - types[i] = c.GuessType(t.Children[i]); - } - var method = parentType.GetMethodInfo(t.Name, types); - if (method != null) - { - return method.ReturnType; - } - throw new CompileException(tag, $"[FunctaionTag]: \"{t.Name}\" is not defined"); - }; - } - /// - public override Func BuildParseMethod() - { - return (parser, tc) => - { - if (tc != null - && parser != null - && tc.First.TokenKind == TokenKind.TextData - && tc.Count > 2 - && tc[1].TokenKind == TokenKind.LeftParentheses - && tc.Last.TokenKind == TokenKind.RightParentheses - ) - { - var tcs = tc.Split(TokenKind.LeftParentheses, TokenKind.RightParentheses); - if (tcs.Length != 2 - || tcs[1].Count < 2 - || tcs[1].First.TokenKind != TokenKind.LeftParentheses - || tcs[1].Last.TokenKind != TokenKind.RightParentheses) - { - return null; - } - - var tag = new FunctaionTag(); - tag.Name = tc.First.Text; - var ntc = tcs[1].Split(1, tcs[1].Count - 1, TokenKind.Comma); - for (int i = 0; i < ntc.Length; i++) - { - if (ntc[i].Count == 1 && ntc[i][0].TokenKind == TokenKind.Comma) - { - continue; - } - tag.AddChild(parser.Read(ntc[i])); - } - - return tag; - - } - - return null; - }; - } - /// - public override Func BuildExcuteMethod() - { - return (tag, context) => - { - var t = tag as FunctaionTag; - object[] args = new object[t.Children.Count]; - for (int i = 0; i < t.Children.Count; i++) - { - args[i] = context.Execute(t.Children[i]); - } - Type type = null; - object parentValue; - if (t.Parent == null) - { - parentValue = context.TempData[t.Name]; - } - else - { - parentValue = context.Execute(t.Parent); - if (parentValue != null) - { - type = parentValue.GetType(); - } - else - { - if (t.Parent is VariableTag variable) - { - type = context.TempData.GetType(variable.Name); - } - } - } - - if (parentValue == null && type == null) - { - return null; - } - if (t.Parent == null || (t.Parent != null && string.IsNullOrEmpty(t.Name))) - { - //if (parentValue is FuncHandler funcHandler) - //{ - // return funcHandler(args); - //} - if (parentValue is Delegate func) - { - //var ps = func.Method.GetParameters(); - //if (ps.Length==0) - return func.DynamicInvoke(args); - } - return null; - } - - var result = type.CallMethod(parentValue, t.Name, args); - - if (result != null) - { - return result; - } - - result = parentValue.CallPropertyOrField(t.Name, type); - - if (result != null && result is Delegate) - { - return (result as Delegate).DynamicInvoke(args); - } - - return null; - }; - } - } -} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/FunctionVisitor.cs b/src/JinianNet.JNTemplate/Parsers/FunctionVisitor.cs new file mode 100644 index 0000000..2551f74 --- /dev/null +++ b/src/JinianNet.JNTemplate/Parsers/FunctionVisitor.cs @@ -0,0 +1,345 @@ +/******************************************************************************** + Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. + Licensed under the MIT license. See licence.txt file in the project root for full license information. + ********************************************************************************/ +using System; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; +using JinianNet.JNTemplate.CodeCompilation; +using JinianNet.JNTemplate.Dynamic; +using JinianNet.JNTemplate.Exceptions; +using JinianNet.JNTemplate.Nodes; + +namespace JinianNet.JNTemplate.Parsers +{ + /// + /// The registrar + /// + public class FunctionVisitor : TagVisitor, ITagVisitor + { + /// + public MethodInfo Compile(ITag tag, CompileContext c) + { + var getVariableScope = typeof(TemplateContext).GetPropertyGetMethod("TempData"); + var getVariableValue = typeof(IVariableScope).GetMethodInfo("get_Item", new[] { typeof(string) }); + var t = tag as FunctionTag; + Type baseType; + MethodInfo method; + Type[] paramType = new Type[t.Children.Count]; + for (int i = 0; i < t.Children.Count; i++) + { + paramType[i] = c.GuessType(t.Children[i]); + if (paramType[i].FullName == "System.Void") + { + throw new CompileException(tag, "[FunctaionTag]:parameter error"); + } + } + + MethodInfo childMethd = null; + FieldInfo field = null; + if (t.Parent != null) + { + baseType = c.GuessType(t.Parent); + method = baseType.GetMethodInfo(t.Name, paramType, false); + if (method == null) + { + var property = baseType.GetPropertyInfo(t.Name); + Type funcType = null; + if (property == null) + { + field = baseType.GetFieldInfo(t.Name); + if (field != null) + { + funcType = field.FieldType; + } + } + else + { + funcType = property.PropertyType; +#if NF40 || NF20 || NF35 + childMethd = property.GetGetMethod(); +#else + childMethd = property.GetMethod; +#endif + } + if (funcType != null) + { + if (funcType.BaseType.Name != "MulticastDelegate") + { + throw new ArgumentException($"[FunctaionTag]:\"{t.Name}\" must be delegate"); + } + method = funcType.GetMethod("Invoke"); + } + if (method == null) + { + throw new CompileException(tag, $"[FunctaionTag]:method \"{t.Name}\" cannot be found!"); + } + } + } + else + { + baseType = c.Data.GetType(t.Name); + if (baseType.BaseType.Name != "MulticastDelegate") + { + throw new ArgumentException("[FunctaionTag]:functaion must be delegate"); // Delegate’s actions must be addressed ! + } + method = baseType.GetMethod("Invoke"); + } + var isVoid = method.ReturnType.FullName == "System.Void"; + var mb = c.CreateReutrnMethod(method.ReturnType); + var il = mb.GetILGenerator(); + il.DeclareLocal(baseType); + for (int i = 0; i < paramType.Length; i++) + { + il.DeclareLocal(paramType[i]); + } + if (t.Parent != null) + { + if (!method.IsStatic + && (childMethd == null || !childMethd.IsStatic) + && (field == null || !field.IsStatic)) + { + il.Emit(OpCodes.Nop); + var parentMethod = c.CompileTag(t.Parent); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, parentMethod); + il.Emit(OpCodes.Stloc_0); + } + + } + else + { + var localVar = il.DeclareLocal(typeof(object)); + il.Emit(OpCodes.Nop); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Callvirt, getVariableScope); + il.Emit(OpCodes.Ldstr, t.Name); + il.Emit(OpCodes.Callvirt, getVariableValue); + il.Emit(OpCodes.Stloc, localVar.LocalIndex); + il.Emit(OpCodes.Ldloc, localVar.LocalIndex); + il.Emit(OpCodes.Isinst, baseType); + il.Emit(OpCodes.Stloc_0); + } + + for (int i = 0; i < t.Children.Count; i++) + { + + il.CallTag(c, t.Children[i], (nil, hasReturn, needCall) => + { + if (!hasReturn) + { + throw new ArgumentNullException($"[FunctaionTag]:cannot compile functaion \"{t.Name}\" when the parameter is void."); + } + if (needCall) + { + nil.Emit(OpCodes.Ldarg_0); + nil.Emit(OpCodes.Ldarg_1); + } + }, (nil, returnType) => + { + if (returnType == null) + { + return; + } + il.Emit(OpCodes.Stloc, i + 1); + }); + } + if (!method.IsStatic + && (childMethd == null || !childMethd.IsStatic) + && (field == null || !field.IsStatic)) + { + il.LoadVariable(baseType, 0); + } + if (childMethd != null) + { + il.Emit(OpCodes.Call, childMethd); + } + if (field != null) + { + if (field.IsStatic) + { + il.Emit(OpCodes.Ldsfld, field); + } + else + { + il.Emit(OpCodes.Ldfld, field); + } + } + var ps = method.GetParameters(); + for (int i = 0; i < paramType.Length; i++) + { + il.Emit(OpCodes.Ldloc, i + 1); + if (i < ps.Length && paramType[i] != ps[i].ParameterType) + { + if (ps[i].ParameterType.Name == "Nullable`1") + { + var genericType = +#if NF40 || NF35 || NF20 + ps[i].ParameterType.GetGenericArguments()[0] +#else + ps[i].ParameterType.GenericTypeArguments[0] +#endif + ; + + if (genericType != paramType[i]) + { + il.ConvertTo(paramType[i], genericType); + paramType[i] = genericType; + } + il.Emit(OpCodes.Newobj, ps[i].ParameterType.GetConstructor(new Type[] { paramType[i] })); + } + else + { + il.ConvertTo(paramType[i], ps[i].ParameterType); + } + } + + } + il.Call(baseType, method); + //il.Emit(OpCodes.Callvirt, method); + if (!isVoid) + { + var localVar = il.DeclareLocal(method.ReturnType); + il.Emit(OpCodes.Stloc, localVar.LocalIndex); + il.Emit(OpCodes.Ldloc, localVar.LocalIndex); + } + il.Emit(OpCodes.Ret); + return mb.GetBaseDefinition(); + } + /// + public Type GuessType(ITag tag, CompileContext c) + { + var t = tag as FunctionTag; + if (t.Parent == null) + { + var bodyType = c.Data.GetType(t.Name); + if (bodyType.BaseType.FullName != "System.MulticastDelegate") + { + throw new CompileException(tag, $"[FunctaionTag]: \"{bodyType.BaseType}\" is not supported."); + } + var invokeMethod = bodyType.GetMethod("Invoke"); + return invokeMethod.ReturnType; + + } + var parentType = c.GuessType(t.Parent); + Type[] types = new Type[t.Children.Count]; + for (int i = 0; i < types.Length; i++) + { + types[i] = c.GuessType(t.Children[i]); + } + var method = parentType.GetMethodInfo(t.Name, types); + if (method != null) + { + return method.ReturnType; + } + throw new CompileException(tag, $"[FunctaionTag]: \"{t.Name}\" is not defined"); + + } + /// + public ITag Parse(TemplateParser parser, TokenCollection tc) + { + if (tc.First.TokenKind == TokenKind.TextData + && tc.Count > 2 + && tc[1].TokenKind == TokenKind.LeftParentheses + && tc.Last.TokenKind == TokenKind.RightParentheses + ) + { + var tcs = tc.Split(TokenKind.LeftParentheses, TokenKind.RightParentheses); + if (tcs.Length != 2 + || tcs[1].Count < 2 + || tcs[1].First.TokenKind != TokenKind.LeftParentheses + || tcs[1].Last.TokenKind != TokenKind.RightParentheses) + { + return null; + } + + var tag = new FunctionTag(); + tag.Name = tc.First.Text; + var ntc = tcs[1].Split(1, tcs[1].Count - 1, TokenKind.Comma); + for (int i = 0; i < ntc.Length; i++) + { + if (ntc[i].Count == 1 && ntc[i][0].TokenKind == TokenKind.Comma) + { + continue; + } + tag.AddChild(parser.ReadSimple(ntc[i])); + } + + return tag; + + } + + return null; + + } + /// + public object Excute(ITag tag, TemplateContext context) + { + var t = tag as FunctionTag; + object[] args = new object[t.Children.Count]; + for (int i = 0; i < t.Children.Count; i++) + { + args[i] = context.Execute(t.Children[i]); + } + Type type = null; + object parentValue; + if (t.Parent == null) + { + parentValue = context.TempData[t.Name]; + } + else + { + parentValue = context.Execute(t.Parent); + if (parentValue != null) + { + type = parentValue.GetType(); + } + else + { + if (t.Parent is VariableTag variable) + { + type = context.TempData.GetType(variable.Name); + } + } + } + + if (parentValue == null && type == null) + { + return null; + } + if (t.Parent == null || (t.Parent != null && string.IsNullOrEmpty(t.Name))) + { + //if (parentValue is FuncHandler funcHandler) + //{ + // return funcHandler(args); + //} + if (parentValue is Delegate func) + { + var ps = func.Method.GetParameters(); + args = args.ChangeArguments(ps); + return func.DynamicInvoke(args); + } + return null; + } + + var result = type.CallMethod(parentValue, t.Name, args); + + if (result != null) + { + return result; + } + + result = parentValue.CallPropertyOrField(t.Name, type); + + if (result != null && result is Delegate) + { + return (result as Delegate).DynamicInvoke(args); + } + + return null; + + } + } +} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/ITagVisitor.cs b/src/JinianNet.JNTemplate/Parsers/ITagVisitor.cs new file mode 100644 index 0000000..9a5e624 --- /dev/null +++ b/src/JinianNet.JNTemplate/Parsers/ITagVisitor.cs @@ -0,0 +1,43 @@ +/******************************************************************************** + Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. + Licensed under the MIT license. See licence.txt file in the project root for full license information. + ********************************************************************************/ +using JinianNet.JNTemplate.CodeCompilation; +using JinianNet.JNTemplate.Nodes; +using System; +using System.Reflection; + +namespace JinianNet.JNTemplate.Parsers +{ + /// + /// + /// + public interface ITagVisitor : ITagParser + { + /// + /// + /// + string Name { get; } + /// + /// + /// + /// + /// + /// + MethodInfo Compile(ITag tag, CompileContext context); + /// + /// + /// + /// + /// + /// + Type GuessType(ITag tag, CompileContext context); + /// + /// + /// + /// + /// + /// + object Excute(ITag tag, TemplateContext context); + } +} diff --git a/src/JinianNet.JNTemplate/Parsers/IfRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/IfRegistrar.cs deleted file mode 100644 index a116fd3..0000000 --- a/src/JinianNet.JNTemplate/Parsers/IfRegistrar.cs +++ /dev/null @@ -1,310 +0,0 @@ -/******************************************************************************** - Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. - Licensed under the MIT license. See licence.txt file in the project root for full license information. - ********************************************************************************/ -using JinianNet.JNTemplate.CodeCompilation; -using JinianNet.JNTemplate.Dynamic; -using JinianNet.JNTemplate.Nodes; -using JinianNet.JNTemplate.Exceptions; -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Reflection.Emit; - -namespace JinianNet.JNTemplate.Parsers -{ - /// - /// The registrar - /// - public class IfRegistrar : TagRegistrar, IRegistrar - { - /// - public override Func BuildParseMethod() - { - return (parser, tc) => - { - if (tc != null - && parser != null - && tc.Count > 3 - && Utility.IsEqual(tc.First.Text, Const.KEY_IF)) - { - - if (tc[1].TokenKind == TokenKind.LeftParentheses - && tc.Last.TokenKind == TokenKind.RightParentheses) - { - var tag = new IfTag(); - - var t = new ElseifTag(); - TokenCollection coll = tc[2, -1]; - t.Condition = parser.Read(coll); - t.FirstToken = coll.First; - //t.LastToken = coll.Last; - tag.AddChild(t); - - while (parser.MoveNext()) - { - if (parser.Current is EndTag) - { - tag.AddChild(parser.Current); - return tag; - } - else if (parser.Current is ElseifTag - || parser.Current is ElseTag) - { - tag.AddChild(parser.Current); - } - else - { - tag.Children[tag.Children.Count - 1].AddChild(parser.Current); - } - } - - throw new ParseException($"if is not properly closed by a end tag: {tc.ToString()}", tc.First.BeginLine, tc.First.BeginColumn); - } - else - { - throw new ParseException($"syntax error near if: {tc.ToString()}", tc.First.BeginLine, tc.First.BeginColumn); - } - - } - - return null; - }; - } - - /// - public override Func BuildCompileMethod() - { - return (tag, c) => - { - var t = tag as IfTag; - var type = c.GuessType(t); - var mb = c.CreateReutrnMethod(type); - var il = mb.GetILGenerator(); - var labelEnd = il.DefineLabel(); - var labelSuccess = il.DefineLabel(); - var hasReturn = type.FullName != "System.Void"; - if (type.FullName != "System.Void") - { - il.DeclareLocal(type); - hasReturn = true; - } - var lables = new Label[t.Children.Count - 1]; - for (var i = 0; i < lables.Length; i++) - { - lables[i] = il.DefineLabel(); - } - for (var i = 0; i < t.Children.Count; i++) - { - if (t.Children[i] is EndTag) - { - continue; - } - var ifTag = t.Children[i] as ElseifTag; - - if (!(t.Children[i] is ElseTag)) - { - var m = c.CompileTag(ifTag.Condition); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Call, m); - if (m.ReturnType.Name != "Boolean") - { - var localVar = il.DeclareLocal(m.ReturnType); - il.Emit(OpCodes.Stloc, localVar.LocalIndex); - il.LoadVariable(m.ReturnType, localVar.LocalIndex); - var cm = typeof(Utility).GetMethodInfo("ToBoolean", new Type[] { m.ReturnType }); - if (cm == null) - { - cm = typeof(Utility).GetMethodInfo("ToBoolean", new Type[] { typeof(object) }); - if (m.ReturnType.IsValueType) - { - il.Emit(OpCodes.Box, m.ReturnType); - } - else - { - il.Emit(OpCodes.Castclass, typeof(object)); - } - } - il.Emit(OpCodes.Call, cm); - } - il.Emit(OpCodes.Brfalse, lables[i]); - } - - var execute = c.CompileTag(ifTag); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Call, execute); - if (execute.ReturnType.FullName != type.FullName) - { - if (type.FullName == "System.String") - { - var localVar = il.DeclareLocal(execute.ReturnType); - il.Emit(OpCodes.Stloc, localVar.LocalIndex); - il.LoadVariable(execute.ReturnType, localVar.LocalIndex); - il.Call(execute.ReturnType, typeof(object).GetMethodInfo("ToString", Type.EmptyTypes)); - } - else - { - if (execute.ReturnType.IsValueType) - { - if (!type.IsValueType) - { - il.Emit(OpCodes.Box, type); - } - else - { - switch (type.FullName) - { - case "System.Decimal": - switch (execute.ReturnType.FullName) - { - case "System.Int16": - case "System.UInt16": - case "System.Byte": - il.Emit(OpCodes.Conv_I4); - break; - } - il.Emit(OpCodes.Call, typeof(decimal).GetConstructor(new Type[] { execute.ReturnType })); - break; - case "System.Double": - il.Emit(OpCodes.Conv_R8); - break; - case "System.Single": - il.Emit(OpCodes.Conv_R4); - break; - case "System.Int64": - il.Emit(OpCodes.Conv_I8); - break; - case "System.UInt64": - il.Emit(OpCodes.Conv_U8); - break; - case "System.Int32": - il.Emit(OpCodes.Conv_I4); - break; - case "System.UInt32": - il.Emit(OpCodes.Conv_U4); - break; - case "System.Int16": - il.Emit(OpCodes.Conv_I2); - break; - case "System.UInt16": - il.Emit(OpCodes.Conv_U2); - break; - case "System.Byte": - il.Emit(OpCodes.Conv_U1); - break; - default: - il.Emit(OpCodes.Isinst, type); - break; - } - } - } - else - { - if (type.IsValueType) - { - il.Emit(OpCodes.Unbox, type); - } - else - { - il.Emit(OpCodes.Castclass, type); - } - } - } - } - if (hasReturn) - { - il.Emit(OpCodes.Stloc_0); - il.Emit(OpCodes.Br, labelSuccess);// lables[lables.Length - 1] - } - else - { - il.Emit(OpCodes.Br, labelEnd); - } - - il.MarkLabel(lables[i]); - } - - if (hasReturn) - { - if (type.IsValueType) - { - var defaultMethod = typeof(Utility).GetGenericMethod(new Type[] { type }, "GenerateDefaultValue", Type.EmptyTypes); - il.Emit(OpCodes.Call, defaultMethod); - il.Emit(OpCodes.Stloc_0); - } - else - { - il.Emit(OpCodes.Ldnull); - il.Emit(OpCodes.Stloc_0); - } - } - - il.MarkLabel(labelSuccess); - il.Emit(OpCodes.Ldloc_0); - il.MarkLabel(labelEnd); - il.Emit(OpCodes.Ret); - return mb.GetBaseDefinition(); - }; - } - /// - public override Func BuildGuessMethod() - { - return (tag, c) => - { - //var t = tag as IfTag; - //Type type = null; - //for (var i = 0; i < t.Children.Count; i++) - //{ - // if (t.Children[i] is EndTag) - // { - // continue; - // } - // var cType = c.GuessType(t.Children[i]); - // if (type == null) - // { - // type = cType; - // } - // else - // { - // if (cType == null || type.FullName != cType.FullName) - // { - // return typeof(string); - // } - // } - //} - //return type; - return typeof(string); - }; - } - /// - public override Func BuildExcuteMethod() - { - return (tag, context) => - { - var t = tag as IfTag; - for (int i = 0; i < t.Children.Count - 1; i++) - { - var c = (ElseifTag)t.Children[i]; - if (c == null) - { - continue; - } - if (t.Children[i] is ElseTag) - { - return context.Execute(t.Children[i]); - } - - var condition = context.Execute(c.Condition); - if (Utility.ToBoolean(condition)) - { - return context.Execute(t.Children[i]); - } - } - return null; - }; - } - - } -} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/ArithmeticRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/IfVisitor.cs similarity index 32% rename from src/JinianNet.JNTemplate/Parsers/ArithmeticRegistrar.cs rename to src/JinianNet.JNTemplate/Parsers/IfVisitor.cs index 3838dd9..b2e21fb 100644 --- a/src/JinianNet.JNTemplate/Parsers/ArithmeticRegistrar.cs +++ b/src/JinianNet.JNTemplate/Parsers/IfVisitor.cs @@ -1,157 +1,158 @@ -/******************************************************************************** +/******************************************************************************** Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. Licensed under the MIT license. See licence.txt file in the project root for full license information. ********************************************************************************/ +using JinianNet.JNTemplate.CodeCompilation; +using JinianNet.JNTemplate.Dynamic; +using JinianNet.JNTemplate.Nodes; +using JinianNet.JNTemplate.Exceptions; using System; using System.Collections.Generic; -using System.Linq; using System.Reflection; using System.Reflection.Emit; -using System.Text; -using JinianNet.JNTemplate.CodeCompilation; -using JinianNet.JNTemplate.Dynamic; -using JinianNet.JNTemplate.Exceptions; -using JinianNet.JNTemplate.Nodes; namespace JinianNet.JNTemplate.Parsers { /// - /// The registrar + /// The registrar /// - public class ArithmeticRegistrar : TagRegistrar, IRegistrar + public class IfVisitor : TagVisitor, ITagVisitor { /// - public override Func BuildParseMethod() + public ITag Parse(TemplateParser parser, TokenCollection tc) { - return (parser, tc) => + if (tc.Count > 3 + && Utility.IsEqual(tc.First.Text, Const.KEY_IF)) { - if (tc != null - && parser != null - && tc.Count > 2) + + if (tc[1].TokenKind == TokenKind.LeftParentheses + && tc.Last.TokenKind == TokenKind.RightParentheses) { - var tcs = tc.Split(TokenKind.Arithmetic); - if (tcs.Length <= 1) - { + var tag = new IfTag(); + + var t = new ElseifTag(); + TokenCollection coll = tc[2, -1]; + t.Condition = parser.ReadSimple(coll); + if (t.Condition == null) return null; - } + t.FirstToken = coll.First; + //t.LastToken = coll.Last; + tag.AddChild(t); - var tags = new List(); - for (int i = 0; i < tcs.Length; i++) + while (parser.MoveNext()) { - if (tcs[i].Count == 1 && tcs[i][0].TokenKind == TokenKind.Arithmetic) + if (parser.Current is EndTag) { - tags.Add(new OperatorTag(tcs[i][0])); + tag.AddChild(parser.Current); + return tag; } - else + else if (parser.Current is ElseifTag + || parser.Current is ElseTag) { - tags.Add(parser.Read(tcs[i])); + tag.AddChild(parser.Current); } - } - - if (tags.Count == 1) - { - return tags[0]; - } - - if (tags.Count > 1) - { - ITag t = new ArithmeticTag(); - for (int i = 0; i < tags.Count; i++) + else { - t.AddChild(tags[i]); + tag.Children[tag.Children.Count - 1].AddChild(parser.Current); } - tags.Clear(); - return t; } + throw new ParseException($"if is not properly closed by a end tag: {tc.ToString()}", tc.First.BeginLine, tc.First.BeginColumn); + } + else + { + throw new ParseException($"syntax error near if: {tc.ToString()}", tc.First.BeginLine, tc.First.BeginColumn); } - return null; - }; + } + + return null; } /// - public override Func BuildCompileMethod() + public MethodInfo Compile(ITag tag, CompileContext c) { - return (tag, c) => + var t = tag as IfTag; + var type = c.GuessType(t); + var mb = c.CreateReutrnMethod(type); + var il = mb.GetILGenerator(); + var labelEnd = il.DefineLabel(); + var labelSuccess = il.DefineLabel(); + var hasReturn = type.FullName != "System.Void"; + if (type.FullName != "System.Void") { - var stringBuilderType = typeof(StringBuilder); - var t = tag as ArithmeticTag; - var type = c.GuessType(t); - var mb = c.CreateReutrnMethod(type); - var il = mb.GetILGenerator(); - Label labelEnd = il.DefineLabel(); il.DeclareLocal(type); - var array = new object[t.Children.Count]; - var types = new List(); - var opts = new List(); - var message = new List(); - for (int i = 0; i < t.Children.Count; i++) + hasReturn = true; + } + var lables = new Label[t.Children.Count - 1]; + for (var i = 0; i < lables.Length; i++) + { + lables[i] = il.DefineLabel(); + } + for (var i = 0; i < t.Children.Count; i++) + { + if (t.Children[i] is EndTag) { - var opt = t.Children[i] as OperatorTag; - if (opt != null) - { - if (!opts.Contains(opt.Value)) - { - opts.Add(opt.Value); - } - array[i] = opt.Value; - message.Add(OperatorConvert.ToString(opt.Value)); - } - else - { - array[i] = t.Children[i]; - types.Add(c.GuessType(t.Children[i])); - message.Add(types[types.Count - 1].Name); - } + continue; } - if (types.Contains(typeof(string)) && opts.Count == 1 && opts[0] == Operator.Add) + var ifTag = t.Children[i] as ElseifTag; + + if (!(t.Children[i] is ElseTag)) { - il.DeclareLocal(stringBuilderType); - il.Emit(OpCodes.Newobj, stringBuilderType.GetConstructor(Type.EmptyTypes)); - il.Emit(OpCodes.Stloc_1); - for (int i = 0; i < t.Children.Count; i++) + var m = c.CompileTag(ifTag.Condition); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, m); + if (m.ReturnType.Name != "Boolean") { - var opt = t.Children[i] as OperatorTag; - if (opt != null) + var localVar = il.DeclareLocal(m.ReturnType); + il.Emit(OpCodes.Stloc, localVar.LocalIndex); + il.LoadVariable(m.ReturnType, localVar.LocalIndex); + var cm = typeof(Utility).GetMethodInfo("ToBoolean", new Type[] { m.ReturnType }); + if (cm == null) { - continue; + cm = typeof(Utility).GetMethodInfo("ToBoolean", new Type[] { typeof(object) }); + if (m.ReturnType.IsValueType) + { + il.Emit(OpCodes.Box, m.ReturnType); + } + else + { + il.Emit(OpCodes.Castclass, typeof(object)); + } } - il.Emit(OpCodes.Ldloc_1); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - var m = c.CompileTag(t.Children[i]); - il.Emit(OpCodes.Call, m); - il.StringAppend(c, m.ReturnType); - il.Emit(OpCodes.Pop); + il.Emit(OpCodes.Call, cm); } - - il.Emit(OpCodes.Ldloc_1); - il.Call(stringBuilderType, typeof(object).GetMethodInfo("ToString", Type.EmptyTypes)); - il.Emit(OpCodes.Stloc_0); + il.Emit(OpCodes.Brfalse, lables[i]); } - else + + var execute = c.CompileTag(ifTag); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, execute); + if (execute.ReturnType.FullName != type.FullName) { - var bestType = TypeGuesser.FindBestType(types.ToArray()); - var stack = ExpressionEvaluator.ProcessExpression(array); - var arr = stack.ToArray(); - for (var i = arr.Length - 1; i >= 0; i--) + if (type.FullName == "System.String") + { + var localVar = il.DeclareLocal(execute.ReturnType); + il.Emit(OpCodes.Stloc, localVar.LocalIndex); + il.LoadVariable(execute.ReturnType, localVar.LocalIndex); + il.Call(execute.ReturnType, typeof(object).GetMethodInfo("ToString", Type.EmptyTypes)); + } + else { - var obj = arr[i]; - var childTag = obj as ITag; - if (childTag != null) + if (execute.ReturnType.IsValueType) { - var m = c.CompileTag(childTag); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Call, m); - if (m.ReturnType.FullName != bestType.FullName) + if (!type.IsValueType) + { + il.Emit(OpCodes.Box, type); + } + else { - switch (bestType.FullName) + switch (type.FullName) { case "System.Decimal": - Type cType = m.ReturnType; - switch (cType.FullName) + switch (execute.ReturnType.FullName) { case "System.Int16": case "System.UInt16": @@ -159,7 +160,7 @@ namespace JinianNet.JNTemplate.Parsers il.Emit(OpCodes.Conv_I4); break; } - il.Emit(OpCodes.Call, typeof(decimal).GetConstructor(new Type[] { cType })); + il.Emit(OpCodes.Call, typeof(decimal).GetConstructor(new Type[] { execute.ReturnType })); break; case "System.Double": il.Emit(OpCodes.Conv_R8); @@ -189,126 +190,109 @@ namespace JinianNet.JNTemplate.Parsers il.Emit(OpCodes.Conv_U1); break; default: - throw new CompileException(tag, $"[ExpressionTag] : The type \"{bestType.FullName}\" is not supported ."); + il.Emit(OpCodes.Isinst, type); + break; } } } else { - switch ((Operator)obj) + if (type.IsValueType) { - case Operator.Add: - il.Emit(OpCodes.Add); - break; - case Operator.Subtract: - il.Emit(OpCodes.Sub); - break; - case Operator.Multiply: - il.Emit(OpCodes.Mul); - break; - case Operator.Divided: - il.Emit(OpCodes.Div); - break; - case Operator.Remainder: - il.Emit(OpCodes.Rem); - break; - case Operator.GreaterThan: - il.Emit(OpCodes.Cgt); - break; - case Operator.GreaterThanOrEqual: - il.Emit(OpCodes.Clt_Un); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - break; - case Operator.LessThan: - il.Emit(OpCodes.Clt); - break; - case Operator.LessThanOrEqual: - il.Emit(OpCodes.Cgt_Un); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - break; - case Operator.Equal: - il.Emit(OpCodes.Ceq); - break; - case Operator.NotEqual: - il.Emit(OpCodes.Ceq); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - break; - default: - throw new CompileException(tag, $"[ExpressionTag] : The expression \"{string.Concat(message)}\" is not supported ."); - //throw new CompileException($"The operator \"{obj}\" is not supported on type \"{bestType.FullName}\" ."); - //case Operator.Or: - // il.Emit(OpCodes.Blt); - // break; + il.Emit(OpCodes.Unbox, type); + } + else + { + il.Emit(OpCodes.Castclass, type); } } } - il.Emit(OpCodes.Stloc_0); } - il.MarkLabel(labelEnd); - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Ret); - return mb.GetBaseDefinition(); - }; - } - /// - public override Func BuildGuessMethod() - { - return (tag, c) => - { - var t = tag as ArithmeticTag; - var types = new List(); - var opts = new List(); - for (var i = 0; i < t.Children.Count; i++) + if (hasReturn) { - var opt = t.Children[i] as OperatorTag; - if (opt == null) - { - types.Add(c.GuessType(t.Children[i])); - } + il.Emit(OpCodes.Stloc_0); + il.Emit(OpCodes.Br, labelSuccess);// lables[lables.Length - 1] } - if (types.Count == 1) + else { - return types[0]; + il.Emit(OpCodes.Br, labelEnd); } - if (types.Contains(typeof(string))) + + il.MarkLabel(lables[i]); + } + + if (hasReturn) + { + if (type.IsValueType) { - return typeof(string); + var defaultMethod = typeof(Utility).GetGenericMethod(new Type[] { type }, "GenerateDefaultValue", Type.EmptyTypes); + il.Emit(OpCodes.Call, defaultMethod); + il.Emit(OpCodes.Stloc_0); } - if (types.Count > 0) + else { - return TypeGuesser.FindBestType(types.ToArray()); + il.Emit(OpCodes.Ldnull); + il.Emit(OpCodes.Stloc_0); } + } - return typeof(int); - }; + il.MarkLabel(labelSuccess); + il.Emit(OpCodes.Ldloc_0); + il.MarkLabel(labelEnd); + il.Emit(OpCodes.Ret); + return mb.GetBaseDefinition(); + } + /// + public Type GuessType(ITag tag, CompileContext c) + { + //var t = tag as IfTag; + //Type type = null; + //for (var i = 0; i < t.Children.Count; i++) + //{ + // if (t.Children[i] is EndTag) + // { + // continue; + // } + // var cType = c.GuessType(t.Children[i]); + // if (type == null) + // { + // type = cType; + // } + // else + // { + // if (cType == null || type.FullName != cType.FullName) + // { + // return typeof(string); + // } + // } + //} + //return type; + return typeof(string); } /// - public override Func BuildExcuteMethod() + public object Excute(ITag tag, TemplateContext context) { - return (tag, context) => + var t = tag as IfTag; + for (int i = 0; i < t.Children.Count - 1; i++) { - var t = tag as ArithmeticTag; - var parameters = new List(); + var c = (ElseifTag)t.Children[i]; + if (c == null) + { + continue; + } + if (t.Children[i] is ElseTag) + { + return context.Execute(t.Children[i]); + } - for (int i = 0; i < t.Children.Count; i++) + var condition = context.Execute(c.Condition); + if (Utility.ToBoolean(condition)) { - var opt = t.Children[i] as OperatorTag; - if (opt != null) - { - parameters.Add(opt.Value); - } - else - { - parameters.Add(context.Execute(t.Children[i])); - } + return context.Execute(t.Children[i]); } - var stack = ExpressionEvaluator.ProcessExpression(parameters.ToArray()); - return ExpressionEvaluator.Calculate(stack); - }; + } + return null; } } -} +} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/IncludeRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/IncludeRegistrar.cs deleted file mode 100644 index 9a8edbd..0000000 --- a/src/JinianNet.JNTemplate/Parsers/IncludeRegistrar.cs +++ /dev/null @@ -1,153 +0,0 @@ -/******************************************************************************** - Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. - Licensed under the MIT license. See licence.txt file in the project root for full license information. - ********************************************************************************/ -using JinianNet.JNTemplate.CodeCompilation; -using JinianNet.JNTemplate.Dynamic; -using JinianNet.JNTemplate.Nodes; -using System; -using System.Reflection; -using System.Reflection.Emit; - -namespace JinianNet.JNTemplate.Parsers -{ - /// - /// The registrar - /// - public class IncludeRegistrar : TagRegistrar, IRegistrar - { - /// - public override Func BuildParseMethod() - { - return (parser, tc) => - { - if (Utility.IsEqual(tc.First.Text, Const.KEY_INCLUDE)) - { - if (tc != null - && parser != null - && tc.Count > 2 - && (tc[1].TokenKind == TokenKind.LeftParentheses) - && tc.Last.TokenKind == TokenKind.RightParentheses) - { - IncludeTag tag = new IncludeTag(); - tag.Path = parser.Read(new TokenCollection(tc, 2, tc.Count - 2)); - return tag; - } - } - - return null; - }; - } - /// - public override Func BuildCompileMethod() - { - return (tag, c) => - { - var t = tag as IncludeTag; - var type = c.GuessType(t); - var mb = c.CreateReutrnMethod(type); - var il = mb.GetILGenerator(); - var strTag = t.Path as StringTag; - if (strTag != null) - { - var res = c.Load(strTag.Value); - if (res != null) - { - il.Emit(OpCodes.Ldstr, res.Content); - } - else - { - il.Emit(OpCodes.Ldstr, $"\"{strTag.Value}\" not found!"); - } - } - else - { - var strType = typeof(string); - var resType = typeof(Resources.ResourceInfo); - var ctxType = typeof(TemplateContext); - var labelEnd = il.DefineLabel(); - var labelSuccess = il.DefineLabel(); - il.DeclareLocal(strType); - il.DeclareLocal(typeof(bool)); - il.DeclareLocal(resType); - il.DeclareLocal(typeof(bool)); - il.DeclareLocal(type); - - var m = c.CompileTag(t.Path); - - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Call, m); - if (m.ReturnType.FullName != "System.String") - { - var localVar = il.DeclareLocal(m.ReturnType); - il.Emit(OpCodes.Stloc, localVar.LocalIndex); - il.LoadVariable(m.ReturnType, localVar.LocalIndex); - il.Call(m.ReturnType, typeof(object).GetMethodInfo("ToString", Type.EmptyTypes)); - } - il.Emit(OpCodes.Stloc_0); - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Ldnull); - il.Emit(OpCodes.Cgt_Un); - il.Emit(OpCodes.Stloc_1); - il.Emit(OpCodes.Ldloc_1); - il.Emit(OpCodes.Brfalse, labelEnd); - - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Call, typeof(TemplateContextExtensions).GetMethodInfo("Load", new Type[] { typeof(Context), strType })); - il.Emit(OpCodes.Stloc_2); - il.Emit(OpCodes.Ldloc_2); - il.Emit(OpCodes.Ldnull); - il.Emit(OpCodes.Cgt_Un); - il.Emit(OpCodes.Stloc, 3); - il.Emit(OpCodes.Ldloc, 3); - il.Emit(OpCodes.Brfalse, labelEnd); - - il.Emit(OpCodes.Ldloc_2); - il.Emit(OpCodes.Callvirt, resType.GetPropertyGetMethod("Content")); - il.Emit(OpCodes.Stloc, 4); - il.Emit(OpCodes.Br, labelSuccess); - - il.MarkLabel(labelEnd); - il.Emit(OpCodes.Ldstr, $"[IncludeTag] : \"{t.Path.ToSource()}\" cannot be found."); - il.Emit(OpCodes.Stloc, 4); - - - il.MarkLabel(labelSuccess); - il.Emit(OpCodes.Ldloc, 4); - } - il.Emit(OpCodes.Ret); - return mb.GetBaseDefinition(); - }; - } - /// - public override Func BuildGuessMethod() - { - return (tag, c) => - { - return typeof(string); - }; - } - /// - public override Func BuildExcuteMethod() - { - return (tag, context) => - { - var t = tag as IncludeTag; - object path = context.Execute(t.Path); - if (path == null) - { - return null; - } - var res = context.Load(path.ToString()); - if (res != null) - { - return res.Content; - } - return null; - }; - } - - } -} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/IncludeVisitor.cs b/src/JinianNet.JNTemplate/Parsers/IncludeVisitor.cs new file mode 100644 index 0000000..b64ba93 --- /dev/null +++ b/src/JinianNet.JNTemplate/Parsers/IncludeVisitor.cs @@ -0,0 +1,141 @@ +/******************************************************************************** + Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. + Licensed under the MIT license. See licence.txt file in the project root for full license information. + ********************************************************************************/ +using JinianNet.JNTemplate.CodeCompilation; +using JinianNet.JNTemplate.Dynamic; +using JinianNet.JNTemplate.Nodes; +using System; +using System.Reflection; +using System.Reflection.Emit; + +namespace JinianNet.JNTemplate.Parsers +{ + /// + /// The registrar + /// + public class IncludeVisitor : TagVisitor, ITagVisitor + { + /// + public ITag Parse(TemplateParser parser, TokenCollection tc) + { + if (Utility.IsEqual(tc.First.Text, Const.KEY_INCLUDE)) + { + if (tc.Count > 2 + && (tc[1].TokenKind == TokenKind.LeftParentheses) + && tc.Last.TokenKind == TokenKind.RightParentheses) + { + IncludeTag tag = new IncludeTag(); + tag.Path = parser.ReadSimple(new TokenCollection(tc, 2, tc.Count - 2)); + if (tag.Path == null) + return null; + return tag; + } + } + + return null; + } + /// + public MethodInfo Compile(ITag tag, CompileContext c) + { + var t = tag as IncludeTag; + var type = c.GuessType(t); + var mb = c.CreateReutrnMethod(type); + var il = mb.GetILGenerator(); + var strTag = t.Path as StringTag; + if (strTag != null) + { + var res = c.Load(strTag.Value); + if (res != null) + { + il.Emit(OpCodes.Ldstr, res.Content); + } + else + { + il.Emit(OpCodes.Ldstr, $"\"{strTag.Value}\" not found!"); + } + } + else + { + var strType = typeof(string); + var resType = typeof(Resources.ResourceInfo); + var ctxType = typeof(TemplateContext); + var labelEnd = il.DefineLabel(); + var labelSuccess = il.DefineLabel(); + il.DeclareLocal(strType); + il.DeclareLocal(typeof(bool)); + il.DeclareLocal(resType); + il.DeclareLocal(typeof(bool)); + il.DeclareLocal(type); + + var m = c.CompileTag(t.Path); + + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, m); + if (m.ReturnType.FullName != "System.String") + { + var localVar = il.DeclareLocal(m.ReturnType); + il.Emit(OpCodes.Stloc, localVar.LocalIndex); + il.LoadVariable(m.ReturnType, localVar.LocalIndex); + il.Call(m.ReturnType, typeof(object).GetMethodInfo("ToString", Type.EmptyTypes)); + } + il.Emit(OpCodes.Stloc_0); + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Ldnull); + il.Emit(OpCodes.Cgt_Un); + il.Emit(OpCodes.Stloc_1); + il.Emit(OpCodes.Ldloc_1); + il.Emit(OpCodes.Brfalse, labelEnd); + + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Call, typeof(TemplateContextExtensions).GetMethodInfo("Load", new Type[] { typeof(Context), strType })); + il.Emit(OpCodes.Stloc_2); + il.Emit(OpCodes.Ldloc_2); + il.Emit(OpCodes.Ldnull); + il.Emit(OpCodes.Cgt_Un); + il.Emit(OpCodes.Stloc, 3); + il.Emit(OpCodes.Ldloc, 3); + il.Emit(OpCodes.Brfalse, labelEnd); + + il.Emit(OpCodes.Ldloc_2); + il.Emit(OpCodes.Callvirt, resType.GetPropertyGetMethod("Content")); + il.Emit(OpCodes.Stloc, 4); + il.Emit(OpCodes.Br, labelSuccess); + + il.MarkLabel(labelEnd); + il.Emit(OpCodes.Ldstr, $"[IncludeTag] : \"{t.Path.ToSource()}\" cannot be found."); + il.Emit(OpCodes.Stloc, 4); + + + il.MarkLabel(labelSuccess); + il.Emit(OpCodes.Ldloc, 4); + } + il.Emit(OpCodes.Ret); + return mb.GetBaseDefinition(); + } + /// + public Type GuessType(ITag tag, CompileContext c) + { + return typeof(string); + } + /// + public object Excute(ITag tag, TemplateContext context) + { + var t = tag as IncludeTag; + object path = context.Execute(t.Path); + if (path == null) + { + return null; + } + var res = context.Load(path.ToString()); + if (res != null) + { + return res.Content; + } + return null; + } + + } +} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/IndexValueRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/IndexValueVisitor.cs similarity index 38% rename from src/JinianNet.JNTemplate/Parsers/IndexValueRegistrar.cs rename to src/JinianNet.JNTemplate/Parsers/IndexValueVisitor.cs index d19db70..5f9a1bb 100644 --- a/src/JinianNet.JNTemplate/Parsers/IndexValueRegistrar.cs +++ b/src/JinianNet.JNTemplate/Parsers/IndexValueVisitor.cs @@ -16,12 +16,11 @@ namespace JinianNet.JNTemplate.Parsers /// /// The registrar /// - public class IndexValueRegistrar : TagRegistrar, IRegistrar + public class IndexValueVisitor : TagVisitor, ITagVisitor { /// - public override Func BuildParseMethod() + public ITag Parse(TemplateParser parser, TokenCollection tc) { - return (parser, tc) => { if (tc != null && parser != null @@ -59,8 +58,9 @@ namespace JinianNet.JNTemplate.Parsers return null; } var tag = new IndexValueTag(); - tag.Index = parser.Read(tc[x + 1, y]); - + tag.Index = parser.ReadSimple(tc[x + 1, y]); + if (tag.Index == null) + return null; return tag; } @@ -68,118 +68,114 @@ namespace JinianNet.JNTemplate.Parsers }; } /// - public override Func BuildCompileMethod() + public MethodInfo Compile(ITag tag, CompileContext c) { - return (tag, c) => + var t = tag as IndexValueTag; + var type = c.GuessType(t); + var mb = c.CreateReutrnMethod(type); + var il = mb.GetILGenerator(); + if (t.Parent == null) { - var t = tag as IndexValueTag; - var type = c.GuessType(t); - var mb = c.CreateReutrnMethod(type); - var il = mb.GetILGenerator(); - if (t.Parent == null) - { - throw new CompileException(tag, $"[IndexValutTag] : tag error on {t.ToSource()}."); - } - var parentType = c.GuessType(t.Parent); - bool toArray = false; - if (parentType.FullName == "System.String") - { - toArray = true; - parentType = typeof(char[]); - } - var indexType = c.GuessType(t.Index); - Label labelEnd = il.DefineLabel(); - Label labelInit = il.DefineLabel(); - il.DeclareLocal(parentType); - il.DeclareLocal(indexType); - il.DeclareLocal(type); + throw new CompileException(tag, $"[IndexValutTag] : tag error on {t.ToSource()}."); + } + var parentType = c.GuessType(t.Parent); + bool toArray = false; + if (parentType.FullName == "System.String") + { + toArray = true; + parentType = typeof(char[]); + } + var indexType = c.GuessType(t.Index); + Label labelEnd = il.DefineLabel(); + Label labelInit = il.DefineLabel(); + il.DeclareLocal(parentType); + il.DeclareLocal(indexType); + il.DeclareLocal(type); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Call, c.CompileTag(t.Parent)); - if (toArray) - { - il.DeclareLocal(typeof(string)); - il.Emit(OpCodes.Stloc_3); - il.Emit(OpCodes.Ldloc_3); - il.Emit(OpCodes.Call, typeof(string).GetMethodInfo("ToCharArray", Type.EmptyTypes)); - } - il.Emit(OpCodes.Stloc_0); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, c.CompileTag(t.Parent)); + if (toArray) + { + il.DeclareLocal(typeof(string)); + il.Emit(OpCodes.Stloc_3); + il.Emit(OpCodes.Ldloc_3); + il.Emit(OpCodes.Call, typeof(string).GetMethodInfo("ToCharArray", Type.EmptyTypes)); + } + il.Emit(OpCodes.Stloc_0); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Call, c.CompileTag(t.Index)); - il.Emit(OpCodes.Stloc_1); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, c.CompileTag(t.Index)); + il.Emit(OpCodes.Stloc_1); - il.Emit(OpCodes.Ldloc, 0); - il.Emit(OpCodes.Ldloc, 1); - if (parentType.IsArray) + il.Emit(OpCodes.Ldloc, 0); + il.Emit(OpCodes.Ldloc, 1); + if (parentType.IsArray) + { + il.Ldelem(type); + il.Emit(OpCodes.Stloc_2); + } + else + { + var getItem = parentType.GetMethodInfo("get_Item", new Type[] { indexType }); + ParameterInfo[] ps; + if (getItem == null || (ps = getItem.GetParameters()).Length != 1) { - il.Ldelem(type); - il.Emit(OpCodes.Stloc_2); + throw new CompileException(tag, $"[IndexValutTag] : Cannot not compile."); } - else + if (indexType != ps[0].ParameterType) { - var getItem = parentType.GetMethodInfo("get_Item", new Type[] { indexType }); - if (getItem == null) - { - throw new CompileException(tag, $"[IndexValutTag] : Cannot not compile."); - } - il.Emit(OpCodes.Callvirt, getItem); - il.Emit(OpCodes.Stloc_2); + il.ConvertTo(indexType, ps[0].ParameterType); } + il.Emit(OpCodes.Callvirt, getItem); + il.Emit(OpCodes.Stloc_2); + } - il.Emit(OpCodes.Ldloc_2); - il.Emit(OpCodes.Ret); - return mb.GetBaseDefinition(); - }; + il.Emit(OpCodes.Ldloc_2); + il.Emit(OpCodes.Ret); + return mb.GetBaseDefinition(); } /// - public override Func BuildGuessMethod() + public Type GuessType(ITag tag, CompileContext c) { - return (tag, c) => + var t = tag as IndexValueTag; + if (t.Parent == null) { - var t = tag as IndexValueTag; - if (t.Parent == null) - { - throw new CompileException(tag, "[IndexValueTag] : Parent cannot be null"); - } - var parentType = c.GuessType(t.Parent); - if (parentType.FullName == "System.String") - { - return typeof(char); - } - var indexType = c.GuessType(t.Index); - if (parentType.IsArray) - { - var method = parentType.GetMethodInfo("get", new Type[] { indexType }); - if (method != null) - { - return method.ReturnType; - } - } - var m = parentType.GetMethodInfo("get_Item", new Type[] { indexType }); - if (m != null) + throw new CompileException(tag, "[IndexValueTag] : Parent cannot be null"); + } + var parentType = c.GuessType(t.Parent); + if (parentType.FullName == "System.String") + { + return typeof(char); + } + var indexType = c.GuessType(t.Index); + if (parentType.IsArray) + { + var method = parentType.GetMethodInfo("get", new Type[] { indexType }); + if (method != null) { - return m.ReturnType; + return method.ReturnType; } + } + var m = parentType.GetMethodInfo("get_Item", new Type[] { indexType }); + if (m != null) + { + return m.ReturnType; + } - throw new CompileException(tag, $"[IndexValueTag]: \"{tag.ToSource()}\" is not defined"); - }; + throw new CompileException(tag, $"[IndexValueTag]: \"{tag.ToSource()}\" is not defined"); } /// - public override Func BuildExcuteMethod() + public object Excute(ITag tag, TemplateContext context) { - return (tag, context) => - { - var t = tag as IndexValueTag; - object obj = context.Execute(t.Parent); - object index = context.Execute(t.Index); - return obj.CallIndexValue(index); - }; + var t = tag as IndexValueTag; + object obj = context.Execute(t.Parent); + object index = context.Execute(t.Index); + return obj.CallIndexValue(index); } } } diff --git a/src/JinianNet.JNTemplate/Parsers/JsonVisitor.cs b/src/JinianNet.JNTemplate/Parsers/JsonVisitor.cs new file mode 100644 index 0000000..c05fbd2 --- /dev/null +++ b/src/JinianNet.JNTemplate/Parsers/JsonVisitor.cs @@ -0,0 +1,126 @@ +/******************************************************************************** + Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. + Licensed under the MIT license. See licence.txt file in the project root for full license information. + ********************************************************************************/ +using JinianNet.JNTemplate.CodeCompilation; +using JinianNet.JNTemplate.Dynamic; +using JinianNet.JNTemplate.Exceptions; +using JinianNet.JNTemplate.Nodes; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; + +namespace JinianNet.JNTemplate.Parsers +{ + /// + /// The registrar + /// + public class JsonVisitor : TagVisitor, ITagVisitor + { + /// + public ITag Parse(TemplateParser parser, TokenCollection tc) + { + if (tc.Count > 2 + && (tc[0].TokenKind == TokenKind.LeftBrace) + && tc.Last.TokenKind == TokenKind.RightBrace) + { + var tag = new JsonTag(); + var tcs = tc.Split(1, tc.Count - 1, TokenKind.Comma); + for (int i = 0; i < tcs.Length; i++) + { + if (tcs[i].Count == 1 && tcs[i][0].TokenKind == TokenKind.Comma) + { + continue; + } + var keyValuePair = tcs[i].Split(0, tcs[i].Count, TokenKind.Colon); + if (keyValuePair.Length != 3) + { + //不符合规范 + return null; + } + var key = parser.ReadSimple(keyValuePair[0]); + var value = parser.ReadSimple(keyValuePair[2]); + tag.Dict.Add(key, value); + } + return tag; + } + + return null; + } + /// + public MethodInfo Compile(ITag tag, CompileContext c) + { + var t = tag as JsonTag; + var type = typeof(Dictionary); + var mb = c.CreateReutrnMethod(type); + var il = mb.GetILGenerator(); + il.DeclareLocal(type); + il.DeclareLocal(type); + il.Emit(OpCodes.Newobj, type.GetConstructor(Type.EmptyTypes)); + il.Emit(OpCodes.Stloc_0); + + var dict = t.Dict; + foreach(var kv in dict) + { + if (kv.Value == null) + throw new CompileException($"The Value cannot be null:{tag.ToSource()}"); + var keyMethod = c.CompileTag(kv.Key); + var localVar1 = il.DeclareLocal(keyMethod.ReturnType); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, keyMethod); + il.Emit(OpCodes.Stloc, localVar1.LocalIndex); + + if (kv.Value != null) + { + var valueMethod = c.CompileTag(kv.Value); + var localVar2 = il.DeclareLocal(valueMethod.ReturnType); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, valueMethod); + il.Emit(OpCodes.Stloc, localVar2.LocalIndex); + + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Ldloc, localVar1.LocalIndex); + if (keyMethod.ReturnType.IsValueType) + il.Emit(OpCodes.Box,keyMethod.ReturnType); + il.Emit(OpCodes.Ldloc_S, localVar2.LocalIndex); + if (valueMethod.ReturnType.IsValueType) + il.Emit(OpCodes.Box, valueMethod.ReturnType); + } + else + { + il.Emit(OpCodes.Ldloc_0); + il.LoadVariable(keyMethod.ReturnType, localVar1.LocalIndex); + il.Emit(OpCodes.Ldnull); + } + il.Emit(OpCodes.Callvirt, type.GetMethod("set_Item")); + //il.Emit(OpCodes.Ldloc, localVar.LocalIndex); + } + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Stloc_1); + il.Emit(OpCodes.Ldloc_1); + il.Emit(OpCodes.Ret); + return mb.GetBaseDefinition(); + } + /// + public Type GuessType(ITag tag, CompileContext c) + { + return typeof(Dictionary); + } + /// + public object Excute(ITag tag, TemplateContext context) + { + var t = tag as JsonTag; + var result = new Dictionary(); + foreach (var kv in t.Dict) + { + var key = kv.Key == null ? null : context.Execute(kv.Key); + var value = kv.Value == null ? null : context.Execute(kv.Value); + result.Add(key, value); + } + return result; + } + } +} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/LayoutRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/LayoutRegistrar.cs deleted file mode 100644 index 50f737e..0000000 --- a/src/JinianNet.JNTemplate/Parsers/LayoutRegistrar.cs +++ /dev/null @@ -1,134 +0,0 @@ -/******************************************************************************** - Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. - Licensed under the MIT license. See licence.txt file in the project root for full license information. - ********************************************************************************/ -using System; -using System.IO; -using System.Reflection; -using System.Reflection.Emit; -using JinianNet.JNTemplate.CodeCompilation; -using JinianNet.JNTemplate.Dynamic; -using JinianNet.JNTemplate.Nodes; -using JinianNet.JNTemplate.Exceptions; -using System.Collections.Generic; - -namespace JinianNet.JNTemplate.Parsers -{ - /// - /// The registrar - /// - public class LayoutRegistrar : TagRegistrar, IRegistrar - { - /// - public override Func BuildParseMethod() - { - return (parser, tc) => - { - if (tc != null - && parser != null - && tc.Count > 2 - && Utility.IsEqual(tc.First.Text, Const.KEY_LAYOUT) - && (tc[1].TokenKind == TokenKind.LeftParentheses) - && tc.Last.TokenKind == TokenKind.RightParentheses) - { - var tag = new LayoutTag(); - tag.Path = parser.Read(new TokenCollection(tc, 2, tc.Count - 2)); - while (parser.MoveNext()) - { - tag.Children.Add(parser.Current); - } - return tag; - } - return null; - }; - } - /// - public override Func BuildCompileMethod() - { - return (tag, c) => - { - var t = tag as LayoutTag; - var type = c.GuessType(t); - var mb = c.CreateReutrnMethod(type); - var il = mb.GetILGenerator(); - var strTag = t.Path as StringTag; - if (strTag == null) - { - throw new CompileException(tag, $"[LayoutTag] : path must be a string."); - } - var res = c.Load(strTag.Value); - if (res == null) - { - throw new CompileException(tag, $"[LayoutTag] : \"{strTag.Value}\" cannot be found."); - } - - var tags = c.Lexer(res.Content); - - for (int i = 0; i < tags.Length; i++) - { - if (tags[i] is BodyTag body) - { - for (int j = 0; j < t.Children.Count; j++) - { - body.AddChild(t.Children[j]); - } - } - } - - c.BlockCompile(il, tags); - - il.Emit(OpCodes.Ret); - return mb.GetBaseDefinition(); - }; - } - /// - public override Func BuildGuessMethod() - { - return (tag, c) => - { - return typeof(string); - }; - } - /// - public override Func BuildExcuteMethod() - { - return (tag, context) => - { - var t = tag as LayoutTag; - object path = context.Execute(t.Path); - if (path == null) - { - return null; - } - var res = context.FindFullPath(path.ToString()); - if (string.IsNullOrEmpty(res)) - { - return null; - } - var reader = new Resources.ResourceReader(res); - - var result = (InterpretResult)context.InterpretTemplate(res, reader); - var tags = new List(); - for (int i = 0; i < result.Tags.Length; i++) - { - if (result.Tags[i] is BodyTag _) - { - for (int j = 0; j < t.Children.Count; j++) - { - tags.Add(t.Children[j]); - } - continue; - } - tags.Add(result.Tags[i]); - } - using (System.IO.StringWriter writer = new StringWriter()) - { - context.Render(writer, tags.ToArray()); - return writer.ToString(); - } - - }; - } - - } -} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/LayoutVisitor.cs b/src/JinianNet.JNTemplate/Parsers/LayoutVisitor.cs new file mode 100644 index 0000000..c10ab06 --- /dev/null +++ b/src/JinianNet.JNTemplate/Parsers/LayoutVisitor.cs @@ -0,0 +1,120 @@ +/******************************************************************************** + Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. + Licensed under the MIT license. See licence.txt file in the project root for full license information. + ********************************************************************************/ +using System; +using System.IO; +using System.Reflection; +using System.Reflection.Emit; +using JinianNet.JNTemplate.CodeCompilation; +using JinianNet.JNTemplate.Dynamic; +using JinianNet.JNTemplate.Nodes; +using JinianNet.JNTemplate.Exceptions; +using System.Collections.Generic; + +namespace JinianNet.JNTemplate.Parsers +{ + /// + /// The registrar + /// + public class LayoutVisitor : TagVisitor, ITagVisitor + { + /// + public ITag Parse(TemplateParser parser, TokenCollection tc) + { + if (tc.Count > 2 + && Utility.IsEqual(tc.First.Text, Const.KEY_LAYOUT) + && (tc[1].TokenKind == TokenKind.LeftParentheses) + && tc.Last.TokenKind == TokenKind.RightParentheses) + { + var tag = new LayoutTag(); + tag.Path = parser.ReadSimple(new TokenCollection(tc, 2, tc.Count - 2)); + if (tag.Path == null) + return null; + while (parser.MoveNext()) + { + tag.Children.Add(parser.Current); + } + return tag; + } + return null; + } + /// + public MethodInfo Compile(ITag tag, CompileContext c) + { + var t = tag as LayoutTag; + var type = c.GuessType(t); + var mb = c.CreateReutrnMethod(type); + var il = mb.GetILGenerator(); + var strTag = t.Path as StringTag; + if (strTag == null) + { + throw new CompileException(tag, $"[LayoutTag] : path must be a string."); + } + var res = c.Load(strTag.Value); + if (res == null) + { + throw new CompileException(tag, $"[LayoutTag] : \"{strTag.Value}\" cannot be found."); + } + + var tags = c.Lexer(res.Content); + + for (int i = 0; i < tags.Count; i++) + { + if (tags[i] is BodyTag body) + { + for (int j = 0; j < t.Children.Count; j++) + { + body.AddChild(t.Children[j]); + } + } + } + + c.BlockCompile(il, tags); + + il.Emit(OpCodes.Ret); + return mb.GetBaseDefinition(); + } + /// + public Type GuessType(ITag tag, CompileContext c) + { + return typeof(string); + } + /// + public object Excute(ITag tag, TemplateContext context) + { + var t = tag as LayoutTag; + object path = context.Execute(t.Path); + if (path == null) + { + return null; + } + var res = context.FindFullPath(path.ToString()); + if (string.IsNullOrEmpty(res)) + { + return null; + } + var reader = new Resources.ResourceReader(res); + + var result = (InterpretResult)context.InterpretTemplate(res, reader); + var tags = new TagCollection(); + for (int i = 0; i < result.Tags.Count; i++) + { + if (result.Tags[i] is BodyTag _) + { + for (int j = 0; j < t.Children.Count; j++) + { + tags.Add(t.Children[j]); + } + continue; + } + tags.Add(result.Tags[i]); + } + using (System.IO.StringWriter writer = new StringWriter()) + { + context.Render(writer, tags); + return writer.ToString(); + } + } + } +} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/LoadRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/LoadRegistrar.cs deleted file mode 100644 index b841751..0000000 --- a/src/JinianNet.JNTemplate/Parsers/LoadRegistrar.cs +++ /dev/null @@ -1,169 +0,0 @@ -/******************************************************************************** - Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. - Licensed under the MIT license. See licence.txt file in the project root for full license information. - ********************************************************************************/ -using JinianNet.JNTemplate.CodeCompilation; -using JinianNet.JNTemplate.Dynamic; -using JinianNet.JNTemplate.Nodes; -using JinianNet.JNTemplate.Exceptions; -using System; -using System.IO; -using System.Reflection; -using System.Reflection.Emit; - -namespace JinianNet.JNTemplate.Parsers -{ - /// - /// The registrar - /// - - public class LoadRegistrar : TagRegistrar, IRegistrar - { - /// - public override Func BuildParseMethod() - { - return (parser, tc) => - { - if (Utility.IsEqual(tc.First.Text, Const.KEY_LOAD)) - { - if (tc != null - && parser != null - && tc.Count > 2 - && (tc[1].TokenKind == TokenKind.LeftParentheses) - && tc.Last.TokenKind == TokenKind.RightParentheses) - { - var tag = new LoadTag(); - tag.Path = parser.Read(new TokenCollection(tc, 2, tc.Count - 2)); - return tag; - } - } - - return null; - }; - } - /// - public override Func BuildCompileMethod() - { - return (tag, c) => - { - var t = tag as LoadTag; - var type = c.GuessType(t); - var mb = c.CreateReutrnMethod(type); - var il = mb.GetILGenerator(); - var strTag = t.Path as StringTag; - if (strTag != null) - { - var res = c.Load(strTag.Value); - if (res == null) - { - throw new CompileException(tag, $"[LoadTag] : \"{strTag.Value}\" cannot be found."); - } - - var tags = c.Lexer(res.Content); - - c.BlockCompile(il, tags); - } - else - { - var labelEnd = il.DefineLabel(); - var labelSuccess = il.DefineLabel(); - var labelTry = il.DefineLabel(); - var labelFinally = il.DefineLabel(); - - var m = c.CompileTag(t.Path); - il.DeclareLocal(typeof(string)); - il.DeclareLocal(typeof(bool)); - il.DeclareLocal(typeof(string)); - - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Call, m); - if (m.ReturnType.FullName != "System.String") - { - il.DeclareLocal(m.ReturnType); - il.Emit(OpCodes.Stloc, 3); - if (m.ReturnType.IsValueType) - { - il.Emit(OpCodes.Ldloca, 3); - } - else - { - il.Emit(OpCodes.Ldloc, 3); - il.DeclareLocal(typeof(bool)); - il.Emit(OpCodes.Ldnull); - il.Emit(OpCodes.Cgt_Un); - il.Emit(OpCodes.Stloc, 4); - il.Emit(OpCodes.Ldloc, 4); - il.Emit(OpCodes.Brfalse, labelEnd); - - il.Emit(OpCodes.Ldloc, 3); - } - il.Call(m.ReturnType, typeof(object).GetMethodInfo("ToString", Type.EmptyTypes)); - il.Emit(OpCodes.Stloc, 0); - il.Emit(OpCodes.Ldloc, 0); - } - else - { - il.Emit(OpCodes.Stloc, 0); - il.Emit(OpCodes.Ldloc, 0); - } - il.Emit(OpCodes.Ldnull); - il.Emit(OpCodes.Cgt_Un); - il.Emit(OpCodes.Stloc, 1); - il.Emit(OpCodes.Ldloc, 1); - il.Emit(OpCodes.Brfalse, labelEnd); - - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Call, typeof(TemplateContextExtensions).GetMethodInfo("CompileAndRenderFile", new Type[] { typeof(TemplateContext), typeof(string) })); - il.Emit(OpCodes.Stloc, 2); - - il.Emit(OpCodes.Br, labelSuccess); - - il.MarkLabel(labelEnd); - il.Emit(OpCodes.Ldstr, $"[LoadTag] : \"{t.Path.ToSource()}\" cannot be found."); - //il.Emit(OpCodes.Ldnull); - il.Emit(OpCodes.Stloc, 2); - - il.MarkLabel(labelSuccess); - il.Emit(OpCodes.Ldloc, 2); - } - il.Emit(OpCodes.Ret); - return mb.GetBaseDefinition(); - }; - } - /// - public override Func BuildGuessMethod() - { - return (tag, c) => typeof(string); - } - /// - public override Func BuildExcuteMethod() - { - return (tag, context) => - { - var t = tag as LoadTag; - object path = context.Execute(t.Path); - if (path == null) - { - return null; - } - var res = context.FindFullPath(path.ToString()); - if (string.IsNullOrEmpty(res)) - { - return null; - } - - var reader = new Resources.ResourceReader(res); - - var result = context.InterpretTemplate(res, reader); - - using (System.IO.StringWriter writer = new StringWriter()) - { - result.Render(writer, context); - return writer.ToString(); - } - }; - } - } -} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/LoadVisitor.cs b/src/JinianNet.JNTemplate/Parsers/LoadVisitor.cs new file mode 100644 index 0000000..9dff6e5 --- /dev/null +++ b/src/JinianNet.JNTemplate/Parsers/LoadVisitor.cs @@ -0,0 +1,160 @@ +/******************************************************************************** + Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. + Licensed under the MIT license. See licence.txt file in the project root for full license information. + ********************************************************************************/ +using JinianNet.JNTemplate.CodeCompilation; +using JinianNet.JNTemplate.Dynamic; +using JinianNet.JNTemplate.Nodes; +using JinianNet.JNTemplate.Exceptions; +using System; +using System.IO; +using System.Reflection; +using System.Reflection.Emit; + +namespace JinianNet.JNTemplate.Parsers +{ + /// + /// The registrar + /// + + public class LoadVisitor : TagVisitor, ITagVisitor + { + /// + public ITag Parse(TemplateParser parser, TokenCollection tc) + { + if (Utility.IsEqual(tc.First.Text, Const.KEY_LOAD)) + { + if (tc.Count > 2 + && (tc[1].TokenKind == TokenKind.LeftParentheses) + && tc.Last.TokenKind == TokenKind.RightParentheses) + { + var tag = new LoadTag(); + tag.Path = parser.ReadSimple(new TokenCollection(tc, 2, tc.Count - 2)); + if (tag.Path == null) + return null; + return tag; + } + } + + return null; + } + /// + public MethodInfo Compile(ITag tag, CompileContext c) + { + var t = tag as LoadTag; + var type = c.GuessType(t); + var mb = c.CreateReutrnMethod(type); + var il = mb.GetILGenerator(); + var strTag = t.Path as StringTag; + if (strTag != null) + { + var res = c.Load(strTag.Value); + if (res == null) + { + throw new CompileException(tag, $"[LoadTag] : \"{strTag.Value}\" cannot be found."); + } + + var tags = c.Lexer(res.Content); + + c.BlockCompile(il, tags); + } + else + { + var labelEnd = il.DefineLabel(); + var labelSuccess = il.DefineLabel(); + var labelTry = il.DefineLabel(); + var labelFinally = il.DefineLabel(); + + var m = c.CompileTag(t.Path); + il.DeclareLocal(typeof(string)); + il.DeclareLocal(typeof(bool)); + il.DeclareLocal(typeof(string)); + + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, m); + if (m.ReturnType.FullName != "System.String") + { + il.DeclareLocal(m.ReturnType); + il.Emit(OpCodes.Stloc, 3); + if (m.ReturnType.IsValueType) + { + il.Emit(OpCodes.Ldloca, 3); + } + else + { + il.Emit(OpCodes.Ldloc, 3); + il.DeclareLocal(typeof(bool)); + il.Emit(OpCodes.Ldnull); + il.Emit(OpCodes.Cgt_Un); + il.Emit(OpCodes.Stloc, 4); + il.Emit(OpCodes.Ldloc, 4); + il.Emit(OpCodes.Brfalse, labelEnd); + + il.Emit(OpCodes.Ldloc, 3); + } + il.Call(m.ReturnType, typeof(object).GetMethodInfo("ToString", Type.EmptyTypes)); + il.Emit(OpCodes.Stloc, 0); + il.Emit(OpCodes.Ldloc, 0); + } + else + { + il.Emit(OpCodes.Stloc, 0); + il.Emit(OpCodes.Ldloc, 0); + } + il.Emit(OpCodes.Ldnull); + il.Emit(OpCodes.Cgt_Un); + il.Emit(OpCodes.Stloc, 1); + il.Emit(OpCodes.Ldloc, 1); + il.Emit(OpCodes.Brfalse, labelEnd); + + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Call, typeof(TemplateContextExtensions).GetMethodInfo("CompileAndRenderFile", new Type[] { typeof(TemplateContext), typeof(string) })); + il.Emit(OpCodes.Stloc, 2); + + il.Emit(OpCodes.Br, labelSuccess); + + il.MarkLabel(labelEnd); + il.Emit(OpCodes.Ldstr, $"[LoadTag] : \"{t.Path.ToSource()}\" cannot be found."); + //il.Emit(OpCodes.Ldnull); + il.Emit(OpCodes.Stloc, 2); + + il.MarkLabel(labelSuccess); + il.Emit(OpCodes.Ldloc, 2); + } + il.Emit(OpCodes.Ret); + return mb.GetBaseDefinition(); + } + /// + public Type GuessType(ITag tag, CompileContext c) + { + return typeof(string); + } + /// + public object Excute(ITag tag, TemplateContext context) + { + var t = tag as LoadTag; + object path = context.Execute(t.Path); + if (path == null) + { + return null; + } + var res = context.FindFullPath(path.ToString()); + if (string.IsNullOrEmpty(res)) + { + return null; + } + + var reader = new Resources.ResourceReader(res); + + var result = context.InterpretTemplate(res, reader); + + using (System.IO.StringWriter writer = new StringWriter()) + { + result.Render(writer, context); + return writer.ToString(); + } + } + } +} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/LogicRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/LogicVisitor.cs similarity index 36% rename from src/JinianNet.JNTemplate/Parsers/LogicRegistrar.cs rename to src/JinianNet.JNTemplate/Parsers/LogicVisitor.cs index 2e5fa49..3b027cc 100644 --- a/src/JinianNet.JNTemplate/Parsers/LogicRegistrar.cs +++ b/src/JinianNet.JNTemplate/Parsers/LogicVisitor.cs @@ -17,341 +17,328 @@ namespace JinianNet.JNTemplate.Parsers /// /// The registrar /// - public class LogicRegistrar : TagRegistrar, IRegistrar + public class LogicVisitor : TagVisitor, ITagVisitor { #region private static List operators = new List(new Operator[] { Operator.Equal, Operator.NotEqual, Operator.LessThan, Operator.LessThanOrEqual, Operator.GreaterThan, Operator.GreaterThanOrEqual }); #endregion /// - public override Func BuildParseMethod() + public ITag Parse(TemplateParser parser, TokenCollection tc) { - return (parser, tc) => + if (tc.Count > 2) { - if (tc != null - && parser != null - && tc.Count > 2) + var tcs = tc.Split(TokenKind.Logic); + if (tcs.Length <= 1) { - var tcs = tc.Split(TokenKind.Logic); - if (tcs.Length <= 1) - { - return null; - } + return null; + } - var tags = new List(); - for (int i = 0; i < tcs.Length; i++) + var tags = new List(); + for (int i = 0; i < tcs.Length; i++) + { + if (tcs[i].Count == 1 && tcs[i][0].TokenKind == TokenKind.Logic) { - if (tcs[i].Count == 1 && tcs[i][0].TokenKind == TokenKind.Logic) - { - tags.Add(new OperatorTag(tcs[i][0])); - } - else - { - tags.Add(parser.Read(tcs[i])); - } + tags.Add(new OperatorTag(tcs[i][0])); } - - if (tags.Count == 1) + else { - return tags[0]; + tags.Add(parser.ReadSimple(tcs[i])); } + } + + if (tags.Count == 1) + { + return tags[0]; + } - if (tags.Count > 1) + if (tags.Count > 1) + { + var list = new List>(); + ITag t = new LogicTag(); + var arr = Analysis(tags, new List(new Operator[] { Operator.And, Operator.Or })); + if (arr.Length == 1) { - var list = new List>(); - ITag t = new LogicTag(); - var arr = Analysis(tags, new List(new Operator[] { Operator.And, Operator.Or })); - if (arr.Length == 1) - { - return arr[0]; - } - AddRange(t, arr); - tags.Clear(); - return t; + return arr[0]; } - + AddRange(t, arr); + tags.Clear(); + return t; } - return null; - }; + } + + return null; } /// - public override Func BuildCompileMethod() + public MethodInfo Compile(ITag tag, CompileContext c) { - return (tag, c) => + var t = tag as LogicTag; + var type = c.GuessType(t); + var mb = c.CreateReutrnMethod(type); + var il = mb.GetILGenerator(); + Label labelEnd = il.DefineLabel(); + il.DeclareLocal(type); + var array = new object[t.Children.Count]; + var types = new List(); + var opts = new List(); + var message = new List(); + for (int i = 0; i < t.Children.Count; i++) { - var t = tag as LogicTag; - var type = c.GuessType(t); - var mb = c.CreateReutrnMethod(type); - var il = mb.GetILGenerator(); - Label labelEnd = il.DefineLabel(); - il.DeclareLocal(type); - var array = new object[t.Children.Count]; - var types = new List(); - var opts = new List(); - var message = new List(); - for (int i = 0; i < t.Children.Count; i++) + var opt = t.Children[i] as OperatorTag; + if (opt != null) { - var opt = t.Children[i] as OperatorTag; - if (opt != null) - { - if (!opts.Contains(opt.Value)) - { - opts.Add(opt.Value); - } - array[i] = opt.Value; - message.Add(OperatorConvert.ToString(opt.Value)); - } - else + if (!opts.Contains(opt.Value)) { - array[i] = t.Children[i]; - types.Add(c.GuessType(t.Children[i])); - message.Add(types[types.Count - 1].Name); + opts.Add(opt.Value); } + array[i] = opt.Value; + message.Add(OperatorConvert.ToString(opt.Value)); } - - if (opts.Contains(Operator.Or) || opts.Contains(Operator.And)) + else { + array[i] = t.Children[i]; + types.Add(c.GuessType(t.Children[i])); + message.Add(types[types.Count - 1].Name); + } + } - Label labelTrue = il.DefineLabel(); - Label labelFalse = il.DefineLabel(); - Operator pre = Operator.None; - for (int i = 0; i < t.Children.Count; i++) + if (opts.Contains(Operator.Or) || opts.Contains(Operator.And)) + { + + Label labelTrue = il.DefineLabel(); + Label labelFalse = il.DefineLabel(); + Operator pre = Operator.None; + for (int i = 0; i < t.Children.Count; i++) + { + var opt = t.Children[i] as OperatorTag; + if (opt != null) { - var opt = t.Children[i] as OperatorTag; - if (opt != null) - { - pre = opt.Value; - continue; - } + pre = opt.Value; + continue; + } - var m = c.CompileTag(t.Children[i]); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Call, m); - if (m.ReturnType.Name != "Boolean") + var m = c.CompileTag(t.Children[i]); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, m); + if (m.ReturnType.Name != "Boolean") + { + var cm = typeof(Utility).GetMethodInfo("ToBoolean", new Type[] { m.ReturnType }); + if (cm == null) { - var cm = typeof(Utility).GetMethodInfo("ToBoolean", new Type[] { m.ReturnType }); - if (cm == null) + cm = typeof(Utility).GetMethodInfo("ToBoolean", new Type[] { typeof(object) }); + if (m.ReturnType.IsValueType) { - cm = typeof(Utility).GetMethodInfo("ToBoolean", new Type[] { typeof(object) }); - if (m.ReturnType.IsValueType) - { - il.Emit(OpCodes.Box, m.ReturnType); - } - else - { - il.Emit(OpCodes.Castclass, typeof(object)); - } + il.Emit(OpCodes.Box, m.ReturnType); + } + else + { + il.Emit(OpCodes.Castclass, typeof(object)); } - il.Emit(OpCodes.Call, cm); - } - //il.Emit(OpCodes.Stloc_0); - if (pre == Operator.None) - { - pre = (t.Children[i + 1] as OperatorTag).Value; - } - - if (pre == Operator.Or) - { - il.Emit(OpCodes.Brtrue, labelTrue); - } - if (pre == Operator.And) - { - il.Emit(OpCodes.Brfalse, labelFalse); } + il.Emit(OpCodes.Call, cm); + } + //il.Emit(OpCodes.Stloc_0); + if (pre == Operator.None) + { + pre = (t.Children[i + 1] as OperatorTag).Value; } if (pre == Operator.Or) { - il.Emit(OpCodes.Br, labelEnd); + il.Emit(OpCodes.Brtrue, labelTrue); } - if (pre == Operator.And) { - il.Emit(OpCodes.Br, labelTrue); + il.Emit(OpCodes.Brfalse, labelFalse); } - il.MarkLabel(labelTrue); - il.Emit(OpCodes.Ldc_I4_1); - il.Emit(OpCodes.Stloc_0); + } + + if (pre == Operator.Or) + { il.Emit(OpCodes.Br, labelEnd); + } + + if (pre == Operator.And) + { + il.Emit(OpCodes.Br, labelTrue); + } + il.MarkLabel(labelTrue); + il.Emit(OpCodes.Ldc_I4_1); + il.Emit(OpCodes.Stloc_0); + il.Emit(OpCodes.Br, labelEnd); - il.MarkLabel(labelFalse); - il.Emit(OpCodes.Ldc_I4_0); + il.MarkLabel(labelFalse); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Stloc_0); + } + else + { + if (t.Children.Count == 1) + { + var m = c.CompileTag(t.Children[0]); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, m); il.Emit(OpCodes.Stloc_0); } - else + else if (t.Children.Count == 3) { - if (t.Children.Count == 1) - { - var m = c.CompileTag(t.Children[0]); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Call, m); - il.Emit(OpCodes.Stloc_0); - } - else if (t.Children.Count == 3) - { - var bestType = TypeGuesser.FindBestType(types.ToArray()); - var stack = ExpressionEvaluator.ProcessExpression(array); - var arr = stack.ToArray(); - for (var i = arr.Length - 1; i >= 0; i--) + var bestType = TypeGuesser.FindBestType(types.ToArray()); + var stack = ExpressionEvaluator.ProcessExpression(array); + var arr = stack.ToArray(); + for (var i = arr.Length - 1; i >= 0; i--) + { + var obj = arr[i]; + var childTag = obj as ITag; + if (childTag != null) { - var obj = arr[i]; - var childTag = obj as ITag; - if (childTag != null) + var m = c.CompileTag(childTag); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, m); + if (m.ReturnType.FullName != bestType.FullName) { - var m = c.CompileTag(childTag); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Call, m); - if (m.ReturnType.FullName != bestType.FullName) - { - il.ConvertTo(m.ReturnType,bestType); - } + il.ConvertTo(m.ReturnType, bestType); } - else + } + else + { + switch ((Operator)obj) { - switch ((Operator)obj) - { - case Operator.GreaterThan: - if (TypeGuesser.CanUseEqual(bestType)) - { - il.Emit(OpCodes.Cgt); - } - else - { - var m = bestType.GetMethodInfo("op_GreaterThan", new Type[] { bestType, bestType }); - if (m == null) - { - throw new TemplateException($"Operator \">\" can not be applied operand \"{bestType.FullName}\" and \"{bestType.FullName}\""); - } - il.Emit(OpCodes.Call, m); - } - break; - case Operator.GreaterThanOrEqual: - if (TypeGuesser.CanUseEqual(bestType)) - { - il.Emit(OpCodes.Clt_Un); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - } - else - { - var m = bestType.GetMethodInfo("op_GreaterThanOrEqual", new Type[] { bestType, bestType }); - if (m == null) - { - throw new TemplateException($"Operator \">=\" can not be applied operand \"{bestType.FullName}\" and \"{bestType.FullName}\""); - } - il.Emit(OpCodes.Call, m); - } - break; - case Operator.LessThan: - if (TypeGuesser.CanUseEqual(bestType)) - { - il.Emit(OpCodes.Clt); - } - else + case Operator.GreaterThan: + if (TypeGuesser.CanUseEqual(bestType)) + { + il.Emit(OpCodes.Cgt); + } + else + { + var m = bestType.GetMethodInfo("op_GreaterThan", new Type[] { bestType, bestType }); + if (m == null) { - var m = bestType.GetMethodInfo("op_LessThan", new Type[] { bestType, bestType }); - if (m == null) - { - throw new TemplateException($"Operator \"<\" can not be applied operand \"{bestType.FullName}\" and \"{bestType.FullName}\""); - } - il.Emit(OpCodes.Call, m); + throw new TemplateException($"Operator \">\" can not be applied operand \"{bestType.FullName}\" and \"{bestType.FullName}\""); } - break; - case Operator.LessThanOrEqual: - if (TypeGuesser.CanUseEqual(bestType)) - { - il.Emit(OpCodes.Cgt_Un); - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - } - else - { - var m = bestType.GetMethodInfo("op_LessThanOrEqual", new Type[] { bestType, bestType }); - if (m == null) - { - throw new TemplateException($"Operator \"<=\" can not be applied operand \"{bestType.FullName}\" and \"{bestType.FullName}\""); - } - il.Emit(OpCodes.Call, m); - } - break; - case Operator.Equal: - if (TypeGuesser.CanUseEqual(bestType)) - { - il.Emit(OpCodes.Ceq); - } - else + il.Emit(OpCodes.Call, m); + } + break; + case Operator.GreaterThanOrEqual: + if (TypeGuesser.CanUseEqual(bestType)) + { + il.Emit(OpCodes.Clt_Un); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Ceq); + } + else + { + var m = bestType.GetMethodInfo("op_GreaterThanOrEqual", new Type[] { bestType, bestType }); + if (m == null) { - il.EmitEquals(bestType); - //il.Emit(OpCodes.Call, DynamicHelpers.GetMethodInfo(bestType, "Equals", new Type[] { bestType })); + throw new TemplateException($"Operator \">=\" can not be applied operand \"{bestType.FullName}\" and \"{bestType.FullName}\""); } - break; - case Operator.NotEqual: - if (TypeGuesser.CanUseEqual(bestType)) + il.Emit(OpCodes.Call, m); + } + break; + case Operator.LessThan: + if (TypeGuesser.CanUseEqual(bestType)) + { + il.Emit(OpCodes.Clt); + } + else + { + var m = bestType.GetMethodInfo("op_LessThan", new Type[] { bestType, bestType }); + if (m == null) { - il.Emit(OpCodes.Ceq); + throw new TemplateException($"Operator \"<\" can not be applied operand \"{bestType.FullName}\" and \"{bestType.FullName}\""); } - else + il.Emit(OpCodes.Call, m); + } + break; + case Operator.LessThanOrEqual: + if (TypeGuesser.CanUseEqual(bestType)) + { + il.Emit(OpCodes.Cgt_Un); + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Ceq); + } + else + { + var m = bestType.GetMethodInfo("op_LessThanOrEqual", new Type[] { bestType, bestType }); + if (m == null) { - il.EmitEquals(bestType); - //il.Emit(OpCodes.Call, DynamicHelpers.GetMethodInfo(bestType, "Equals", new Type[] { bestType })); + throw new TemplateException($"Operator \"<=\" can not be applied operand \"{bestType.FullName}\" and \"{bestType.FullName}\""); } - il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Call, m); + } + break; + case Operator.Equal: + if (TypeGuesser.CanUseEqual(bestType)) + { + il.Emit(OpCodes.Ceq); + } + else + { + il.EmitEquals(bestType); + //il.Emit(OpCodes.Call, DynamicHelpers.GetMethodInfo(bestType, "Equals", new Type[] { bestType })); + } + break; + case Operator.NotEqual: + if (TypeGuesser.CanUseEqual(bestType)) + { il.Emit(OpCodes.Ceq); - break; - default: - throw new CompileException(tag, $"The operator \"{obj}\" is not supported on type \"{bestType.FullName}\" ."); - } + } + else + { + il.EmitEquals(bestType); + //il.Emit(OpCodes.Call, DynamicHelpers.GetMethodInfo(bestType, "Equals", new Type[] { bestType })); + } + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Ceq); + break; + default: + throw new CompileException(tag, $"The operator \"{obj}\" is not supported on type \"{bestType.FullName}\" ."); } } - il.Emit(OpCodes.Stloc_0); - } - else - { - throw new CompileException(tag, $"[LogicExpressionTag] : The expression \"{string.Concat(message)}\" is not supported ."); } + il.Emit(OpCodes.Stloc_0); + } + else + { + throw new CompileException(tag, $"[LogicExpressionTag] : The expression \"{string.Concat(message)}\" is not supported ."); } + } - il.MarkLabel(labelEnd); - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Ret); - return mb.GetBaseDefinition(); - }; + il.MarkLabel(labelEnd); + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Ret); + return mb.GetBaseDefinition(); } /// - public override Func BuildGuessMethod() + public Type GuessType(ITag tag, CompileContext c) { - return (tag, c) => - { - return typeof(bool); - }; + return typeof(bool); } /// - public override Func BuildExcuteMethod() + public object Excute(ITag tag, TemplateContext context) { - return (tag, context) => - { - var t = tag as LogicTag; - List parameters = new List(); + var t = tag as LogicTag; + List parameters = new List(); - for (int i = 0; i < t.Children.Count; i++) + for (int i = 0; i < t.Children.Count; i++) + { + bool isOperator = t.Children[i] is OperatorTag; + object result = context.Execute(t.Children[i]); + if (Eval(parameters, isOperator, result)) { - bool isOperator = t.Children[i] is OperatorTag; - object result = context.Execute(t.Children[i]); - if (Eval(parameters, isOperator, result)) - { - return parameters[parameters.Count - 1]; - } + return parameters[parameters.Count - 1]; } + } + + var stack = ExpressionEvaluator.ProcessExpression(parameters.ToArray()); + return ExpressionEvaluator.Calculate(stack); - var stack = ExpressionEvaluator.ProcessExpression(parameters.ToArray()); - return ExpressionEvaluator.Calculate(stack); - }; } /// @@ -496,6 +483,5 @@ namespace JinianNet.JNTemplate.Parsers tag.AddChild(list[i]); } } - } } diff --git a/src/JinianNet.JNTemplate/Parsers/NullVisitor.cs b/src/JinianNet.JNTemplate/Parsers/NullVisitor.cs new file mode 100644 index 0000000..48ae3d1 --- /dev/null +++ b/src/JinianNet.JNTemplate/Parsers/NullVisitor.cs @@ -0,0 +1,51 @@ +/******************************************************************************** + Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. + Licensed under the MIT license. See licence.txt file in the project root for full license information. + ********************************************************************************/ +using JinianNet.JNTemplate.CodeCompilation; +using JinianNet.JNTemplate.Nodes; +using System; +using System.Reflection; +using System.Reflection.Emit; + +namespace JinianNet.JNTemplate.Parsers +{ + /// + /// The registrar + /// + public class NullVisitor : TagVisitor, ITagVisitor + { + /// + public ITag Parse(TemplateParser parser, TokenCollection tc) + { + if (tc.Count == 1 + && tc.First.TokenKind == TokenKind.TextData + && tc.First.Text == "null") + { + return new NullTag(); + } + return null; + } + /// + public MethodInfo Compile(ITag tag, CompileContext c) + { + var type = typeof(object); + var mb = c.CreateReutrnMethod(type); + var il = mb.GetILGenerator(); + il.Emit(OpCodes.Ldnull); + il.Emit(OpCodes.Ret); + return mb.GetBaseDefinition(); + + } + /// + public Type GuessType(ITag tag, CompileContext c) + { + return typeof(object); + } + /// + public object Excute(ITag tag, TemplateContext context) + { + return null; + } + } +} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/NumberRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/NumberVisitor.cs similarity index 34% rename from src/JinianNet.JNTemplate/Parsers/NumberRegistrar.cs rename to src/JinianNet.JNTemplate/Parsers/NumberVisitor.cs index aecdcbc..02a07b5 100644 --- a/src/JinianNet.JNTemplate/Parsers/NumberRegistrar.cs +++ b/src/JinianNet.JNTemplate/Parsers/NumberVisitor.cs @@ -13,85 +13,72 @@ namespace JinianNet.JNTemplate.Parsers /// /// The registrar /// - public class NumberRegistrar : TagRegistrar, IRegistrar + public class NumberVisitor : TagVisitor, ITagVisitor { /// - public override Func BuildParseMethod() + public ITag Parse(TemplateParser parser, TokenCollection tc) { - return (parser, tc) => + if (tc.Count == 1 + && tc.First.TokenKind == TokenKind.Number) { - if (tc != null - && tc.Count == 1 - && tc.First.TokenKind == TokenKind.Number) + var tag = new NumberTag(); + if (tc.First.Text.IndexOf('.') == -1) { - var tag = new NumberTag(); - if (tc.First.Text.IndexOf('.') == -1) + if (tc.First.Text.Length < 9) { - if (tc.First.Text.Length < 9) + tag.Value = int.Parse(tc.First.Text); + } + else if (tc.First.Text.Length == 9) + { + var value = long.Parse(tc.First.Text); + if (value <= int.MaxValue) { tag.Value = int.Parse(tc.First.Text); } - else if (tc.First.Text.Length == 9) - { - var value = long.Parse(tc.First.Text); - if (value <= int.MaxValue) - { - tag.Value = int.Parse(tc.First.Text); - } - else - { - tag.Value = value; - } - } else { - tag.Value = long.Parse(tc.First.Text); + tag.Value = value; } } else { - tag.Value = Double.Parse(tc.First.Text); + tag.Value = long.Parse(tc.First.Text); } - - return tag; } + else + { + tag.Value = Double.Parse(tc.First.Text); + } + + return tag; + } - return null; - }; + return null; } /// - public override Func BuildCompileMethod() + public MethodInfo Compile(ITag tag, CompileContext c) { - return (tag, c) => - { - var t = tag as NumberTag; - var type = t.Value.GetType(); - var mb = c.CreateReutrnMethod(type); - var il = mb.GetILGenerator(); - il.DeclareLocal(type); - il.CallTypeTag(t); - il.Emit(OpCodes.Stloc_0); - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Ret); - return mb.GetBaseDefinition(); - }; + var t = tag as NumberTag; + var type = t.Value.GetType(); + var mb = c.CreateReutrnMethod(type); + var il = mb.GetILGenerator(); + il.DeclareLocal(type); + il.CallTypeTag(t); + il.Emit(OpCodes.Stloc_0); + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Ret); + return mb.GetBaseDefinition(); } /// - public override Func BuildGuessMethod() + public Type GuessType(ITag tag, CompileContext c) { - return (tag, c) => - { - return ((NumberTag)tag).Value.GetType(); - }; + return ((NumberTag)tag).Value.GetType(); } /// - public override Func BuildExcuteMethod() - { - return (tag, context) => - { - var t = tag as NumberTag; - return t.Value; - }; + public object Excute(ITag tag, TemplateContext context) + { + var t = tag as NumberTag; + return t.Value; } } } \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/OperatorRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/OperatorVisitor.cs similarity index 58% rename from src/JinianNet.JNTemplate/Parsers/OperatorRegistrar.cs rename to src/JinianNet.JNTemplate/Parsers/OperatorVisitor.cs index 84275c2..9b1ff7d 100644 --- a/src/JinianNet.JNTemplate/Parsers/OperatorRegistrar.cs +++ b/src/JinianNet.JNTemplate/Parsers/OperatorVisitor.cs @@ -11,33 +11,30 @@ using System; namespace JinianNet.JNTemplate.Parsers { /// - /// The registrar + /// The registrar /// - public class OperatorRegistrar : TagRegistrar, IRegistrar + public class OperatorVisitor : TagVisitor, ITagVisitor { /// - public override Func BuildParseMethod() + public ITag Parse(TemplateParser parser, TokenCollection tc) { return null; } /// - public override Func BuildCompileMethod() + public MethodInfo Compile(ITag tag, CompileContext c) { return null; } /// - public override Func BuildGuessMethod() + public Type GuessType(ITag tag, CompileContext c) { return null; } /// - public override Func BuildExcuteMethod() + public object Excute(ITag tag, TemplateContext context) { - return (tag, context) => - { - var t = tag as OperatorTag; - return t.Value; - }; + var t = tag as OperatorTag; + return t.Value; } } } \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/ReferenceRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/ReferenceRegistrar.cs deleted file mode 100644 index 41f0412..0000000 --- a/src/JinianNet.JNTemplate/Parsers/ReferenceRegistrar.cs +++ /dev/null @@ -1,99 +0,0 @@ -/******************************************************************************** - Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. - Licensed under the MIT license. See licence.txt file in the project root for full license information. - ********************************************************************************/ -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Reflection.Emit; -using System.Text; -using JinianNet.JNTemplate.CodeCompilation; -using JinianNet.JNTemplate.Dynamic; -using JinianNet.JNTemplate.Exceptions; -using JinianNet.JNTemplate.Nodes; - -namespace JinianNet.JNTemplate.Parsers -{ - /// - /// The registrar - /// - public class ReferenceRegistrar : TagRegistrar, IRegistrar - { - /// - public override Func BuildParseMethod() - { - return (parser, tc) => - { - if (tc != null - && parser != null - && tc.Count > 2) - { - var tcs = tc.Split(TokenKind.Dot, TokenKind.LeftBracket, TokenKind.RightBracket); - if (tcs.Length == 1 - /*&& ( - tc.First.TokenKind != TokenKind.LeftBrace - || tc.Last.TokenKind != TokenKind.RightBracket - || tc.Count <= 2 - || (tcs = tc.Split(1, tc.Count - 1, TokenKind.Dot, TokenKind.LeftBracket, TokenKind.RightBracket)).Length == 1 - )*/) - { - return null; - } - var tag = new ReferenceTag(); - for (int i = 0; i < tcs.Length; i++) - { - if (tcs[i].Count == 1 && tcs[i][0].TokenKind == TokenKind.Dot) - { - if (tag.Children.Count == 0 || i == tcs[i].Count - 1 || (tcs[i + 1].Count == 1 && (tcs[i + 1][0].TokenKind == TokenKind.Dot || tcs[i + 1][0].TokenKind == TokenKind.Operator))) - { - throw new ParseException($"syntax error near '.': {tc.ToString()}", tcs[i][0].BeginLine, tcs[i][0].BeginColumn); - } - } - else if (tcs[i].Count > 0) - { - tag.AddChild(parser.Read(tcs[i])); - } - } - if (tag.Child == null) - { - return null; - } - return tag; - } - - return null; - }; - } - - /// - public override Func BuildCompileMethod() - { - return (tag, c) => - { - return c.CompileTag(((ReferenceTag)tag).Child); - }; - } - /// - public override Func BuildGuessMethod() - { - return (tag, c) => - { - return c.GuessType(((ReferenceTag)tag).Child); - }; - } - /// - public override Func BuildExcuteMethod() - { - return (tag, context) => - { - var t = tag as ReferenceTag; - if (t.Child != null) - { - return context.Execute(t.Child); - } - return null; - }; - } - - } -} diff --git a/src/JinianNet.JNTemplate/Parsers/JsonRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/ReferenceVisitor.cs similarity index 33% rename from src/JinianNet.JNTemplate/Parsers/JsonRegistrar.cs rename to src/JinianNet.JNTemplate/Parsers/ReferenceVisitor.cs index 835cc95..afc3e43 100644 --- a/src/JinianNet.JNTemplate/Parsers/JsonRegistrar.cs +++ b/src/JinianNet.JNTemplate/Parsers/ReferenceVisitor.cs @@ -2,78 +2,83 @@ Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. Licensed under the MIT license. See licence.txt file in the project root for full license information. ********************************************************************************/ -using JinianNet.JNTemplate.CodeCompilation; -using JinianNet.JNTemplate.Dynamic; -using JinianNet.JNTemplate.Nodes; using System; using System.Collections.Generic; using System.Reflection; +using System.Reflection.Emit; +using System.Text; +using JinianNet.JNTemplate.CodeCompilation; +using JinianNet.JNTemplate.Dynamic; +using JinianNet.JNTemplate.Exceptions; +using JinianNet.JNTemplate.Nodes; namespace JinianNet.JNTemplate.Parsers { /// - /// The registrar + /// The registrar /// - public class JsonRegistrar : TagRegistrar, IRegistrar + public class ReferenceVisitor : TagVisitor, ITagVisitor { /// - public override Func BuildParseMethod() + public ITag Parse(TemplateParser parser, TokenCollection tc) { - return (parser, tc) => + if (tc.Count > 2) { - if (tc.Count > 2 - && (tc[0].TokenKind == TokenKind.LeftBrace) - && tc.Last.TokenKind == TokenKind.RightBrace) + var tcs = tc.Split(TokenKind.Dot, TokenKind.LeftBracket, TokenKind.RightBracket); + if (tcs.Length == 1 + /*&& ( + tc.First.TokenKind != TokenKind.LeftBrace + || tc.Last.TokenKind != TokenKind.RightBracket + || tc.Count <= 2 + || (tcs = tc.Split(1, tc.Count - 1, TokenKind.Dot, TokenKind.LeftBracket, TokenKind.RightBracket)).Length == 1 + )*/) { - var tag = new JsonTag(); - var tcs = tc.Split(1, tc.Count - 1, TokenKind.Comma); - for (int i = 0; i < tcs.Length; i++) + return null; + } + var tag = new ReferenceTag(); + for (int i = 0; i < tcs.Length; i++) + { + if (tcs[i].Count == 1 && tcs[i][0].TokenKind == TokenKind.Dot) { - if (tcs[i].Count == 1 && tcs[i][0].TokenKind == TokenKind.Comma) + if (tag.Children.Count == 0 || i == tcs[i].Count - 1 || (tcs[i + 1].Count == 1 && (tcs[i + 1][0].TokenKind == TokenKind.Dot || tcs[i + 1][0].TokenKind == TokenKind.Operator))) { - continue; + throw new ParseException($"syntax error near '.': {tc.ToString()}", tcs[i][0].BeginLine, tcs[i][0].BeginColumn); } - var keyValuePair = tcs[i].Split(0, tcs[i].Count, TokenKind.Colon); - if (keyValuePair.Length != 3) - { - //不符合规范 - return null; - } - var key = parser.Read(keyValuePair[0]); - var value = parser.Read(keyValuePair[2]); - tag.Dict.Add(key, value); } - return tag; + else if (tcs[i].Count > 0) + { + tag.AddChild(parser.ReadSimple(tcs[i])); + } + } + if (tag.Child == null) + { + return null; } + return tag; + } - return null; - }; + return null; } + /// - public override Func BuildCompileMethod() + public MethodInfo Compile(ITag tag, CompileContext c) { - return null; + return c.CompileTag(((ReferenceTag)tag).Child); } /// - public override Func BuildGuessMethod() + public Type GuessType(ITag tag, CompileContext c) { - return null; + return c.GuessType(((ReferenceTag)tag).Child); } /// - public override Func BuildExcuteMethod() + public object Excute(ITag tag, TemplateContext context) { - return (tag, context) => + var t = tag as ReferenceTag; + if (t.Child != null) { - var t = tag as JsonTag; - var result = new Dictionary(); - foreach (var kv in t.Dict) - { - var key = kv.Key == null ? null : context.Execute(kv.Key); - var value = kv.Value == null ? null : context.Execute(kv.Value); - result.Add(key, value); - } - return result; - }; + return context.Execute(t.Child); + } + return null; } } -} \ No newline at end of file +} diff --git a/src/JinianNet.JNTemplate/Parsers/SetRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/SetRegistrar.cs deleted file mode 100644 index 3504c5e..0000000 --- a/src/JinianNet.JNTemplate/Parsers/SetRegistrar.cs +++ /dev/null @@ -1,162 +0,0 @@ -/******************************************************************************** - Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. - Licensed under the MIT license. See licence.txt file in the project root for full license information. - ********************************************************************************/ -using JinianNet.JNTemplate.CodeCompilation; -using JinianNet.JNTemplate.Dynamic; -using JinianNet.JNTemplate.Nodes; -using System; -using System.Reflection; -using System.Reflection.Emit; - -namespace JinianNet.JNTemplate.Parsers -{ - /// - /// The registrar - /// - public class SetRegistrar : TagRegistrar, IRegistrar - { - /// - public override Func BuildParseMethod() - { - return (parser, tc) => - { - if (tc == null - || parser == null) - { - return null; - } - - if (tc.Count > 5 - && Utility.IsEqual(tc.First.Text, Const.KEY_SET) - && tc[1].TokenKind == TokenKind.LeftParentheses - && tc[3].Text == "=" - && tc.Last.TokenKind == TokenKind.RightParentheses) - { - var tag = new SetTag(); - tag.Name = tc[2].Text; - - tag.Value = parser.Read(tc[4, -1]); - return tag; - - } - - if (tc.Count == 2 - && tc.First.TokenKind == TokenKind.TextData - && tc.Last.TokenKind == TokenKind.Arithmetic - && (tc.Last.Text == "++" || tc.Last.Text == "--")) - { - var tag = new SetTag(); - tag.Name = tc.First.Text; - - var c = new ArithmeticTag(); - c.AddChild(new VariableTag() - { - FirstToken = tc.First, - Name = tc.First.Text - }); - c.AddChild(new OperatorTag(new Token(TokenKind.Arithmetic, tc.Last.Text[0].ToString()))); - - //c.AddChild(new TextTag() - //{ - // FirstToken = new Token(TokenKind.Operator, tc.Last.Text[0].ToString()) - //}); - - c.AddChild(new NumberTag() - { - Value = 1, - FirstToken = new Token(TokenKind.Number, "1") - }); - - tag.Value = c; - return tag; - } - - if (tc.Count > 2 - && tc.First.TokenKind == TokenKind.TextData - && tc[1].Text == "=") - { - var tag = new SetTag(); - tag.Name = tc.First.Text; - tag.Value = parser.Read(tc[2, tc.Count]); - return tag; - } - - return null; - }; - } - /// - public override Func BuildCompileMethod() - { - return (tag, c) => - { - var getVariableScope = typeof(TemplateContext).GetPropertyGetMethod("TempData"); - //var getVariableValue = typeof(VariableScope).GetMethod("get_Item", new[] { typeof(string) }); - var t = tag as SetTag; - var type = typeof(void); - var retunType = c.GuessType(t.Value); - c.Set(t.Name, retunType); - var mb = c.CreateReutrnMethod(type); - var il = mb.GetILGenerator(); - var labelEnd = il.DefineLabel(); - il.DeclareLocal(retunType); - il.DeclareLocal(typeof(bool)); - - var method = c.CompileTag(t.Value); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Call, method); - il.Emit(OpCodes.Stloc, 0); - //il.Emit(OpCodes.Ldloc_0); - - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Callvirt, getVariableScope); - il.Emit(OpCodes.Ldstr, t.Name); - il.Emit(OpCodes.Ldloc_S, 0); - il.Emit(OpCodes.Callvirt, typeof(IVariableScope).GetGenericMethod(new Type[] { retunType }, "Update", new Type[] { typeof(string), retunType })); - - il.Emit(OpCodes.Ldc_I4_0); - il.Emit(OpCodes.Ceq); - il.Emit(OpCodes.Stloc, 1); - il.Emit(OpCodes.Ldloc, 1); - il.Emit(OpCodes.Brfalse_S, labelEnd); - - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Callvirt, getVariableScope); - il.Emit(OpCodes.Ldstr, t.Name); - il.Emit(OpCodes.Ldloc_S, 0); - il.Emit(OpCodes.Callvirt, typeof(IVariableScope).GetGenericMethod(new Type[] { retunType }, "Set", new Type[] { typeof(string), retunType })); - - - il.MarkLabel(labelEnd); - il.Emit(OpCodes.Ret); - return mb.GetBaseDefinition(); - }; - } - /// - public override Func BuildGuessMethod() - { - return (tag, c) => - { - return typeof(void); - }; - } - /// - public override Func BuildExcuteMethod() - { - return (tag, context) => - { - var t = tag as SetTag; - object value = context.Execute(t.Value); - if (value != null) - { - if (!context.TempData.Update(t.Name, value)) - { - context.TempData.Set(t.Name, value, null); - } - } - return null; - }; - } - } -} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/SetVisitor.cs b/src/JinianNet.JNTemplate/Parsers/SetVisitor.cs new file mode 100644 index 0000000..1f206bf --- /dev/null +++ b/src/JinianNet.JNTemplate/Parsers/SetVisitor.cs @@ -0,0 +1,147 @@ +/******************************************************************************** + Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. + Licensed under the MIT license. See licence.txt file in the project root for full license information. + ********************************************************************************/ +using JinianNet.JNTemplate.CodeCompilation; +using JinianNet.JNTemplate.Dynamic; +using JinianNet.JNTemplate.Nodes; +using System; +using System.Reflection; +using System.Reflection.Emit; + +namespace JinianNet.JNTemplate.Parsers +{ + /// + /// The registrar + /// + public class SetVisitor : TagVisitor, ITagVisitor + { + /// + public ITag Parse(TemplateParser parser, TokenCollection tc) + { + if (tc.Count > 5 + && Utility.IsEqual(tc.First.Text, Const.KEY_SET) + && tc[1].TokenKind == TokenKind.LeftParentheses + && tc[3].Text == "=" + && tc.Last.TokenKind == TokenKind.RightParentheses) + { + var tag = new SetTag(); + tag.Name = tc[2].Text; + + tag.Value = parser.ReadSimple(tc[4, -1]); + return tag; + + } + + if (tc.Count == 2 + && tc.First.TokenKind == TokenKind.TextData + && tc.Last.TokenKind == TokenKind.Arithmetic + && (tc.Last.Text == "++" || tc.Last.Text == "--")) + { + var tag = new SetTag(); + tag.Name = tc.First.Text; + + var c = new ArithmeticTag(); + c.AddChild(new VariableTag() + { + FirstToken = tc.First, + Name = tc.First.Text + }); + c.AddChild(new OperatorTag(new Token(TokenKind.Arithmetic, tc.Last.Text[0].ToString()))); + + //c.AddChild(new TextTag() + //{ + // FirstToken = new Token(TokenKind.Operator, tc.Last.Text[0].ToString()) + //}); + + c.AddChild(new NumberTag() + { + Value = 1, + FirstToken = new Token(TokenKind.Number, "1") + }); + + tag.Value = c; + return tag; + } + + if (tc.Count > 2 + && tc.First.TokenKind == TokenKind.TextData + && tc[1].Text == "=") + { + var tag = new SetTag(); + tag.Name = tc.First.Text; + tag.Value = parser.ReadSimple(tc[2, tc.Count]); + if (tag.Value == null) + return null; + return tag; + } + + return null; + } + /// + public MethodInfo Compile(ITag tag, CompileContext c) + { + var getVariableScope = typeof(TemplateContext).GetPropertyGetMethod("TempData"); + //var getVariableValue = typeof(VariableScope).GetMethod("get_Item", new[] { typeof(string) }); + var t = tag as SetTag; + var type = typeof(void); + var retunType = c.GuessType(t.Value); + c.Set(t.Name, retunType); + var mb = c.CreateReutrnMethod(type); + var il = mb.GetILGenerator(); + var labelEnd = il.DefineLabel(); + il.DeclareLocal(retunType); + il.DeclareLocal(typeof(bool)); + + var method = c.CompileTag(t.Value); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, method); + il.Emit(OpCodes.Stloc, 0); + //il.Emit(OpCodes.Ldloc_0); + + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Callvirt, getVariableScope); + il.Emit(OpCodes.Ldstr, t.Name); + il.Emit(OpCodes.Ldloc_S, 0); + il.Emit(OpCodes.Callvirt, typeof(IVariableScope).GetGenericMethod(new Type[] { retunType }, "Update", new Type[] { typeof(string), retunType })); + + il.Emit(OpCodes.Ldc_I4_0); + il.Emit(OpCodes.Ceq); + il.Emit(OpCodes.Stloc, 1); + il.Emit(OpCodes.Ldloc, 1); + il.Emit(OpCodes.Brfalse_S, labelEnd); + + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Callvirt, getVariableScope); + il.Emit(OpCodes.Ldstr, t.Name); + il.Emit(OpCodes.Ldloc_S, 0); + il.Emit(OpCodes.Callvirt, typeof(IVariableScope).GetGenericMethod(new Type[] { retunType }, "Set", new Type[] { typeof(string), retunType })); + + + il.MarkLabel(labelEnd); + il.Emit(OpCodes.Ret); + return mb.GetBaseDefinition(); + + } + /// + public Type GuessType(ITag tag, CompileContext c) + { + return typeof(void); + } + /// + public object Excute(ITag tag, TemplateContext context) + { + var t = tag as SetTag; + object value = context.Execute(t.Value); + if (value != null) + { + if (!context.TempData.Update(t.Name, value)) + { + context.TempData.Set(t.Name, value, null); + } + } + return null; + } + } +} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/StringRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/StringVisitor.cs similarity index 34% rename from src/JinianNet.JNTemplate/Parsers/StringRegistrar.cs rename to src/JinianNet.JNTemplate/Parsers/StringVisitor.cs index 58b59f9..65bc7b9 100644 --- a/src/JinianNet.JNTemplate/Parsers/StringRegistrar.cs +++ b/src/JinianNet.JNTemplate/Parsers/StringVisitor.cs @@ -13,57 +13,44 @@ namespace JinianNet.JNTemplate.Parsers /// /// The registrar /// - public class StringRegistrar : TagRegistrar, IRegistrar + public class StringVisitor : TagVisitor, ITagVisitor { /// - public override Func BuildParseMethod() + public ITag Parse(TemplateParser parser, TokenCollection tc) { - return (parser, tc) => + if ((tc.Count == 3 || tc.Count == 2) + && tc.First.TokenKind == TokenKind.StringStart + && tc.Last.TokenKind == TokenKind.StringEnd + ) { - if (tc != null - && (tc.Count == 3 || tc.Count == 2) - && tc.First.TokenKind == TokenKind.StringStart - && tc.Last.TokenKind == TokenKind.StringEnd - ) - { - var tag = new StringTag(); - tag.Value = tc.Count == 3 ? tc[1].Text : string.Empty; - return tag; - } - return null; - }; + var tag = new StringTag(); + tag.Value = tc.Count == 3 ? tc[1].Text : string.Empty; + return tag; + } + return null; } /// - public override Func BuildCompileMethod() + public MethodInfo Compile(ITag tag, CompileContext c) { - return (tag, c) => - { - var t = tag as StringTag; - var type = typeof(string); - var mb = c.CreateReutrnMethod(type); - var il = mb.GetILGenerator(); - il.CallTypeTag(t); - il.Emit(OpCodes.Ret); - return mb.GetBaseDefinition(); - }; + var t = tag as StringTag; + var type = typeof(string); + var mb = c.CreateReutrnMethod(type); + var il = mb.GetILGenerator(); + il.CallTypeTag(t); + il.Emit(OpCodes.Ret); + return mb.GetBaseDefinition(); } /// - public override Func BuildGuessMethod() + public Type GuessType(ITag tag, CompileContext c) { - return (tag, c) => - { - return typeof(string); - }; + return typeof(string); } /// - public override Func BuildExcuteMethod() + public object Excute(ITag tag, TemplateContext context) { - return (tag, context) => - { - var t = tag as StringTag; - return t.Value; - }; + var t = tag as StringTag; + return t.Value; } } } \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/TagParser.cs b/src/JinianNet.JNTemplate/Parsers/TagParser.cs index 8d5577d..1a441fb 100644 --- a/src/JinianNet.JNTemplate/Parsers/TagParser.cs +++ b/src/JinianNet.JNTemplate/Parsers/TagParser.cs @@ -29,8 +29,14 @@ namespace JinianNet.JNTemplate.Parsers /// public int Count => delegates.Count; - /// - public ITag Parsing(TemplateParser parser, TokenCollection tc) + /// + /// + /// + /// + /// + /// + /// + public ITag Parsing(TemplateParser parser, TokenCollection tc, Func func) { if (tc == null || tc.Count == 0 @@ -53,6 +59,8 @@ namespace JinianNet.JNTemplate.Parsers { t.LastToken = tc.Last; } + if (func != null && !func(t)) + continue; return t; } } diff --git a/src/JinianNet.JNTemplate/Parsers/TagRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/TagRegistrar.cs index 23f1afb..948ef2f 100644 --- a/src/JinianNet.JNTemplate/Parsers/TagRegistrar.cs +++ b/src/JinianNet.JNTemplate/Parsers/TagRegistrar.cs @@ -21,34 +21,34 @@ namespace JinianNet.JNTemplate.Parsers /// public virtual void Regiser(IHost host) { - var parseMethod = BuildParseMethod(); - if (parseMethod != null) - { - host.RegisterParseFunc(parseMethod, -1); - } - var options = host.HostEnvironment.Options; - if (options.Mode == EngineMode.Compiled) - { - var compileMethod = BuildCompileMethod(); - if (compileMethod != null) - { - host.RegisterCompileFunc(compileMethod); - } + //var parseMethod = BuildParseMethod(); + //if (parseMethod != null) + //{ + // host.RegisterParseFunc(parseMethod, -1); + //} + //var options = host.HostEnvironment.Options; + //if (options.Mode == EngineMode.Compiled) + //{ + // var compileMethod = BuildCompileMethod(); + // if (compileMethod != null) + // { + // host.RegisterCompileFunc(compileMethod); + // } - var guessMethod = BuildGuessMethod(); - if (guessMethod != null) - { - host.RegisterGuessFunc(guessMethod); - } - } - else - { - var excuteMethod = BuildExcuteMethod(); - if (excuteMethod != null) - { - host.RegisterExecuteFunc(excuteMethod); - } - } + // var guessMethod = BuildGuessMethod(); + // if (guessMethod != null) + // { + // host.RegisterGuessFunc(guessMethod); + // } + //} + //else + //{ + // var excuteMethod = BuildExcuteMethod(); + // if (excuteMethod != null) + // { + // host.RegisterExecuteFunc(excuteMethod); + // } + //} } diff --git a/src/JinianNet.JNTemplate/Parsers/TagVisitor.cs b/src/JinianNet.JNTemplate/Parsers/TagVisitor.cs new file mode 100644 index 0000000..103d9cf --- /dev/null +++ b/src/JinianNet.JNTemplate/Parsers/TagVisitor.cs @@ -0,0 +1,29 @@ +/******************************************************************************** + Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. + Licensed under the MIT license. See licence.txt file in the project root for full license information. + ********************************************************************************/ +using JinianNet.JNTemplate.CodeCompilation; +using JinianNet.JNTemplate.Nodes; +using System; +using System.Reflection; + +namespace JinianNet.JNTemplate.Parsers +{ + /// + /// + /// + /// + public abstract class TagVisitor where T : ITag + { + private string name; + /// + /// + /// + public TagVisitor() + { + name = typeof(T).Name; + } + /// + public string Name => name; + } +} diff --git a/src/JinianNet.JNTemplate/Parsers/NullRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/TextVisitor.cs similarity index 43% rename from src/JinianNet.JNTemplate/Parsers/NullRegistrar.cs rename to src/JinianNet.JNTemplate/Parsers/TextVisitor.cs index 06eaa97..9a59b3c 100644 --- a/src/JinianNet.JNTemplate/Parsers/NullRegistrar.cs +++ b/src/JinianNet.JNTemplate/Parsers/TextVisitor.cs @@ -5,59 +5,51 @@ using JinianNet.JNTemplate.CodeCompilation; using JinianNet.JNTemplate.Nodes; using System; +using System.Linq; using System.Reflection; using System.Reflection.Emit; namespace JinianNet.JNTemplate.Parsers { /// - /// The registrar + /// The registrar /// - public class NullRegistrar : TagRegistrar, IRegistrar + public class TextVisitor : ITagVisitor { /// - public override Func BuildParseMethod() + public ITag Parse(TemplateParser parser, TokenCollection tc) { - return (parser, tc) => - { - if (tc != null - && tc.Count == 1 - && tc.First.TokenKind == TokenKind.TextData - && tc.First.Text == "null") - { - return new NullTag(); - } - return null; - }; + return null; } + + /// + public string Name => nameof(TextTag); + /// - public override Func BuildCompileMethod() + public MethodInfo Compile(ITag tag, CompileContext c) { - return (tag, c) => + var t = tag as TextTag; + if (!string.IsNullOrEmpty(t.Text)) { - var type = typeof(object); - var mb = c.CreateReutrnMethod(type); + var type = typeof(string); + var mb = c.CreateReutrnMethod(type); var il = mb.GetILGenerator(); - il.Emit(OpCodes.Ldnull); + il.Emit(OpCodes.Ldstr, t.Text); il.Emit(OpCodes.Ret); return mb.GetBaseDefinition(); - }; + } + return null; } /// - public override Func BuildGuessMethod() + public Type GuessType(ITag tag, CompileContext c) { - return (tag, c) => - { - return typeof(object); - }; + return typeof(string); } /// - public override Func BuildExcuteMethod() + public object Excute(ITag tag, TemplateContext context) { - return (tag, context) => - { - return null; - }; - } + var t = tag as TextTag; + return t.ToString(context.OutMode); + } } } \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/VariableRegistrar.cs b/src/JinianNet.JNTemplate/Parsers/VariableRegistrar.cs deleted file mode 100644 index 4a65d6a..0000000 --- a/src/JinianNet.JNTemplate/Parsers/VariableRegistrar.cs +++ /dev/null @@ -1,260 +0,0 @@ -/******************************************************************************** - Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. - Licensed under the MIT license. See licence.txt file in the project root for full license information. - ********************************************************************************/ -using JinianNet.JNTemplate.CodeCompilation; -using JinianNet.JNTemplate.Dynamic; -using JinianNet.JNTemplate.Exceptions; -using JinianNet.JNTemplate.Nodes; -using System; -using System.Reflection; -using System.Reflection.Emit; - -namespace JinianNet.JNTemplate.Parsers -{ - - /// - /// The registrar - /// - public class VariableRegistrar : TagRegistrar, IRegistrar - { - /// - public override Func BuildParseMethod() - { - return (parser, tc) => - { - if (tc != null - && tc.Count == 1 - && tc.First.TokenKind == TokenKind.TextData) - { - var tag = new VariableTag(); - tag.Name = tc.First.Text; - return tag; - } - return null; - }; - } - /// - public override Func BuildCompileMethod() - { - return (tag, c) => - { - var getVariableScope = typeof(TemplateContext).GetPropertyGetMethod("TempData"); - var getVariableValue = typeof(IVariableScope).GetMethod("get_Item", new[] { typeof(string) }); - var t = tag as VariableTag; - var type = c.GuessType(t); - var mb = c.CreateReutrnMethod(type); - var il = mb.GetILGenerator(); - Label labelEnd = il.DefineLabel(); - Label labelInit = il.DefineLabel(); - il.DeclareLocal(typeof(object)); - il.DeclareLocal(type); - if (t.Parent == null) - { - il.DeclareLocal(typeof(bool)); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Callvirt, getVariableScope); - il.Emit(OpCodes.Ldstr, t.Name); - il.Emit(OpCodes.Callvirt, getVariableValue); - il.Emit(OpCodes.Stloc_0); - il.Emit(OpCodes.Ldloc_0); - il.Emit(OpCodes.Ldnull); - il.Emit(OpCodes.Cgt_Un); - il.Emit(OpCodes.Stloc_2); - il.Emit(OpCodes.Ldloc_2); - il.Emit(OpCodes.Brfalse, labelInit); - - - il.Emit(OpCodes.Ldloc_0); - if (type != typeof(object)) - { - if (type.IsValueType) - { - il.Emit(OpCodes.Unbox_Any, type); - } - else - { - il.Emit(OpCodes.Castclass, type); - } - } - //il.ObjectTo(type); - il.Emit(OpCodes.Stloc_1); - il.Emit(OpCodes.Br, labelEnd); - } - else - { - var parentType = c.GuessType(t.Parent); - - var property = parentType.GetPropertyInfo(t.Name); - if (property == null) - { - var field = parentType.GetFieldInfo(t.Name); - if (field == null) - { - var indexMethod = parentType.GetMethodInfo("get_Item", new Type[] { typeof(string) }); - if (indexMethod == null) - { - throw new CompileException(tag, $"[VariableTag] : {parentType.Name} Cannot find property {t.Name}"); - } - var method = c.CompileTag(t.Parent); - il.DeclareLocal(parentType); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Call, method); - il.Emit(OpCodes.Stloc, 2); - il.LoadVariable(parentType, 2); - il.Emit(OpCodes.Ldstr, t.Name); - il.Call(parentType, indexMethod); - } - else - { - if (!field.IsStatic) - { - var method = c.CompileTag(t.Parent); - il.DeclareLocal(parentType); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Call, method); - il.Emit(OpCodes.Stloc, 2); - il.LoadVariable(parentType, 2); - il.Emit(OpCodes.Ldfld, field); - } - else - { - il.Emit(OpCodes.Ldsfld, field); - } - } - il.Emit(OpCodes.Stloc, 1); - } - else - { - var getMethod = -#if NF40 || NF35 || NF20 - property.GetGetMethod(); -#else - property.GetMethod; -#endif - if (!getMethod.IsStatic) - { - var method = c.CompileTag(t.Parent); - il.DeclareLocal(parentType); - il.Emit(OpCodes.Ldarg_0); - il.Emit(OpCodes.Ldarg_1); - il.Emit(OpCodes.Call, method); - il.Emit(OpCodes.Stloc, 2); - il.LoadVariable(parentType, 2); - } - il.Call(parentType, getMethod); - il.Emit(OpCodes.Stloc, 1); - } - il.Emit(OpCodes.Br, labelEnd); - } - - il.MarkLabel(labelInit); - if (t.Parent == null) - { - if (type.IsClass) - { - il.Emit(OpCodes.Ldnull); - il.Emit(OpCodes.Stloc, 1); - } - else - { - switch (type.Name) - { - case "Boolean": - case "Int16": - case "Int32": - il.Emit(OpCodes.Ldc_I4, 0); - break; - case "Int64": - il.Emit(OpCodes.Ldc_I8, 0L); - break; - case "Single": - il.Emit(OpCodes.Ldc_R4, 0F); - break; - case "Double": - il.Emit(OpCodes.Ldc_R8, 0D); - break; - default: - var defaultMethod = typeof(Utility).GetGenericMethod(new Type[] { type }, "GenerateDefaultValue", Type.EmptyTypes); - il.Emit(OpCodes.Call, defaultMethod); - break; - } - il.Emit(OpCodes.Stloc, 1); - } - } - il.MarkLabel(labelEnd); - il.Emit(OpCodes.Ldloc_1); - il.Emit(OpCodes.Ret); - return mb.GetBaseDefinition(); - }; - } - /// - public override Func BuildGuessMethod() - { - return (tag, c) => - { - var t = tag as VariableTag; - if (t.Parent == null) - { - return c.Data.GetType(t.Name); - } - var parentType = c.GuessType(t.Parent); - if (parentType == typeof(System.Data.DataRow)) - return typeof(object); - var p = parentType.GetPropertyInfo(t.Name); - if (p != null) - { - return p.PropertyType; - } - var f = parentType.GetFieldInfo(t.Name); - if (f != null) - { - return f.FieldType; - } - var m = parentType.GetMethodInfo("get_Item", new Type[] { typeof(string) }); - if (m != null) - { - return m.ReturnType; - } - throw new CompileException(tag, $"[VariableTag]: \"{t.Name}\" is not defined"); - }; - } - - /// - public override Func BuildExcuteMethod() - { - return ((tag, context) => - { - var t = tag as VariableTag; - object baseValue = null; - Type type = null; - if (t.Parent == null) - { - return context.TempData[t.Name]; - } - baseValue = context.Execute(t.Parent); - if (baseValue == null && t.Parent is VariableTag variable) - { - type = context.TempData.GetType(variable.Name); - } - else - { - type = baseValue.GetType(); - } - - if (type == null) - { - throw new CompileException(tag, $"[VariableTag]: \"{t.Name}\" is not defined"); - } - var data = baseValue.CallPropertyOrField(t.Name, type); - if (data == null && baseValue != null) - { - return baseValue.CallIndexValue(t.Name); - } - return data; - }); - } - } -} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Parsers/VariableVisitor.cs b/src/JinianNet.JNTemplate/Parsers/VariableVisitor.cs new file mode 100644 index 0000000..cf279fe --- /dev/null +++ b/src/JinianNet.JNTemplate/Parsers/VariableVisitor.cs @@ -0,0 +1,247 @@ +/******************************************************************************** + Copyright (c) jiniannet (http://www.jiniannet.com). All rights reserved. + Licensed under the MIT license. See licence.txt file in the project root for full license information. + ********************************************************************************/ +using JinianNet.JNTemplate.CodeCompilation; +using JinianNet.JNTemplate.Dynamic; +using JinianNet.JNTemplate.Exceptions; +using JinianNet.JNTemplate.Nodes; +using System; +using System.Reflection; +using System.Reflection.Emit; + +namespace JinianNet.JNTemplate.Parsers +{ + + /// + /// The registrar + /// + public class VariableVisitor : TagVisitor, ITagVisitor + { + /// + public ITag Parse(TemplateParser parser, TokenCollection tc) + { + if (tc.Count == 1 + && tc.First.TokenKind == TokenKind.TextData) + { + var tag = new VariableTag(); + tag.Name = tc.First.Text; + return tag; + } + return null; + } + /// + public MethodInfo Compile(ITag tag, CompileContext c) + { + var getVariableScope = typeof(TemplateContext).GetPropertyGetMethod("TempData"); + var getVariableValue = typeof(IVariableScope).GetMethod("get_Item", new[] { typeof(string) }); + var t = tag as VariableTag; + var type = c.GuessType(t); + var mb = c.CreateReutrnMethod(type); + var il = mb.GetILGenerator(); + Label labelEnd = il.DefineLabel(); + Label labelInit = il.DefineLabel(); + il.DeclareLocal(typeof(object)); + il.DeclareLocal(type); + if (t.Parent == null) + { + il.DeclareLocal(typeof(bool)); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Callvirt, getVariableScope); + il.Emit(OpCodes.Ldstr, t.Name); + il.Emit(OpCodes.Callvirt, getVariableValue); + il.Emit(OpCodes.Stloc_0); + il.Emit(OpCodes.Ldloc_0); + il.Emit(OpCodes.Ldnull); + il.Emit(OpCodes.Cgt_Un); + il.Emit(OpCodes.Stloc_2); + il.Emit(OpCodes.Ldloc_2); + il.Emit(OpCodes.Brfalse, labelInit); + + + il.Emit(OpCodes.Ldloc_0); + if (type != typeof(object)) + { + if (type.IsValueType) + { + il.Emit(OpCodes.Unbox_Any, type); + } + else + { + il.Emit(OpCodes.Castclass, type); + } + } + //il.ObjectTo(type); + il.Emit(OpCodes.Stloc_1); + il.Emit(OpCodes.Br, labelEnd); + } + else + { + var parentType = c.GuessType(t.Parent); + + var property = parentType.GetPropertyInfo(t.Name); + if (property == null) + { + var field = parentType.GetFieldInfo(t.Name); + if (field == null) + { + var indexMethod = parentType.GetMethodInfo("get_Item", new Type[] { typeof(string) }); + if (indexMethod == null) + { + throw new CompileException(tag, $"[VariableTag] : {parentType.Name} Cannot find property {t.Name}"); + } + var method = c.CompileTag(t.Parent); + il.DeclareLocal(parentType); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, method); + il.Emit(OpCodes.Stloc, 2); + il.LoadVariable(parentType, 2); + il.Emit(OpCodes.Ldstr, t.Name); + il.Call(parentType, indexMethod); + } + else + { + if (!field.IsStatic) + { + var method = c.CompileTag(t.Parent); + il.DeclareLocal(parentType); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, method); + il.Emit(OpCodes.Stloc, 2); + il.LoadVariable(parentType, 2); + il.Emit(OpCodes.Ldfld, field); + } + else + { + il.Emit(OpCodes.Ldsfld, field); + } + } + il.Emit(OpCodes.Stloc, 1); + } + else + { + var getMethod = +#if NF40 || NF35 || NF20 + property.GetGetMethod(); +#else + property.GetMethod; +#endif + if (!getMethod.IsStatic) + { + var method = c.CompileTag(t.Parent); + il.DeclareLocal(parentType); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, method); + il.Emit(OpCodes.Stloc, 2); + il.LoadVariable(parentType, 2); + } + il.Call(parentType, getMethod); + il.Emit(OpCodes.Stloc, 1); + } + il.Emit(OpCodes.Br, labelEnd); + } + + il.MarkLabel(labelInit); + if (t.Parent == null) + { + if (type.IsClass) + { + il.Emit(OpCodes.Ldnull); + il.Emit(OpCodes.Stloc, 1); + } + else + { + switch (type.Name) + { + case "Boolean": + case "Int16": + case "Int32": + il.Emit(OpCodes.Ldc_I4, 0); + break; + case "Int64": + il.Emit(OpCodes.Ldc_I8, 0L); + break; + case "Single": + il.Emit(OpCodes.Ldc_R4, 0F); + break; + case "Double": + il.Emit(OpCodes.Ldc_R8, 0D); + break; + default: + var defaultMethod = typeof(Utility).GetGenericMethod(new Type[] { type }, "GenerateDefaultValue", Type.EmptyTypes); + il.Emit(OpCodes.Call, defaultMethod); + break; + } + il.Emit(OpCodes.Stloc, 1); + } + } + il.MarkLabel(labelEnd); + il.Emit(OpCodes.Ldloc_1); + il.Emit(OpCodes.Ret); + return mb.GetBaseDefinition(); + } + /// + public Type GuessType(ITag tag, CompileContext c) + { + var t = tag as VariableTag; + if (t.Parent == null) + { + return c.Data.GetType(t.Name); + } + var parentType = c.GuessType(t.Parent); + if (parentType == typeof(System.Data.DataRow)) + return typeof(object); + var p = parentType.GetPropertyInfo(t.Name); + if (p != null) + { + return p.PropertyType; + } + var f = parentType.GetFieldInfo(t.Name); + if (f != null) + { + return f.FieldType; + } + var m = parentType.GetMethodInfo("get_Item", new Type[] { typeof(string) }); + if (m != null) + { + return m.ReturnType; + } + throw new CompileException(tag, $"[VariableTag]: \"{t.Name}\" is not defined"); + } + + /// + public object Excute(ITag tag, TemplateContext context) + { + var t = tag as VariableTag; + object baseValue = null; + Type type = null; + if (t.Parent == null) + { + return context.TempData[t.Name]; + } + baseValue = context.Execute(t.Parent); + if (baseValue == null && t.Parent is VariableTag variable) + { + type = context.TempData.GetType(variable.Name); + } + else + { + type = baseValue.GetType(); + } + + if (type == null) + { + throw new CompileException(tag, $"[VariableTag]: \"{t.Name}\" is not defined"); + } + var data = baseValue.CallPropertyOrField(t.Name, type); + if (data == null && baseValue != null) + { + return baseValue.CallIndexValue(t.Name); + } + return data; + } + } +} \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/Runtime/IOptions.cs b/src/JinianNet.JNTemplate/Runtime/IOptions.cs index a4e76c2..61b3920 100644 --- a/src/JinianNet.JNTemplate/Runtime/IOptions.cs +++ b/src/JinianNet.JNTemplate/Runtime/IOptions.cs @@ -15,6 +15,10 @@ namespace JinianNet.JNTemplate.Runtime /// public interface IOptions { + /// + /// + /// + string ModelName { get; set; } /// /// Gets or sets whether disablee logogram . /// @@ -66,10 +70,11 @@ namespace JinianNet.JNTemplate.Runtime /// bool ThrowExceptions { get; set; } - /// - /// Gets or sets the detect patterns. - /// - TypeDetect TypeDetectPattern { get; set; } + // /// + // /// Gets or sets the detect patterns. + // /// + // TypeDetect TypeDetectPattern { get; set; } + /// /// Gets or sets the output mode. /// diff --git a/src/JinianNet.JNTemplate/Runtime/RuntimeOptions.cs b/src/JinianNet.JNTemplate/Runtime/RuntimeOptions.cs index 9e93ad8..6acf594 100644 --- a/src/JinianNet.JNTemplate/Runtime/RuntimeOptions.cs +++ b/src/JinianNet.JNTemplate/Runtime/RuntimeOptions.cs @@ -29,14 +29,17 @@ namespace JinianNet.JNTemplate.Runtime this.TagPrefix = "${"; this.TagSuffix = "}"; this.ThrowExceptions = true; - this.TypeDetectPattern = TypeDetect.Standard; + //this.TypeDetectPattern = TypeDetect.Standard; this.OutMode = OutMode.None; this.Mode = EngineMode.Compiled; this.DisableeLogogram = false; this.EnableCache = true; this.ResourceDirectories = new List(); + this.ModelName = "Model"; } /// + public string ModelName { get; set; } + /// public bool DisableeLogogram { get; set; } /// public string TagPrefix { get; set; } @@ -67,8 +70,8 @@ namespace JinianNet.JNTemplate.Runtime /// public bool ThrowExceptions { get; set; } - /// - public TypeDetect TypeDetectPattern { get; set; } + // /// + //public TypeDetect TypeDetectPattern { get; set; } /// public OutMode OutMode { get; set; } /// diff --git a/src/JinianNet.JNTemplate/TagResolver.cs b/src/JinianNet.JNTemplate/TagResolver.cs new file mode 100644 index 0000000..a938a22 --- /dev/null +++ b/src/JinianNet.JNTemplate/TagResolver.cs @@ -0,0 +1,422 @@ +using JinianNet.JNTemplate.CodeCompilation; +using JinianNet.JNTemplate.Exceptions; +using JinianNet.JNTemplate.Nodes; +using JinianNet.JNTemplate.Parsers; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace JinianNet.JNTemplate +{ + /// + /// + /// + public class VisitorEntry + { + /// + /// + /// + public ITagVisitor Visitor { get; set; } + /// + /// + /// + public int Index { get; set; } + /// + /// + /// + public string Next { get; set; } + } + /// + /// + /// + public class Resolver : IList + { + private Dictionary refs; + private string firstKey; + private string lastKey; + + /// + public int Count => refs.Count; + /// + public bool IsReadOnly => false; + + /// + public ITagVisitor this[int index] + { + get + { + var key = firstKey; + foreach (var value in refs.Values) + if (value.Index == index) + return value.Visitor; + return null; + } + set + { + var old = this[index]; + if (old == null) + Register(index, value); + else + { + throw new NotImplementedException(); + } + + } + } + /// + /// + /// + public Resolver() + { + // Visitor + //Internal + //CompilerResults + refs = new Dictionary(); + } + /// + /// + /// + /// + /// + public VisitorEntry this[string key] + { + get + { + if (!string.IsNullOrEmpty(key) && refs.TryGetValue(key, out var entry)) + return entry; + return null; + } + } + /// + /// + /// + /// + /// + /// + public ITag Parsing(TemplateParser parser, TokenCollection tc) + { + if (tc == null + || tc.Count == 0 + || parser == null) + { + return null; + } + ITag t; + var f = refs[firstKey]; + while (f != null) + { + ITagVisitor resolver = f.Visitor; + t = resolver.Parse(parser, tc); + if (t != null) + { + t.FirstToken = tc.First; + + if (t.Children.Count == 0 || + (t.LastToken = t.Children[t.Children.Count - 1].LastToken ?? t.Children[t.Children.Count - 1].FirstToken) == null + || tc.Last.CompareTo(t.LastToken) > 0) + { + t.LastToken = tc.Last; + } + return t; + } + f = refs[f.Next]; + } + return null; + } + + + /// + /// + /// + /// + /// + /// + public object Excute(ITag tag, TemplateContext context) + { + if (tag == null) + { + return null; + } + return Excute(tag.GetType().Name, tag, context); + } + + /// + /// + /// + /// + /// + /// + /// + public object Excute(string name, ITag tag, TemplateContext context) + { + var resolver = FindEntry(name); + if (resolver != null) + { + return resolver.Excute(tag, context); + } + throw new ParseException($"The tag \"{name}\" is not supported ."); + } + + /// + /// + /// + /// + /// + /// + /// + public MethodInfo Compile(T tag, CompileContext context) + where T : ITag + { + return Compile(typeof(T).Name, tag, context); + } + + /// + /// + /// + /// + /// + /// + public MethodInfo Compile(ITag tag, CompileContext context) + { + return Compile(tag.GetType().Name, tag, context); + } + + /// + /// + /// + /// + /// + /// + /// + public MethodInfo Compile(string name, ITag tag, CompileContext context) + { + var resolver = FindEntry(name); + if (resolver != null) + { + return resolver.Compile(tag, context); + } + throw new CompileException($"The tag \"{name}\" is not supported ."); + } + + + /// + /// Gets the with the specified tag. + /// + /// The tag of the type to get. + /// The . + /// + public Type GetType(ITag tag, CompileContext ctx) + { + return GuessType(tag.GetType().Name, tag, ctx); + } + + /// + /// Gets the with the specified tag. + /// + /// ITag + /// The tag of the type to get. + /// The . + /// + public Type GuessType(T tag, CompileContext context) + where T : ITag + { + return GuessType(typeof(T).Name, tag, context); + } + + /// + /// Gets the with the specified tag. + /// + /// The tag of the type to get. + /// The . + /// + public Type GuessType(ITag tag, CompileContext ctx) + { + return GuessType(tag.GetType().Name, tag, ctx); + } + + /// + /// Gets the with the specified tag. + /// + /// The tag name of the type to get. + /// The tag of the type to get. + /// The . + /// The type with the specified tag, if found; otherwise, null. + public Type GuessType(string name, ITag tag, CompileContext ctx) + { + var resolver = FindEntry(name); + if (resolver != null) + { + var type = resolver.GuessType(tag, ctx); + if (type != null) + { + return type; + } + } + throw new CompileException(tag, $"[{name}]:\"{tag.ToSource()}\" is not defined!"); + } + + /// + /// Register a guess mehtod for the tag. + /// + /// The guess method. + public bool Register(ITagVisitor visitor) + { + VisitorEntry entry; + if (!refs.TryGetValue(visitor.Name, out entry)) + { + entry = new VisitorEntry(); + if (refs.Count == 0) + { + firstKey = visitor.Name; + } + var last = this[lastKey]; + if (last != null) + { + last.Next = visitor.Name; + } + lastKey = visitor.Name; + entry.Index = refs.Count; + } + entry.Visitor = visitor; + + refs[visitor.Name] = entry; + + return true; + } + + /// + /// Register a guess mehtod for the tag. + /// + /// + /// The guess method. + public bool Register(int index, ITagVisitor visitor) + { + if (refs.Count == 0) + return Register(visitor); + + VisitorEntry entry; + if (refs.TryGetValue(visitor.Name, out entry)) + return false; + + entry = new VisitorEntry(); + entry.Index = index; + entry.Visitor = visitor; + var start = firstKey; + var next = this[start]; + while (next != null) + { + if (next.Index == index - 1) + { + var nextKey = next.Next; + next.Next = entry.Visitor.Name; + next = this[nextKey]; + //next.Index++; + continue; + } + else if (next.Index == index) + { + entry.Next = next.Visitor.Name; + next.Index++; + refs[visitor.Name] = entry; + } + else if (next.Index > index) + { + next.Index++; + } + lastKey = next.Visitor.Name; + next = this[next.Next]; + + } + if (index == 0) + firstKey = visitor.Name; + return true; + } + + private ITagVisitor FindEntry(string key) + { + var entry = this[key]; + if (entry != null) + { + return entry.Visitor; + } + return null; + } + + /// + public int IndexOf(ITagVisitor item) + { + var key = firstKey; + while (!string.IsNullOrEmpty(key)) + { + var value = refs[key]; + if (value.Visitor.Name == item.Name) + return value.Index; + key = value.Next; + } + return -1; + } + + /// + public void Insert(int index, ITagVisitor item) + { + Register(index, item); + } + + /// + public void RemoveAt(int index) + { + throw new NotImplementedException(); + } + + /// + public void Add(ITagVisitor item) + { + Register(item); + } + + /// + public void Clear() + { + refs.Clear(); + } + + /// + public bool Contains(ITagVisitor item) + { + return refs.ContainsKey(item.Name); + } + + /// + public void CopyTo(ITagVisitor[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + /// + public bool Remove(ITagVisitor item) + { + throw new NotImplementedException(); + } + + /// + public IEnumerator GetEnumerator() + { + var key = firstKey; + while (!string.IsNullOrEmpty(key)) + { + var value = refs[key]; + key = value.Next; + yield return value.Visitor; + } + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/src/JinianNet.JNTemplate/TemplateContext.cs b/src/JinianNet.JNTemplate/TemplateContext.cs index e0af418..7714c26 100644 --- a/src/JinianNet.JNTemplate/TemplateContext.cs +++ b/src/JinianNet.JNTemplate/TemplateContext.cs @@ -33,11 +33,6 @@ namespace JinianNet.JNTemplate this.AllErrors = new List(); } - /// - /// Gets the - /// - public ExecutorBuilder ExecutorBuilder => Environment.ExecutorBuilder; - /// /// Enable or Disenable the cache. /// diff --git a/src/JinianNet.JNTemplate/TemplateContextExtensions.cs b/src/JinianNet.JNTemplate/TemplateContextExtensions.cs index ae109ac..2c76646 100644 --- a/src/JinianNet.JNTemplate/TemplateContextExtensions.cs +++ b/src/JinianNet.JNTemplate/TemplateContextExtensions.cs @@ -36,6 +36,8 @@ namespace JinianNet.JNTemplate //paths[0] = ctx.CurrentPath; //ctx.Options.ResourceDirectories.CopyTo(paths, 1); //return paths; + if (ctx.Environment.Options.ResourceDirectories?.Count == 0) + return new string[] { System.IO.Directory.GetCurrentDirectory() }; return ctx.Environment.Options.ResourceDirectories.ToArray(); } @@ -174,12 +176,12 @@ namespace JinianNet.JNTemplate /// /// /// - public static ITag[] Lexer(this Context context, string text) + public static TagCollection Lexer(this Context context, string text) { if (string.IsNullOrEmpty(text)) { - return new ITag[0]; + return new TagCollection(); } var lexer = CreateTemplateLexer(context, text); @@ -200,7 +202,7 @@ namespace JinianNet.JNTemplate /// An TemplateParser. public static TemplateParser CreateTemplateParser(this Context ctx, Token[] ts) { - return new TemplateParser(ctx.Environment.Parser, ts); + return new TemplateParser(ctx.Environment.Resolver, ts); } /// @@ -285,60 +287,53 @@ namespace JinianNet.JNTemplate /// /// See the . /// The tags collection. - public static void Render(this TemplateContext ctx, System.IO.TextWriter writer, ITag[] collection) + public static void Render(this TemplateContext ctx, System.IO.TextWriter writer, TagCollection collection) { if (writer == null) { throw new ArgumentNullException("\"writer\" cannot be null."); } + if (collection == null || collection.Count <= 0) + return; - if (collection != null && collection.Length > 0) + for (int i = 0; i < collection.Count; i++) { - for (int i = 0; i < collection.Length; i++) + try { - try - { - var tagResult = Execute(ctx, collection[i]); - if (tagResult != null) - { + var tagResult = Execute(ctx, collection[i]); + if (tagResult == null) + continue; #if !NF35 && !NF20 - if (tagResult is Task task) - { - var type = tagResult.GetType(); - if (type.IsGenericType) - { - var taskResult = typeof(Utility).CallGenericMethod(null, "ExcuteTaskAsync", type.GetGenericArguments(), tagResult); -#if NF40 - writer.Write((string)taskResult); -#else - writer.Write(((Task)taskResult).GetAwaiter().GetResult()); -#endif - } - else - { + if (tagResult is Task task) + { #if NF40 - task.Wait(); + task.Wait(); #else - task.ConfigureAwait(false).GetAwaiter(); -#endif - } - continue; - } + task.ConfigureAwait(false).GetAwaiter(); #endif - writer.Write(tagResult.ToString()); + var pi = task.GetType().GetPropertyInfo("Result"); + if (pi != null) + { + var value = pi.GetValue(task, null); + writer.Write(value?.ToString()); } + continue; } - catch (TemplateException e) - { - ThrowException(ctx, e, collection[i], writer); - } - catch (System.Exception e) - { - var baseException = e.GetBaseException(); - ThrowException(ctx, new ParseException(collection[i], baseException), collection[i], writer); - } +#endif + writer.Write(tagResult.ToString()); + + } + catch (TemplateException e) + { + ThrowException(ctx, e, collection[i], writer); + } + catch (System.Exception e) + { + var baseException = e.GetBaseException(); + ThrowException(ctx, new ParseException(collection[i], baseException), collection[i], writer); } } + } /// @@ -366,49 +361,47 @@ namespace JinianNet.JNTemplate /// See the . /// The tags collection. /// See the . - public static async Task RenderAsync(this TemplateContext ctx, System.IO.TextWriter writer, ITag[] collection, CancellationToken cancellationToken = default) + public static async Task RenderAsync(this TemplateContext ctx, System.IO.TextWriter writer, TagCollection collection, CancellationToken cancellationToken = default) { if (writer == null) { throw new ArgumentNullException("\"writer\" cannot be null."); } - - if (collection != null && collection.Length > 0) + if (collection == null || collection.Count <= 0) + return; + for (int i = 0; i < collection.Count; i++) { - for (int i = 0; i < collection.Length; i++) + try { - try + var tagResult = Execute(ctx, collection[i]); + if (tagResult != null) { - var tagResult = Execute(ctx, collection[i]); - if (tagResult != null) + if (tagResult is Task task) { - if (tagResult is Task task) + var type = tagResult.GetType(); + if (type.IsGenericType) { - var type = tagResult.GetType(); - if (type.IsGenericType) - { - var taskResult = (Task)typeof(Utility).CallGenericMethod(null, "ExcuteTaskAsync", type.GetGenericArguments(), tagResult); - var taskValue = await taskResult; - await writer.WriteAsync(taskValue); - } - else - { - await task; - } - continue; + var taskResult = (Task)typeof(Utility).CallGenericMethod(null, "ExcuteTaskAsync", type.GetGenericArguments(), tagResult); + var taskValue = await taskResult; + await writer.WriteAsync(taskValue); } - await writer.WriteAsync(tagResult.ToString()); + else + { + await task; + } + continue; } + await writer.WriteAsync(OutputFormat(ctx, tagResult)); } - catch (TemplateException e) - { - await ThrowExceptionAsync(ctx, e, collection[i], writer); - } - catch (System.Exception e) - { - var baseException = e.GetBaseException(); - await ThrowExceptionAsync(ctx, new ParseException(collection[i], baseException), collection[i], writer); - } + } + catch (TemplateException e) + { + await ThrowExceptionAsync(ctx, e, collection[i], writer); + } + catch (System.Exception e) + { + var baseException = e.GetBaseException(); + await ThrowExceptionAsync(ctx, new ParseException(collection[i], baseException), collection[i], writer); } } } @@ -438,8 +431,7 @@ namespace JinianNet.JNTemplate /// public static object Execute(this TemplateContext ctx, ITag tag) { - var func = ctx.ExecutorBuilder.Build(tag); - return func(tag, ctx); + return ctx.Resolver.Excute(tag,ctx); } @@ -472,12 +464,27 @@ namespace JinianNet.JNTemplate { throw new ArgumentNullException(nameof(key)); } - var type = ObjectBuilder.GetOrGenerateType(anonymousType); - if (value != null) + if (value == null) + return; + if (value is Array arr) + { + var eType = anonymousType.GetElementType(); + var type = ObjectBuilder.GetOrGenerateType(eType); + var newArr = Array.CreateInstance(type, arr.Length); + for (var i = 0; i < arr.Length; i++) + { + var item = ObjectBuilder.FromAnonymousObject(arr.GetValue(i), type); + newArr.SetValue(item, i); + } + c.TempData.Set(key, newArr, newArr.GetType()); + + } + else { + var type = ObjectBuilder.GetOrGenerateType(anonymousType); value = ObjectBuilder.FromAnonymousObject(value, type); + c.TempData.Set(key, value, type); } - c.TempData.Set(key, value, type); } @@ -497,11 +504,14 @@ namespace JinianNet.JNTemplate if (ctx.Mode == EngineMode.Compiled) { type = type ?? value?.GetType(); - if (type != null && !type.IsVisible) + var isArray = type?.IsArray ?? false; + Type elementType = isArray ? type.GetElementType() : type; + + if (elementType != null && !elementType.IsVisible) { - if (!type.Name.Contains("AnonymousType")) + if (!elementType.Name.Contains("AnonymousType")) { - throw new ArgumentException($"The type \"{type.FullName}\" is not accessible"); + throw new ArgumentException($"The type \"{elementType.FullName}\" is not accessible"); } TemplateContextExtensions.SetAnonymousObject(ctx, key, value, type); } @@ -523,6 +533,55 @@ namespace JinianNet.JNTemplate } } + /// + /// + /// + /// + /// + /// + public static bool MustFormat(this TemplateContext ctx, object value) + { + if (value == null) + return false; + var type = value.GetType(); + return MustFormat(ctx, type); + } + + /// + /// + /// + /// + /// + /// + public static bool MustFormat(this TemplateContext ctx, Type type) + { + foreach (var f in ctx.Environment.OutputFormatters) + { + if (f.CanWriteType(type)) + return true; + } + return false; + } + + /// + /// + /// + /// + /// + /// + public static string OutputFormat(this Context ctx, object value) + { + if (value == null) + return null; + var type = value.GetType(); + foreach (var f in ctx.Environment.OutputFormatters) + { + if (f.CanWriteType(type)) + return f.Format(value); + } + return value.ToString(); + } + /// /// /// @@ -532,8 +591,7 @@ namespace JinianNet.JNTemplate /// private static object Execute(this TemplateContext ctx, string name, ITag tag) { - var func = ctx.ExecutorBuilder.Build(name); - return func(tag, ctx); + return ctx.Resolver.Excute(tag, ctx); } } } diff --git a/src/JinianNet.JNTemplate/TemplateParser.cs b/src/JinianNet.JNTemplate/TemplateParser.cs index e08e8de..fff72f4 100644 --- a/src/JinianNet.JNTemplate/TemplateParser.cs +++ b/src/JinianNet.JNTemplate/TemplateParser.cs @@ -13,27 +13,27 @@ namespace JinianNet.JNTemplate /// /// Provides methods for parsing template strings. /// - public class TemplateParser : Executor, IEnumerator + public class TemplateParser : Executor, IEnumerator { private ITag tag; private Token[] tokens; private int index; private TagCollection tags; - private TagParser tagParser; + private Resolver resolver; /// /// Initializes a new instance of the class /// - /// The . + /// The . /// The collection of tokens. - public TemplateParser(TagParser p, Token[] ts) + public TemplateParser(Resolver r, Token[] ts) : base() { if (ts == null) { throw new ArgumentNullException("\"ts\" cannot be null."); } - this.tagParser = p; + this.resolver = r; this.tokens = ts; Reset(); } @@ -78,14 +78,14 @@ namespace JinianNet.JNTemplate TokenCollection tc = new TokenCollection(); if (t1 == null) { - return null; + return null; } do { this.index++; t2.Next = GetToken(); t2 = t2.Next; - if(t2 == null) + if (t2 == null) { break; } @@ -141,20 +141,42 @@ namespace JinianNet.JNTemplate } return t; } + /// + /// Reads the next tag from the tokens. + /// + /// The collection of tokens. + /// + public ITag ReadSimple(TokenCollection tc) + { + return Read(tc, m => m.IsSimple); + } + /// /// Reads the next tag from the tokens. /// /// The collection of tokens. /// public ITag Read(TokenCollection tc) + { + return Read(tc, null); + } + /// + /// Reads the next tag from the tokens. + /// + /// The collection of tokens. + /// + /// + public ITag Read(TokenCollection tc, Func func) { if (tc == null || tc.Count == 0) { throw new ParseException("Invalid TokenCollection!"); } - return tagParser.Parsing(this, tc.TrimParentheses()); + var tag = resolver.Parsing(this, tc.TrimParentheses());//func + if (func != null && !func(tag)) + return null; + return tag; } - private bool IsTagEnd() { return IsTagEnd(GetToken()); @@ -203,7 +225,7 @@ namespace JinianNet.JNTemplate } /// - public override ITag[] Execute() + public override TagCollection Execute() { if (tags == null) { @@ -214,7 +236,7 @@ namespace JinianNet.JNTemplate tags.Add(Current); } } - return tags.ToArray(); + return tags; } } } \ No newline at end of file diff --git a/src/JinianNet.JNTemplate/TemplatingEngine.cs b/src/JinianNet.JNTemplate/TemplatingEngine.cs index ceba837..988ca23 100644 --- a/src/JinianNet.JNTemplate/TemplatingEngine.cs +++ b/src/JinianNet.JNTemplate/TemplatingEngine.cs @@ -13,6 +13,7 @@ using JinianNet.JNTemplate.Caching; using JinianNet.JNTemplate.Exceptions; using JinianNet.JNTemplate.Nodes; using JinianNet.JNTemplate.Runtime; +using JinianNet.JNTemplate.Parsers; #if !NF35 && !NF20 using System.Threading.Tasks; #endif @@ -25,39 +26,40 @@ namespace JinianNet.JNTemplate { #region Registrar //private static Lazy< IRegistrar[]> LazyRegistrars = new Lazy(()=> LoadDefaultRegistrar(),true); - private static IRegistrar[] LoadDefaultRegistrar() - { - return new IRegistrar[] { - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar(), - LoadRegistrar() + private static ITagVisitor[] LoadDefaultVisitor() + { + return new ITagVisitor[] { + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor(), + LoadVisitor() }; } - private static IRegistrar LoadRegistrar() - where T : IRegistrar, new() + private static ITagVisitor LoadVisitor() + where T : ITagVisitor, new() { return new T(); } @@ -102,7 +104,7 @@ namespace JinianNet.JNTemplate HostEnvironment.Options.TagFlag = option.TagFlag; HostEnvironment.Options.Encoding = option.Encoding; HostEnvironment.Options.ThrowExceptions = option.ThrowExceptions; - HostEnvironment.Options.TypeDetectPattern = option.TypeDetectPattern; + //HostEnvironment.Options.TypeDetectPattern = option.TypeDetectPattern; HostEnvironment.Options.OutMode = option.OutMode; if (option.ResourceDirectories?.Count > 0) { @@ -124,10 +126,10 @@ namespace JinianNet.JNTemplate } var mode = option.EnableCompile ? EngineMode.Compiled : EngineMode.Interpreted; - if (HostEnvironment.Options.TypeDetectPattern == TypeDetect.None && mode == EngineMode.Compiled) - { - HostEnvironment.Options.TypeDetectPattern = TypeDetect.Standard; - } + //if (HostEnvironment.Options.TypeDetectPattern == TypeDetect.None && mode == EngineMode.Compiled) + //{ + // HostEnvironment.Options.TypeDetectPattern = TypeDetect.Standard; + //} if (mode != HostEnvironment.Options.Mode) { @@ -225,11 +227,14 @@ namespace JinianNet.JNTemplate if (t.IsNotPublic && t.Name.Contains("Anonymous")) return ParseAnonymous(name, reader, t, data); - string key; - if (!t.IsArray && !t.IsEnum) - key = t.Name; - else - key = "Model"; + string key = HostEnvironment.Options.ModelName; + if (string.IsNullOrEmpty(key)) + { + if (!t.IsArray && !t.IsEnum) + key = t.Name; + else + key = "Model"; + } return Parse(name, reader, ctx => ctx.Set(key, data, typeof(T))); } private string ParseAnonymous(string name, IReader reader, Type type, object data) @@ -452,10 +457,10 @@ namespace JinianNet.JNTemplate lock (this) { Reset(); - var list = LoadDefaultRegistrar();// LazyRegistrars.Value; + var list = LoadDefaultVisitor();// LazyRegistrars.Value; for (var i = 0; i < list.Length; i++) { - list[i]?.Regiser(this); + HostEnvironment.Resolver.Register(list[i]); } } } @@ -497,56 +502,41 @@ namespace JinianNet.JNTemplate //HostEnvironment.Cache?.Clear(); } + /// + [Obsolete] public void Register(Func parseMethod, Func compileMethod, Func guessMethod, int index = 0) where T : ITag { - HostEnvironment.Parser.Register(parseMethod, index); - HostEnvironment.Builder?.Register(compileMethod); - HostEnvironment.Guesser?.Register(guessMethod); - } - - /// - public void RegisterParseFunc(Func func, - int index = 0) - { - HostEnvironment.Parser.Register(func, index); - } - - /// - public void RegisterCompileFunc(Func func) - where T : ITag - { - HostEnvironment.Builder?.Register(func); - } - - /// - public void RegisterGuessFunc(Func func) - where T : ITag - { - HostEnvironment.Guesser?.Register(func); + var visitor = new DynamicVisitor(typeof(T), parseMethod, compileMethod, guessMethod); + HostEnvironment.Resolver.Register(index, visitor); } /// + [Obsolete] public void RegisterExecuteFunc(Func func) where T : ITag { - HostEnvironment.ExecutorBuilder?.Register(func); + var visitor = new DynamicVisitor(typeof(T), func); + HostEnvironment.Resolver.Register(visitor); } /// public void Reset() { - HostEnvironment.ExecutorBuilder.Clear(); - HostEnvironment.Guesser.Clear(); - HostEnvironment.Builder.Clear(); - HostEnvironment.Parser.Clear(); Clean(); } - + /// + /// Register an new excute method. + /// + /// + public void Register(ITagVisitor visitor) + { + HostEnvironment.Resolver.Register(0, visitor); + } #endregion } -- Gitee