uppsrc: Skylark moved from sandbox

git-svn-id: svn://ultimatepp.org/upp/trunk@5125 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
cxl 2012-07-05 17:57:50 +00:00
parent 00baf70dbc
commit 8e070bb698
19 changed files with 3072 additions and 0 deletions

288
uppsrc/Skylark/App.cpp Normal file
View file

@ -0,0 +1,288 @@
#include "Skylark.h"
#ifdef PLATFORM_POSIX
#include <sys/wait.h>
#endif
#ifdef PLATFORM_WIN32
#include <wincon.h>
#endif
#define CONSOLE(x) Cout() << x << '\n'
#ifdef PLATFORM_WIN32
BOOL WINAPI SkylarkApp::CtrlCHandlerRoutine(__in DWORD dwCtrlType)
{
LOG("Ctrl+C handler");
TheApp().quit = true;
Cout() << "Ctrl + C\n";
TcpSocket h;
h.Connect("127.0.0.1", TheApp().port);
h.Put("quit");
return TRUE;
}
#endif
void SkylarkApp::WorkThread()
{
RunThread();
}
void SkylarkApp::ThreadRun()
{
WorkThread();
}
void SkylarkApp::RunThread()
{
SQL.ClearError();
SQLR.ClearError();
SQL.GetSession().ThrowOnError();
SQLR.GetSession().ThrowOnError();
for(;;) {
TcpSocket request;
accept_mutex.Enter();
if(quit) {
accept_mutex.Leave();
break;
}
bool b = request.Timeout(2000).Accept(server);
accept_mutex.Leave();
if(quit)
break;
if(b) {
CONSOLE("Accepted " << Thread::GetCurrentId());
#ifdef PLATFORM_POSIX
if(prefork)
alarm(timeout);
#endif
Http http(*this);
http.Dispatch(request);
#ifdef PLATFORM_POSIX
if(prefork)
alarm(0);
#endif
CONSOLE("Finished " << Thread::GetCurrentId());
}
else
CONSOLE("Waiting " << Thread::GetCurrentId());
}
}
void SkylarkApp::Main()
{
Buffer<Thread> uwt(threads);
for(int i = 0; i < threads; i++)
Thread::Start(THISBACK(ThreadRun));
while(Thread::GetCount()) {
if(getpid() == main_pid && (msecs() % 1000) == 0)
ThreadRun();
Sleep(100);
}
}
void SkylarkApp::Broadcast(int signal)
{
#ifdef PLATFORM_POSIX
if(getpid() == main_pid)
for(int i = 0; i < child_pid.GetCount(); i++)
kill(child_pid[i], signal);
#endif
}
void SkylarkApp::Signal(int signal)
{
#ifdef PLATFORM_POSIX
switch(signal) {
case SIGTERM:
case SIGHUP:
quit = true;
Broadcast(signal);
break;
case SIGINT:
Broadcast(signal);
exit(0);
break;
case SIGALRM:
if(getpid() != TheApp().main_pid) {
// "Timeout - session stoped"
exit(0);
}
break;
}
#endif
}
void SkylarkApp::SignalHandler(int signal)
{
TheApp().Signal(signal);
}
void DisableHUP()
{
#ifdef PLATFORM_POSIX
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGHUP);
sigprocmask(SIG_BLOCK, &mask, NULL);
#endif
}
void EnableHUP()
{
#ifdef PLATFORM_POSIX
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGHUP);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
#endif
}
void SkylarkApp::Run()
{
// DisableHUP();
SqlSession::PerThread();
SqlId::UseQuotes();
FinalizeViews();
#ifdef PLATFORM_WIN32
SetConsoleCtrlHandler(CtrlCHandlerRoutine, true);
#endif
main_pid = getpid();
quit = false;
#if defined(PLATFORM_POSIX) && defined(_DEBUG)
// Avoid the need to close running server in debug mode...
String pidf = ConfigFile("debug_pid");
int prev_pid = atoi(LoadFile(pidf));
if(prev_pid) {
kill(prev_pid, SIGTERM);
int status = 0;
waitpid(prev_pid, &status, 0);
}
for(int i = 0; i < 100; i++) {
Cout() << i;
if(server.Listen(port, 5))
goto listening;
Sleep(10);
}
LOG("Cannot open server socket!");
Cout() << "Cannot open server socket!\n";
return;
listening:;
#else
if(!server.Listen(port, 5)) {
LOG("Cannot open server socket!");
Cout() << "Cannot open server socket!\n";
return;
}
#endif
#ifdef PLATFORM_POSIX
#ifdef _DEBUG
SaveFile(pidf, AsString(main_pid));
#endif
if(prefork) {
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SignalHandler;
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGALRM, &sa, NULL);
// EnableHUP();
while(!quit) {
while(child_pid.GetCount() < prefork && !quit) {
pid_t p = fork();
if(p == 0) {
Main();
return;
}
else
if(p > 0)
child_pid.Add(p);
else {
// "cant create new process"
Broadcast(SIGINT);
abort();
}
}
int status = 0;
pid_t p = wait(&status);
if(p > 0) {
int q = FindIndex(child_pid, p);
if(q >= 0)
child_pid.Remove(q);
}
}
Broadcast(SIGTERM);
int status = 0;
for(int i = 0; i < child_pid.GetCount(); i++)
waitpid(child_pid[i], &status, 0);
// "server stopped";
}
else
#endif
Main();
#if defined(_DEBUG) && defined(POSIX)
FileDelete(pidf);
#endif
CONSOLE("ExitSkylark");
}
void SkylarkApp::SqlError(Http& http)
{
}
void SkylarkApp::InternalError(Http& http)
{
}
void SkylarkApp::NotFound(Http& http)
{
}
void SkylarkApp::Unauthorized(Http& http)
{
}
SkylarkApp *SkylarkApp::app;
SkylarkApp& SkylarkApp::TheApp()
{
ASSERT(app);
return *app;
}
const SkylarkConfig& SkylarkApp::Config()
{
ASSERT(app);
return *app;
}
SkylarkApp::SkylarkApp()
{
ASSERT(!app);
app = this;
threads = 3 * CPU_Cores() + 1;
port = 8001;
use_caching = true;
#ifdef _DEBUG
prefork = 0;
timeout = 0;
#else
prefork = 1;
timeout = 300;
#endif
}
SkylarkApp::~SkylarkApp()
{
app = NULL;
}

14
uppsrc/Skylark/Base.witz Normal file
View file

@ -0,0 +1,14 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="cs" lang="cs">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>#TITLE</title>
<script type="text/javascript" src="static/Skylark/skylark.js"></script>
#HEAD
</head>
<body id="body_id">
#BODY
</body>
</html>
#define TITLE Skylark application

398
uppsrc/Skylark/Compile.cpp Normal file
View file

