#include "Skylark.h" #define LLOG(x) // DLOG(x) namespace Upp { String RawHtmlText::ToString() const { return "RAW: " + text; } Value Raw(const String& s) { RawHtmlText r; r.text = s; return RichToValue(r); } VectorMap&, const Renderer *)>& Compiler::functions() { static VectorMap&, const Renderer *)> x; return x; } void Compiler::Register(const String& id, Value (*fn)(const Vector&, const Renderer *)) { functions().GetAdd(id) = fn; } int Compiler::ForVar(String id, int i) { if(i + 1 < var.GetCount() && forvar[i]) return i + 1; p.ThrowError(id + " is not 'for' iterator"); return 0; } int CountLinkArgs(const Vector& part) { int args = 0; for(int i = 0; i < part.GetCount(); i++) { int p = (byte)*part[i]; if(p == 255) // it's a ** node, we can supply unlimited number of arguments return -1; if(p >= 0 && p < 30) args = max(args, p + 1); } return args; } void Compiler::DoExeField(One& result) { One r; ExeField& f = r.Create(); f.value = pick(result); f.id = p.ReadId(); result = pick(r); } void Compiler::DoIndicies(One& result) { for(;;) { if(p.Char('.')) DoExeField(result); else if(p.Char('[')) { One r; ExeBracket& f = r.Create(); f.value = pick(result); f.index = Exp(); result = pick(r); p.PassChar(']'); } else break; } } One Compiler::Prim() { One result; if(p.Char('!')) result = Create(Prim()); else if(p.Char('-')) result = Create(Prim()); else if(p.Char('+')) result = Prim(); else if(p.IsId() || p.IsChar('.') || p.IsChar('@')) { String id; if(p.Char('.')) id = "."; else if(p.Char('@')) id = "@"; id << p.ReadId(); while(p.Char(':')) id << ':' << p.ReadId(); int n = var.Find(id); if(p.Char('(')) { Value (*f)(const Vector&, const Renderer *) = functions().Get(id, NULL); if(!f) { Vector *part = GetUrlViewLinkParts(id); if(!part) p.ThrowError("function nor link not found '" + id + "'"); ExeLink& ln = result.Create(); ln.part = part; if(!p.Char(')')) { do ln.arg.AddPick(Exp()); while(p.Char(',')); p.PassChar(')'); } if(CountLinkArgs(*part) > ln.arg.GetCount()) p.ThrowError("invalid number of link arguments '" + id + "'"); } else { ExeFn& fn = result.Create(); fn.fn = f; if(!p.Char(')')) { do fn.arg.AddPick(Exp()); while(p.Char(',')); p.PassChar(')'); } DoIndicies(result); } return result; } if(n < 0) { Vector *part = GetUrlViewLinkParts(id); ExeConst& c = result.Create(); if(!part) { return result; } String l = "\"/"; for(int i = 0; i < (*part).GetCount(); i++) { if(i) l << '/'; l << UrlEncode((*part)[i]); } l << '\"'; c.value = Raw(l); } else if(p.Char('.')) { if(p.Id("_first")) result.Create().var_index = ForVar(id, n); else if(p.Id("_last")) result.Create().var_index = ForVar(id, n); else if(p.Id("_index")) result.Create().var_index = ForVar(id, n); else if(p.Id("_key")) result.Create().var_index = ForVar(id, n); else { result.Create().var_index = n; DoExeField(result); DoIndicies(result); } } else { result.Create().var_index = n; DoIndicies(result); } } else if(p.Char('{')) { ExeMap& m = result.Create(); do { m.key.AddPick(Exp()); p.PassChar(':'); m.value.AddPick(Exp()); } while(p.Char(',')); p.PassChar('}'); } else if(p.Char('[')) { ExeArray& m = result.Create(); do { m.item.AddPick(Exp()); } while(p.Char(',')); p.PassChar(']'); } else if(p.Char('(')) { result = Exp(); p.PassChar(')'); } else { ExeConst& c = result.Create(); if(p.Char2('0', 'x') || p.Char2('0', 'X')) c.value = (int)p.ReadNumber(16); else if(p.Char('0')) c.value = int(p.IsNumber() ? p.ReadNumber(8) : 0); else c.value = p.IsString() ? Value(p.ReadString()) : Value(p.ReadDouble()); } return result; } One Compiler::Mul() { One result = Prim(); for(;;) if(p.Char('*')) result = Create(pick(result), Prim()); else if(p.Char('/')) result = Create(pick(result), Prim()); else if(p.Char('%')) result = Create(pick(result), Prim()); else return result; } One Compiler::Add() { One result = Mul(); for(;;) if(p.Char('+')) result = Create(pick(result), Mul()); else if(p.Char('-')) result = Create(pick(result), Mul()); else return result; } One Compiler::Shift() { One result = Add(); for(;;) if(p.Char3('>', '>', '>')) result = Create(pick(result), Add()); else if(p.Char2('>', '>')) result = Create(pick(result), Add()); else if(p.Char2('<', '<')) result = Create(pick(result), Add()); else return result; } One Compiler::Rel() { One result = Shift(); for(;;) if(p.Char2('<', '=')) result = Create(pick(result), Shift()); else if(p.Char2('>', '=')) result = Create(Shift(), pick(result)); else if(p.Char('<')) result = Create(pick(result), Shift()); else if(p.Char('>')) result = Create(Shift(), pick(result)); else return result; } One Compiler::Eq() { One result = Rel(); for(;;) if(p.Char2('=', '=')) result = Create(pick(result), Rel()); else if(p.Char2('!', '=')) result = Create(Rel(), pick(result)); else return result; } One Compiler::And() { One result = Eq(); while(!p.IsChar2('&', '&') && p.Char('&')) result = Create(pick(result), Eq()); return result; } One Compiler::Xor() { One result = And(); while(p.Char('^')) result = Create(pick(result), And()); return result; } One Compiler::Or() { One result = Xor(); while(!p.IsChar2('|', '|') && p.Char('|')) result = Create(pick(result), Xor()); return result; } One Compiler::LogAnd() { One result = Or(); while(p.Char2('&', '&')) result = Create(pick(result), Or()); return result; } One Compiler::LogOr() { One result = LogAnd(); while(p.Char2('|', '|')) result = Create(pick(result), LogAnd()); return result; } One Compiler::Conditional() { One result = LogOr(); if(p.Char('?')) { One r; ExeCond& c = r.Create(); c.cond = pick(result); c.ontrue = LogOr(); p.PassChar(':'); c.onfalse = LogOr(); result = pick(r); } return result; } One Compiler::Exp() { return Conditional(); } void Compiler::ExeBlock::AddText(const char *b, const char *s) { if(s > b) { RawHtmlText t; while(b < s) { if(*b == CParser::LINEINFO_ESC) { b++; while(b < s && *b++ != CParser::LINEINFO_ESC); } else t.text.Cat(*b++); } item.Add().Create().value = RawToValue(t); } } One Compiler::Block() { One result; ExeBlock& blk = result.Create(); const char *b = p.GetSpacePtr(); while(!p.IsEof()) { const char *s = p.GetPtr(); if(p.Char2('$', '$')) { blk.AddText(b, s + 1); b = p.GetSpacePtr(); } else if(p.Char('$')) { blk.AddText(b, s); if(p.Id("if")) { ExeCond& c = blk.item.Add().Create(); p.PassChar('('); c.cond = Exp(); p.PassChar(')'); c.ontrue = Block(); if(p.Id("else")) c.onfalse = Block(); if(!p.Char('/')) p.PassId("endif"); } else if(p.Id("for")) { ExeFor& c = blk.item.Add().Create(); p.PassChar('('); int q = var.GetCount(); var.Add(p.ReadId()); var.Add(Null); // LoopInfo placeholder forvar.Add(true); forvar.Add(true); p.PassId("in"); c.value = Exp(); p.PassChar(')'); c.body = Block(); var.Trim(q); forvar.SetCount(q); if(p.Id("else")) c.onempty = Block(); if(!p.Char('/')) p.PassId("endfor"); } else if(p.IsId("else") || p.IsId("endif") || p.IsId("endfor") || p.IsChar('/')) return result; else blk.item.AddPick(Prim()); b = p.GetSpacePtr(); } else { // We need to advance CParser, but SkipTerm is not good, because '$' can be in quoites - "$x" // We also need CParser to process whitespaces to allow for correct lineinfo processing CParser::Pos pos = p.GetPos(); pos.ptr++; const char *s = p.GetPtr(); while((byte)*s > ' ' && *s != '$') s++; p.SetPos(pos); } } blk.AddText(b, p.GetPtr()); return result; } One Compile(const char *code, const Index& vars) { Compiler c(code, vars); One exe = c.Block(); LLOG("Before optimization node count: " << c.GetNodeCount(exe)); c.Optimize(exe); LLOG("After optimization node count: " << c.GetNodeCount(exe)); return exe; } };