Skip to content

Commit

Permalink
pull out quote/unquote into own file
Browse files Browse the repository at this point in the history
- move quote/unquote to util.cpp
- partial merge of upstream changes to inspect.cpp
- escape backslash
- factor out interpolant fns
- correct implementation of next_unescaped_interpolant
- ignore Emacs TAGS files
- reverse sense of "is unquoted" test
  • Loading branch information
smikes committed Jan 5, 2015
1 parent 065b366 commit c07a7fe
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 66 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ libsass/*
*.a
a.out
libsass.js
TAGS

bin/*
.deps/
Expand Down
35 changes: 0 additions & 35 deletions inspect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -700,41 +700,6 @@ namespace Sass {
append_to_buffer(indent);
}

string unquote(const string& s)
{
if (s.empty()) return "";
if (s.length() == 1) {
if (s[0] == '"' || s[0] == '\'') return "";
}
char q;
if (*s.begin() == '"' && *s.rbegin() == '"') q = '"';
else if (*s.begin() == '\'' && *s.rbegin() == '\'') q = '\'';
else return s;
string t;
t.reserve(s.length()-2);
for (size_t i = 1, L = s.length()-1; i < L; ++i) {
// if we see a quote, we need to remove the preceding backslash from t
if (s[i-1] == '\\' && s[i] == q) t.erase(t.length()-1);
t.push_back(s[i]);
}
return t;
}

string quote(const string& s, char q)
{
if (s.empty()) return string(2, q);
if (!q || s[0] == '"' || s[0] == '\'') return s;
string t;
t.reserve(s.length()+2);
t.push_back(q);
for (size_t i = 0, L = s.length(); i < L; ++i) {
if (s[i] == q) t.push_back('\\');
t.push_back(s[i]);
}
t.push_back(q);
return t;
}

void Inspect::append_to_buffer(const string& text)
{
buffer += text;
Expand Down
91 changes: 60 additions & 31 deletions parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -397,14 +397,13 @@ namespace Sass {
String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);

while (i < end_of_selector) {
p = find_first_in_interval< exactly<hash_lbrace> >(i, end_of_selector);
p = next_interpolant(i, end_of_selector);
if (p) {
// accumulate the preceding segment if there is one
if (i < p) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p));
// find the end of the interpolant and parse it
const char* j = find_first_in_interval< exactly<rbrace> >(p, end_of_selector);
Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list();
interp_node->is_interpolant(true);
const char* j = end_interpolant(p, end_of_selector);
Expression* interp_node = parse_interpolant(Token(p+2, j));
(*schema) << interp_node;
i = j + 1;
}
Expand Down Expand Up @@ -1208,40 +1207,74 @@ namespace Sass {
return 0;
}

const char* Parser::next_unescaped_interpolant(const char* b, const char* e) {
const char * p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(b, e);

if (p > b && p[-1] == '\\') {
return next_unescaped_interpolant(p+1, e);
}

return p;
}

const char* Parser::next_interpolant(const char* b, const char* e) {
return find_first_in_interval< exactly<hash_lbrace> >(b, e);
}

const char* Parser::end_interpolant(const char* b, const char * e) {
return find_first_in_interval< exactly<rbrace> >(b, e); // find the closing brace
}

String_Constant* Parser::make_string_constant(Token chunk, bool unq = false) {
return new (ctx.mem) String_Constant(path, source_position, chunk, unq);
}

Expression* Parser::parse_interpolant(Token chunk) {
Expression * interp_node = Parser::from_token(chunk, ctx, path, source_position).parse_list();
interp_node->is_interpolant(true);
return interp_node;
}

String* Parser::parse_interpolated_chunk(Token chunk)
{
const char* i = chunk.begin;
// see if there any interpolants
const char* p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(chunk.begin, chunk.end);

const char* p = next_unescaped_interpolant(chunk.begin, chunk.end);
if (!p) {
String_Constant* str_node = new (ctx.mem) String_Constant(path, source_position, chunk, dequote);
// if no interpolants
String_Constant* str_node = make_string_constant(chunk, dequote);
str_node->is_delayed(true);
return str_node;
}

String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
schema->quote_mark(*chunk.begin);
while (i < chunk.end) {
p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(i, chunk.end);
p = next_unescaped_interpolant(i, chunk.end);
if (p) {
if (i < p) {
(*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // accumulate the preceding segment if it's nonempty
// accumulate the preceding segment if it's nonempty
(*schema) << make_string_constant(Token(i, p));
}
const char* j = find_first_in_interval< exactly<rbrace> >(p, chunk.end); // find the closing brace

const char* j = end_interpolant(p, chunk.end);

if (j) {
// parse the interpolant and accumulate it
Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list();
interp_node->is_interpolant(true);
(*schema) << interp_node;
(*schema) << parse_interpolant(Token(p+2, j));
i = j+1;
}
else {
// throw an error if the interpolant is unterminated
error("unterminated interpolant inside string constant " + chunk.to_string());
}
}
else { // no interpolants left; add the last segment if nonempty
if (i < chunk.end) (*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, chunk.end));
else {
// no interpolants left
if (i < chunk.end) {
// add the last segment if nonempty
(*schema) << make_string_constant(Token(i, chunk.end));
}
break;
}
}
Expand Down Expand Up @@ -1308,7 +1341,7 @@ namespace Sass {
Token str(lexed);
const char* i = str.begin;
// see if there any interpolants
const char* p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(str.begin, str.end);
const char* p = next_unescaped_interpolant(str.begin, str.end);
if (!p) {
String_Constant* str_node = new (ctx.mem) String_Constant(path, source_position, str);
str_node->is_delayed(true);
Expand All @@ -1317,16 +1350,15 @@ namespace Sass {

String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
while (i < str.end) {
p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(i, str.end);
p = next_unescaped_interpolant(i, str.end);
if (p) {
if (i < p) {
(*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // accumulate the preceding segment if it's nonempty
}
const char* j = find_first_in_interval< exactly<rbrace> >(p, str.end); // find the closing brace
const char* j = end_interpolant(p, str.end); // find the closing brace
if (j) {
// parse the interpolant and accumulate it
Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list();
interp_node->is_interpolant(true);
Expression* interp_node = parse_interpolant(Token(p+2, j));
(*schema) << interp_node;
i = j+1;
}
Expand Down Expand Up @@ -1368,9 +1400,8 @@ namespace Sass {
size_t num_items = 0;
while (position < end) {
if (lex< interpolant >()) {
Token insides(Token(lexed.begin + 2, lexed.end - 1));
Expression* interp_node = Parser::from_token(insides, ctx, path, source_position).parse_list();
interp_node->is_interpolant(true);
Token insides(lexed.begin + 2, lexed.end - 1);
Expression* interp_node = parse_interpolant(insides);
(*schema) << interp_node;
}
else if (lex< exactly<'%'> >()) {
Expand Down Expand Up @@ -1417,9 +1448,8 @@ namespace Sass {
++position;
}
else if (lex< interpolant >()) {
Token insides(Token(lexed.begin + 2, lexed.end - 1));
Expression* interp_node = Parser::from_token(insides, ctx, path, source_position).parse_list();
interp_node->is_interpolant(true);
Token insides(lexed.begin + 2, lexed.end - 1);
Expression* interp_node = parse_interpolant(insides);
(*schema) << interp_node;
}
else if (lex< sequence< identifier, exactly<':'> > >()) {
Expand All @@ -1441,23 +1471,22 @@ namespace Sass {
Token id(lexed);
const char* i = id.begin;
// see if there any interpolants
const char* p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(id.begin, id.end);
const char* p = next_unescaped_interpolant(id.begin, id.end);
if (!p) {
return new (ctx.mem) String_Constant(path, source_position, id);
}

String_Schema* schema = new (ctx.mem) String_Schema(path, source_position);
while (i < id.end) {
p = find_first_in_interval< sequence< negate< exactly<'\\'> >, exactly<hash_lbrace> > >(i, id.end);
p = next_unescaped_interpolant(i, id.end);
if (p) {
if (i < p) {
(*schema) << new (ctx.mem) String_Constant(path, source_position, Token(i, p)); // accumulate the preceding segment if it's nonempty
}
const char* j = find_first_in_interval< exactly<rbrace> >(p, id.end); // find the closing brace
const char* j = end_interpolant(p, id.end); // find the closing brace
if (j) {
// parse the interpolant and accumulate it
Expression* interp_node = Parser::from_token(Token(p+2, j), ctx, path, source_position).parse_list();
interp_node->is_interpolant(true);
Expression* interp_node = parse_interpolant(Token(p+2, j));
(*schema) << interp_node;
schema->has_interpolants(true);
i = j+1;
Expand Down
7 changes: 7 additions & 0 deletions parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,13 @@ namespace Sass {

void throw_syntax_error(string message, size_t ln = 0);
void throw_read_error(string message, size_t ln = 0);

static const char* next_unescaped_interpolant(const char* b, const char* e);
static const char* next_interpolant(const char* b, const char* e);
static const char* end_interpolant(const char* b, const char* e);

String_Constant * make_string_constant(Token chunk, bool unq);
Expression* parse_interpolant(Token);
};

size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len);
Expand Down
42 changes: 42 additions & 0 deletions util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,4 +186,46 @@ namespace Sass {
}

}
// end Sass::Util namespace

string unquote(const string& s)
{
if (s.empty()) return "";
if (s.length() == 1) {
if (s[0] == '"' || s[0] == '\'') return "";
}

// quoted string begins and ends with single or double quote
if ((*s.begin() != '"' && *s.begin() != '\'') ||
(*s.begin() != *s.rbegin())) {
return s;
}

string t;
t.reserve(s.length()-2);
for (size_t i = 1, L = s.length()-1; i < L; ++i) {
// if we see a backslash, we remove it because it quotes
// the following character
if (s[i] == '\\') {
++i;
}
t.push_back(s[i]);
}
return t;
}

string quote(const string& s, char q)
{
if (s.empty()) return string(2, q);
if (!q || s[0] == '"' || s[0] == '\'') return s;
string t;
t.reserve(s.length()+2);
t.push_back(q);
for (size_t i = 0, L = s.length(); i < L; ++i) {
if (s[i] == q || s[i] == '\\') t.push_back('\\');
t.push_back(s[i]);
}
t.push_back(q);
return t;
}
}

0 comments on commit c07a7fe

Please sign in to comment.