master
Harald Wolff 2019-02-14 16:44:33 +01:00
parent 09be9fb80f
commit fada8eea23
5 changed files with 146 additions and 41 deletions

View File

@ -3,6 +3,8 @@ using System.Reflection;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
namespace ln.templates namespace ln.templates
{ {
delegate bool ConditionDelegate(char ch); delegate bool ConditionDelegate(char ch);
@ -34,54 +36,99 @@ namespace ln.templates
private void Compile() private void Compile()
{ {
StringReader reader = new StringReader(Source); try
List<Token> tokens = new List<Token>(); {
while (reader.Peek() != -1) StringReader reader = new StringReader(Source);
ReadToken(reader, tokens); List<Token> tokens = new List<Token>();
TopEval = BuildEval(tokens); while (reader.Peek() != -1)
ReadToken(reader, tokens);
Queue<Token> qtokens = new Queue<Token>(tokens);
TopEval = BuildExpression(qtokens);
} catch (Exception e)
{
throw new Exception(String.Format("Failed to compile expression: {0}", Source), e);
}
} }
private Eval BuildEval(List<Token> tokens)
private Eval BuildName(Token nameToken,Eval parent,Queue<Token> tokens)
{ {
Eval currentEval = null; if ((tokens.Count > 0) && (tokens.Peek().TokenType == TokenType.OPENLIST))
{
tokens.Dequeue();
List<Eval> pl = new List<Eval>();
while (tokens.Peek().TokenType != TokenType.ENDLIST)
{
pl.Add(BuildExpression(tokens));
if (tokens.Peek().TokenType == TokenType.ENDLIST)
break;
if (tokens.Peek().TokenType != TokenType.COMMA)
throw new FormatException("Expected ',' between parameters to call statement");
tokens.Dequeue();
}
tokens.Dequeue();
return new MethodCall(parent, nameToken.Value, pl.ToArray());
}
else
{
return new Name(parent, nameToken.Value);
}
}
private Eval BuildPath(Queue<Token> tokens)
{
Eval currentEval = BuildName(tokens.Dequeue(), null, tokens);
while (tokens.Count > 0) while (tokens.Count > 0)
{ {
Token token = tokens[0]; Token token = tokens.Peek();
tokens.RemoveAt(0);
switch (token.TokenType) switch (token.TokenType)
{ {
case TokenType.DOT: case TokenType.DOT:
token = tokens[0]; tokens.Dequeue();
tokens.RemoveAt(0); currentEval = BuildName(tokens.Dequeue(), currentEval, tokens);
if (token.TokenType != TokenType.NAME)
throw new ArgumentException("missing fieldname after .");
currentEval = new Name(currentEval, token.Value);
break; break;
case TokenType.NAME: case TokenType.OPENINDEXER:
currentEval = new Name(currentEval, token.Value); tokens.Dequeue();
break; Eval index = BuildExpression(tokens);
case TokenType.NUMBER: token = tokens.Dequeue();
currentEval = new Number(token.Value); if (token.TokenType != TokenType.ENDINDEXER)
break; throw new FormatException("Expected ']'");
case TokenType.STRING: currentEval = new Indexer(currentEval, index);
currentEval = new CString(token.Value);
break;
case TokenType.BRACKET:
if (token.Value.Equals("["))
{
currentEval = new Indexer(currentEval, BuildEval(tokens));
} else if (token.Value.Equals("]"))
{
return currentEval;
}
break; break;
default:
return currentEval;
} }
} }
return currentEval; return currentEval;
} }
private Eval BuildExpression(Queue<Token> tokens)
{
Token next = tokens.Peek();
switch (next.TokenType)
{
case TokenType.NAME:
return BuildPath(tokens);
case TokenType.NUMBER:
return new Number(tokens.Dequeue().Value);
case TokenType.STRING:
return new CString(tokens.Dequeue().Value);
}
throw new FormatException(String.Format("unexpected Token: {0}", next.Value));
}
private void ReadToken(TextReader reader, List<Token> tokens) private void ReadToken(TextReader reader, List<Token> tokens)
{ {
if (reader.Peek() == -1) if (reader.Peek() == -1)
@ -91,7 +138,7 @@ namespace ln.templates
{ {
reader.Read(); reader.Read();
} }
else if (char.IsLetter(ch)) else if (char.IsLetter(ch) || (ch == '_'))
{ {
ReadName(reader, tokens); ReadName(reader, tokens);
} }
@ -108,10 +155,30 @@ namespace ln.templates
reader.Read(); reader.Read();
tokens.Add(new Token(TokenType.DOT, ".")); tokens.Add(new Token(TokenType.DOT, "."));
} }
else if ((ch == '[')| (ch == ']')) else if (ch == '[')
{ {
reader.Read(); reader.Read();
tokens.Add(new Token(TokenType.BRACKET, new string(new char[] { ch }))); tokens.Add(new Token(TokenType.OPENINDEXER, new string(new char[] { ch })));
}
else if (ch == ']')
{
reader.Read();
tokens.Add(new Token(TokenType.ENDINDEXER, new string(new char[] { ch })));
}
else if (ch == '(')
{
reader.Read();
tokens.Add(new Token(TokenType.OPENLIST, new string(new char[] { ch })));
}
else if (ch == ')')
{
reader.Read();
tokens.Add(new Token(TokenType.ENDLIST, new string(new char[] { ch })));
}
else if (ch == ',')
{
reader.Read();
tokens.Add(new Token(TokenType.COMMA, new string(new char[] { ch })));
} }
else else
{ {
@ -122,7 +189,7 @@ namespace ln.templates
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
int ch = 0; int ch = 0;
while (char.IsLetterOrDigit((char)(ch = reader.Peek()))) while (char.IsLetterOrDigit((char)(ch = reader.Peek())) || (ch == '_'))
{ {
sb.Append((char)reader.Read()); sb.Append((char)reader.Read());
} }
@ -176,7 +243,7 @@ namespace ln.templates
} }
enum TokenType { NAME, DOT, NUMBER, STRING, BRACKET } enum TokenType { NAME, DOT, NUMBER, STRING, OPENINDEXER, ENDINDEXER, OPENLIST, ENDLIST, COMMA }
class Token class Token
{ {
public TokenType TokenType; public TokenType TokenType;
@ -336,6 +403,40 @@ namespace ln.templates
} }
} }
class MethodCall : Eval
{
Eval[] Parameters;
String Name;
public MethodCall(Eval parent, String name,params Eval[] p)
: base(parent)
{
Name = name;
Parameters = p;
}
public override object Evaluate(Context context)
{
object p = Parent.Evaluate(context);
object[] pvalues = new object[Parameters.Length];
Type[] ptypes = new Type[Parameters.Length];
for (int i=0;i<pvalues.Length;i++)
{
pvalues[i] = Parameters[i].Evaluate(context);
if (pvalues[i] == null)
ptypes[i] = null;
else
ptypes[i] = pvalues[i].GetType();
}
MethodInfo methodInfo = p.GetType().GetRuntimeMethod(Name, ptypes);
return methodInfo.Invoke(p, pvalues);
}
}
class Number : Eval class Number : Eval
{ {
object value; object value;

View File

@ -70,7 +70,11 @@ namespace ln.templates
if (FrameExpression != null) if (FrameExpression != null)
{ {
Template frame = context.Provider.FindTemplate(FrameExpression.Evaluate(null) as string); Template frame = context.Provider.FindTemplate(FrameExpression.Evaluate(context.ExpressionContext) as string);
if (frame == null)
{
throw new NullReferenceException(String.Format("FindTemplate() returned null for {0}",FrameExpression.Source));
}
return frame.Generate(new Context(context, writer.ToString())); return frame.Generate(new Context(context, writer.ToString()));
} }
return writer.ToString(); return writer.ToString();

View File

@ -1,8 +1,8 @@
using System; using System;
namespace ln.templates namespace ln.templates
{ {
public abstract class TemplateProvider public interface TemplateProvider
{ {
public abstract Template FindTemplate(string templatePath); Template FindTemplate(string templatePath);
} }
} }

View File

@ -34,6 +34,7 @@ namespace ln.templates
{ {
t = Source; t = Source;
op = null; op = null;
Source = "";
} }
else else
{ {

View File

@ -29,7 +29,6 @@
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="System.Web" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />