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.Text;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
namespace ln.templates
{
delegate bool ConditionDelegate(char ch);
@ -34,54 +36,99 @@ namespace ln.templates
private void Compile()
{
StringReader reader = new StringReader(Source);
List<Token> tokens = new List<Token>();
while (reader.Peek() != -1)
ReadToken(reader, tokens);
TopEval = BuildEval(tokens);
try
{
StringReader reader = new StringReader(Source);
List<Token> tokens = new List<Token>();
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)
{
Token token = tokens[0];
tokens.RemoveAt(0);
Token token = tokens.Peek();
switch (token.TokenType)
{
case TokenType.DOT:
token = tokens[0];
tokens.RemoveAt(0);
if (token.TokenType != TokenType.NAME)
throw new ArgumentException("missing fieldname after .");
currentEval = new Name(currentEval, token.Value);
tokens.Dequeue();
currentEval = BuildName(tokens.Dequeue(), currentEval, tokens);
break;
case TokenType.NAME:
currentEval = new Name(currentEval, token.Value);
break;
case TokenType.NUMBER:
currentEval = new Number(token.Value);
break;
case TokenType.STRING:
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;
}
case TokenType.OPENINDEXER:
tokens.Dequeue();
Eval index = BuildExpression(tokens);
token = tokens.Dequeue();
if (token.TokenType != TokenType.ENDINDEXER)
throw new FormatException("Expected ']'");
currentEval = new Indexer(currentEval, index);
break;
default:
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)
{
if (reader.Peek() == -1)
@ -91,7 +138,7 @@ namespace ln.templates
{
reader.Read();
}
else if (char.IsLetter(ch))
else if (char.IsLetter(ch) || (ch == '_'))
{
ReadName(reader, tokens);
}
@ -108,10 +155,30 @@ namespace ln.templates
reader.Read();
tokens.Add(new Token(TokenType.DOT, "."));
}
else if ((ch == '[')| (ch == ']'))
else if (ch == '[')
{
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
{
@ -122,7 +189,7 @@ namespace ln.templates
{
StringBuilder sb = new StringBuilder();
int ch = 0;
while (char.IsLetterOrDigit((char)(ch = reader.Peek())))
while (char.IsLetterOrDigit((char)(ch = reader.Peek())) || (ch == '_'))
{
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
{
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
{
object value;

View File

@ -70,7 +70,11 @@ namespace ln.templates
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 writer.ToString();

View File

@ -1,8 +1,8 @@
using System;
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;
op = null;
Source = "";
}
else
{

View File

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