@ -0,0 +1,398 @@
#include "Skylark.h"
#define LLOG(x) // DLOG(x)
Value Raw(const String& s)
{
RawHtmlText r;
r.text = s;
return RawToValue(r);
}
VectorMap<String, Value (*)(const Vector<Value>&, const Renderer *)>& Compiler::functions()
{
static VectorMap<String, Value (*)(const Vector<Value>&, const Renderer *)> x;
return x;
}
void Compiler::Register(const String& id, Value (*fn)(const Vector<Value>&, 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<String>& part)
{
int args = 0;
for(int i = 0; i < part.GetCount(); i++) {
int p = (byte)*part[i];
if(p >= 0 && p < 30)
args = max(args, p + 1);
}
return args;
}
One<Exe> Compiler::Prim()
{
One<Exe> result;
if(p.Char('!'))
result = Create<ExeNot>(Prim());
else
if(p.Char('-'))
result = Create<ExeNeg>(Prim());
else
if(p.Char('+'))
result = Prim();
else
if(p.IsId()) {
String id = p.ReadId();
int n = var.Find(id);
if(p.Char('(')) {
Value (*f)(const Vector<Value>&, const Renderer *) = functions().Get(id, NULL);
if(!f) {
Vector<String> *part = GetUrlViewLinkParts(id);
if(!part)
p.ThrowError("function nor link not found '" + id + "'");
ExeLink& ln = result.Create<ExeLink>();
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<ExeFn>();
fn.fn = f;
if(!p.Char(')')) {
do
fn.arg.AddPick(Exp());
while(p.Char(','));
p.PassChar(')');
}
while(p.Char('.')) {
One<Exe> r;
ExeField& f = r.Create<ExeField>();
f.value = result;
f.id = p.ReadId();
result = r;
}
}
return result;
}
if(n < 0) {
Vector<String> *part = GetUrlViewLinkParts(id);
ExeConst& c = result.Create<ExeConst>();
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<ExeFirst>().var_index = ForVar(id, n);
else
if(p.Id("_last"))
result.Create<ExeLast>().var_index = ForVar(id, n);
else
if(p.Id("_index"))
result.Create<ExeIndex>().var_index = ForVar(id, n);
else
if(p.Id("_key"))
result.Create<ExeKey>().var_index = ForVar(id, n);
else {
result.Create<ExeVar>().var_index = n;
do {
One<Exe> r;
ExeField& f = r.Create<ExeField>();
f.value = result;
f.id = p.ReadId();
result = r;
}
while(p.Char('.'));
}
}
else
result.Create<ExeVar>().var_index = n;
}
else
if(p.Char('{')) {
ExeMap& m = result.Create<ExeMap>();
do {
m.key.AddPick(Exp());
p.PassChar(':');
m.value.AddPick(Exp());
}
while(p.Char(','));
p.PassChar('}');
}
else
if(p.Char('[')) {
ExeArray& m = result.Create<ExeArray>();
do {
m.item.AddPick(Exp());
}
while(p.Char(','));
p.PassChar(']');
}
else
if(p.Char('(')) {
result = Exp();
p.PassChar(')');
}
else {
ExeConst& c = result.Create<ExeConst>();
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<Exe> Compiler::Mul()
{
One<Exe> result = Prim();
for(;;)
if(p.Char('*'))
result = Create<ExeMul>(result, Prim());
else
if(p.Char('/'))
result = Create<ExeDiv>(result, Prim());
else
if(p.Char('%'))
result = Create<ExeMod>(result, Prim());
else
return result;
}
One<Exe> Compiler::Add()
{
One<Exe> result = Mul();
for(;;)
if(p.Char('+'))
result = Create<ExeAdd>(result, Mul());
else
if(p.Char('-'))
result = Create<ExeSub>(result, Mul());
else
return result;
}
One<Exe> Compiler::Shift()
{
One<Exe> result = Add();
for(;;)
if(p.Char3('>', '>', '>'))
result = Create<ExeSrl>(result, Add());
else
if(p.Char2('>', '>'))
result = Create<ExeSra>(result, Add());
else
if(p.Char2('<', '<'))
result = Create<ExeSll>(result, Add());
else
return result;
}
One<Exe> Compiler::Rel()
{
One<Exe> result = Shift();
for(;;)
if(p.Char2('<', '='))
result = Create<ExeLte>(result, Shift());
else
if(p.Char2('>', '='))
result = Create<ExeLte>(Shift(), result);
else
if(p.Char('<'))
result = Create<ExeLt>(result, Shift());
else
if(p.Char('>'))
result = Create<ExeLt>(Shift(), result);
else
return result;
}
One<Exe> Compiler::Eq()
{
One<Exe> result = Rel();
for(;;)
if(p.Char2('=', '='))
result = Create<ExeEq>(result, Rel());
else
if(p.Char2('!', '='))
result = Create<ExeNeq>(Rel(), result);
else
return result;
}
One<Exe> Compiler::And()
{
One<Exe> result = Eq();
while(!p.IsChar2('&', '&') && p.Char('&'))
result = Create<ExeAnd>(result, Eq());
return result;
}
One<Exe> Compiler::Xor()
{
One<Exe> result = And();
while(p.Char('^'))
result = Create<ExeXor>(result, And());
return result;
}
One<Exe> Compiler::Or()
{
One<Exe> result = Xor();
while(!p.IsChar2('|', '|') && p.Char('|'))
result = Create<ExeOr>(result, Xor());
return result;
}
One<Exe> Compiler::LogAnd()
{
One<Exe> result = Or();
while(p.Char2('&', '&'))
result = Create<ExeAnl>(result, Or());
return result;
}
One<Exe> Compiler::LogOr()
{
One<Exe> result = LogAnd();
while(p.Char2('|', '|'))
result = Create<ExeOrl>(result, LogAnd());
return result;
}
One<Exe> Compiler::Conditional()
{
One<Exe> result = LogOr();
if(p.Char('?')) {
One<Exe> r;
ExeCond& c = r.Create<ExeCond>();
c.cond = result;
c.ontrue = LogOr();
p.PassChar(':');
c.onfalse = LogOr();
result = r;
}
return result;
}
One<Exe> Compiler::Exp()
{
return Conditional();
}
void Compiler::ExeBlock::AddText(const char *b, const char *s)
{
if(s > b) {
RawHtmlText t;
t.text = String(b, s);
item.Add().Create<ExeConst>().value = RawToValue(t);
}
}
One<Exe> Compiler::Block()
{
One<Exe> result;
ExeBlock& blk = result.Create<ExeBlock>();
const char *s = p.GetSpacePtr();
const char *b = s;
int line = 1;
while(*s) {
if(*s == '$') {
if(s[1] == '$')
s += 2;
else {
blk.AddText(b, s);
p.Set(s + 1, NULL, line);
if(p.Id("if")) {
ExeCond& c = blk.item.Add().Create<ExeCond>();
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<ExeFor>();
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 = s = p.GetSpacePtr();
line = p.GetLine();
}
}
else
if(*s++ == '\n')
line++;
}
blk.AddText(b, s);
p.Set(s, NULL, line);
return result;
}
One<Exe> Compile(const char *code, const Index<String>& vars)
{
Compiler c(code, vars);
try {
One<Exe> exe = c.Block();
LLOG("Before optimization node count: " << c.GetNodeCount(exe));
c.Optimize(exe);
LLOG("After optimization node count: " << c.GetNodeCount(exe));
return exe;
}
catch(CParser::Error e) {
One<Exe> result;
result.Create<Compiler::ExeConst>().value = e;
return result;
}
}

374
uppsrc/Skylark/Dispatch.cpp Normal file
View file

@ -0,0 +1,374 @@
#include "Skylark.h"
#define LLOG(x) DLOG(x)
#define LDUMP(x) DDUMP(x)
#define LTIMING(x) RTIMING(x)
enum { DISPATCH_VARARGS = -1 };
struct DispatchNode : Moveable<DispatchNode> {
VectorMap<String, int> subnode;
void (*view)(Http&);
int argpos;
int method;
bool post_raw;
String id;
enum { GET, POST };
DispatchNode() { view = NULL; argpos = Null; method = GET; post_raw = false; }
};
static Vector<DispatchNode>& sDispatchMap()
{
static Vector<DispatchNode> x;
return x;
}
static VectorMap<String, Vector<String> >& sLinkMap()
{
static VectorMap<String, Vector<String> > x;
return x;
}
static Index<uintptr_t>& sViewIndex()
{
static Index<uintptr_t> x;
return x;
}
void DumpDispatchMap()
{
Vector<DispatchNode>& DispatchMap = sDispatchMap();
for(int i = 0; i < DispatchMap.GetCount(); i++) {
LLOG("-------------");
String sub;
for(int j = 0; j < DispatchMap[i].subnode.GetCount(); j++)
sub << DispatchMap[i].subnode.GetKey(j) << "->" << DispatchMap[i].subnode[j] << ", ";
LLOG(i << " " << (bool)DispatchMap[i].view << ": " << sub);
}
}
Vector<String> *GetUrlViewLinkParts(const String& id)
{
int q = sLinkMap().Find(id);
if(q < 0)
return NULL;
return &sLinkMap()[q];
}
String MakeLink(void (*view)(Http&), const Vector<Value>& arg)
{
int q = sViewIndex().Find((uintptr_t)view);
if(q < 0)
throw Exc("Invalid view");
if(q < 0)
return String();
StringBuffer out;
MakeLink(out, sLinkMap()[q], arg);
return out;
}
void RegisterView0(void (*view)(Http&), const char *id, String path, bool primary)
{
LLOG("RegisterView " << path);
Vector<String>& linkpart = sLinkMap().GetAdd(id);
sViewIndex().FindAdd((uintptr_t)view);
Vector<DispatchNode>& DispatchMap = sDispatchMap();
int method = DispatchNode::GET;
bool post_raw = false;
int q = path.Find(':');
if(q >= 0) {
if(path.Mid(q + 1) == "POST")
method = DispatchNode::POST;
if(path.Mid(q + 1) == "POST_RAW") {
method = DispatchNode::POST;
post_raw = true;
}
path = path.Mid(0, q);
}
Vector<String> h = Split(path, '/');
if(DispatchMap.GetCount() == 0)
DispatchMap.Add();
q = 0;
int linkargpos = 0;
for(int i = 0; i < h.GetCount(); i++) {
String s = h[i];
LLOG(" Node " << h[i]);
DispatchNode& n = DispatchMap[q];
if(*s == '*') {
int argpos = Null;
if(IsDigit(s[1]))
linkargpos = argpos = minmax(atoi(~s + 1), 0, 30);
else
if(s[1] == '*')
argpos = DISPATCH_VARARGS;
q = DispatchMap.GetCount();
LLOG(" Adding arg " << argpos << ": " << q);
n.subnode.Add(Null, q);
DispatchMap.Add();
DispatchMap[q].argpos = argpos;
if(primary)
linkpart.Add(String(linkargpos++, 1));
}
else {
if(primary)
linkpart.Add(s);
q = n.subnode.Get(s, -1);
if(q < 0) {
q = DispatchMap.GetCount();
LLOG(" Adding " << s << ": " << q);
n.subnode.Add(s, q);
DispatchMap.Add();
}
}
}
ASSERT_(!DispatchMap[q].view, "duplicate view " + String(path));
DispatchMap[q].view = view;
DispatchMap[q].method = method;
DispatchMap[q].id = id;
DispatchMap[q].post_raw = post_raw;
// DumpDispatchMap();
}
struct ViewData {
void (*view)(Http&);
String id;
String path;
};
static Array<ViewData>& sViewData()
{
static Array<ViewData> x;
return x;
}
void RegisterView(void (*view)(Http&), const char *id, const char *path)
{
Array<ViewData>& v = sViewData();
ViewData& w = v.Add();
w.view = view;
w.id = id;
w.path = path;
}
void SkylarkApp::FinalizeViews()
{
Array<ViewData>& w = sViewData();
for(int i = 0; i < w.GetCount(); i++) {
const ViewData& v = w[i];
ASSERT_(sViewIndex().Find((uintptr_t)v.view) < 0, "duplicate view function registration " + String(v.id));
Vector<String> h = Split(ReplaceVars(root + '/' + v.path, view_var, '$'), ';');
for(int i = 0; i < h.GetCount(); i++)
RegisterView0(v.view, v.id, h[i], i == 0);
}
w.Clear();
}
struct BestDispatch {
void (*view)(Http&);
int matched_parts;
int matched_params;
Vector<String>& arg;
String id;
bool post_raw;
BestDispatch(Vector<String>& arg) : arg(arg) { matched_parts = -1; matched_params = 0; view = NULL; post_raw = false; }
};
void GetBestDispatch(int method,
const Vector<String>& part, int ii, const DispatchNode& n, Vector<String>& arg,
BestDispatch& bd, int matched_parts, int matched_params)
{
Vector<DispatchNode>& DispatchMap = sDispatchMap();
if(ii >= part.GetCount()) {
if(n.view && n.method == method &&
(matched_parts > bd.matched_parts ||
matched_parts == bd.matched_parts && matched_params > bd.matched_params)) {
bd.arg <<= arg;
bd.view = n.view;
bd.matched_parts = matched_parts;
bd.id = n.id;
bd.post_raw = n.post_raw;
}
int q = n.subnode.Find(String());
while(q >= 0) {
const DispatchNode& an = DispatchMap[n.subnode[q]];
if(an.argpos == DISPATCH_VARARGS && an.view && an.method == method) {
bd.view = an.view;
bd.arg.Clear();
break;
}
q = n.subnode.FindNext(q);
}
return;
}
int qq = n.subnode.Get(part[ii], -1);
if(qq >= 0)
GetBestDispatch(method, part, ii + 1, DispatchMap[qq], arg, bd, matched_parts + 1, matched_params);
int q = n.subnode.Find(String());
while(q >= 0) {
int qq = n.subnode[q];
int ac = arg.GetCount();
const DispatchNode& an = DispatchMap[qq];
int apos = an.argpos;
LLOG(" *" << qq << " apos: " << apos);
if(apos == DISPATCH_VARARGS) {
if(an.view && an.method == method &&
(matched_parts > bd.matched_parts || matched_parts == bd.matched_parts && matched_params > bd.matched_params)) {
bd.arg <<= arg;
bd.arg.Append(part, ii, part.GetCount() - ii);
bd.view = an.view;
bd.matched_parts = matched_parts;
bd.id = an.id;
}
}
else {
String pv;
if(IsNull(apos))
arg.Add(part[ii]);
else {
String& at = arg.At(apos);
pv = at;
at = part[ii];
}
GetBestDispatch(method, part, ii + 1, an, arg, bd, matched_parts, matched_params + 1);
if(!IsNull(apos))
arg[apos] = pv;
}
arg.SetCount(ac);
q = n.subnode.FindNext(q);
}
}
void Http::Dispatch(TcpSocket& socket)
{
Vector<DispatchNode>& DispatchMap = sDispatchMap();
if(hdr.Read(socket)) {
int len = GetLength();
content = socket.GetAll(len);
LLOG("--------------------------------------------");
LLOG(hdr.GetMethod() << " " << hdr.GetURI() << "\n");
LDUMP(content);
Cout() << hdr.GetMethod() << " " << hdr.GetURI() << "\n";
String r;
var.Clear();
arg.Clear();
LTIMING("Request processing");
request_content_type = GetHeader("content-type");
String rc = ToLower(request_content_type);
bool post = hdr.GetMethod() == "POST";
if(post)
if(rc.StartsWith("application/x-www-form-urlencoded"))
ParseRequest(content);
else
if(rc.StartsWith("multipart/"))
ReadMultiPart(content);
String uri = hdr.GetURI();
int q = uri.Find('?');
if(q >= 0) {
if(!post)
ParseRequest(~uri + q + 1);
uri.Trim(q);
}
for(int i = hdr.fields.Find("cookie"); i >= 0; i = hdr.fields.FindNext(i)) {
const String& h = hdr.fields[i];
int q = 0;
for(;;) {
int qq = h.Find('=', q);
if(qq < 0)
break;
String id = ToLower(TrimBoth(h.Mid(q, qq - q)));
qq++;
DUMP(id);
q = h.Find(';', qq);
if(q < 0) {
var.Add(id, UrlDecode(h.Mid(qq)));
break;
}
var.Add(id, UrlDecode(h.Mid(qq, q - qq)));
q++;
}
}
var.GetAdd("__identity__"); // To make StdLib.icpp GetIndentity work without changing preset stack positions
DUMPM(var);
Vector<String> part = Split(uri, '/');
for(int i = 0; i < part.GetCount(); i++)
part[i] = UrlDecode(part[i]);
DUMPC(part);
Vector<String> a;
BestDispatch bd(arg);
if(DispatchMap.GetCount())
GetBestDispatch(post ? DispatchNode::POST : DispatchNode::GET, part, 0, DispatchMap[0], a, bd, 0, 0);
DUMPC(arg);
response.Clear();
if(bd.view) {
try {
SQL.Begin();
LoadSession();
session_dirty = false;
if(post && !bd.post_raw) {
String id = Nvl((*this)["__post_identity__"], (*this)["__js_identity__"]);
if(id != (*this)["__identity__"])
throw AuthExc("identity error");
}
lang = Nvl(Int("__lang__"), LNG_ENGLISH);
SetLanguage(lang);
viewid = bd.id;
(*bd.view)(*this);
if(session_dirty)
SaveSession();
SQL.Commit();
}
catch(SqlExc e) {
SQL.Rollback();
response << "Internal server error<br>"
<< "SQL ERROR: " << e;
code = 500;
code_text = "Internal server error";
app.SqlError(*this);
}
catch(AuthExc e) {
SQL.Rollback();
response << e;
code = 403;
code_text = "Unauthorized";
app.Unauthorized(*this);
}
catch(Exc e) {
SQL.Rollback();
response << "Internal server error<br>"
<< e;
code = 500;
code_text = "Internal server error";
app.InternalError(*this);
}
}
else {
response << "Page not found";
code = 404;
code_text = "Not found";
app.NotFound(*this);
}
r.Clear();
if(redirect.GetCount()) {
r << "HTTP/1.1 " << code << " Found\r\n";
r << "Location: " << redirect << "\r\n";
}
else {
r <<
"HTTP/1.0 " << code << ' ' << code_text << "\r\n"
"Date: " << WwwFormat(GetUtcTime()) << "\r\n"
"Server: U++\r\n"
"Content-Length: " << response.GetCount() << "\r\n"
"Connection: close\r\n"
"Cache-Control: no-cache\r\n"
"Content-Type: " << content_type << "\r\n";
for(int i = 0; i < cookies.GetCount(); i++)
r << cookies[i];
r << "\r\n";
}
socket.PutAll(r);
socket.PutAll(response);
}
}

396
uppsrc/Skylark/Exe.cpp Normal file
View file

@ -0,0 +1,396 @@
#include "Skylark.h"
#define LTIMING(x) // RTIMING(x)
#define LLOG(x) DLOG(x)
force_inline
bool Compiler::IsTrue(const Value& v)
{
return !(IsNull(v) || IsNumber(v) && (int)v == 0 || IsValueArray(v) && v.GetCount() == 0);
}
String TypeMismatch(const char *s)
{
return ErrorValue("<* type mismatch for '" + String(s) + "' *>");
}
Value Compiler::ExeVar::Eval(ExeContext& x) const
{
LLOG("Retrieving var no " << var_index << " = " << x.stack[var_index]);
return x.stack[var_index];
}
Value Compiler::ExeConst::Eval(ExeContext& x) const
{
return value;
}
Value Compiler::ExeArray::Eval(ExeContext& x) const
{
ValueArray va;
for(int i = 0; i < item.GetCount(); i++)
va.Add(item[i]->Eval(x));
return va;
}
Value Compiler::ExeMap::Eval(ExeContext& x) const
{
ValueMap m;
for(int i = 0; i < key.GetCount(); i++)
m.Add(key[i]->Eval(x), value[i]->Eval(x));
return m;
}
Value Compiler::ExeNot::Eval(ExeContext& x) const
{
return !IsTrue(a->Eval(x));
}
Value Compiler::ExeNeg::Eval(ExeContext& x) const
{
Value v = a->Eval(x);
if(IsNumber(v))
return -(double)v;
return TypeMismatch("unary -");
}
Value Compiler::ExeMul::Eval(ExeContext& x) const
{
Value v1 = a->Eval(x);
Value v2 = b->Eval(x);
if(IsNumber(v1) && IsNumber(v2))
return (double)v1 * (double)v2;
return TypeMismatch("*");
}
Value Compiler::ExeDiv::Eval(ExeContext& x) const
{
Value v1 = a->Eval(x);
Value v2 = b->Eval(x);
if(IsNumber(v1) && IsNumber(v2))
return (double)v1 / (double)v2;
return TypeMismatch("/");
}
Value Compiler::ExeMod::Eval(ExeContext& x) const
{
Value v1 = a->Eval(x);
Value v2 = b->Eval(x);
if(IsNumber(v1) && IsNumber(v2)) {
int m = v2;
if(m == 0)
return Null;
return (int)v1 % m;
}
return TypeMismatch("%");
}
Value Compiler::ExeAdd::Eval(ExeContext& x) const
{
Value v1 = a->Eval(x);
Value v2 = b->Eval(x);
if(IsString(v1) && IsString(v2))
return (String)v1 + (String)v2;
if(IsNumber(v1) && IsNumber(v2))
return (double)v1 + (double)v2;
if(v1.Is<RawHtmlText>() || v2.Is<RawHtmlText>()) {
RawHtmlText h;
h.text = AsString(v1) + AsString(v2);
return RawToValue(h);
}
return AsString(v1) + AsString(v2);
}
Value Compiler::ExeSub::Eval(ExeContext& x) const
{
Value v1 = a->Eval(x);
Value v2 = b->Eval(x);
if(IsNumber(v1) && IsNumber(v2))
return (double)v1 - (double)v2;
return TypeMismatch("-");
}
Value Compiler::ExeSll::Eval(ExeContext& x) const
{
Value v1 = a->Eval(x);
Value v2 = b->Eval(x);
if(IsNumber(v1) && IsNumber(v2))
return (int)v1 << min(32, (int)v2);
return TypeMismatch("<<");
}
Value Compiler::ExeSrl::Eval(ExeContext& x) const
{
Value v1 = a->Eval(x);
Value v2 = b->Eval(x);
if(IsNumber(v1) && IsNumber(v2))
return int((unsigned)(int)v1 >> min(32, (int)v2));
return TypeMismatch(">>>");
}
Value Compiler::ExeSra::Eval(ExeContext& x) const
{
Value v1 = a->Eval(x);
Value v2 = b->Eval(x);
if(IsNumber(v1) && IsNumber(v2))
return (int)v1 >> min(32, (int)v2);
return TypeMismatch(">>");
}
Value Compiler::ExeLt::Eval(ExeContext& x) const
{
Value v1 = a->Eval(x);
Value v2 = b->Eval(x);
if(IsString(v1) && IsString(v2))
return (String)v1 < (String)v2;
if(IsNumber(v1) && IsNumber(v2))
return (double)v1 < (double)v2;
return AsString(v1) < AsString(v2);
}
Value Compiler::ExeLte::Eval(ExeContext& x) const
{
Value v1 = a->Eval(x);
Value v2 = b->Eval(x);
if(IsString(v1) && IsString(v2))
return (String)v1 <= (String)v2;
if(IsNumber(v1) && IsNumber(v2))
return (double)v1 <= (double)v2;
return AsString(v1) <= AsString(v2);
}
Value Compiler::ExeEq::Eval(ExeContext& x) const
{
Value v1 = a->Eval(x);
Value v2 = b->Eval(x);
if(IsString(v1) && IsString(v2))
return (String)v1 == (String)v2;
if(IsNumber(v1) && IsNumber(v2))
return (double)v1 == (double)v2;
return AsString(v1) == AsString(v2);
}
Value Compiler::ExeNeq::Eval(ExeContext& x) const
{
Value v1 = a->Eval(x);
Value v2 = b->Eval(x);
if(IsString(v1) && IsString(v2))
return (String)v1 != (String)v2;
if(IsNumber(v1) && IsNumber(v2))
return (double)v1 != (double)v2;
return AsString(v1) != AsString(v2);
}
Value Compiler::ExeAnd::Eval(ExeContext& x) const
{
Value v1 = a->Eval(x);
Value v2 = b->Eval(x);
if(IsNumber(v1) && IsNumber(v2))
return (int)v1 & (int)v2;
return TypeMismatch("&");
}
Value Compiler::ExeXor::Eval(ExeContext& x) const
{
Value v1 = a->Eval(x);
Value v2 = b->Eval(x);
if(IsNumber(v1) && IsNumber(v2))
return (int)v1 ^ (int)v2;
return TypeMismatch("^");
}
Value Compiler::ExeOr::Eval(ExeContext& x) const
{
Value v1 = a->Eval(x);
Value v2 = b->Eval(x);
if(IsNumber(v1) && IsNumber(v2))
return (int)v1 | (int)v2;
return TypeMismatch("|");
}
Value Compiler::ExeAnl::Eval(ExeContext& x) const
{
Value v1 = a->Eval(x);
Value v2 = b->Eval(x);
return IsTrue(v1) && IsTrue(v2);
}
Value Compiler::ExeOrl::Eval(ExeContext& x) const
{
Value v1 = a->Eval(x);
Value v2 = b->Eval(x);
return IsTrue(v1) || IsTrue(v2);
}
Value Compiler::ExeCond::Eval(ExeContext& x) const
{
if(cond->Eval(x))
return ontrue->Eval(x);
else
if(onfalse)
return onfalse->Eval(x);
return Value();
}
Value Compiler::ExeField::Eval(ExeContext& x) const
{
return value->Eval(x)[id];
}
Value Compiler::ExeVarField::Eval(ExeContext& x) const
{
return x.stack[var_index][id];
}
Value Compiler::ExeFn::Eval(ExeContext& x) const
{
Vector<Value> v;
v.SetCount(arg.GetCount());
for(int i = 0; i < arg.GetCount(); i++)
v[i] = arg[i]->Eval(x);
return (*fn)(v, x.renderer);
}
Value Compiler::ExeLink::Eval(ExeContext& x) const
{
LTIMING("ExeLink");
Vector<Value> v;
v.SetCount(arg.GetCount());
for(int i = 0; i < arg.GetCount(); i++) {
LTIMING("arg eval");
v[i] = arg[i]->Eval(x);
}
StringBuffer r;
r << "\"";
MakeLink(r, *part, v);
r << "\"";
return Raw(r);
}
Value Compiler::ExeLinkVarField1::Eval(ExeContext& x) const
{
LTIMING("ExeLinkVarField");
Vector<Value> v;
v.Add(x.stack[var_index][id]);
StringBuffer r;
r << "\"";
MakeLink(r, *part, v);
r << "\"";
return Raw(r);
}
Value Compiler::ExeFirst::Eval(ExeContext& x) const
{
const LoopInfo& f = ValueTo<LoopInfo>(x.stack[var_index]);
return f.first;
}
Value Compiler::ExeLast::Eval(ExeContext& x) const
{
const LoopInfo& f = ValueTo<LoopInfo>(x.stack[var_index]);
return f.last;
}
Value Compiler::ExeIndex::Eval(ExeContext& x) const
{
const LoopInfo& f = ValueTo<LoopInfo>(x.stack[var_index]);
return f.index;
}
Value Compiler::ExeKey::Eval(ExeContext& x) const
{
const LoopInfo& f = ValueTo<LoopInfo>(x.stack[var_index]);
return f.key;
}
force_inline
static void sCatAsString(StringBuffer& out, const Value& v)
{
LTIMING("sCatAsString");
if(IsNull(v))
return;
if(v.Is<RawHtmlText>()) {
LTIMING("Cat RawHtml");
out.Cat(ValueTo<RawHtmlText>(v).text);
}
else {
const char *s;
String h;
if(v.Is<String>())
s = ValueTo<String>(v);
else {
LTIMING("AsString");
h = AsString(v);
s = h;
}
LTIMING("Escape html");
while(*s) {
if(*s == 31)
out.Cat("&nbsp;");
else
if(*s == '<')
out.Cat("&lt;");
else
if(*s == '>')
out.Cat("&gt;");
else
if(*s == '&')
out.Cat("&amp;");
else
if(*s == '\"')
out.Cat("&quot;");
else
if((byte)*s < ' ')
out.Cat(NFormat("&#%d;", (byte)*s));
else
out.Cat(*s);
s++;
}
}
}
Value Compiler::ExeFor::Eval(ExeContext& x) const
{
LTIMING("ExeFor");
Value array = value->Eval(x);
LLOG("ExeFor array: " << array);
if(array.GetCount() == 0 && onempty)
return onempty->Eval(x);
ValueMap m;
bool map = array.Is<ValueMap>();
if(map)
m = array;
int q = x.stack.GetCount();
x.stack.Add();
x.stack.Add();
for(int i = 0; i < array.GetCount(); i++) {
x.stack[q] = array[i];
LoopInfo f;
f.first = i == 0;
f.last = i == array.GetCount() - 1;
f.index = i;
f.key = map ? m.GetKeys()[i] : (Value)i;
x.stack[q + 1] = RawToValue(f);
sCatAsString(x.out, body->Eval(x));
}
x.stack.SetCount(q);
return Value();
}
Value Compiler::ExeBlock::Eval(ExeContext& x) const
{
int q = x.stack.GetCount();
for(int i = 0; i < item.GetCount(); i++)
sCatAsString(x.out, item[i]->Eval(x));
x.stack.SetCount(q);
return Value();
}
String Render(const One<Exe>& exe, Renderer *r, Vector<Value>& var)
{
LTIMING("Render0");
ExeContext x(var, r);
Value v = exe->Eval(x);
x.out.Cat(AsString(v));
return x.out;
}

340
uppsrc/Skylark/Http.cpp Normal file
View file

@ -0,0 +1,340 @@
#include "Skylark.h"
#define LLOG(x) LOG(x)
#define LTIMING(x) RTIMING(x)
Http::Http(SkylarkApp& app)
: app(app)
{
code = 200;
content_type = "text/html; charset=UTF-8";
session_dirty = false;
lang = LNG_ENGLISH;
}
void Http::ParseRequest(const char *p)
{
while(*p) {
const char *last = p;
while(*p && *p != '=' && *p != '&')
p++;
String key = UrlDecode(last, p);
if(*p == '=')
p++;
last = p;
while(*p && *p != '&')
p++;
var.GetAdd(key) = UrlDecode(last, p);
if(*p)
p++;
}
}
String HttpResponse(int code, const char *phrase, const String& data, const char *content_type,
const char *cookies)
{
String r;
r <<
"HTTP/1.0 " << code << ' ' << phrase << "\r\n"
"Date: " << WwwFormat(GetUtcTime()) << "\r\n"
"Server: U++\r\n"
"Content-Length: " << data.GetCount() << "\r\n"
"Connection: close\r\n"
<< cookies;
if(content_type)
r << "Content-Type: " << content_type << "\r\n";
r << "\r\n" << data;
return r;
}
Http& Http::SetRawCookie(const char *id, const String& value, Time expires,
const char *path, const char *domain, bool secure,
bool httponly)
{
var.GetAdd(id) = value;
String& c = cookies.GetAdd(id);
c.Clear();
c << "Set-Cookie:" << ' ' << id << '=' << value;
if(!IsNull(expires))
c << "; " << WwwFormat(expires);
c << "; Path=" << (path && *path ? path : "/");
if(domain && *domain)
c << "; Domain=" << domain;
if(secure)
c << "; Secure";
if(httponly)
c << "; HttpOnly";
c << "\r\n";
return *this;
}
int Http::Int(const char *id) const
{
Value v = operator[](id);
if(v.Is<int>())
return v;
if(IsString(v))
return ScanInt((String)v);
if(IsNull(v))
return Null;
if(IsNumber(v)) {
double d = v;
if(d > INT_MIN && d <= INT_MAX)
return (int)d;
}
return Null;
}
int Http::Int(int i) const
{
return ScanInt(operator[](i));
}
String HttpAsString(const Value& v)
{
if(v.Is<RawHtmlText>())
return v.To<RawHtmlText>().text;
return AsString(v);
}
Http& Http::Content(const char *s, const Value& data)
{
content_type = s;
response = HttpAsString(data);
return *this;
}
Http& Http::operator<<(const Value& s)
{
response << HttpAsString(s);
return *this;
}
Http& Http::SetCookie(const char *id, const String& value, Time expires,
const char *path, const char *domain, bool secure, bool httponly)
{
return SetRawCookie(id, UrlEncode(value), expires, path, domain, secure);
}
void Http::ReadMultiPart(const String& buffer)
{
const char *p = buffer;
while(p[0] != '-' || p[1] != '-') {
while(*p != '\n')
if(*p++ == 0)
return; // end of file, boundary not found
p++;
}
String delimiter;
{ // read multipart delimiter
const char *b = (p += 2);
while(*p && *p++ != '\n')
;
const char *e = p;
while(e > b && (byte)e[-1] <= ' ')
e--;
delimiter = String(b, e);
}
int delta = 4 + delimiter.GetLength();
const char *e = buffer.End();
if(e - p < delta)
return;
e -= delta;
while(p < e) { // read individual parts
String filename, content_type, name;
while(!MemICmp(p, "content-", 8)) { // parse content specifiers
p += 8;
if(!MemICmp(p, "disposition:", 12)) {
p += 12;
while(*p && *p != '\n')
if((byte)*p <= ' ')
p++;
else { // fetch key-value pair
const char *kp = p;
while(*p && *p != '\n' && *p != '=' && *p != ';')
p++;
const char *ke = p;
String value;
if(*p == '=') {
const char *b = ++p;
if(*p == '\"') { // quoted value
b++;
while(*++p && *p != '\n' && *p != '\"')
;
value = String(b, p);
if(*p == '\"')
p++;
}
else {
while(*p && *p != '\n' && *p != ';')
p++;
value = String(b, p);
}
}
if(ke - kp == 4 && !MemICmp(kp, "name", 4))
name = value;
else if(ke - kp == 8 && !MemICmp(kp, "filename", 8))
filename = value;
if(*p == ';')
p++;
}
}
else if(!MemICmp(p, "type:", 5)) {
p += 5;
while(*p && *p != '\n' && (byte)*p <= ' ')
p++;
const char *b = p;
while(*p && *p != '\n')
p++;
const char *e = p;
while(e > b && (byte)e[-1] <= ' ')
e--;
content_type = String(b, e);
}
;
while(*p && *p++ != '\n')
;
}
if(*p++ != '\r' || *p++ != '\n')
return;
const char *b = p;
while(p < e) {
p = (const char *)memchr(p, '\r', e - p);
if(!p)
return;
if(p[0] == '\r' && p[1] == '\n' && p[2] == '-' && p[3] == '-'
&& !memcmp(p + 4, delimiter, delimiter.GetLength()))
break;
p++;
}
if(!name.IsEmpty()) { // add variables
if(!filename.IsEmpty())
var.GetAdd(name + ".filename") = filename;
if(!content_type.IsEmpty())
var.GetAdd(name + ".content_type") = content_type;
var.Add(name, String(b, p));
}
p += delta;
while(*p && *p++ != '\n')
;
}
}
static const char hex_digits[] = "0123456789ABCDEF";
void UrlEncode(StringBuffer& out, const String& s)
{
static bool ok[256];
ONCELOCK {
for(int ch = 0; ch < 256; ch++)
ok[ch] = IsAlNum(ch) || ch == ',' || ch == '.' || ch == '-' || ch == '_';
}
const char *p = s, *e = s.End();
for(; p < e; p++)
{
const char *b = p;
while(p < e && ok[(byte)*p])
p++;
if(p > b)
out.Cat(b, int(p - b));
if(p >= e)
break;
if(*p == ' ')
out << '+';
else
out << '%' << hex_digits[(*p >> 4) & 15] << hex_digits[*p & 15];
}
}
void MakeLink(StringBuffer& out, const Vector<String>& part, const Vector<Value>& arg)
{
LTIMING("MakeLink");
out.Cat("/");
for(int i = 0; i < part.GetCount(); i++) {
const String& p = part[i];
if(i)
out << '/';
int q = (byte)*p;
if(q < 32) {
if(q >= 0 && q < arg.GetCount())
UrlEncode(out, AsString(arg[q]));
}
else
UrlEncode(out, p);
}
bool get = false;
for(int i = 0; i < arg.GetCount(); i++)
if(IsValueMap(arg[i])) {
if(get)
out << '&';
else
out << '?';
get = true;
ValueMap m = arg[i];
for(int i = 0; i < m.GetCount(); i++) {
if(i)
out << '&';
UrlEncode(out, AsString(m.GetKeys()[i]));
out << '=';
UrlEncode(out, AsString(m.GetValues()[i]));
}
}
}
Http& Http::RenderResult(const char *template_name)
{
LTIMING("Render");
response << ::Render(GetTemplate(template_name), this, var.GetValues());
return *this;
}
Http& Http::Redirect(void (*view)(Http&), const Vector<Value>& arg)
{
Redirect(MakeLink(view, arg));
return *this;
}
Http& Http::Redirect(void (*view)(Http&))
{
Vector<Value> arg;
Redirect(view, arg);
return *this;
}
Http& Http::Redirect(void (*view)(Http&), const Value& v1)
{
Vector<Value> arg;
arg.Add(v1);
Redirect(view, arg);
return *this;
}
Http& Http::Redirect(void (*view)(Http&), const Value& v1, const Value& v2)
{
Vector<Value> arg;
arg.Add(v1);
arg.Add(v2);
Redirect(view, arg);
return *this;
}
Http& Http::Ux(const char *id, const String& text)
{
if(response.GetCount())
response << '\1';
response << id << ':' << text;
return *this;
}
Http& Http::UxRender(const char *id, const char *template_name)
{
Ux(id, RenderString(template_name));
return *this;
}
Http& Http::UxSetValue(const char *id, const String& value)
{
Ux(String(">") + id, value);
return *this;
}

142
uppsrc/Skylark/Http.h Normal file
View file

@ -0,0 +1,142 @@
void MakeLink(StringBuffer& out, const Vector<String>& part, const Vector<Value>& arg);
class Renderer {
protected:
VectorMap<String, Value> var;
int lang;
Renderer& Link(const char *id, void (*view)(Http&), const Vector<Value>& arg);
const One<Exe>& GetTemplate(const char *template_name);
public:
Renderer& operator()(const char *id, const Value& v) { var.Add(id, v); return *this; }
Renderer& operator()(const ValueMap& map);
Renderer& operator()(const char *id, void (*view)(Http&));
Renderer& operator()(const char *id, void (*view)(Http&), const Value& arg1);
Renderer& operator()(const char *id, void (*view)(Http&), const Value& arg1, const Value& arg2);
Renderer& operator()(const Sql& sql);
Renderer& operator()(Fields rec);
SqlUpdate Update(SqlId table);
SqlInsert Insert(SqlId table);
Value operator[](const char *id) const { return var.Get(id, Null); }
String RenderString(const String& template_name);
Value Render(const String& template_name) { return Raw(RenderString(template_name)); }
Renderer& Render(const char *id, const String& template_name);
Renderer() { lang = LNG_ENGLISH; }
virtual ~Renderer();
};
class Http : public Renderer {
SkylarkApp& app;
HttpHeader hdr;
String content;
String viewid;
Vector<String> arg;
String session_id;
VectorMap<String, Value> session_var;
bool session_dirty;
String redirect;
int code;
String code_text;
String response;
String content_type;
String request_content_type;
VectorMap<String, String> cookies;
void ParseRequest(const char *s);
void ReadMultiPart(const String& content);
String SessionFile(const String& sid);
void LoadSession();
void SaveSession();
public:
Http& operator()(const char *id, const Value& v) { var.Add(id, v); return *this; }
Http& operator()(const ValueMap& map) { Renderer::operator()(map); return *this; }
Http& operator()(const char *id, void (*view)(Http&)) { Renderer::operator()(id, view); return *this; }
Http& operator()(const char *id, void (*view)(Http&), const Value& arg1) { Renderer::operator()(id, view, arg1); return *this; }
Http& operator()(const char *id, void (*view)(Http&), const Value& arg1, const Value& arg2) { Renderer::operator()(id, view, arg1, arg2); return *this; }
Http& operator()(const Sql& sql) { Renderer::operator()(sql); return *this; }
Http& operator()(Fields rec) { Renderer::operator()(rec); return *this; }
Http& Render(const char *id, const String& template_name) { Renderer::Render(id, template_name); return *this; }
Value Render(const String& template_name) { return Renderer::Render(template_name); }
void Dispatch(TcpSocket& socket);
String GetHeader(const char *s) const { return hdr[s]; }
int GetLength() const { return atoi(GetHeader("content-length")); }
String GetViewId() const { return viewid; }
Value operator[](const char *id) const { return Renderer::operator[](id); }
String operator[](int i) const { return i >= 0 && i < arg.GetCount() ? arg[i] : String(); }
int Int(const char *id) const;
int Int(int i) const;
int GetParamCount() const { return arg.GetCount(); }
Http& ContentType(const char *s) { content_type = s; return *this; }
Http& Content(const char *s, const Value& data);
Http& operator<<(const Value& s);
Http& SetRawCookie(const char *id, const String& value,
Time expires = Null, const char *path = NULL,
const char *domain = NULL, bool secure = false, bool httponly = false);
Http& SetCookie(const char *id, const String& value,
Time expires = Null, const char *path = NULL,
const char *domain = NULL, bool secure = false, bool httponly = false);
Http& ClearSession();
Http& SessionSet(const char *id, const Value& value);
Http& NewIdentity() { SessionSet("__identity__", Null); return *this; }
Http& NewSessionId();
Http& SetLanguage(int lang);
Http& Response(int code_, const String& ctext) { code = code_; code_text = ctext; return *this; }
Http& RenderResult(const char *template_name);
Http& Redirect(const char *url, int code_ = 302) { code = code_; redirect = url; return *this; }
Http& Redirect(void (*view)(Http&), const Vector<Value>& arg);
Http& Redirect(void (*view)(Http&));
Http& Redirect(void (*view)(Http&), const Value& v1);
Http& Redirect(void (*view)(Http&), const Value& v1, const Value& v2);
Http& Ux(const char *id, const String& text);
Http& UxRender(const char *id, const char *template_name);
Http& UxSetValue(const char *id, const String& value);
String GetResponse() const { return response; }
const SkylarkApp& App() const { return app; }
Http(SkylarkApp& app);
};
String HttpResponse(int code, const char *phrase, const String& data, const char *content_type = NULL);
void RegisterView(void (*view)(Http&), const char *id, const char *path);
#define URL_VIEW(name, path) void name(Http& http); INITBLOCK { RegisterView(name, #name, path); } void name(Http& http)
#define SKYLARK(name, path) void name(Http& http); INITBLOCK { RegisterView(name, #name, path); } void name(Http& http)
Vector<String> *GetUrlViewLinkParts(const String& id);
String MakeLink(void (*view)(Http&), const Vector<Value>& arg);
enum {
SESSION_FORMAT_BINARY, SESSION_FORMAT_JSON, SESSION_FORMAT_XML
};

156
uppsrc/Skylark/Optimize.cpp Normal file
View file

@ -0,0 +1,156 @@
#include "Skylark.h"
#define LLOG(x) // DLOG(x)
void Compiler::Iterate(Vector< One<Exe> >& a, Callback1< One<Exe>& > op)
{
for(int i = 0; i < a.GetCount(); i++)
op(a[i]);
}
void Compiler::OptimizeConst(One<Exe>& exe)
{
One<Exe> oxe;
Vector<Value> stack;
ExeContext x(stack);
oxe.Create<ExeConst>().value = exe->Eval(x);
LLOG("OPTIMIZED constant: " << exe->Eval(stack, out));
exe = oxe;
optimized = true;
}
void Compiler::Iterate(One<Exe>& exe, Callback1< One<Exe>& > op)
{
if(ExeBlock *e = dynamic_cast<ExeBlock *>(~exe)) {
Iterate(e->item, op);
return;
}
if(ExeFor *e = dynamic_cast<ExeFor *>(~exe)) {
op(e->value);
op(e->body);
op(e->onempty);
return;
}
if(ExeLink *e = dynamic_cast<ExeLink *>(~exe)) {
Iterate(e->arg, op);
return;
}
if(ExeFn *e = dynamic_cast<ExeFn *>(~exe)) {
Iterate(e->arg, op);
return;
}
if(ExeField *e = dynamic_cast<ExeField *>(~exe)) {
op(e->value);
return;
}
if(ExeCond *e = dynamic_cast<ExeCond *>(~exe)) {
op(e->cond);
op(e->ontrue);
op(e->onfalse);
return;
}
if(ExeMap *e = dynamic_cast<ExeMap *>(~exe)) {
Iterate(e->key, op);
Iterate(e->value, op);
return;
}
if(ExeArray *e = dynamic_cast<ExeArray *>(~exe)) {
Iterate(e->item, op);
return;
}
if(Exe2 *e = dynamic_cast<Exe2 *>(~exe)) {
op(e->a);
op(e->b);
return;
}
if(Exe1 *e = dynamic_cast<Exe1 *>(~exe)) {
op(e->a);
return;
}
}
void Compiler::CountNodes(One<Exe>& exe)
{
count_node++;
Iterate(exe, THISBACK(CountNodes));
}
int Compiler::GetNodeCount(One<Exe>& exe)
{
count_node = 0;
CountNodes(exe);
return count_node;
}
void Compiler::Optimize(One<Exe>& exe)
{
if(!exe)
return;
bool optimized2 = false;
#ifdef _DEBUG0
String name = "Optimizing " + String(typeid(*~exe).name());
LLOGBLOCK(name);
#endif
do {
optimized = false;
Iterate(exe, THISBACK(Optimize));
if(ExeField *e = dynamic_cast<ExeField *>(~exe)) {
if(ExeVar *e1 = dynamic_cast<ExeVar *>(~e->value)) {
One<Exe> oxe;
ExeVarField& vf = oxe.Create<ExeVarField>();
vf.id = e->id;
vf.var_index = e1->var_index;
exe = oxe;
optimized = true;
LLOG("OPTIMIZED ExeVarField");
}
}
if(ExeLink *e = dynamic_cast<ExeLink *>(~exe)) {
ExeVarField *e1;
if(e->arg.GetCount() == 1 && (e1 = dynamic_cast<ExeVarField *>(~e->arg[0]))) {
One<Exe> oxe;
ExeLinkVarField1& o = oxe.Create<ExeLinkVarField1>();
o.id = e1->id;
o.var_index = e1->var_index;
o.part = e->part;
exe = oxe;
optimized = true;
LLOG("OPTIMIZED ExeLinkVarField1");
}
}
if(ExeBlock *e = dynamic_cast<ExeBlock *>(~exe)) {
Vector< One<Exe> >& m = e->item;
int i = 0;
while(i < m.GetCount() - 1) {
ExeConst *e1 = dynamic_cast<ExeConst *>(~m[i]);
ExeConst *e2 = dynamic_cast<ExeConst *>(~m[i + 1]);
if(e1 && e2 && e1->value.Is<RawHtmlText>() && e2->value.Is<RawHtmlText>()) {
RawHtmlText t;
t.text = ValueTo<RawHtmlText>(e1->value).text + ValueTo<RawHtmlText>(e2->value).text;
One<Exe> oxe;
oxe.Create<ExeConst>().value = RawToValue(t);
m[i] = oxe;
m.Remove(i + 1);
optimized = true;
LLOG("OPTIMIZED ExeBlock constant folding");
}
else
i++;
}
}
if(Exe2 *e = dynamic_cast<Exe2 *>(~exe)) {
if(dynamic_cast<ExeConst *>(~e->a) && dynamic_cast<ExeConst *>(~e->b))
OptimizeConst(exe);
}
else
if(Exe1 *e = dynamic_cast<Exe1 *>(~exe)) {
if(dynamic_cast<ExeConst *>(~e->a))
OptimizeConst(exe);
}
optimized2 = optimized2 || optimized;
LLOG("---------");
}
while(optimized);
optimized = optimized2;
}

View file

@ -0,0 +1,126 @@
#include "Skylark.h"
String GetFileOnPath1(const char *file, const char *path)
{
String r = GetFileOnPath(file, path);
return r;
}
String LoadTemplate(const char *file, const String& search_path, int lang)
{
String f = file;
String path = GetFileOnPath1(f + '.' + ToLower(LNGAsText(lang)) + ".witz", search_path);
if(IsNull(path)) {
path = GetFileOnPath1(f + ".witz", search_path);
if(IsNull(path)) {
path = GetFileOnPath1(f, search_path);
if(IsNull(path))
return Null;
}
}
FileIn in(path);
String r;
while(in && !in.IsEof()) {
String line = in.GetLine();
CParser p(line);
if(p.Char('#') && p.Id("include")) {
String file = p.GetPtr();
int q = file.Find(' ');
if(q < 0)
q = file.Find('\t');
if(q >= 0)
file = file.Mid(0, q);
r << LoadTemplate(file, GetFileFolder(path) + ';' + search_path, lang);
}
else
r << line;
r << "\r\n";
}
return r;
}
VectorMap<String, String> GetTemplateDefs(const char *file, int lang)
{
String s = LoadTemplate(file, SkylarkApp::Config().path, lang);
VectorMap<String, String> def;
int ti = def.FindAdd("MAIN");
StringStream ss(s);
while(!ss.IsEof()) {
String line = ss.GetLine();
CParser p(line);
if(p.Char('#') && p.Id("define")) {
String id = "MAIN";
if(p.IsId())
id = p.ReadId();
ti = def.FindAdd(id);
def[ti].Clear();
def[ti] << p.GetPtr();
}
else
def[ti] << line << "\r\n";
}
return def;
}
int CharFilterIsCrLf(int c)
{
return c == '\r' || c == '\n' ? c : 0;
}
String ReplaceVars(const String& src, const VectorMap<String, String>& def, int chr)
{
Index<String> expanded;
String r = src;
bool again;
do {
again = false;
String rr;
const char *s = ~r;
for(;;) {
const char *q = strchr(s, chr);
if(q) {
rr.Cat(s, q);
CParser p(q + 1);
if(p.Char(chr) || !p.IsId())
rr << (char)20;
else {
String id = p.ReadId();
if(expanded.Find(id) >= 0)
rr.Cat(q, p.GetSpacePtr());
else {
rr.Cat(def.Get(id, Null));
expanded.Add(id);
again = true;
}
}
s = p.GetSpacePtr();
}
else {
rr.Cat(s, r.End());
break;
}
}
r = rr;
}
while(again);
return r;
}
int CharFilter20toHash(int c)
{
return c == 20 ? '#' : c;
}
String GetPreprocessedTemplate(const String& name, int lang)
{
String id = "MAIN";
String file = name;
int q = file.Find(':');
if(q >= 0) {
id = file.Mid(q + 1);
file = file.Mid(0, q);
}
VectorMap<String, String> def = GetTemplateDefs(file, lang);
String r = Filter(ReplaceVars(def.Get(id, Null), def, '#'), CharFilter20toHash);
return Join(Split(r, CharFilterIsCrLf), "\r\n");
}

View file

@ -0,0 +1,76 @@
#include "Skylark.h"
#define LLOG(x) DLOG(x)
#define LTIMING(x) // RTIMING(x)
Renderer& Renderer::operator()(const ValueMap& map)
{
ValueArray v = map.GetValues();
const Index<Value>& k = map.GetKeys();
for(int i = 0; i < map.GetCount(); i++)
var.Add(k[i], v[i]);
return *this;
}
Renderer& Renderer::Link(const char *id, void (*view)(Http&), const Vector<Value>& arg)
{
var.Add(id, Raw('\"' + MakeLink(view, arg) + '\"'));
return *this;
}
Renderer& Renderer::operator()(const char *id, void (*view)(Http&))
{
return Link(id, view, Vector<Value>());
}
Renderer& Renderer::operator()(const char *id, void (*view)(Http&), const Value& arg1)
{
return Link(id, view, Vector<Value>() << arg1);
}
Renderer& Renderer::operator()(const char *id, void (*view)(Http&), const Value& arg1, const Value& arg2)
{
return Link(id, view, Vector<Value>() << arg1 << arg2);
}
StaticMutex template_cache_lock;
ArrayMap<String, One<Exe> > template_cache;
const One<Exe>& Renderer::GetTemplate(const char *template_name)
{
LTIMING("GetTemplate");
StringBuffer s;
{
LTIMING("MakeSignature");
for(int i = 0; i < var.GetCount(); i++)
s << var.GetKey(i) << ';';
s << ':' << template_name;
}
String sgn = s;
LLOG("Trying to retrieve " << sgn << " from cache");
DDUMPM(var);
Mutex::Lock __(template_cache_lock);
int q = template_cache.Find(sgn);
if(q >= 0 && SkylarkApp::Config().use_caching)
return template_cache[q];
LLOG("About to compile: " << sgn);
LTIMING("Compile");
One<Exe>& exe = q >= 0 ? template_cache[q] : template_cache.Add(sgn);
exe = Compile(GetPreprocessedTemplate(template_name, lang), var.GetIndex());
return exe;
}
String Renderer::RenderString(const String& template_name)
{
return ::Render(GetTemplate(template_name), this, var.GetValues());
}
Renderer& Renderer::Render(const char *id, const String& template_name)
{
var.Add(id, Render(template_name));
return *this;
}
Renderer::~Renderer()
{
}

152
uppsrc/Skylark/Session.cpp Normal file
View file

@ -0,0 +1,152 @@
#include "Skylark.h"
#define LLOG(x) //DLOG(x)
#define LDUMPC(x) //DDUMPC(x)
#define LDUMPM(x) //DDUMPM(x)
#define LLOGHEX(x) //DLOGHEX(x)
SessionConfig::SessionConfig()
{
cookie = "__skylark_session_cookie__";
dir = ConfigFile("session");
format = SESSION_FORMAT_BINARY;
id_column = "ID";
data_column = "DATA";
lastwrite_column = "LASTWRITE";
expire = 3600 * 24 * 365; // one year to expire the session
}
String Http::SessionFile(const String& sid)
{
ONCELOCK
RealizeDirectory(app.session.dir);
return AppendFileName(app.session.dir, sid);
}
void Http::LoadSession()
{
const SessionConfig& cfg = app.session;
session_var.Clear();
session_id = (*this)[cfg.cookie];
if(IsNull(session_id))
return;
String data;
if(cfg.table.IsNull())
data = LoadFile(SessionFile(session_id));
else
data = SQLR % Select(cfg.data_column).From(cfg.table)
.Where(cfg.id_column == session_id);
LLOGHEX(data);
switch(cfg.format) {
case SESSION_FORMAT_JSON:
LoadFromJson(session_var, data);
break;
case SESSION_FORMAT_XML:
LoadFromXML(session_var, data);
break;
case SESSION_FORMAT_BINARY:
LoadFromString(session_var, data);
break;
}
LLOG("Loaded session: " << session_id);
LDUMPM(session_var);
for(int i = 0; i < session_var.GetCount(); i++)
var.Add(session_var.GetKey(i), session_var[i]);
}
thread__ int s_exp;
void Http::SaveSession()
{
DDUMPM(session_var);
const SessionConfig& cfg = app.session;
SetCookie(cfg.cookie, session_id);
if(IsNull(session_id))
return;
String data;
switch(cfg.format) {
case SESSION_FORMAT_JSON:
data = StoreAsJson(session_var);
break;
case SESSION_FORMAT_XML:
data = StoreAsXML(session_var, "session");
break;
case SESSION_FORMAT_BINARY:
data = StoreAsString(session_var);
break;
}
if(cfg.table.IsNull())
SaveFile(SessionFile(session_id), data);
else {
SqlVal d = SqlBinary(data);
Time tm = GetSysTime();
SQL * Update(cfg.table)
(cfg.data_column, d)
(cfg.lastwrite_column, tm)
.Where(cfg.id_column == session_id);
if(SQL.GetRowsProcessed() == 0)
SQL * Insert(cfg.table)
(cfg.id_column, session_id)
(cfg.data_column, d)
(cfg.lastwrite_column, tm);
}
LLOG("Stored session: " << session_id);
LDUMPM(session_var);
if((s_exp++ % 1000) == 0) {
Time tm = GetSysTime() - cfg.expire;
LLOG("Expiring sessions older than " << tm);
if(cfg.table.IsNull()) {
FindFile ff(AppendFileName(cfg.dir, "*.*"));
Vector<String> todelete;
while(ff) {
DDUMP(ff.GetPath());
DDUMP(Time(ff.GetLastWriteTime()));
if(ff.GetLastWriteTime() < tm)
todelete.Add(ff.GetPath());
ff.Next();
}
DDUMPC(todelete);
for(int i = 0; i < todelete.GetCount(); i++)
FileDelete(todelete[i]);
}
else
SQL * Delete(cfg.table).Where(cfg.lastwrite_column < tm);
}
}
Http& Http::ClearSession()
{
session_var.Clear();
session_id.Clear();
return *this;
}
Http& Http::SessionSet(const char *id, const Value& value)
{
DLOG("SessionSet " << id << " = " << value);
if(IsNull(session_id))
NewSessionId();
session_var.GetAdd(id) = value;
var.GetAdd(id) = value;
DDUMPM(var);
session_dirty = true;
return *this;
}
Http& Http::NewSessionId()
{
session_id = AsString(Uuid::Create());
session_dirty = true;
return *this;
}
Http& Http::SetLanguage(int lang_)
{
DDUMP(lang_);
lang = lang_;
Upp::SetLanguage(lang_);
SessionSet("__lang__", lang);
SessionSet("language", ToLower(LNGAsText(lang)));
return *this;
}

90
uppsrc/Skylark/Skylark.h Normal file
View file

@ -0,0 +1,90 @@
#ifndef _Wpp_Wpp_h
#define _Wpp_Wpp_h
#include <Draw/Draw.h>
#include <plugin/png/png.h>
#include <plugin/jpg/jpg.h>
#include <Sql/Sql.h>
using namespace Upp;
class Renderer;
class Http;
struct SessionConfig {
String cookie;
String dir;
int format;
SqlId table, id_column, data_column, lastwrite_column;
int expire;
SessionConfig();
};
struct AuthExc : Exc {
AuthExc(const String& s) : Exc(s) {}
};
struct SkylarkConfig {
String root;
VectorMap<String, String> view_var;
String path;
SessionConfig session;
int threads;
int port;
int prefork;
int timeout;
bool use_caching;
};
class SkylarkApp : protected SkylarkConfig {
TcpSocket server;
Mutex accept_mutex;
int main_pid;
Vector<int> child_pid;
bool quit;
void ThreadRun();
void Broadcast(int signal);
void Signal(int signal);
static void SignalHandler(int signal);
void Main();
void FinalizeViews();
static SkylarkApp *app;
#ifdef PLATFORM_WIN32
static BOOL WINAPI CtrlCHandlerRoutine(__in DWORD dwCtrlType);
#endif
typedef SkylarkApp CLASSNAME;
friend class Http;
public:
virtual void SqlError(Http& http);
virtual void InternalError(Http& http);
virtual void NotFound(Http& http);
virtual void Unauthorized(Http& http);
virtual void WorkThread() = 0;
void RunThread();
void Run();
static SkylarkApp& TheApp();
static const SkylarkConfig& Config();
SkylarkApp();
virtual ~SkylarkApp();
};
void SetStaticPath(const String& path);
#include "Witz.h"
#include "Http.h"
#endif

View file

@ -0,0 +1,45 @@
description "\377B128,0,0";
uses
Draw,
plugin\png,
plugin\jpg,
Sql;
file
Skylark.h,
App.cpp,
Witz.h,
Preprocess.cpp,
Compile.cpp,
Optimize.cpp,
Exe.cpp,
Http.h,
Renderer.cpp,
Sql.cpp,
Http.cpp,
Session.cpp,
Dispatch.cpp,
StdLib.icpp,
Util readonly separator,
Static.icpp,
Client readonly separator,
Base.witz,
skylark.js;
custom() ".css",
"cp $(PATH) $(EXEDIR)/static/$(PACKAGE)/$(RELPATH)",
"$(EXEDIR)/static/$(PACKAGE)/$(RELPATH)";
custom() ".html",
"cp $(PATH) $(EXEDIR)/static/$(PACKAGE)/$(RELPATH)",
"$(EXEDIR)/static/$(PACKAGE)/$(RELPATH)";
custom() ".js",
"cp $(PATH) $(EXEDIR)/static/$(PACKAGE)/$(RELPATH)",
"$(EXEDIR)/static/$(PACKAGE)/$(RELPATH)";
custom() ".whtml",
"cp $(PATH) $(EXEDIR)/template/$(PACKAGE)/$(RELPATH)",
"$(EXEDIR)/template/$(PACKAGE)/$(RELPATH)";

56
uppsrc/Skylark/Sql.cpp Normal file
View file

@ -0,0 +1,56 @@
#include "Skylark.h"
struct sFieldsToRenderer : public FieldOperator {
Renderer& http;
void Field(const char *name, Ref f) {
http(name, f);
}
sFieldsToRenderer(Renderer& http) : http(http) {}
};
Renderer& Renderer::operator()(Fields rec)
{
sFieldsToRenderer x(*this);
rec(x);
return *this;
}
Renderer& Renderer::operator()(const Sql& sql)
{
int n = sql.GetColumns();
for(int i = 0; i < n; i++)
(*this)(sql.GetColumnInfo(i).name, sql[i]);
return *this;
}
SqlUpdate Renderer::Update(SqlId table)
{
Vector<String> col = GetSchColumns(~table);
SqlUpdate u(table);
for(int i = 0; i < col.GetCount(); i++) {
String c = col[i];
int q = var.Find(c);
if(q < 0)
q = var.Find(ToLower(c));
if(q >= 0)
u(c, var[q]);
}
return u;
}
SqlInsert Renderer::Insert(SqlId table)
{
Vector<String> col = GetSchColumns(~table);
SqlInsert y(table);
for(int i = 0; i < col.GetCount(); i++) {
String c = col[i];
int q = var.Find(c);
if(q < 0)
q = var.Find(ToLower(c));
if(q >= 0)
y(c, var[q]);
}
return y;
}

View file

@ -0,0 +1,46 @@
#include "Skylark.h"
// add static page caching?
SKYLARK(ServeStaticPage, "static/**")
{
String file;
for(int i = 0; i < http.GetParamCount(); i++) {
if(i)
file << '/';
file << http[i];
}
DDUMP(file);
DDUMP(SkylarkApp::Config().path);
String path = GetFileOnPath(file, SkylarkApp::Config().path, false);
if(path.GetCount()) {
String ext = ToLower(GetFileExt(file));
DDUMP(ext);
String type = "text";
if(ext == ".css")
type = "text/css";
else
if(ext == ".js")
type = "text/javascript";
else
if(ext == ".png" || ext == ".jpg" || ext == ".gif")
type = "image/" + ext.Mid(1);
http.Content(type, LoadFile(path));
}
}
SKYLARK(ServeIml, "iml/*")
{
String name = http[0];
int q = name.Find('.');
String ext;
if(q >= 0) {
ext = name.Mid(q + 1);
name = name.Mid(0, q);
}
Image m = GetImlImage(name);
if(ext == "jpg" || ext == "JPG" || ext == "jpeg" || ext == "JPEG")
http.Content("image/jpeg", JPGEncoder().SaveString(m));
else
http.Content("image/png", PNGEncoder().SaveString(m));
}

View file

@ -0,0 +1,47 @@
#include "Skylark.h"
Value Cycle(const Vector<Value>& arg, const Renderer *)
{
if(arg.GetCount() < 3 && !IsNumber(arg[0]))
return String();
return arg[1 + int(arg[0]) % (arg.GetCount() - 1)];
}
Value RawFn(const Vector<Value>& arg, const Renderer *)
{
RawHtmlText r;
for(int i = 0; i < arg.GetCount(); i++)
r.text.Cat(AsString(arg[i]));
return RawToValue(r);
}
String GetIdentity(const Renderer *r)
{
// This ugly hack expects that __identity__ is always present in r->var
Http *http = const_cast<Http *>(dynamic_cast<const Http *>(r));
if(!http)
throw Exc("invalid POST identity call");
String s = (*http)["__identity__"];
if(s.GetCount())
return s;
s = AsString(Uuid::Create());
http->SessionSet("__identity__", s);
return s;
}
Value PostIdentity(const Vector<Value>&, const Renderer *r)
{
return Raw("<input type=\"hidden\" name=\"__post_identity__\" value=\"" + GetIdentity(r) + "\">");
}
Value JsIdentity(const Vector<Value>&, const Renderer *r)
{
return Raw("<script type=\"text/javascript\">var __js_identity__ = \"" + GetIdentity(r) + "\"</script>");
}
INITBLOCK {
Compiler::Register("cycle", Cycle);
Compiler::Register("raw", RawFn);
Compiler::Register("post_identity", PostIdentity);
Compiler::Register("js_identity", JsIdentity);
};

228
uppsrc/Skylark/Witz.h Normal file
View file

@ -0,0 +1,228 @@
struct ExeContext {
const Renderer *renderer;
Vector<Value>& stack;
StringBuffer out;
ExeContext(Vector<Value>& stack, const Renderer *r = NULL) : renderer(r), stack(stack) {}
};
struct Exe {
virtual Value Eval(ExeContext& x) const { return Value(); }
virtual ~Exe() {}
};
struct RawHtmlText {
String text;
};
Value Raw(const String& s);
struct Compiler {
static VectorMap<String, Value (*)(const Vector<Value>&, const Renderer *)>& functions();
static bool IsTrue(const Value& v);
struct Exe1 : Exe {
One<Exe> a;
};
template <class T>
static One<Exe> Create(One<Exe> a) {
One<Exe> rr;
T& m = rr.Create<T>();
m.a = a;
return rr;
}
struct Exe2 : Exe {
One<Exe> a;
One<Exe> b;
};
template <class T>
static One<Exe> Create(One<Exe> a, One<Exe> b) {
One<Exe> rr;
T& m = rr.Create<T>();
m.a = a;
m.b = b;
return rr;
}
struct ExeVar : Exe {
int var_index;
virtual Value Eval(ExeContext& x) const;
};
struct ExeConst : Exe {
Value value;
virtual Value Eval(ExeContext& x) const;
};
struct ExeArray : Exe {
Vector< One<Exe> > item;
virtual Value Eval(ExeContext& x) const;
};
struct ExeMap : Exe {
Vector< One<Exe> > key;
Vector< One<Exe> > value;
virtual Value Eval(ExeContext& x) const;
};
struct ExeNot : Exe1 { virtual Value Eval(ExeContext& x) const; };
struct ExeNeg : Exe1 { virtual Value Eval(ExeContext& x) const; };
struct ExeMul : Exe2 { virtual Value Eval(ExeContext& x) const; };
struct ExeDiv : Exe2 { virtual Value Eval(ExeContext& x) const; };
struct ExeMod : Exe2 { virtual Value Eval(ExeContext& x) const; };
struct ExeAdd : Exe2 { virtual Value Eval(ExeContext& x) const; };
struct ExeSub : Exe2 { virtual Value Eval(ExeContext& x) const; };
struct ExeSll : Exe2 { virtual Value Eval(ExeContext& x) const; };
struct ExeSra : Exe2 { virtual Value Eval(ExeContext& x) const; };
struct ExeSrl : Exe2 { virtual Value Eval(ExeContext& x) const; };
struct ExeLt : Exe2 { virtual Value Eval(ExeContext& x) const; };
struct ExeLte : Exe2 { virtual Value Eval(ExeContext& x) const; };
struct ExeEq : Exe2 { virtual Value Eval(ExeContext& x) const; };
struct ExeNeq : Exe2 { virtual Value Eval(ExeContext& x) const; };
struct ExeAnd : Exe2 { virtual Value Eval(ExeContext& x) const; };
struct ExeXor : Exe2 { virtual Value Eval(ExeContext& x) const; };
struct ExeOr : Exe2 { virtual Value Eval(ExeContext& x) const; };
struct ExeAnl : Exe2 { virtual Value Eval(ExeContext& x) const; };
struct ExeOrl : Exe2 { virtual Value Eval(ExeContext& x) const; };
struct ExeCond : Exe {
One<Exe> cond;
One<Exe> ontrue;
One<Exe> onfalse;
virtual Value Eval(ExeContext& x) const;
};
struct ExeField : Exe {
One<Exe> value;
String id;
virtual Value Eval(ExeContext& x) const;
};
struct ExeVarField : Exe {
int var_index;
String id;
virtual Value Eval(ExeContext& x) const;
};
struct ExeFn : Exe {
Value (*fn)(const Vector<Value>&, const Renderer *);
Vector< One<Exe> > arg;
virtual Value Eval(ExeContext& x) const;
};
struct ExeLink : Exe {
const Vector<String> *part;
Vector< One<Exe> > arg;
virtual Value Eval(ExeContext& x) const;
};
struct ExeLinkVarField1 : Exe {
const Vector<String> *part;
int var_index;
String id;
virtual Value Eval(ExeContext& x) const;
};
struct LoopInfo {
bool first;
bool last;
int index;
Value key;
};
struct ExeFirst : ExeVar { virtual Value Eval(ExeContext& x) const; };
struct ExeLast : ExeVar { virtual Value Eval(ExeContext& x) const; };
struct ExeIndex : ExeVar { virtual Value Eval(ExeContext& x) const; };
struct ExeKey : ExeVar { virtual Value Eval(ExeContext& x) const; };
struct ExeFor : Exe {
One<Exe> value;
One<Exe> body;
One<Exe> onempty;
virtual Value Eval(ExeContext& x) const;
};
struct ExeBlock : Exe {
Vector< One<Exe> > item;
void AddText(const char *b, const char *s);
virtual Value Eval(ExeContext& x) const;
};
struct CompiledTemplate {
String path;
Index<String> var;
Exe program;
Vector<Value> data;
Value Eval();
};
CParser p;
Index<String> var;
Vector<bool> forvar;
bool optimized;
int count_node;
int ForVar(String id, int i);
One<Exe> Prim();
One<Exe> Mul();
One<Exe> Add();
One<Exe> Shift();
One<Exe> Rel();
One<Exe> Eq();
One<Exe> And();
One<Exe> Xor();
One<Exe> Or();
One<Exe> LogAnd();
One<Exe> LogOr();
One<Exe> Conditional();
One<Exe> Exp();
One<Exe> Block();
typedef Compiler CLASSNAME;
void Iterate(Vector< One<Exe> >& a, Callback1< One<Exe>& > op);
void Iterate(One<Exe>& exe, Callback1< One<Exe>& > op);
void OptimizeConst(One<Exe>& exe);
void Optimize(One<Exe>& exe);
void CountNodes(One<Exe>& exe);
int GetNodeCount(One<Exe>& exe);
static void Register(const String& id, Value (*fn)(const Vector<Value>&, const Renderer *));
Compiler(const char *code, const Index<String>& var) : p(code), var(var, 1) { forvar.SetCount(var.GetCount(), false); }
};
One<Exe> Compile(const char *code, const Index<String>& vars);
String Render(const One<Exe>& exe, Renderer *r, Vector<Value>& var);
String GetPreprocessedTemplate(const String& name, int lang);
String ReplaceVars(const String& src, const VectorMap<String, String>& def, int chr);

13
uppsrc/Skylark/init Normal file
View file

@ -0,0 +1,13 @@
#ifndef _Skylark_icpp_init_stub
#define _Skylark_icpp_init_stub
#include "Draw/init"
#include "plugin\png/init"
#include "plugin\jpg/init"
#include "Sql/init"
#define BLITZ_INDEX__ F5727c7d5aaf9771a467d28f05356763c
#include "StdLib.icpp"
#undef BLITZ_INDEX__
#define BLITZ_INDEX__ F8bbcf8bffcf1dc379d0adde61a45f9fe
#include "Static.icpp"
#undef BLITZ_INDEX__
#endif

85
uppsrc/Skylark/skylark.js Normal file
View file

@ -0,0 +1,85 @@
function Log(x)
{
// document.writeln(x + "<br>");
}
function ProcessAjaxResult(ss)
{
for(i = 0; i < ss.length; i++) {
var pos = ss[i].indexOf(':');
if(pos >= 0) {
var id = ss[i].slice(0, pos);
var text = ss[i].slice(pos + 1);
if(id.length > 1 && id.charAt(0) == '>')
document.getElementById(id.slice(1)).value = text;
else
document.getElementById(id).innerHTML = text;
}
}
}
function AjaxRequest()
{
if(window.XMLHttpRequest)
return new XMLHttpRequest();
else
return new ActiveXObject("Microsoft.XMLHTTP");
}
function IsNull(x)
{
return x == null || x.length == 0;
}
function ScanForValues(x, result)
{
if(x) {
if(x.nodeType == 1 &&
(x.nodeName == "INPUT" || x.nodeName == "SELECT" || x.nodeName == "TEXTAREA")) {
if(result.val.length)
result.val += "&";
Log(x.nodeName);
Log("Name: " + x.name);
Log("Id: " + x.id);
Log(x.name == undefined);
Log(x.name == null);
var id = IsNull(x.name) ? x.id : x.name;
Log("ID: " + id);
if(!IsNull(id))
result.val += id + '=' + encodeURIComponent(x.value);
}
for(var i = 0; i < x.childNodes.length; i++)
ScanForValues(x.childNodes[i], result);
}
}
function UxGet(request)
{
var xmlhttp = AjaxRequest();
xmlhttp.onreadystatechange = function() {
if(xmlhttp.readyState == 4 && xmlhttp.status == 200)
ProcessAjaxResult(xmlhttp.responseText.split('\1'));
}
xmlhttp.open("GET", request, true);
xmlhttp.send();
}
function UxPost(request)
{
var xmlhttp = AjaxRequest();
xmlhttp.onreadystatechange = function() {
if(xmlhttp.readyState == 4 && xmlhttp.status == 200)
ProcessAjaxResult(xmlhttp.responseText.split('\1'));
}
var parameters = { val: "" };
for(var i = 1; i < arguments.length; i++)
ScanForValues(document.getElementById(arguments[i]), parameters);
if(__js_identity__ != undefined) {
if(parameters.val.length)
parameters.val += "&";
parameters.val += "__js_identity__=" + __js_identity__;
}
xmlhttp.open("POST", request, true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.send(parameters.val);
}