Initial Commit
commit
09be9fb80f
|
@ -0,0 +1,41 @@
|
||||||
|
# Autosave files
|
||||||
|
*~
|
||||||
|
|
||||||
|
# build
|
||||||
|
[Oo]bj/
|
||||||
|
[Bb]in/
|
||||||
|
packages/
|
||||||
|
TestResults/
|
||||||
|
|
||||||
|
# globs
|
||||||
|
Makefile.in
|
||||||
|
*.DS_Store
|
||||||
|
*.sln.cache
|
||||||
|
*.suo
|
||||||
|
*.cache
|
||||||
|
*.pidb
|
||||||
|
*.userprefs
|
||||||
|
*.usertasks
|
||||||
|
config.log
|
||||||
|
config.make
|
||||||
|
config.status
|
||||||
|
aclocal.m4
|
||||||
|
install-sh
|
||||||
|
autom4te.cache/
|
||||||
|
*.user
|
||||||
|
*.tar.gz
|
||||||
|
tarballs/
|
||||||
|
test-results/
|
||||||
|
Thumbs.db
|
||||||
|
.vs/
|
||||||
|
|
||||||
|
# Mac bundle stuff
|
||||||
|
*.dmg
|
||||||
|
*.app
|
||||||
|
|
||||||
|
# resharper
|
||||||
|
*_Resharper.*
|
||||||
|
*.Resharper
|
||||||
|
|
||||||
|
# dotCover
|
||||||
|
*.dotCover
|
|
@ -0,0 +1,441 @@
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
namespace ln.templates
|
||||||
|
{
|
||||||
|
delegate bool ConditionDelegate(char ch);
|
||||||
|
|
||||||
|
public class Expression
|
||||||
|
{
|
||||||
|
public String Source { get; }
|
||||||
|
private Eval TopEval { get; set; }
|
||||||
|
|
||||||
|
public Expression(String expr)
|
||||||
|
{
|
||||||
|
Source = expr;
|
||||||
|
|
||||||
|
Compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Evaluate(object o)
|
||||||
|
{
|
||||||
|
Context context = new Context(o, null);
|
||||||
|
return TopEval.Evaluate(context);
|
||||||
|
}
|
||||||
|
public object Evaluate(Context context)
|
||||||
|
{
|
||||||
|
return TopEval.Evaluate(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void Compile()
|
||||||
|
{
|
||||||
|
StringReader reader = new StringReader(Source);
|
||||||
|
List<Token> tokens = new List<Token>();
|
||||||
|
while (reader.Peek() != -1)
|
||||||
|
ReadToken(reader, tokens);
|
||||||
|
TopEval = BuildEval(tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Eval BuildEval(List<Token> tokens)
|
||||||
|
{
|
||||||
|
Eval currentEval = null;
|
||||||
|
|
||||||
|
while (tokens.Count > 0)
|
||||||
|
{
|
||||||
|
Token token = tokens[0];
|
||||||
|
tokens.RemoveAt(0);
|
||||||
|
|
||||||
|
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);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentEval;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReadToken(TextReader reader, List<Token> tokens)
|
||||||
|
{
|
||||||
|
if (reader.Peek() == -1)
|
||||||
|
return;
|
||||||
|
char ch = (char)reader.Peek();
|
||||||
|
if (char.IsWhiteSpace(ch))
|
||||||
|
{
|
||||||
|
reader.Read();
|
||||||
|
}
|
||||||
|
else if (char.IsLetter(ch))
|
||||||
|
{
|
||||||
|
ReadName(reader, tokens);
|
||||||
|
}
|
||||||
|
else if (char.IsDigit(ch))
|
||||||
|
{
|
||||||
|
ReadNumber(reader, tokens);
|
||||||
|
}
|
||||||
|
else if (ch == '"')
|
||||||
|
{
|
||||||
|
ReadString(reader, tokens);
|
||||||
|
}
|
||||||
|
else if (ch == '.')
|
||||||
|
{
|
||||||
|
reader.Read();
|
||||||
|
tokens.Add(new Token(TokenType.DOT, "."));
|
||||||
|
}
|
||||||
|
else if ((ch == '[')| (ch == ']'))
|
||||||
|
{
|
||||||
|
reader.Read();
|
||||||
|
tokens.Add(new Token(TokenType.BRACKET, new string(new char[] { ch })));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new FormatException(String.Format("Unexpected character: {0}", (char)reader.Peek()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private void ReadName(TextReader reader, List<Token> tokens)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
int ch = 0;
|
||||||
|
while (char.IsLetterOrDigit((char)(ch = reader.Peek())))
|
||||||
|
{
|
||||||
|
sb.Append((char)reader.Read());
|
||||||
|
}
|
||||||
|
tokens.Add(new Token(TokenType.NAME, sb.ToString()));
|
||||||
|
}
|
||||||
|
private void ReadNumber(TextReader reader, List<Token> tokens)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
int ch = 0;
|
||||||
|
while (char.IsDigit((char)(ch = reader.Peek())) | (ch == '.'))
|
||||||
|
{
|
||||||
|
sb.Append((char)reader.Read());
|
||||||
|
}
|
||||||
|
tokens.Add(new Token(TokenType.NUMBER, sb.ToString()));
|
||||||
|
}
|
||||||
|
private void ReadString(TextReader reader, List<Token> tokens)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
int ch = 0;
|
||||||
|
reader.Read();
|
||||||
|
while ((ch = reader.Peek())!='"')
|
||||||
|
{
|
||||||
|
if (ch == '\\')
|
||||||
|
{
|
||||||
|
reader.Read();
|
||||||
|
char sch = (char)reader.Read();
|
||||||
|
switch (sch)
|
||||||
|
{
|
||||||
|
case 'n':
|
||||||
|
sb.Append('\n');
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
sb.Append('\r');
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
sb.Append('\t');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sb.Append(sch);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.Append((char)reader.Read());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reader.Read();
|
||||||
|
|
||||||
|
tokens.Add(new Token(TokenType.STRING, sb.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum TokenType { NAME, DOT, NUMBER, STRING, BRACKET }
|
||||||
|
class Token
|
||||||
|
{
|
||||||
|
public TokenType TokenType;
|
||||||
|
public string Value;
|
||||||
|
|
||||||
|
public Token(TokenType tokenType,String value)
|
||||||
|
{
|
||||||
|
this.TokenType = tokenType;
|
||||||
|
this.Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return String.Format("[Token TokenType={0:8} Value={1}]", TokenType, Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Context
|
||||||
|
{
|
||||||
|
public object This { get; }
|
||||||
|
public Dictionary<string,object> MappedValues { get; }
|
||||||
|
|
||||||
|
public Context(Context source)
|
||||||
|
: this(source.This, source.MappedValues)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Context(object o,IEnumerable<KeyValuePair<string,object>> mappedValues)
|
||||||
|
{
|
||||||
|
this.This = o;
|
||||||
|
this.MappedValues = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
if (mappedValues != null)
|
||||||
|
foreach (KeyValuePair<string, object> kvp in mappedValues)
|
||||||
|
MappedValues.Add(kvp.Key,kvp.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddMappedValue(string name, object value)
|
||||||
|
{
|
||||||
|
MappedValues.Add(name, value);
|
||||||
|
}
|
||||||
|
public void SetMappedValue(string name, object value)
|
||||||
|
{
|
||||||
|
MappedValues[name] = value;
|
||||||
|
}
|
||||||
|
public void RemoveMappedValue(string name)
|
||||||
|
{
|
||||||
|
MappedValues.Remove(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Evaluate(string name)
|
||||||
|
{
|
||||||
|
if (MappedValues.ContainsKey(name))
|
||||||
|
return MappedValues[name];
|
||||||
|
|
||||||
|
if (This != null)
|
||||||
|
{
|
||||||
|
FieldInfo fieldInfo = This.GetType().GetField(name);
|
||||||
|
if (fieldInfo != null)
|
||||||
|
{
|
||||||
|
return fieldInfo.GetValue(This);
|
||||||
|
}
|
||||||
|
PropertyInfo propertyInfo = This.GetType().GetProperty(name);
|
||||||
|
if (propertyInfo != null)
|
||||||
|
{
|
||||||
|
return propertyInfo.GetValue(This);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new KeyNotFoundException(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Eval
|
||||||
|
{
|
||||||
|
public Eval Parent { get; }
|
||||||
|
|
||||||
|
public Eval(Eval parent)
|
||||||
|
{
|
||||||
|
this.Parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract object Evaluate(Context context);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Name : Eval
|
||||||
|
{
|
||||||
|
public string name;
|
||||||
|
|
||||||
|
public Name(Eval parent,String name)
|
||||||
|
:base(parent)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object Evaluate(Context context)
|
||||||
|
{
|
||||||
|
if (Parent == null)
|
||||||
|
{
|
||||||
|
return context.Evaluate(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
object p = Parent.Evaluate(context);
|
||||||
|
if (p != null)
|
||||||
|
{
|
||||||
|
FieldInfo fieldInfo = p.GetType().GetField(name);
|
||||||
|
if (fieldInfo != null)
|
||||||
|
{
|
||||||
|
return fieldInfo.GetValue(p);
|
||||||
|
}
|
||||||
|
PropertyInfo propertyInfo = p.GetType().GetProperty(name);
|
||||||
|
if (propertyInfo != null)
|
||||||
|
{
|
||||||
|
return propertyInfo.GetValue(p);
|
||||||
|
}
|
||||||
|
throw new KeyNotFoundException(name);
|
||||||
|
}
|
||||||
|
throw new NullReferenceException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Indexer : Eval
|
||||||
|
{
|
||||||
|
Eval index;
|
||||||
|
|
||||||
|
public Indexer(Eval parent,Eval index)
|
||||||
|
:base(parent)
|
||||||
|
{
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object Evaluate(Context context)
|
||||||
|
{
|
||||||
|
object i = index.Evaluate(context);
|
||||||
|
Type itype = i.GetType();
|
||||||
|
|
||||||
|
object p = Parent.Evaluate(context);
|
||||||
|
|
||||||
|
if (p is Array)
|
||||||
|
{
|
||||||
|
Array pa = p as Array;
|
||||||
|
return pa.GetValue((int)i);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (PropertyInfo pi in p.GetType().GetProperties())
|
||||||
|
{
|
||||||
|
ParameterInfo[] infos = pi.GetIndexParameters();
|
||||||
|
if (infos.Length == 1)
|
||||||
|
{
|
||||||
|
if (infos[0].ParameterType.IsAssignableFrom(itype))
|
||||||
|
{
|
||||||
|
return pi.GetValue(p, new object[] { i });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new KeyNotFoundException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Number : Eval
|
||||||
|
{
|
||||||
|
object value;
|
||||||
|
|
||||||
|
public Number(String sourceValue)
|
||||||
|
: base(null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
value = int.Parse(sourceValue);
|
||||||
|
} catch
|
||||||
|
{
|
||||||
|
value = float.Parse(sourceValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object Evaluate(Context context)
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class CString : Eval
|
||||||
|
{
|
||||||
|
string value;
|
||||||
|
|
||||||
|
public CString(String sourceValue)
|
||||||
|
: base(null)
|
||||||
|
{
|
||||||
|
this.value = sourceValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object Evaluate(Context context)
|
||||||
|
{
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//private String read(TextReader reader,ConditionDelegate condition)
|
||||||
|
//{
|
||||||
|
// StringBuilder sb = new StringBuilder();
|
||||||
|
// int ch;
|
||||||
|
// while (((ch = reader.Peek())!=-1)&&condition((char)ch))
|
||||||
|
// {
|
||||||
|
// sb.Append((char)ch);
|
||||||
|
// }
|
||||||
|
// return sb.ToString();
|
||||||
|
//}
|
||||||
|
|
||||||
|
//private Token ReadToken(TextReader reader)
|
||||||
|
//{
|
||||||
|
// int ch = reader.Peek();
|
||||||
|
// if (char.IsLetter((char)ch))
|
||||||
|
// return ReadFieldName(reader);
|
||||||
|
|
||||||
|
// throw new FormatException();
|
||||||
|
//}
|
||||||
|
|
||||||
|
//private Token ReadPath(TextReader reader)
|
||||||
|
//{
|
||||||
|
// String read(reader, (ch) => char.IsLetterOrDigit(ch));
|
||||||
|
//}
|
||||||
|
|
||||||
|
//abstract class Token
|
||||||
|
//{
|
||||||
|
// public abstract object Evaluate();
|
||||||
|
//}
|
||||||
|
|
||||||
|
//class Field : Token
|
||||||
|
//{
|
||||||
|
// Token owner;
|
||||||
|
// String fieldName;
|
||||||
|
|
||||||
|
// public Field(Token owner,String fieldName)
|
||||||
|
// {
|
||||||
|
// this.owner = owner;
|
||||||
|
// this.fieldName = fieldName;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public override object Evaluate()
|
||||||
|
// {
|
||||||
|
// if (o == null)
|
||||||
|
// return null;
|
||||||
|
|
||||||
|
// Type t = o.GetType();
|
||||||
|
// FieldInfo fieldInfo = t.GetField(this.fieldName);
|
||||||
|
// if (fieldInfo != null)
|
||||||
|
// {
|
||||||
|
// return fieldInfo.GetValue(owner.Evaluate());
|
||||||
|
// }
|
||||||
|
// PropertyInfo propertyInfo = t.GetProperty(this.fieldName);
|
||||||
|
// if (propertyInfo != null)
|
||||||
|
// {
|
||||||
|
// return propertyInfo.GetValue(owner.Evaluate());
|
||||||
|
// }
|
||||||
|
// throw new MissingFieldException(t.FullName, this.fieldName);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,254 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Linq;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Resources;
|
||||||
|
|
||||||
|
namespace ln.templates
|
||||||
|
{
|
||||||
|
//public class FormContext
|
||||||
|
//{
|
||||||
|
//private Dictionary<string, object> vars = new Dictionary<string, object>();
|
||||||
|
//protected MemoryStream contentStream, headStream;
|
||||||
|
|
||||||
|
//public Template SharpForm { get; }
|
||||||
|
//public Request Request { get; }
|
||||||
|
|
||||||
|
//public Stream ContentStream => contentStream;
|
||||||
|
//public TextWriter ContentWriter { get; protected set; }
|
||||||
|
|
||||||
|
//public Stream HeadStream => headStream;
|
||||||
|
//public TextWriter HeadWriter { get; protected set; }
|
||||||
|
|
||||||
|
//public FormContext Parent { get; }
|
||||||
|
//public bool UsesClonedVars { get; }
|
||||||
|
|
||||||
|
//public bool IsRootContext => Parent == null;
|
||||||
|
|
||||||
|
//public FormContext SubFrameContext { get; protected set; }
|
||||||
|
|
||||||
|
//public FormContext(Template sharpForm,Request request,object o)
|
||||||
|
//{
|
||||||
|
// Parent = null;
|
||||||
|
|
||||||
|
// SharpForm = sharpForm;
|
||||||
|
// Request = request;
|
||||||
|
|
||||||
|
// contentStream = new MemoryStream();
|
||||||
|
// ContentWriter = new StreamWriter(contentStream);
|
||||||
|
|
||||||
|
// headStream = new MemoryStream();
|
||||||
|
// HeadWriter = new StreamWriter(headStream);
|
||||||
|
|
||||||
|
// vars["o"] = o;
|
||||||
|
// vars["this"] = o;
|
||||||
|
// vars["StrMax"] = GetType().GetMethod("StrMax");
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public FormContext(FormContext parent,bool cloneVars = false,bool independentContent = false,FormContext subFrameContext = null)
|
||||||
|
//{
|
||||||
|
// Parent = parent;
|
||||||
|
|
||||||
|
// SharpForm = parent.SharpForm;
|
||||||
|
// Request = parent.Request;
|
||||||
|
|
||||||
|
// contentStream = independentContent ? new MemoryStream() : parent.contentStream;
|
||||||
|
// ContentWriter = new StreamWriter(ContentStream);
|
||||||
|
|
||||||
|
// headStream = parent.headStream;
|
||||||
|
// HeadWriter = parent.HeadWriter;
|
||||||
|
|
||||||
|
// UsesClonedVars = cloneVars;
|
||||||
|
|
||||||
|
// SubFrameContext = subFrameContext == null ? parent.SubFrameContext : subFrameContext;
|
||||||
|
|
||||||
|
// if (cloneVars){
|
||||||
|
// foreach (KeyValuePair<string,object> var in parent.vars)
|
||||||
|
// {
|
||||||
|
// this.vars.Add(var.Key, var.Value);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// parent.ContentWriter.Flush();
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public byte[] ContentBytes
|
||||||
|
//{
|
||||||
|
// get
|
||||||
|
// {
|
||||||
|
// ContentWriter.Flush();
|
||||||
|
// return contentStream.ToArray();
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//public byte[] HeadBytes
|
||||||
|
//{
|
||||||
|
// get
|
||||||
|
// {
|
||||||
|
// HeadWriter.Flush();
|
||||||
|
// return headStream.ToArray();
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//public String Content => Encoding.UTF8.GetString(ContentBytes);
|
||||||
|
//public String Head => Encoding.UTF8.GetString(HeadBytes);
|
||||||
|
|
||||||
|
//public void Insert(FormContext context, bool allToHead = false)
|
||||||
|
//{
|
||||||
|
// if (context.headStream != headStream)
|
||||||
|
// {
|
||||||
|
// HeadWriter.Flush();
|
||||||
|
// byte[] headBytes = context.HeadBytes;
|
||||||
|
// headStream.Write(headBytes, 0, headBytes.Length);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ContentWriter.Flush();
|
||||||
|
// byte[] contentBytes = context.ContentBytes;
|
||||||
|
|
||||||
|
// if (allToHead)
|
||||||
|
// headStream.Write(contentBytes, 0, contentBytes.Length);
|
||||||
|
// else
|
||||||
|
// contentStream.Write(contentBytes, 0, contentBytes.Length);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public bool HasVar(String name)
|
||||||
|
//{
|
||||||
|
// if (vars.ContainsKey(name))
|
||||||
|
// return true;
|
||||||
|
// if (!UsesClonedVars && (Parent != null))
|
||||||
|
// return Parent.HasVar(name);
|
||||||
|
|
||||||
|
// return false;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public object Get(String name){
|
||||||
|
// if (vars.ContainsKey(name))
|
||||||
|
// return vars[name];
|
||||||
|
// else if (Parent != null)
|
||||||
|
// return Parent.Get(name);
|
||||||
|
// else
|
||||||
|
// return null;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public void Set(String name,object value){
|
||||||
|
// if (!UsesClonedVars && (Parent != null) && (Parent.HasVar(name)))
|
||||||
|
// {
|
||||||
|
// Parent.Set(name, value);
|
||||||
|
// } else {
|
||||||
|
// this.vars[name] = value;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
//public object EvaluateExpression(Token[] tokens)
|
||||||
|
//{
|
||||||
|
// if (tokens.Length == 0)
|
||||||
|
// {
|
||||||
|
// return "";
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Stack<Token> tokenStack = new Stack<Token>(tokens);
|
||||||
|
// object currentValue = null;
|
||||||
|
// Token currentToken = null;
|
||||||
|
|
||||||
|
// while (tokenStack.Count > 0){
|
||||||
|
// currentToken = tokenStack.Pop();
|
||||||
|
|
||||||
|
// if (currentToken is PathToken)
|
||||||
|
// {
|
||||||
|
// PathToken pathToken = (PathToken)currentToken;
|
||||||
|
// Response response = Request.SubRequest(pathToken.Path);
|
||||||
|
// if (response.Reference != null)
|
||||||
|
// currentValue = response.Reference;
|
||||||
|
// else
|
||||||
|
// currentValue = Encoding.UTF8.GetString(response.ContentBytes);
|
||||||
|
// } else if (currentToken is KeywordToken)
|
||||||
|
// {
|
||||||
|
// KeywordToken keywordToken = (KeywordToken)currentToken;
|
||||||
|
// currentValue = keywordToken.Evaluate(this);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (tokenStack.Count > 0){
|
||||||
|
// currentToken = tokenStack.Pop();
|
||||||
|
// throw new NotImplementedException("Currently no expression operators are implemented, sorry");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
// return currentValue;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public object EvaluateValue(String sValue)
|
||||||
|
//{
|
||||||
|
// Stack<String> vpath = new Stack<string>(sValue.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries).Reverse());
|
||||||
|
|
||||||
|
// if (vpath.Count > 0)
|
||||||
|
// {
|
||||||
|
// object value = null;
|
||||||
|
// string key = vpath.Pop();
|
||||||
|
|
||||||
|
// value = Get(key);
|
||||||
|
|
||||||
|
// while (vpath.Count > 0)
|
||||||
|
// {
|
||||||
|
// key = vpath.Pop();
|
||||||
|
// value = GetObjectFieldOrProperty(value, key);
|
||||||
|
// }
|
||||||
|
// return value;
|
||||||
|
// }
|
||||||
|
// return null;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public bool EvaluateBoolean(String expression)
|
||||||
|
//{
|
||||||
|
// bool invert = (expression[0] == '!');
|
||||||
|
// if (invert)
|
||||||
|
// expression = expression.Substring(1);
|
||||||
|
|
||||||
|
// object value = EvaluateValue(expression);
|
||||||
|
// if (value != null)
|
||||||
|
// {
|
||||||
|
// if (
|
||||||
|
// ((value is bool) && (((bool)value))) ||
|
||||||
|
// ((value is string) && (!String.Empty.Equals(value))) ||
|
||||||
|
// ((value is int) && (((int)value) != 0)) ||
|
||||||
|
// ((value is long) && (((long)value) != 0)) ||
|
||||||
|
// ((value is float) && (((float)value) != 0)) ||
|
||||||
|
// ((value is double) && (((double)value) != 0))
|
||||||
|
// )
|
||||||
|
// {
|
||||||
|
// return !invert;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return invert;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//private object GetObjectFieldOrProperty(object o, string name)
|
||||||
|
//{
|
||||||
|
// if (o == null)
|
||||||
|
// {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
// FieldInfo fieldInfo = o.GetType().GetField(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
|
||||||
|
// if (fieldInfo != null)
|
||||||
|
// {
|
||||||
|
// return fieldInfo.GetValue(o);
|
||||||
|
// }
|
||||||
|
// PropertyInfo propertyInfo = o.GetType().GetProperty(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
|
||||||
|
// if (propertyInfo != null)
|
||||||
|
// {
|
||||||
|
// return propertyInfo.GetValue(o);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// throw new KeyNotFoundException(String.Format("object of type {0} has no field/property named {1}", o.GetType().FullName, name));
|
||||||
|
//}
|
||||||
|
|
||||||
|
//public static string StrMax(string s,int len,string suffix = "")
|
||||||
|
//{
|
||||||
|
// if (len < s.Length)
|
||||||
|
// return s.Substring(0, len) + suffix;
|
||||||
|
// return s;
|
||||||
|
//}
|
||||||
|
|
||||||
|
// }
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
using System;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
namespace ln.templates
|
||||||
|
{
|
||||||
|
//public abstract class FormElement
|
||||||
|
//{
|
||||||
|
// /* Instance Members */
|
||||||
|
// FormElement Container { get; }
|
||||||
|
// List<FormElement> children = new List<FormElement>();
|
||||||
|
|
||||||
|
// public FormElement(FormElement container)
|
||||||
|
// {
|
||||||
|
// Container = container;
|
||||||
|
// if (container != null)
|
||||||
|
// {
|
||||||
|
// container.children.Add(this);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public abstract void Run(FormContext context);
|
||||||
|
|
||||||
|
// public FormElement[] Children => children.ToArray();
|
||||||
|
// public virtual Template Form => Container.Form;
|
||||||
|
|
||||||
|
// public void RunChildren(FormContext context)
|
||||||
|
// {
|
||||||
|
// foreach (FormElement child in children)
|
||||||
|
// {
|
||||||
|
// child.Run(context);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
//}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
using System;
|
||||||
|
namespace ln.templates
|
||||||
|
{
|
||||||
|
public class MyClass
|
||||||
|
{
|
||||||
|
public MyClass()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
// Information about this assembly is defined by the following attributes.
|
||||||
|
// Change them to the values specific to your project.
|
||||||
|
|
||||||
|
[assembly: AssemblyTitle("ln.templates")]
|
||||||
|
[assembly: AssemblyDescription("")]
|
||||||
|
[assembly: AssemblyConfiguration("")]
|
||||||
|
[assembly: AssemblyCompany("")]
|
||||||
|
[assembly: AssemblyProduct("")]
|
||||||
|
[assembly: AssemblyCopyright("${AuthorCopyright}")]
|
||||||
|
[assembly: AssemblyTrademark("")]
|
||||||
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
|
// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
|
||||||
|
// The form "{Major}.{Minor}.*" will automatically update the build and revision,
|
||||||
|
// and "{Major}.{Minor}.{Build}.*" will update just the revision.
|
||||||
|
|
||||||
|
[assembly: AssemblyVersion("1.0.*")]
|
||||||
|
|
||||||
|
// The following attributes are used to specify the signing key for the assembly,
|
||||||
|
// if desired. See the Mono documentation for more information about signing.
|
||||||
|
|
||||||
|
//[assembly: AssemblyDelaySign(false)]
|
||||||
|
//[assembly: AssemblyKeyFile("")]
|
|
@ -0,0 +1,283 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using ln.templates.streams;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
namespace ln.templates
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Expression Syntax
|
||||||
|
*
|
||||||
|
* expr := ( <constant> | ( <objpath> | <path> ) [ "(" <arguments> ")" ] )
|
||||||
|
* objpath := <keyword> [ "." <objpath> ]
|
||||||
|
* arguments := <expr> [ "," <arguments> ]
|
||||||
|
*
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
|
||||||
|
|
||||||
|
//public static class Scanner
|
||||||
|
//{
|
||||||
|
// private static char[] letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray();
|
||||||
|
// private static char[] digits = "0123456789".ToCharArray();
|
||||||
|
|
||||||
|
// private static char[] keywordChars = letters.Concat(digits).Concat(new char[] { '_' }).ToArray();
|
||||||
|
// private static char[] pathChars = keywordChars.Concat(new char[] { '.','/' }).ToArray();
|
||||||
|
// private static char[] numberChars = digits.Concat(new char[] { '.' }).ToArray();
|
||||||
|
|
||||||
|
// private static char[] operatorChars = new char[] { '+', '-', '*', '/' };
|
||||||
|
|
||||||
|
|
||||||
|
// private static char[] ScanValidCharacters(CharStream charStream,char[] validCharacters)
|
||||||
|
// {
|
||||||
|
// charStream.Mark();
|
||||||
|
// while (!charStream.EndOfBuffer && validCharacters.Contains(charStream.Peek())){
|
||||||
|
// charStream.Read();
|
||||||
|
// }
|
||||||
|
// return charStream.GetMarkedIntervall();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public static String ScanKeyword(CharStream charStream)
|
||||||
|
// {
|
||||||
|
// if (!Char.IsLetter(charStream.Peek()))
|
||||||
|
// throw new ArgumentException(String.Format("ScanKeyword positioned on character {0}", charStream.Peek()));
|
||||||
|
|
||||||
|
// return new string(ScanValidCharacters(charStream,keywordChars));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public static String ScanNumber(CharStream charStream)
|
||||||
|
// {
|
||||||
|
// if (!Char.IsDigit(charStream.Peek()))
|
||||||
|
// throw new ArgumentException(String.Format("ScanNumber positioned on character {0}", charStream.Peek()));
|
||||||
|
|
||||||
|
// return new string(ScanValidCharacters(charStream, numberChars));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public static string ScanString(CharStream charStream)
|
||||||
|
// {
|
||||||
|
// if (charStream.Peek() != '"')
|
||||||
|
// throw new ArgumentException(String.Format("ScanString positioned on {0}", charStream.Peek()));
|
||||||
|
|
||||||
|
// charStream.Read();
|
||||||
|
// charStream.Mark();
|
||||||
|
|
||||||
|
// while (charStream.Peek() != '"')
|
||||||
|
// {
|
||||||
|
// if (charStream.Read() == '\\')
|
||||||
|
// {
|
||||||
|
// charStream.Read();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// string result = new string(charStream.GetMarkedIntervall());
|
||||||
|
|
||||||
|
// charStream.Read();
|
||||||
|
|
||||||
|
// return result;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public static PathToken ScanPath(CharStream charStream)
|
||||||
|
// {
|
||||||
|
// if (charStream.Peek() != '/')
|
||||||
|
// throw new ArgumentException(String.Format("ScanPath positioned on character {0}", charStream.Peek()));
|
||||||
|
|
||||||
|
// return new PathToken(new string(ScanValidCharacters(charStream, pathChars)));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public static Token ScanObjectPath(CharStream charStream,ObjectPathToken parent = null){
|
||||||
|
// if (!Char.IsLetter(charStream.Peek()))
|
||||||
|
// throw new ArgumentOutOfRangeException(String.Format("ScanObjectPath is not positioned on a letter: {0}",charStream.Peek()));
|
||||||
|
|
||||||
|
// String component = ScanKeyword(charStream);
|
||||||
|
|
||||||
|
// if (charStream.Peek() == '.')
|
||||||
|
// {
|
||||||
|
// charStream.Read();
|
||||||
|
// return ScanObjectPath(charStream, new ObjectPathToken(component, parent));
|
||||||
|
// } else if (charStream.Peek() == '(')
|
||||||
|
// {
|
||||||
|
// Token[] arguments = ScanArguments(charStream);
|
||||||
|
// return new CallToken(component, arguments, parent);
|
||||||
|
// } else {
|
||||||
|
// return new ObjectPathToken(component, parent);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public static char[] SeekProcessingInstruction(CharStream charStream)
|
||||||
|
// {
|
||||||
|
// charStream.Mark();
|
||||||
|
// while (!charStream.EndOfBuffer)
|
||||||
|
// {
|
||||||
|
// if ((charStream.Remaining > 1) && (charStream.Peek(0) == '<') && (charStream.Peek(1) == '?'))
|
||||||
|
// break;
|
||||||
|
// charStream.Read();
|
||||||
|
// }
|
||||||
|
// return charStream.GetMarkedIntervall();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public static Token ScanExpression(CharStream charStream)
|
||||||
|
// {
|
||||||
|
// while (!charStream.EndOfBuffer)
|
||||||
|
// {
|
||||||
|
// if (charStream.Peek() == '"')
|
||||||
|
// {
|
||||||
|
// return new ValueToken(ScanString(charStream));
|
||||||
|
// }
|
||||||
|
// else if (Char.IsLetter(charStream.Peek()))
|
||||||
|
// {
|
||||||
|
// return ScanObjectPath(charStream);
|
||||||
|
// }
|
||||||
|
// else if (charStream.Peek() == '/')
|
||||||
|
// {
|
||||||
|
// return ScanPath(charStream);
|
||||||
|
// }
|
||||||
|
// else if (Char.IsDigit(charStream.Peek()))
|
||||||
|
// {
|
||||||
|
// return new NumberToken(ScanNumber(charStream));
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// Console.WriteLine("Unexpected character: {0}", charStream.Read());
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public static Token[] ScanArguments(CharStream charStream){
|
||||||
|
|
||||||
|
// if (charStream.Peek() != '(')
|
||||||
|
// throw new ArgumentException(String.Format("ScanArguments not positioned on ("));
|
||||||
|
|
||||||
|
// charStream.Read();
|
||||||
|
|
||||||
|
// List<Token> arguments = new List<Token>();
|
||||||
|
|
||||||
|
// while (!charStream.EndOfBuffer && (charStream.Peek() != ')'))
|
||||||
|
// {
|
||||||
|
// if (Char.IsWhiteSpace(charStream.Peek()))
|
||||||
|
// {
|
||||||
|
// charStream.Read();
|
||||||
|
// } else {
|
||||||
|
// arguments.Add(ScanExpression(charStream));
|
||||||
|
// while (Char.IsWhiteSpace(charStream.Peek()))
|
||||||
|
// {
|
||||||
|
// charStream.Read();
|
||||||
|
// }
|
||||||
|
// if (charStream.Peek() == ')')
|
||||||
|
// {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// if (charStream.Peek() != ',')
|
||||||
|
// throw new ArgumentException(String.Format("expected , or ) after argument"));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// charStream.Read();
|
||||||
|
// return arguments.ToArray();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public static Token[] ScanParameters(CharStream charStream)
|
||||||
|
// {
|
||||||
|
// List<Token> tokens = new List<Token>();
|
||||||
|
|
||||||
|
// while (!charStream.EndOfBuffer)
|
||||||
|
// {
|
||||||
|
// if ((charStream.Remaining > 1) && (charStream.Peek() == '?') && (charStream.Peek(1) == '>'))
|
||||||
|
// {
|
||||||
|
// charStream.Read();
|
||||||
|
// charStream.Read();
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// else if (Char.IsWhiteSpace(charStream.Peek()))
|
||||||
|
// {
|
||||||
|
// charStream.Read();
|
||||||
|
// } else {
|
||||||
|
// tokens.Add(ScanExpression(charStream));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return tokens.ToArray();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public static void Scan(CharStream sourceStream, FormElement container)
|
||||||
|
// {
|
||||||
|
// while (!sourceStream.EndOfBuffer)
|
||||||
|
// {
|
||||||
|
// char[] textPart = SeekProcessingInstruction(sourceStream);
|
||||||
|
// if (textPart.Length > 0)
|
||||||
|
// {
|
||||||
|
// new Text(container, textPart);
|
||||||
|
// }
|
||||||
|
// if (!sourceStream.EndOfBuffer)
|
||||||
|
// {
|
||||||
|
// if (!ScanProcessingInstruction(sourceStream, container))
|
||||||
|
// {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public static bool ScanProcessingInstruction(CharStream sourceStream, FormElement container)
|
||||||
|
// {
|
||||||
|
// if ((sourceStream.Remaining > 1) && (sourceStream.Peek(0) == '<') && (sourceStream.Peek(1) == '?'))
|
||||||
|
// {
|
||||||
|
// sourceStream.Read();
|
||||||
|
// sourceStream.Read();
|
||||||
|
|
||||||
|
// if (sourceStream.Peek() == '=')
|
||||||
|
// {
|
||||||
|
// sourceStream.Read();
|
||||||
|
// new Var(container, ScanParameters(sourceStream));
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// String keyword = ScanKeyword(sourceStream);
|
||||||
|
// Token[] parameters = ScanParameters(sourceStream);
|
||||||
|
|
||||||
|
// switch (keyword)
|
||||||
|
// {
|
||||||
|
// case "include":
|
||||||
|
// new Include(container, parameters);
|
||||||
|
// break;
|
||||||
|
// case "head":
|
||||||
|
// Scan(sourceStream, new Head(container));
|
||||||
|
// break;
|
||||||
|
// case "set":
|
||||||
|
// new Set(container, ((ObjectPathToken)parameters[0]).Component ,parameters[1]);
|
||||||
|
// break;
|
||||||
|
// case "iterate":
|
||||||
|
// Scan(sourceStream, new Iterate(container,((ObjectPathToken)parameters[0]).Component,parameters[1]));
|
||||||
|
// break;
|
||||||
|
// case "if":
|
||||||
|
// Scan(sourceStream, new If(container, parameters[0]));
|
||||||
|
// break;
|
||||||
|
// case "editor":
|
||||||
|
// new Editor(container, parameters);
|
||||||
|
// break;
|
||||||
|
// case "frame":
|
||||||
|
// if (!(parameters[0] is PathToken))
|
||||||
|
// throw new ArgumentException("<?frame ...?> needs path as argument.");
|
||||||
|
|
||||||
|
// container.Form.Frame = (PathToken)parameters[0];
|
||||||
|
|
||||||
|
// break;
|
||||||
|
// case "framed":
|
||||||
|
// new Framed(container);
|
||||||
|
// break;
|
||||||
|
// case "end":
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// throw new ArgumentException("ScanProcessingInstruction was not positioned on PI");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
//}
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using ln.templates.streams;
|
||||||
|
using ln.templates.elements;
|
||||||
|
|
||||||
|
namespace ln.templates
|
||||||
|
{
|
||||||
|
public class Template
|
||||||
|
{
|
||||||
|
public TemplateProvider Provider { get; private set; }
|
||||||
|
public String SourceFilename { get; private set; }
|
||||||
|
|
||||||
|
public Element RootElement { get; private set; }
|
||||||
|
|
||||||
|
public Expression FrameExpression { get; private set; }
|
||||||
|
|
||||||
|
public Template(String sourceFilename)
|
||||||
|
{
|
||||||
|
SourceFilename = sourceFilename;
|
||||||
|
|
||||||
|
LoadSource(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Template(String sourceFilename, TemplateProvider provider)
|
||||||
|
{
|
||||||
|
Provider = provider;
|
||||||
|
SourceFilename = sourceFilename;
|
||||||
|
|
||||||
|
LoadSource(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Template(String source,String pseudoFilename,TemplateProvider provider)
|
||||||
|
{
|
||||||
|
Provider = provider;
|
||||||
|
SourceFilename = pseudoFilename;
|
||||||
|
|
||||||
|
LoadSource(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadSource(String source)
|
||||||
|
{
|
||||||
|
if (source == null)
|
||||||
|
{
|
||||||
|
using (FileStream fileStream = new FileStream(SourceFilename, FileMode.Open))
|
||||||
|
{
|
||||||
|
byte[] loadBuffer = new byte[fileStream.Length];
|
||||||
|
fileStream.Read(loadBuffer, 0, loadBuffer.Length);
|
||||||
|
fileStream.Close();
|
||||||
|
source = Encoding.UTF8.GetString(loadBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TemplateReader templateReader = new TemplateReader(source);
|
||||||
|
|
||||||
|
RootElement = templateReader.RootElement;
|
||||||
|
FrameExpression = templateReader.FrameExpression;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String Generate()
|
||||||
|
{
|
||||||
|
Context context = new Context(this);
|
||||||
|
return Generate(context);
|
||||||
|
}
|
||||||
|
public String Generate(Context context)
|
||||||
|
{
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
RootElement.Generate(writer, context);
|
||||||
|
|
||||||
|
if (FrameExpression != null)
|
||||||
|
{
|
||||||
|
Template frame = context.Provider.FindTemplate(FrameExpression.Evaluate(null) as string);
|
||||||
|
return frame.Generate(new Context(context, writer.ToString()));
|
||||||
|
}
|
||||||
|
return writer.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class Context
|
||||||
|
{
|
||||||
|
public Context Parent { get; }
|
||||||
|
public Template Template { get; }
|
||||||
|
public TemplateProvider Provider => Template.Provider;
|
||||||
|
|
||||||
|
public String FramedContent { get; }
|
||||||
|
public Expression.Context ExpressionContext { get; }
|
||||||
|
|
||||||
|
public Context(Template template)
|
||||||
|
{
|
||||||
|
Template = template;
|
||||||
|
ExpressionContext = new Expression.Context(null, null);
|
||||||
|
ExpressionContext.AddMappedValue("__template__", template.SourceFilename);
|
||||||
|
}
|
||||||
|
public Context(Context parent,String framedContent)
|
||||||
|
{
|
||||||
|
Parent = parent;
|
||||||
|
Template = parent.Template;
|
||||||
|
FramedContent = framedContent;
|
||||||
|
ExpressionContext = new Expression.Context(parent.ExpressionContext);
|
||||||
|
if (framedContent != null)
|
||||||
|
ExpressionContext.SetMappedValue("__frame__", framedContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
using System;
|
||||||
|
namespace ln.templates
|
||||||
|
{
|
||||||
|
public abstract class TemplateProvider
|
||||||
|
{
|
||||||
|
public abstract Template FindTemplate(string templatePath);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using ln.templates.elements;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
namespace ln.templates
|
||||||
|
{
|
||||||
|
public class TemplateReader
|
||||||
|
{
|
||||||
|
public ContainerElement RootElement { get; private set; } = new ContainerElement(null);
|
||||||
|
public ContainerElement CurrentElement { get; private set; }
|
||||||
|
|
||||||
|
public Expression FrameExpression { get; private set; }
|
||||||
|
|
||||||
|
public String Source { get; private set; }
|
||||||
|
|
||||||
|
public TemplateReader(String source)
|
||||||
|
{
|
||||||
|
Source = source;
|
||||||
|
Parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Parse()
|
||||||
|
{
|
||||||
|
CurrentElement = RootElement;
|
||||||
|
|
||||||
|
while (Source.Length > 0)
|
||||||
|
{
|
||||||
|
String t, op;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
i = Source.IndexOf("<%");
|
||||||
|
if (i == -1)
|
||||||
|
{
|
||||||
|
t = Source;
|
||||||
|
op = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
t = Source.Substring(0, i);
|
||||||
|
j = Source.IndexOf("%>", i + 1);
|
||||||
|
if (j == -1)
|
||||||
|
{
|
||||||
|
throw new FormatException(String.Format("missing '%>' in {0}", Source.Substring(i, 64)));
|
||||||
|
}
|
||||||
|
op = Source.Substring(i + 2, j - i - 2);
|
||||||
|
Source = Source.Substring(j + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
new TextElement(CurrentElement, t);
|
||||||
|
if (op != null)
|
||||||
|
ParseOP(op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseOP(String op)
|
||||||
|
{
|
||||||
|
StringReader reader = new StringReader(op);
|
||||||
|
String opkey;
|
||||||
|
|
||||||
|
if (reader.Peek() == '=')
|
||||||
|
{
|
||||||
|
reader.Read();
|
||||||
|
opkey = "=";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
opkey = reader.PopWord();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (opkey)
|
||||||
|
{
|
||||||
|
case "=":
|
||||||
|
new ExpressionElement(CurrentElement, new Expression(reader.ReadToEnd()));
|
||||||
|
break;
|
||||||
|
case "frame":
|
||||||
|
FrameExpression = new Expression(reader.ReadToEnd());
|
||||||
|
break;
|
||||||
|
case "iterate":
|
||||||
|
String iterName = reader.PopWord();
|
||||||
|
CurrentElement = new IteratorElement(CurrentElement, iterName, new Expression(reader.ReadToEnd()));
|
||||||
|
break;
|
||||||
|
case "if":
|
||||||
|
CurrentElement = new ConditionalElement(CurrentElement, new Expression(reader.ReadToEnd()));
|
||||||
|
break;
|
||||||
|
case "else":
|
||||||
|
if (CurrentElement is ConditionalElement)
|
||||||
|
{
|
||||||
|
ConditionalElement ce = CurrentElement as ConditionalElement;
|
||||||
|
CurrentElement = ce.AlternativeContainer;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "elseif":
|
||||||
|
if (CurrentElement is ConditionalElement)
|
||||||
|
{
|
||||||
|
ConditionalElement ce = CurrentElement as ConditionalElement;
|
||||||
|
CurrentElement = new ConditionalElement(null, new Expression(reader.ReadToEnd()));
|
||||||
|
ce.AlternativeContainer = CurrentElement;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "include":
|
||||||
|
new IncludeElement(CurrentElement, new Expression(reader.ReadToEnd()));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "set":
|
||||||
|
String setName = reader.PopWord();
|
||||||
|
new SetElement(CurrentElement, setName, new Expression(reader.ReadToEnd()));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "end":
|
||||||
|
CurrentElement = CurrentElement.Container;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException(opkey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class StringHelper
|
||||||
|
{
|
||||||
|
public static int IndexOf(this String s,Func<char,bool> predicate)
|
||||||
|
{
|
||||||
|
for (int i=0;i<s.Length;i++)
|
||||||
|
{
|
||||||
|
if (predicate(s[i]))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SkipWhiteSpace(this TextReader reader)
|
||||||
|
{
|
||||||
|
while (char.IsWhiteSpace((char)reader.Peek()))
|
||||||
|
{
|
||||||
|
reader.Read();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static String PopWord(this TextReader reader)
|
||||||
|
{
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
while ((reader.Peek()!=-1) && !char.IsWhiteSpace((char)reader.Peek()))
|
||||||
|
{
|
||||||
|
stringBuilder.Append((char)reader.Read());
|
||||||
|
}
|
||||||
|
reader.SkipWhiteSpace();
|
||||||
|
return stringBuilder.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Runtime.Remoting.Contexts;
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace ln.templates.elements
|
||||||
|
{
|
||||||
|
public class ConditionalElement : ContainerElement
|
||||||
|
{
|
||||||
|
private ContainerElement alternativeContainer;
|
||||||
|
|
||||||
|
public Expression Expression { get; private set; }
|
||||||
|
|
||||||
|
public ConditionalElement(ContainerElement container,Expression expression)
|
||||||
|
:base(container)
|
||||||
|
{
|
||||||
|
Expression = expression;
|
||||||
|
AlternativeContainer = new ContainerElement(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsTrue(object value)
|
||||||
|
{
|
||||||
|
return (
|
||||||
|
(value != null) &&
|
||||||
|
((value is int) && ((int)value != 0)) ||
|
||||||
|
((value is double) && ((double)value != 0.0)) ||
|
||||||
|
((value is bool) && ((bool)value)) ||
|
||||||
|
((value is string) && (!String.Empty.Equals(value)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Generate(TextWriter writer, Template.Context context)
|
||||||
|
{
|
||||||
|
object v = Expression.Evaluate(context.ExpressionContext);
|
||||||
|
|
||||||
|
if (IsTrue(v))
|
||||||
|
{
|
||||||
|
GenerateChildren(writer, context);
|
||||||
|
}
|
||||||
|
else if (AlternativeContainer != null)
|
||||||
|
{
|
||||||
|
AlternativeContainer.Generate(writer, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ContainerElement AlternativeContainer {
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return alternativeContainer;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (alternativeContainer != null)
|
||||||
|
{
|
||||||
|
alternativeContainer.Container = null;
|
||||||
|
}
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
value.Container = Container;
|
||||||
|
}
|
||||||
|
alternativeContainer = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class IteratorElement : ConditionalElement
|
||||||
|
{
|
||||||
|
public String IterName { get; private set; }
|
||||||
|
|
||||||
|
public IteratorElement(ContainerElement container,String iterName,Expression expression)
|
||||||
|
:base(container, expression)
|
||||||
|
{
|
||||||
|
IterName = iterName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Generate(TextWriter writer, Template.Context context)
|
||||||
|
{
|
||||||
|
object v = Expression.Evaluate(context.ExpressionContext);
|
||||||
|
|
||||||
|
if (v == null)
|
||||||
|
{
|
||||||
|
AlternativeContainer.Generate(writer, context);
|
||||||
|
}
|
||||||
|
else if (v is IEnumerable)
|
||||||
|
{
|
||||||
|
Template.Context icontext = new Template.Context(context,null);
|
||||||
|
|
||||||
|
bool b = false;
|
||||||
|
IEnumerable en = v as IEnumerable;
|
||||||
|
foreach (object o in en)
|
||||||
|
{
|
||||||
|
b = true;
|
||||||
|
icontext.ExpressionContext.SetMappedValue(IterName, o);
|
||||||
|
GenerateChildren(writer, icontext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace ln.templates.elements
|
||||||
|
{
|
||||||
|
public abstract class Element
|
||||||
|
{
|
||||||
|
public ContainerElement Container { get; internal set; }
|
||||||
|
|
||||||
|
protected Element(ContainerElement container)
|
||||||
|
{
|
||||||
|
Container = container;
|
||||||
|
if (Container != null)
|
||||||
|
Container.children.Add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void Generate(TextWriter writer, Template.Context context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ContainerElement : Element
|
||||||
|
{
|
||||||
|
internal List<Element> children { get; } = new List<Element>();
|
||||||
|
|
||||||
|
public ContainerElement(ContainerElement container)
|
||||||
|
:base(container)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GenerateChildren(TextWriter writer, Template.Context context)
|
||||||
|
{
|
||||||
|
foreach (Element c in children)
|
||||||
|
c.Generate(writer, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Generate(TextWriter writer, Template.Context context)
|
||||||
|
{
|
||||||
|
GenerateChildren(writer, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Element[] Children
|
||||||
|
{
|
||||||
|
get => children.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TextElement : Element
|
||||||
|
{
|
||||||
|
public String Text { get; }
|
||||||
|
public TextElement(ContainerElement container,String text)
|
||||||
|
:base(container)
|
||||||
|
{
|
||||||
|
Text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Generate(TextWriter writer, Template.Context context)
|
||||||
|
{
|
||||||
|
writer.Write(Text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ExpressionElement : Element
|
||||||
|
{
|
||||||
|
public Expression Expression { get; }
|
||||||
|
|
||||||
|
public ExpressionElement(ContainerElement container,Expression expression)
|
||||||
|
:base(container)
|
||||||
|
{
|
||||||
|
Expression = expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Generate(TextWriter writer, Template.Context context)
|
||||||
|
{
|
||||||
|
object v = Expression.Evaluate(context.ExpressionContext);
|
||||||
|
if (v != null)
|
||||||
|
writer.Write(v.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class SetElement : Element
|
||||||
|
{
|
||||||
|
public String SetName { get; }
|
||||||
|
public Expression Expression { get; }
|
||||||
|
|
||||||
|
public SetElement(ContainerElement container, String setName,Expression expression)
|
||||||
|
: base(container)
|
||||||
|
{
|
||||||
|
SetName = setName;
|
||||||
|
Expression = expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Generate(TextWriter writer, Template.Context context)
|
||||||
|
{
|
||||||
|
object v = Expression.Evaluate(context.ExpressionContext);
|
||||||
|
context.ExpressionContext.SetMappedValue(SetName, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Runtime.Remoting.Contexts;
|
||||||
|
|
||||||
|
namespace ln.templates.elements
|
||||||
|
{
|
||||||
|
public class IncludeElement : Element
|
||||||
|
{
|
||||||
|
Expression TargetExpression { get; set; }
|
||||||
|
|
||||||
|
public IncludeElement(ContainerElement container,Expression targetExpression)
|
||||||
|
:base(container)
|
||||||
|
{
|
||||||
|
TargetExpression = targetExpression;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Generate(TextWriter writer, Template.Context context,String templatePath)
|
||||||
|
{
|
||||||
|
Template iTemplate = context.Provider.FindTemplate(templatePath);
|
||||||
|
Template.Context iContext = new Template.Context(context, null);
|
||||||
|
String iContent = iTemplate.Generate(iContext);
|
||||||
|
|
||||||
|
writer.Write(iContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Generate(TextWriter writer, Template.Context context)
|
||||||
|
{
|
||||||
|
object target = TargetExpression.Evaluate(context.ExpressionContext);
|
||||||
|
|
||||||
|
if (target is IEnumerable<String>)
|
||||||
|
{
|
||||||
|
IEnumerable<String> targets = (IEnumerable<String>)target;
|
||||||
|
foreach (String t in targets)
|
||||||
|
{
|
||||||
|
Generate(writer, context, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Generate(writer, context, target as String);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{AD0267BB-F08C-4BE1-A88D-010D49041761}</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<RootNamespace>ln.templates</RootNamespace>
|
||||||
|
<AssemblyName>ln.templates</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<ConsolePause>false</ConsolePause>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release</OutputPath>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<ConsolePause>false</ConsolePause>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="System.Web" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="FormContext.cs" />
|
||||||
|
<Compile Include="FormElement.cs" />
|
||||||
|
<Compile Include="Scanner.cs" />
|
||||||
|
<Compile Include="Template.cs" />
|
||||||
|
<Compile Include="TemplateProvider.cs" />
|
||||||
|
<Compile Include="streams\CharStream.cs" />
|
||||||
|
<Compile Include="streams\PeekableStream.cs" />
|
||||||
|
<Compile Include="TemplateReader.cs" />
|
||||||
|
<Compile Include="Expression.cs" />
|
||||||
|
<Compile Include="elements\Element.cs" />
|
||||||
|
<Compile Include="elements\IncludeElement.cs" />
|
||||||
|
<Compile Include="elements\Conditionals.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="elements\" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||||
|
</Project>
|
|
@ -0,0 +1,20 @@
|
||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
namespace ln.templates.streams
|
||||||
|
{
|
||||||
|
public class CharStream : PeekAbleStream<char>
|
||||||
|
{
|
||||||
|
public CharStream(char[] buffer)
|
||||||
|
:base(buffer)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public CharStream(byte[] byteBuffer, Encoding encoding)
|
||||||
|
: this(encoding.GetChars(byteBuffer))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public CharStream(byte[] byteBuffer)
|
||||||
|
:this(Encoding.UTF8.GetChars(byteBuffer))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
namespace ln.templates.streams
|
||||||
|
{
|
||||||
|
public class PeekAbleStream<T>
|
||||||
|
{
|
||||||
|
private T[] buffer;
|
||||||
|
private int position;
|
||||||
|
|
||||||
|
public int Position => position;
|
||||||
|
public bool EndOfBuffer => (position >= buffer.Length);
|
||||||
|
|
||||||
|
public int MarkedPosition { get; set; }
|
||||||
|
|
||||||
|
public PeekAbleStream(T[] buffer)
|
||||||
|
{
|
||||||
|
this.buffer = buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Read(){
|
||||||
|
if (position < buffer.Length)
|
||||||
|
return buffer[position++];
|
||||||
|
|
||||||
|
throw new IndexOutOfRangeException("Tried to read after end of buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Peek(int shift = 0)
|
||||||
|
{
|
||||||
|
if ((position + shift) < buffer.Length)
|
||||||
|
return buffer[position + shift];
|
||||||
|
|
||||||
|
throw new IndexOutOfRangeException("Tried to peek after end of buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Mark()
|
||||||
|
{
|
||||||
|
MarkedPosition = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T[] GetMarkedIntervall()
|
||||||
|
{
|
||||||
|
T[] intervall = new T[position - MarkedPosition];
|
||||||
|
Array.Copy(buffer, MarkedPosition, intervall, 0, intervall.Length);
|
||||||
|
return intervall;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Remaining => buffer.Length - position;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue