fix unittest; CSS parser improvements

This commit is contained in:
Vadim Lopatin 2015-12-24 15:17:19 +03:00
parent 1686fde76d
commit 9f768c85af
1 changed files with 64 additions and 55 deletions

View File

@ -10,18 +10,33 @@ import std.ascii : isAlpha;
import dlangui.core.dom; import dlangui.core.dom;
import dlangui.core.css; import dlangui.core.css;
/// skip spaces, move to new location, return true if there are some characters left in source line /// skip specified count of chars of string, returns next available character, or 0 if end of string reached
private bool skipSpaces(ref string src) { private char skip(ref string src, int count = 1) {
if (count >= src.length) {
src = null;
return 0;
}
src = src[count .. $];
return src[0];
}
/// returns first char of string or 0 if end of string reached
private char peek(string str) {
return str.empty ? 0 : str[0];
}
/// skip spaces, move to new location, return first character in string, 0 if end of string reached
private char skipSpaces(ref string src) {
for(;;) { for(;;) {
if (src.empty) { if (src.empty) {
src = null; src = null;
return false; return 0;
} }
char ch = src[0]; char ch = src[0];
if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') { if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') {
src = src[1 .. $]; src.skip;
} else { } else {
return !src.empty; return src.empty ? 0 : src[0];
} }
} }
} }
@ -40,19 +55,16 @@ private string parseIdent(ref string src) {
if (!pos) if (!pos)
return null; return null;
string res = src[0 .. pos]; string res = src[0 .. pos];
if (pos < src.length) src.skip(pos);
src = src[pos .. $]; src.skipSpaces;
else
src = null;
skipSpaces(src);
return res; return res;
} }
private bool skipChar(ref string src, char ch) { private bool skipChar(ref string src, char ch) {
skipSpaces(src); src.skipSpaces;
if (src.length > 0 && src[0] == ch) { if (src.length > 0 && src[0] == ch) {
src = src[1 .. $]; src.skip;
skipSpaces(src); src.skipSpaces;
return true; return true;
} }
return false; return false;
@ -110,8 +122,8 @@ private bool nextProperty(ref string str) {
break; break;
} }
} }
str = pos < str.length ? str[pos .. $] : null; str.skip(pos);
skipSpaces(str); str.skipSpaces;
return !str.empty && str[0] != '}'; return !str.empty && str[0] != '}';
} }
@ -153,8 +165,7 @@ private bool parseColor(ref string src, ref CssValue value)
{ {
value.type = CssValueType.unspecified; value.type = CssValueType.unspecified;
value.value = 0; value.value = 0;
skipSpaces(src); if (!src.skipSpaces)
if (src.empty)
return false; return false;
string ident = parseIdent(src); string ident = parseIdent(src);
if (!ident.empty) { if (!ident.empty) {
@ -177,7 +188,7 @@ private bool parseColor(ref string src, ref CssValue value)
char ch = src[0]; char ch = src[0];
if (ch == '#') { if (ch == '#') {
// #rgb or #rrggbb colors // #rgb or #rrggbb colors
src = src[1 .. $]; ch = src.skip;
int nDigits = 0; int nDigits = 0;
for ( ; nDigits < src.length && hexDigit(src[nDigits])>=0; nDigits++ ) { for ( ; nDigits < src.length && hexDigit(src[nDigits])>=0; nDigits++ ) {
} }
@ -187,7 +198,7 @@ private bool parseColor(ref string src, ref CssValue value)
int b = hexDigit( src[2] ); int b = hexDigit( src[2] );
value.type = CssValueType.color; value.type = CssValueType.color;
value.value = (((r + r*16) * 256) | (g + g*16)) * 256 | (b + b*16); value.value = (((r + r*16) * 256) | (g + g*16)) * 256 | (b + b*16);
src = src[3..$]; src.skip(3);
return true; return true;
} else if ( nDigits==6 ) { } else if ( nDigits==6 ) {
int r = hexDigit( src[0] ) * 16; int r = hexDigit( src[0] ) * 16;
@ -198,7 +209,7 @@ private bool parseColor(ref string src, ref CssValue value)
b += hexDigit( src[5] ); b += hexDigit( src[5] );
value.type = CssValueType.color; value.type = CssValueType.color;
value.value = ((r * 256) | g) * 256 | b; value.value = ((r * 256) | g) * 256 | b;
src = src[6..$]; src.skip(6);
return true; return true;
} }
} }
@ -209,7 +220,7 @@ private bool parseLength(ref string src, ref CssValue value)
{ {
value.type = CssValueType.unspecified; value.type = CssValueType.unspecified;
value.value = 0; value.value = 0;
skipSpaces(src); src.skipSpaces;
string ident = parseIdent(src); string ident = parseIdent(src);
if (!ident.empty) { if (!ident.empty) {
switch(ident) { switch(ident) {
@ -230,31 +241,29 @@ private bool parseLength(ref string src, ref CssValue value)
} }
while (ch >= '0' && ch <= '9') { while (ch >= '0' && ch <= '9') {
n = n*10 + (ch - '0'); n = n*10 + (ch - '0');
src = src[1 .. $]; ch = src.skip;
if (src.empty) if (!ch)
break; break;
ch = src[0];
} }
} }
int frac = 0; int frac = 0;
int frac_div = 1; int frac_div = 1;
if (ch == '.') { if (ch == '.') {
src = src[1 .. $]; src.skip;
if (!src.empty) { if (!src.empty) {
ch = src[0]; ch = src[0];
while (ch >= '0' && ch <= '9') { while (ch >= '0' && ch <= '9') {
frac = frac*10 + (ch - '0'); frac = frac*10 + (ch - '0');
frac_div *= 10; frac_div *= 10;
src = src[1 .. $]; ch = src.skip;
if (src.empty) if (!ch)
break; break;
ch = src[0];
} }
} }
} }
if (ch == '%') { if (ch == '%') {
value.type = CssValueType.percent; value.type = CssValueType.percent;
src = src[1 .. $]; src.skip;
} else { } else {
ident = parseIdent(src); ident = parseIdent(src);
if (!ident.empty) { if (!ident.empty) {
@ -399,11 +408,11 @@ private bool parseAttrValue(ref string str, ref string attrvalue)
{ {
char[] buf; char[] buf;
int pos = 0; int pos = 0;
if (!skipSpaces(str)) if (!str.skipSpaces)
return false; return false;
char ch = str[0]; char ch = str[0];
if (ch == '\"') { if (ch == '\"') {
str = str[1 .. $]; str.skip;
for ( ; pos < str.length && str[pos] != '\"'; pos++) { for ( ; pos < str.length && str[pos] != '\"'; pos++) {
if (pos >= 1000) if (pos >= 1000)
return false; return false;
@ -411,12 +420,12 @@ private bool parseAttrValue(ref string str, ref string attrvalue)
if (pos >= str.length || str[pos] != '\"') if (pos >= str.length || str[pos] != '\"')
return false; return false;
buf ~= str[0 .. pos]; buf ~= str[0 .. pos];
str = str[pos + 1 .. $]; str.skip(pos + 1);
if (!skipSpaces(str)) if (!str.skipSpaces)
return false; return false;
if (str[0] != ']') if (str[0] != ']')
return false; return false;
str = str[1 .. $]; str.skip;
attrvalue = buf.dup; attrvalue = buf.dup;
return true; return true;
} else { } else {
@ -427,7 +436,7 @@ private bool parseAttrValue(ref string str, ref string attrvalue)
if (pos >= str.length || str[pos] != ']') if (pos >= str.length || str[pos] != ']')
return false; return false;
buf ~= str[0 .. pos]; buf ~= str[0 .. pos];
str = str[pos + 1 .. $]; str.skip(pos + 1);
attrvalue = buf.dup; attrvalue = buf.dup;
return true; return true;
} }
@ -439,8 +448,8 @@ private CssSelectorRule parseAttr(ref string str, Document doc)
char ch = str[0]; char ch = str[0];
if (ch == '.') { if (ch == '.') {
// E.class // E.class
str = str[1 .. $]; str.skip;
skipSpaces(str); str.skipSpaces;
string attrvalue = parseIdent(str); string attrvalue = parseIdent(str);
if (attrvalue.empty) if (attrvalue.empty)
return null; return null;
@ -449,8 +458,8 @@ private CssSelectorRule parseAttr(ref string str, Document doc)
return rule; return rule;
} else if (ch == '#') { } else if (ch == '#') {
// E#id // E#id
str = str[1 .. $]; str.skip;
skipSpaces(str); str.skipSpaces;
string attrvalue = parseIdent(str); string attrvalue = parseIdent(str);
if (attrvalue.empty) if (attrvalue.empty)
return null; return null;
@ -460,31 +469,31 @@ private CssSelectorRule parseAttr(ref string str, Document doc)
} else if (ch != '[') } else if (ch != '[')
return null; return null;
// [.....] rule // [.....] rule
str = str[1 .. $]; // skip [ str.skip; // skip [
skipSpaces(str); str.skipSpaces;
string attrname = parseIdent(str); string attrname = parseIdent(str);
if (attrname.empty) if (attrname.empty)
return null; return null;
if (!skipSpaces(str)) if (!str.skipSpaces)
return null; return null;
string attrvalue = null; string attrvalue = null;
ch = str[0]; ch = str[0];
if (ch == ']') { if (ch == ']') {
// empty [] // empty []
st = CssSelectorRuleType.attrset; st = CssSelectorRuleType.attrset;
str = str[1 .. $]; // skip ] str.skip; // skip ]
} else if (ch == '=') { } else if (ch == '=') {
str = str[1 .. $]; // skip = str.skip; // skip =
if (!parseAttrValue(str, attrvalue)) if (!parseAttrValue(str, attrvalue))
return null; return null;
st = CssSelectorRuleType.attreq; st = CssSelectorRuleType.attreq;
} else if (ch == '~' && str.length > 1 && str[1] == '=') { } else if (ch == '~' && str.length > 1 && str[1] == '=') {
str = str[2 .. $]; // skip ~= str.skip(2); // skip ~=
if (!parseAttrValue(str, attrvalue)) if (!parseAttrValue(str, attrvalue))
return null; return null;
st = CssSelectorRuleType.attrhas; st = CssSelectorRuleType.attrhas;
} else if (ch == '|' && str.length > 1 && str[1] == '=') { } else if (ch == '|' && str.length > 1 && str[1] == '=') {
str = str[2 .. $]; // skip |= str.skip(2); // skip |=
if (!parseAttrValue(str, attrvalue)) if (!parseAttrValue(str, attrvalue))
return null; return null;
st = CssSelectorRuleType.attrstarts; st = CssSelectorRuleType.attrstarts;
@ -498,7 +507,7 @@ private CssSelectorRule parseAttr(ref string str, Document doc)
} }
CssDeclaration parseCssDeclaration(ref string src, bool mustBeInBrackets = true) { CssDeclaration parseCssDeclaration(ref string src, bool mustBeInBrackets = true) {
if (!skipSpaces(src)) if (!src.skipSpaces)
return null; return null;
if (mustBeInBrackets && !skipChar(src, '{')) if (mustBeInBrackets && !skipChar(src, '{'))
return null; // decl must start with { return null; // decl must start with {
@ -584,12 +593,12 @@ CssDeclaration parseCssDeclaration(ref string src, bool mustBeInBrackets = true)
CssValue len; CssValue len;
bool negative = false; bool negative = false;
if (src[0] == '-') { if (src[0] == '-') {
src = src[1 .. $]; src.skip;
negative = true; negative = true;
} }
if (parseLength(src, len)) { if (parseLength(src, len)) {
// read optional "hanging" flag // read optional "hanging" flag
skipSpaces(src); src.skipSpaces;
string attr = parseIdent(src); string attr = parseIdent(src);
if (attr == "hanging") if (attr == "hanging")
len.value = -len.value; len.value = -len.value;
@ -685,13 +694,13 @@ CssSelector parseCssSelector(ref string str, Document doc) {
return null; return null;
CssSelector res = new CssSelector(); CssSelector res = new CssSelector();
for (;;) { for (;;) {
if (!skipSpaces(str)) if (!str.skipSpaces)
return null; return null;
char ch = str[0]; char ch = str[0];
string ident = parseIdent(str); string ident = parseIdent(str);
if (ch == '*') { // universal selector if (ch == '*') { // universal selector
str = str[1 .. $]; str.skip;
skipSpaces(str); str.skipSpaces;
res.id = 0; res.id = 0;
} else if (ch == '.') { // classname follows } else if (ch == '.') { // classname follows
res.id = 0; res.id = 0;
@ -714,20 +723,20 @@ CssSelector parseCssSelector(ref string str, Document doc) {
if (!rule) if (!rule)
return null; return null;
res.insertRuleStart(rule); //insertRuleAfterStart res.insertRuleStart(rule); //insertRuleAfterStart
skipSpaces(str); ch = str.skipSpaces;
attr_rule = true; attr_rule = true;
//continue; //continue;
} }
// element relation // element relation
if (ch == '>') { if (ch == '>') {
str = str[1 .. $]; str.skip;
CssSelectorRule rule = new CssSelectorRule(CssSelectorRuleType.parent); CssSelectorRule rule = new CssSelectorRule(CssSelectorRuleType.parent);
rule.id = res.id; rule.id = res.id;
res.insertRuleStart(rule); res.insertRuleStart(rule);
res.id = 0; res.id = 0;
continue; continue;
} else if (ch == '+') { } else if (ch == '+') {
str = str[1 .. $]; str.skip;
CssSelectorRule rule = new CssSelectorRule(CssSelectorRuleType.predecessor); CssSelectorRule rule = new CssSelectorRule(CssSelectorRuleType.predecessor);
rule.id = res.id; rule.id = res.id;
res.insertRuleStart(rule); res.insertRuleStart(rule);