/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "RegexParser.h"
#include "LexUtil.h"
RegexNode RegexParser::parse(std::string source) {
fSource = source;
fIndex = 0;
SkASSERT(fStack.size() == 0);
this->regex();
SkASSERT(fStack.size() == 1);
SkASSERT(fIndex == source.size());
return this->pop();
}
char RegexParser::peek() {
if (fIndex >= fSource.size()) {
return END;
}
return fSource[fIndex];
}
void RegexParser::expect(char c) {
if (this->peek() != c) {
printf("expected '%c' at index %d, but found '%c'", c, (int) fIndex, this->peek());
exit(1);
}
++fIndex;
}
RegexNode RegexParser::pop() {
RegexNode result = fStack.top();
fStack.pop();
return result;
}
void RegexParser::term() {
switch (this->peek()) {
case '(': this->group(); break;
case '[': this->set(); break;
case '.': this->dot(); break;
default: this->literal();
}
}
void RegexParser::quantifiedTerm() {
this->term();
switch (this->peek()) {
case '*': fStack.push(RegexNode(RegexNode::kStar_Kind, this->pop())); ++fIndex; break;
case '+': fStack.push(RegexNode(RegexNode::kPlus_Kind, this->pop())); ++fIndex; break;
case '?': fStack.push(RegexNode(RegexNode::kQuestion_Kind, this->pop())); ++fIndex; break;
default: break;
}
}
void RegexParser::sequence() {
this->quantifiedTerm();
for (;;) {
switch (this->peek()) {
case END: // fall through
case '|': // fall through
case ')': return;
default:
this->sequence();
RegexNode right = this->pop();
RegexNode left = this->pop();
fStack.emplace(RegexNode::kConcat_Kind, std::move(left), std::move(right));
}
}
}
RegexNode RegexParser::escapeSequence(char c) {
switch (c) {
case 'n': return RegexNode(RegexNode::kChar_Kind, '\n');
case 'r': return RegexNode(RegexNode::kChar_Kind, '\r');
case 't': return RegexNode(RegexNode::kChar_Kind, '\t');
case 's': return RegexNode(RegexNode::kCharset_Kind, " \t\n\r");
default: return RegexNode(RegexNode::kChar_Kind, c);
}
}
void RegexParser::literal() {
char c = this->peek();
if (c == '\\') {
++fIndex;
fStack.push(this->escapeSequence(peek()));
++fIndex;
}
else {
fStack.push(RegexNode(RegexNode::kChar_Kind, c));
++fIndex;
}
}
void RegexParser::dot() {
this->expect('.');
fStack.push(RegexNode(RegexNode::kDot_Kind));
}
void RegexParser::group() {
this->expect('(');
this->regex();
this->expect(')');
}
void RegexParser::setItem() {
this->literal();
if (this->peek() == '-') {
++fIndex;
if (peek() == ']') {
fStack.push(RegexNode(RegexNode::kChar_Kind, '-'));
}
else {
literal();
RegexNode end = this->pop();
SkASSERT(end.fKind == RegexNode::kChar_Kind);
RegexNode start = this->pop();
SkASSERT(start.fKind == RegexNode::kChar_Kind);
fStack.push(RegexNode(RegexNode::kRange_Kind, std::move(start), std::move(end)));
}
}
}
void RegexParser::set() {
expect('[');
size_t depth = fStack.size();
RegexNode set(RegexNode::kCharset_Kind);
if (this->peek() == '^') {
++fIndex;
set.fPayload.fBool = true;
}
else {
set.fPayload.fBool = false;
}
for (;;) {
switch (this->peek()) {
case ']':
++fIndex;
while (fStack.size() > depth) {
set.fChildren.push_back(this->pop());
}
fStack.push(std::move(set));
return;
case END:
printf("unterminated character set\n");
exit(1);
default:
this->setItem();
}
}
}
void RegexParser::regex() {
this->sequence();
switch (this->peek()) {
case '|': {
++fIndex;
this->regex();
RegexNode right = this->pop();
RegexNode left = this->pop();
fStack.push(RegexNode(RegexNode::kOr_Kind, left, right));
break;
}
case END: // fall through
case ')':
return;
default:
SkASSERT(false);
}
}