summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Paassen <bpaassen@techfak.uni-bielefeld.de>2014-12-05 16:09:53 +0100
committerBenjamin Paassen <bpaassen@techfak.uni-bielefeld.de>2014-12-05 16:09:53 +0100
commit30711f43a6cad9889ee1af8539658c4ca1f59854 (patch)
treed37c88faf794ad2632fc787ec1724c9ec08bdbd1
parent30765a8bbf30aafad89a632afc39966c5b4029b8 (diff)
Further work on implementing CSSRuleSets. This needs further testing, though.
-rw-r--r--src/core/BufferedCharReader.cpp2
-rw-r--r--src/core/CSS.hpp18
-rw-r--r--src/core/CodeTokenizer.cpp26
-rw-r--r--src/core/CodeTokenizer.hpp4
-rw-r--r--src/plugins/css/CSSParser.cpp66
-rw-r--r--src/plugins/css/CSSParser.hpp32
-rw-r--r--test/plugins/css/CSSParserTest.cpp31
7 files changed, 149 insertions, 30 deletions
diff --git a/src/core/BufferedCharReader.cpp b/src/core/BufferedCharReader.cpp
index 23c219a..f3792ec 100644
--- a/src/core/BufferedCharReader.cpp
+++ b/src/core/BufferedCharReader.cpp
@@ -147,7 +147,7 @@ bool BufferedCharReader::readCharacterAtCursor(ReadCursor &cursor, char *c)
// If data has been read, append it to the input buffer and try
// again
if (cnt > 0) {
- buffer.emplace_back(buf.data());
+ buffer.emplace_back(std::string(buf.data(), cnt));
continue;
}
diff --git a/src/core/CSS.hpp b/src/core/CSS.hpp
index 3b4289a..4cf15be 100644
--- a/src/core/CSS.hpp
+++ b/src/core/CSS.hpp
@@ -70,17 +70,24 @@ struct Specificity {
*/
class RuleSet : public Managed {
private:
- const std::map<std::string, variant::Variant> values;
+ std::map<std::string, variant::Variant> rules;
public:
/**
* Initializes an empty RuleSet.
*/
- RuleSet(Manager &mgr) : Managed(mgr), values() {}
+ RuleSet(Manager &mgr) : Managed(mgr), rules() {}
- const std::map<std::string, variant::Variant> &getValues() const
+ std::map<std::string, variant::Variant> &getRules() { return rules; }
+
+ const std::map<std::string, variant::Variant> &getRules() const
{
- return values;
+ return rules;
+ }
+
+ void merge(Rooted<RuleSet> other)
+ {
+ rules.insert(other->rules.begin(), other->rules.end());
}
};
@@ -244,7 +251,7 @@ private:
const PseudoSelector pseudoSelector;
ManagedVector<SelectorEdge> edges;
Owned<RuleSet> ruleSet;
- bool accepting;
+ bool accepting = false;
/**
* This is an internal method all getChildren variants refer to.
@@ -254,7 +261,6 @@ private:
const PseudoSelector *select);
public:
-
/**
* This initializes an empty SelectorNode with the given name and the
* given PseudoSelector.
diff --git a/src/core/CodeTokenizer.cpp b/src/core/CodeTokenizer.cpp
index ce4afe5..e9c1bbf 100644
--- a/src/core/CodeTokenizer.cpp
+++ b/src/core/CodeTokenizer.cpp
@@ -59,10 +59,14 @@ bool CodeTokenizer::doPrepare(const Token &t, std::deque<Token> &peeked)
state = CodeTokenizerState::IN_LINE_COMMENT;
break;
case CodeTokenMode::LINEBREAK:
- peeked.push_back({it->second.id, t.content, t.startColumn,
- t.startLine, t.endColumn, t.endLine});
- return true;
+ if (!ignoreLinebreaks) {
+ peeked.push_back({it->second.id, t.content,
+ t.startColumn, t.startLine,
+ t.endColumn, t.endLine});
+ }
+ return !ignoreLinebreaks;
default:
+ bool empty = true;
if (t.tokenId == TOKEN_TEXT) {
int begin = -1;
for (size_t c = 0; c < t.content.length(); c++) {
@@ -86,20 +90,22 @@ bool CodeTokenizer::doPrepare(const Token &t, std::deque<Token> &peeked)
t.startColumn + begin, t.startLine,
t.startColumn + (int)c, t.endLine});
begin = -1;
+ empty = false;
}
}
}
- if(begin >= 0){
- peeked.push_back(Token{
- TOKEN_TEXT,
- t.content.substr(begin),
- t.startColumn + begin, t.startLine,
- t.endColumn, t.endLine});
+ if (begin >= 0) {
+ peeked.push_back(
+ Token{TOKEN_TEXT, t.content.substr(begin),
+ t.startColumn + begin, t.startLine,
+ t.endColumn, t.endLine});
+ empty = false;
}
} else {
+ empty = false;
peeked.push_back(t);
}
- return true;
+ return !empty;
}
startToken = t;
returnTokenId = it->second.id;
diff --git a/src/core/CodeTokenizer.hpp b/src/core/CodeTokenizer.hpp
index 247918e..43c7abb 100644
--- a/src/core/CodeTokenizer.hpp
+++ b/src/core/CodeTokenizer.hpp
@@ -101,6 +101,10 @@ public:
* true.
*/
bool ignoreComments = false;
+ /**
+ * If you do not want linebreaks to be returned you can set this to true.
+ */
+ bool ignoreLinebreaks = false;
/**
*
diff --git a/src/plugins/css/CSSParser.cpp b/src/plugins/css/CSSParser.cpp
index 85d8858..82ed7e1 100644
--- a/src/plugins/css/CSSParser.cpp
+++ b/src/plugins/css/CSSParser.cpp
@@ -64,12 +64,7 @@ static const TokenTreeNode CSS_ROOT{{{"{", CURLY_OPEN},
{"*/", COMMENT_CLOSE},
{"\"", DOUBLE_QUOTE},
{"\\", ESCAPE},
- // linux linebreak
- {"\n", LINEBREAK},
- // windows linebreak
- {"\r\n", LINEBREAK},
- // Mac OS linebreak
- {"\r", LINEBREAK}}};
+ {"\n", LINEBREAK}}};
static const std::map<int, CodeTokenDescriptor> CSS_DESCRIPTORS = {
{COMMENT_OPEN, {CodeTokenMode::BLOCK_COMMENT_START, COMMENT}},
@@ -83,6 +78,7 @@ Rooted<Node> CSSParser::parse(std::istream &is, ParserContext &ctx)
BufferedCharReader input{is};
CodeTokenizer tokenizer{input, CSS_ROOT, CSS_DESCRIPTORS};
tokenizer.ignoreComments = true;
+ tokenizer.ignoreLinebreaks = true;
Rooted<SelectorNode> root = {new SelectorNode{ctx.manager, "root"}};
parseDocument(root, tokenizer, ctx);
return root;
@@ -97,16 +93,22 @@ void CSSParser::parseDocument(Rooted<SelectorNode> root,
}
tokenizer.resetPeek();
std::vector<Rooted<SelectorNode>> leafList;
+ // parse the SelectorTree for this ruleSet.
parseSelectors(root, tokenizer, leafList, ctx);
- // TODO: Parse Ruleset
+ // parse the RuleSet itself.
+ Rooted<RuleSet> ruleSet = parseRuleSet(tokenizer, ctx);
for (auto &leaf : leafList) {
- /* every leaf is an accepting node, if one considers the SelectorTree
+ /*
+ * every leaf is an accepting node, if one considers the SelectorTree
* to be a finite state machine. This is relevant, if users do not use
* the CSS Parser to parse actual Ruleset content but to construct a
* SelectorTree just to identify a part of the DocumentTree.
*/
leaf->setAccepting(true);
- //TODO: append RuleSets
+ /*
+ * similarly we append the found rules to all leafs.
+ */
+ leaf->getRuleSet()->merge(ruleSet);
}
parseDocument(root, tokenizer, ctx);
}
@@ -289,7 +291,51 @@ Rooted<SelectorNode> CSSParser::parsePrimitiveSelector(CodeTokenizer &tokenizer,
}
}
-// TODO: Add RuleSet parsing methods.
+Rooted<RuleSet> CSSParser::parseRuleSet(CodeTokenizer &tokenizer,
+ ParserContext &ctx)
+{
+ Rooted<RuleSet> ruleSet{new RuleSet(ctx.manager)};
+ // if we have no ruleset content, we return an empty ruleset.
+ Token t;
+ if (!expect(CURLY_OPEN, tokenizer, t, false, ctx)) {
+ return ruleSet;
+ }
+ // otherwise we parse the rules.
+ parseRules(tokenizer, ruleSet, ctx);
+ // and we expect closing curly braces.
+ expect(CURLY_CLOSE, tokenizer, t, true, ctx);
+ return ruleSet;
+}
+
+void CSSParser::parseRules(CodeTokenizer &tokenizer, Rooted<RuleSet> ruleSet,
+ ParserContext &ctx)
+{
+ std::string key;
+ variant::Variant value;
+ while (parseRule(tokenizer, ctx, key, value)) {
+ ruleSet->getRules().insert({key, value});
+ }
+}
+
+bool CSSParser::parseRule(CodeTokenizer &tokenizer, ParserContext &ctx,
+ std::string &key, variant::Variant &value)
+{
+ Token t;
+ if (!expect(TOKEN_TEXT, tokenizer, t, false, ctx)) {
+ return false;
+ }
+ // if we find text that is the key first.
+ key = t.content;
+ // then we expect a :
+ expect(COLON, tokenizer, t, true, ctx);
+ // then the value
+ // TODO: Resolve key for appropriate parsing function here.
+ expect(STRING, tokenizer, t, true, ctx);
+ value = variant::Variant(t.content.c_str());
+ // and a ;
+ expect(SEMICOLON, tokenizer, t, true, ctx);
+ return true;
+}
bool CSSParser::expect(int expectedType, CodeTokenizer &tokenizer, Token &t,
bool force, ParserContext &ctx)
diff --git a/src/plugins/css/CSSParser.hpp b/src/plugins/css/CSSParser.hpp
index eb51dfa..27a483d 100644
--- a/src/plugins/css/CSSParser.hpp
+++ b/src/plugins/css/CSSParser.hpp
@@ -89,7 +89,30 @@ private:
Rooted<SelectorNode> parsePrimitiveSelector(CodeTokenizer &tokenizer,
ParserContext &ctx);
- // TODO: Add RuleSet parsing methods.
+ /**
+ * Implements the RULESET Nonterminal, which parses an entire RuleSet. Note
+ * that we do not require RuleSets to be parsed. It is permitted to just
+ * insert Selector expressions.
+ */
+ Rooted<RuleSet> parseRuleSet(CodeTokenizer &tokenizer, ParserContext &ctx);
+
+ /**
+ * Implements the RULES Nonterminal, which parses CSSRules inside a RuleSet.
+ */
+ void parseRules(CodeTokenizer &tokenizer, Rooted<RuleSet> ruleSet,
+ ParserContext &ctx);
+
+ /**
+ * Implements the RULE Nonterminal, which parses one single CSSRule. Key
+ * and value are stored in the input references.
+ *
+ * @param key is a (possibly empty) string reference for the key found.
+ * @param value is a (possibly empty) Variant reference for the value found.
+ *
+ * @return true if a rule was found.
+ */
+ bool parseRule(CodeTokenizer &tokenizer, ParserContext &ctx,
+ std::string &key, variant::Variant &value);
/**
* A convenience function to wrap around the tokenizer peek() function that
@@ -112,9 +135,12 @@ public:
/**
* This parses the given input as CSS content as specified by the grammar
* seen above. The return value is a Rooted reference to the root of the
- * SelectorTree.
- * TODO: The RuleSet at the respective node at the tree lists all CSS Style
+ * SelectorTree. SelectorTrees are documented in detail in the CSS.hpp
+ * The RuleSet at the respective node at the tree lists all CSS Style
* rules that apply.
+ * Note that you are not required to insert CSS code containing actual
+ * rules. You are permitted to just insert a CSS Selector expression
+ * specifying some part of a DocumentTree you want to refer to.
*
* @param is is a reference to the input stream that should be parsed.
* @param ctx is a reference to the context that should be used while
diff --git a/test/plugins/css/CSSParserTest.cpp b/test/plugins/css/CSSParserTest.cpp
index 20d0836..8880fc5 100644
--- a/test/plugins/css/CSSParserTest.cpp
+++ b/test/plugins/css/CSSParserTest.cpp
@@ -121,6 +121,37 @@ TEST(CSSParser, testParseSelectors)
ASSERT_EQ(0, Ag->getEdges().size());
ASSERT_TRUE(Ag->isAccepting());
}
+
+TEST(CSSParser, testParseCSS)
+{
+ // create the CSS input
+ std::stringstream input;
+ input << "A, B A {\n";
+ input << "/*\n";
+ input << " * Some multiline\n";
+ input << " * comment\n";
+ input << " */\n";
+ input << "\t ident1 : \"val1\";\n";
+ input << "\t ident2 : \"val2\";\n";
+ input << "}\n";
+ input << "A:select(a,b) {\n";
+ input << "\t ident3 : \"val3\";\n";
+ input << "}\n";
+ input << "A {\n";
+ input << "\t ident1 : \"val4\";\n";
+ input << "}\n";
+
+
+ // initialize an empty parser context.
+ StandaloneParserContext ctx;
+
+ // parse the input.
+ CSSParser instance;
+ Rooted<SelectorNode> root = instance.parse(input, ctx).cast<SelectorNode>();
+
+
+}
+
}
}
}