Core: CParser LineInfoComment, Skylark: Improved witz error handling

git-svn-id: svn://ultimatepp.org/upp/trunk@7318 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
cxl 2014-04-26 09:38:18 +00:00
parent c1e6bcb309
commit e7f12ab537
10 changed files with 180 additions and 93 deletions

View file

@ -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);

View file

@ -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)

View file

@ -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; &]

View file

@ -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<ExeConst>().value = RawToValue(t);
}
}
@ -354,65 +361,66 @@ One<Exe> Compiler::Block()
{
One<Exe> result;
ExeBlock& blk = result.Create<ExeBlock>();
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<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();
}
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<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 = 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;
}

View file

@ -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) {

View file

@ -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++;
}

View file

@ -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<String, String> 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";

View file

@ -61,33 +61,32 @@ 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;
}
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>& 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>& 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() << "<html><body>Error in template: " << e << "</body></html>", var.GetIndex());
}
return exe;
NEVER();
return template_cache[0];
}

View file

@ -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

View file

@ -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; &]