diff --git a/uppsrc/Core/Parser.h b/uppsrc/Core/Parser.h index 510a82554..e1dcbbc7e 100644 --- a/uppsrc/Core/Parser.h +++ b/uppsrc/Core/Parser.h @@ -88,7 +88,12 @@ public: operator bool() const { return *term; } int GetLine() const { return line; } + int GetColumn(int tabsize) const; String GetFileName() const { return fn; } + + static String LineInfoComment(const String& filename, int line = 1, int column = 1); + String GetLineInfoComment(int tabsize = 4) const; + enum { LINEINFO_ESC = '\2' }; void Set(const char *ptr, const char *fn, int line = 1); void Set(const char *ptr); diff --git a/uppsrc/Core/parser.cpp b/uppsrc/Core/parser.cpp index b2581e751..e5753859e 100644 --- a/uppsrc/Core/parser.cpp +++ b/uppsrc/Core/parser.cpp @@ -2,12 +2,12 @@ NAMESPACE_UPP -#define LLOG(x) // LOG(x) +#define LLOG(x) // DLOG(x) #define LTIMING(x) // RTIMING(x) void CParser::ThrowError(const char *s) { LLOG("CParser::Error: " << s); - LLOG(~String(term, min(strlen((const char *)term), 512U))); + LLOG(~String(term, min((int)strlen((const char *)term), 512))); Pos pos = GetPos(); Error err(fn + Format("(%d,%d): ", line, pos.GetColumn()) + s); // err.term = (const char *)term; @@ -21,6 +21,30 @@ bool CParser::Spaces0() { !(term[0] == '/' && term[1] == '*')) return false; for(;;) { + if(*term == LINEINFO_ESC) { + term++; + fn.Clear(); + while(*term) { + if(*term == LINEINFO_ESC) { + ++term; + break; + } + if(*term == '\3') { + line = atoi(++term); + while(*term) { + if(*term == LINEINFO_ESC) { + ++term; + break; + } + term++; + } + break; + } + fn.Cat(*term++); + } + continue; + } + else if(term[0] == '/' && term[1] == '/') { term += 2; while(*term && *term != '\n') @@ -48,6 +72,17 @@ bool CParser::Spaces0() { return true; } +String CParser::LineInfoComment(const String& file, int line, int column) +{ + return String().Cat() << (char)LINEINFO_ESC << file << '\3' + << line << '\3' << column << (char)LINEINFO_ESC; +} + +String CParser::GetLineInfoComment(int tabsize) const +{ + return LineInfoComment(GetFileName(), GetLine(), GetColumn(tabsize)); +} + const char *CParser::IsId0(const char *s) const { const char *t = term + 1; s++; @@ -409,6 +444,13 @@ int CParser::Pos::GetColumn(int tabsize) const { int pos = 1; for(const char *s = lineptr; s < ptr; s++) { + if(*s == CParser::LINEINFO_ESC) { + s++; + while(s < ptr && *s != CParser::LINEINFO_ESC) + if(*s++ == '\3') + pos = atoi(s); + } + else if(*s == '\t') pos = (pos + tabsize - 1) / tabsize * tabsize + 1; else @@ -417,8 +459,14 @@ int CParser::Pos::GetColumn(int tabsize) const return pos; } +int CParser::GetColumn(int tabsize) const +{ + return GetPos().GetColumn(tabsize); +} + void CParser::SetPos(const CParser::Pos& p) { + LLOG("SetPos " << p.fn << ":" << p.line); line = p.line; fn = p.fn; term = p.ptr; @@ -459,6 +507,7 @@ void CParser::Set(const char *_ptr, const char *_fn, int _line) line = _line; skipspaces = true; Spaces(); + LLOG("Set " << fn << ":" << line); } void CParser::Set(const char *_ptr) diff --git a/uppsrc/Core/src.tpp/CParser$en-us.tpp b/uppsrc/Core/src.tpp/CParser$en-us.tpp index 52a676a37..dea8c49ea 100644 --- a/uppsrc/Core/src.tpp/CParser$en-us.tpp +++ b/uppsrc/Core/src.tpp/CParser$en-us.tpp @@ -366,12 +366,37 @@ a different [* CParser].&] [s2; Returns the current line number.&] [s3; &] [s4; &] +[s5;:CParser`:`:GetColumn`(int`)const: [@(0.0.255) int]_[* GetColumn]([@(0.0.255) int]_[*@3 t +absize])_[@(0.0.255) const]&] +[s2;%% Returns the current column, with given [%-*@3 tabsize].&] +[s3;%% &] +[s4; &] [s5;:CParser`:`:GetFileName`(`)const: [_^String^ String]_[* GetFileName]()_[@(0.0.255) cons t]&] [s2; Returns the actual filename.&] [s3;%% &] [s3; &] [s4; &] +[s5;:CParser`:`:LineInfoComment`(const String`&`,int`,int`): [@(0.0.255) static] +[_^String^ String]_[* LineInfoComment]([@(0.0.255) const]_[_^String^ String][@(0.0.255) `&]_ +[*@3 filename], [@(0.0.255) int]_[*@3 line]_`=_[@3 1], [@(0.0.255) int]_[*@3 column]_`=_[@3 1]) +&] +[s2;%% This function creates a special comment that when parsed by +CParser, switches filename and line number. This is supposed +to help in situations when parsed text is actually a result of +e.g. include operations of some original files, to improve error +reporting. Not that such comment is lexically treated as comment. +Comment is created using LINEINFO`_ESC characters begin/end delimiter +(current value is `'`\2`').&] +[s3;%% &] +[s4; &] +[s5;:CParser`:`:GetLineInfoComment`(int`)const: [_^String^ String]_[* GetLineInfoComment]( +[@(0.0.255) int]_[*@3 tabsize]_`=_[@3 4])_[@(0.0.255) const]&] +[s2;%% Calls LineInfoComment(GetFileName(), GetLine(), GetColumn([%-*@3 tabsize])) +`- creates a comment to identify current file position in further +processing.&] +[s3; &] +[s4; &] [s5;:CParser`:`:Set`(const char`*`,const char`*`,int`): [@(0.0.255) void]_[* Set]([@(0.0.255) c onst]_[@(0.0.255) char]_`*[*@3 ptr], [@(0.0.255) const]_[@(0.0.255) char]_`*[*@3 fn], [@(0.0.255) int]_[*@3 line]_`=_[@3 1])&] @@ -478,10 +503,9 @@ at the beginning of the line when the line length is exceeded.&] [s0;~~~1408;%% -|ASCSTRING`_SMART-|breaks string into lines when too long&] [s0;~~~1408;%% -|ASCSTRING`_OCTALHI-|escapes characters >128&] -[s0;~~~1408;%% -|ASCSTRING`_JSON-|uses JSON notation for escapes (`\u0001 --| instead of `\001)&] +[s0;~~~1408;%% -|ASCSTRING`_JSON-|uses JSON notation for escapes &] +[s0;~~~1408;%% -|(`\u0001 instead of `\001)&] [s0;~~~1408;%% &] -[s0;%% &] [s7;%% [*/ Return value]-|C`-like literal.&] [s3;%% &] [s4; &] diff --git a/uppsrc/Skylark/Compile.cpp b/uppsrc/Skylark/Compile.cpp index 4867408b7..18b76d587 100644 --- a/uppsrc/Skylark/Compile.cpp +++ b/uppsrc/Skylark/Compile.cpp @@ -345,7 +345,14 @@ void Compiler::ExeBlock::AddText(const char *b, const char *s) { if(s > b) { RawHtmlText t; - t.text = String(b, s); + 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); } } @@ -354,65 +361,66 @@ One Compiler::Block() { One result; ExeBlock& blk = result.Create(); - const char *s = p.GetSpacePtr(); - const char *b = s; - int line = p.GetLine(); - while(*s) { - if(*s == '$') { - if(s[1] == '$') { - blk.AddText(b, s + 1); - p.Set(s + 2, NULL, line); - b = s = p.GetSpacePtr(); - } - else { - blk.AddText(b, s); - p.Set(s + 1, NULL, line); - 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 = s = p.GetSpacePtr(); - line = p.GetLine(); - } + 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(*s++ == '\n') - line++; + 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, s); - p.Set(s, NULL, line); + blk.AddText(b, p.GetPtr()); return result; } diff --git a/uppsrc/Skylark/Dispatch.cpp b/uppsrc/Skylark/Dispatch.cpp index 8e351a5c1..af4c4188f 100644 --- a/uppsrc/Skylark/Dispatch.cpp +++ b/uppsrc/Skylark/Dispatch.cpp @@ -418,18 +418,17 @@ void Http::Dispatch(TcpSocket& socket) socket.WhenWait = callback2(this, &Http::WaitHandler, bd.progress, rsocket); } content = socket.GetAll(len); - if(bd.progress) - { + if(bd.progress) { socket.WhenWait.Clear(); (*bd.progress)(PROGRESS_END, *this, len); } - if(post) + if(post) { if(rc.StartsWith("application/x-www-form-urlencoded")) ParseRequest(content); else if(rc.StartsWith("multipart/")) ReadMultiPart(content); - + } response.Clear(); if(bd.handler) { if(post && !bd.post_raw) { diff --git a/uppsrc/Skylark/Http.cpp b/uppsrc/Skylark/Http.cpp index 15be2b937..981411af9 100644 --- a/uppsrc/Skylark/Http.cpp +++ b/uppsrc/Skylark/Http.cpp @@ -36,7 +36,7 @@ void Http::ParseRequest(const char *p) last = p; while(*p && *p != '&') p++; - if(*key != '.' && *key != '@') + if(*key != '.' && *key != '@') { if(key.EndsWith("[]")) { Value &v = var.GetAdd(key); if(v.IsNull()) @@ -45,6 +45,7 @@ void Http::ParseRequest(const char *p) } else var.GetAdd(key) = UrlDecode(last, p); + } if(*p) p++; } diff --git a/uppsrc/Skylark/Preprocess.cpp b/uppsrc/Skylark/Preprocess.cpp index 625990361..180c415a6 100644 --- a/uppsrc/Skylark/Preprocess.cpp +++ b/uppsrc/Skylark/Preprocess.cpp @@ -28,8 +28,10 @@ String LoadTemplate(const char *file, const String& search_path, int lang) SKYLARKLOG("Loading template file " << path); FileIn in(path); String r; + int lineno = 0; while(in && !in.IsEof()) { String line = in.GetLine(); + lineno++; CParser p(line); if(p.Char('#') && p.Id("include")) { String file = p.GetPtr(); @@ -41,7 +43,7 @@ String LoadTemplate(const char *file, const String& search_path, int lang) r << LoadTemplate(file, GetFileFolder(path) + ';' + search_path, lang); } else - r << line; + r << CParser::LineInfoComment(path, lineno) << line; r << "\n"; } return r; @@ -62,7 +64,7 @@ VectorMap GetTemplateDefs(const char *file, int lang) id = p.ReadId(); ti = def.FindAdd(id); def[ti].Clear(); - def[ti] << p.GetPtr(); + def[ti] << p.GetLineInfoComment() << p.GetPtr(); } else def[ti] << line << "\n"; diff --git a/uppsrc/Skylark/Renderer.cpp b/uppsrc/Skylark/Renderer.cpp index 39df54cbd..b48a900f3 100644 --- a/uppsrc/Skylark/Renderer.cpp +++ b/uppsrc/Skylark/Renderer.cpp @@ -61,33 +61,32 @@ ArrayMap > template_cache; const One& 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; + } + if(!SkylarkApp::Config().use_caching) // Templates get overwritten is not cached, MT hazard + s << ';' << Thread::GetCurrentId(); + String sgn = s; + LLOG("Trying to retrieve " << sgn << " from cache"); + Mutex::Lock __(template_cache_lock); + int q = template_cache.Find(sgn); + if(q >= 0 && SkylarkApp::Config().use_caching) + return template_cache[q]; + One& exe = q >= 0 ? template_cache[q] : template_cache.Add(sgn); try { - LTIMING("GetTemplate"); - StringBuffer s; - { - LTIMING("MakeSignature"); - for(int i = 0; i < var.GetCount(); i++) - s << var.GetKey(i) << ';'; - s << ':' << template_name; - } - if(!SkylarkApp::Config().use_caching) // Templates get overwritten is not cached, MT hazard - s << ';' << Thread::GetCurrentId(); - String sgn = s; - LLOG("Trying to retrieve " << sgn << " from cache"); - 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 = q >= 0 ? template_cache[q] : template_cache.Add(sgn); exe = Compile(GetPreprocessedTemplate(template_name, lang), var.GetIndex()); - return exe; } - catch(CParser::Error e) - { - throw TemplateExc(e); + catch(CParser::Error e) { + exe = Compile(String().Cat() << "Error in template: " << e << "", var.GetIndex()); } + return exe; NEVER(); return template_cache[0]; } diff --git a/uppsrc/Skylark/init b/uppsrc/Skylark/init index 4681dd209..e13b3e6ca 100644 --- a/uppsrc/Skylark/init +++ b/uppsrc/Skylark/init @@ -1,10 +1,10 @@ #ifndef _Skylark_icpp_init_stub #define _Skylark_icpp_init_stub #include "Sql/init" -#define BLITZ_INDEX__ Fdc34a156e19f70e79e3af4bc5aee3879 +#define BLITZ_INDEX__ F99b1b565e5194ee2f9bb1998be2e6f71 #include "StdLib.icpp" #undef BLITZ_INDEX__ -#define BLITZ_INDEX__ F4f56e545eab9f60b3cd674a218cc0915 +#define BLITZ_INDEX__ Fadc563b9c69ab7173de2094508da8708 #include "Static.icpp" #undef BLITZ_INDEX__ #endif diff --git a/uppsrc/Skylark/src.tpp/Witz$en-us.tpp b/uppsrc/Skylark/src.tpp/Witz$en-us.tpp index 8ad5029e8..efd58be07 100644 --- a/uppsrc/Skylark/src.tpp/Witz$en-us.tpp +++ b/uppsrc/Skylark/src.tpp/Witz$en-us.tpp @@ -40,7 +40,7 @@ specific version of template first, then defaults to common version.&] [s0; [@5 #define] [/ id]&] [s0; &] [s0; This starts a new template subblock which ends with another -subblock. First subblock (the one befure first #define is encountered) +subblock. First subblock (the one before first #define is encountered) has [/ id] `"MAIN`". The value of `"MAIN`" subblock represents the template at the end of preprocessing process.&] [s0; &]