Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve interpolate parsing and unquote output #847

Merged
merged 3 commits into from
Feb 1, 2015
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 50 additions & 6 deletions inspect.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "inspect.hpp"
#include "ast.hpp"
#include "context.hpp"
#include "utf8/checked.h"
#include <cmath>
#include <string>
#include <iostream>
Expand Down Expand Up @@ -768,17 +769,60 @@ namespace Sass {
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 = '\'';
// 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]);

// implement the same strange ruby sass behavior
// an escape sequence can also mean a unicode char
if (s[i] == '\\') {

// skip it
++ i;

// escape length
size_t len = 0;

// parse as many sequence chars as possible
// ToDo: Check if ruby aborts after possible max
while (s[i + len] && isxdigit(s[i + len])) ++ len;

// hex string?
if (len == 0) {

// add next char
t.push_back(s[i]);

} else {

// convert the extracted hex string to code point value
// ToDo: Maybe we could do this without creating a substring
uint32_t cp = strtol(s.substr (i, len).c_str(), nullptr, 16);

// use a very simple approach to convert via utf8 lib
// maybe there is a more elegant way; maybe we shoud
// convert the whole output from string to a stream!?
// allocate memory for utf8 char and convert to utf8
unsigned char u[5] = {0,0,0,0,0}; utf8::append(cp, u);
for(size_t m = 0; u[m] && m < 5; m++) t.push_back(u[m]);

// skip some more chars?
if (len > 1) i += len - 1;

}
// EO if hex

} else {
// add single char
t.push_back(s[i]);
}
}

return t;
}

Expand Down
13 changes: 8 additions & 5 deletions prelexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ namespace Sass {


const char* interpolant(const char* src) {
return delimited_by<hash_lbrace, rbrace, false>(src);
return smartdel_by<hash_lbrace, rbrace, false>(src);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO almost all uses of delimited_by are erroneous. We should probably aim to wholesale replace this with something smarter.

#615 is good example where delimited_by fails spectacularly.

}

// Whitespace handling.
Expand Down Expand Up @@ -592,8 +592,7 @@ namespace Sass {
> >
> >,
spaces_and_comments,
exactly<')'>,
spaces_and_comments
exactly<')'>
> >
>(src);
}
Expand Down Expand Up @@ -686,6 +685,7 @@ namespace Sass {
const char* static_component(const char* src) {
return alternatives< identifier,
static_string,
percentage,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch

hex,
number,
sequence< exactly<'!'>, exactly<important_kwd> >
Expand All @@ -696,8 +696,11 @@ namespace Sass {
return sequence< static_component,
zero_plus < sequence<
alternatives<
sequence< optional_spaces, exactly<'/'>, optional_spaces >,
sequence< optional_spaces, exactly<','>, optional_spaces >,
sequence< optional_spaces, alternatives<
exactly < '/' >,
exactly < ',' >,
exactly < ' ' >
>, optional_spaces >,
spaces
>,
static_component
Expand Down
104 changes: 104 additions & 0 deletions prelexer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,58 @@ namespace Sass {
}
}

// Match a sequence of characters delimited by the supplied chars.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's worth mentioning what makes this smart

template <char beg, char end, bool esc>
const char* smartdel_by(const char* src) {

size_t level = 0;
bool in_squote = false;
bool in_dquote = false;
// bool in_braces = false;

src = exactly<beg>(src);

if (!src) return 0;

while (1) {

// end of string?
if (!*src) return 0;

// has escaped sequence?
if (!esc && *src == '\\') {
++ src; // skip this (and next)
}
else if (*src == '"') {
in_dquote = ! in_dquote;
}
else if (*src == '\'') {
in_squote = ! in_squote;
}
else if (in_dquote || in_squote) {
// take everything literally
}

// find another opener inside?
else if (exactly<beg>(src)) {
++ level; // increase counter
}

// look for the closer (maybe final, maybe not)
else if (const char* stop = exactly<end>(src)) {
// only close one level?
if (level > 0) -- level;
// return position at end of stop
// delimiter may be multiple chars
else return stop;
}

// next
++ src;

}
}

// Match a sequence of characters delimited by the supplied strings.
template <const char* beg, const char* end, bool esc>
const char* delimited_by(const char* src) {
Expand All @@ -97,6 +149,58 @@ namespace Sass {
}
}

// Match a sequence of characters delimited by the supplied strings.
template <const char* beg, const char* end, bool esc>
const char* smartdel_by(const char* src) {

size_t level = 0;
bool in_squote = false;
bool in_dquote = false;
// bool in_braces = false;

src = exactly<beg>(src);

if (!src) return 0;

while (1) {

// end of string?
if (!*src) return 0;

// has escaped sequence?
if (!esc && *src == '\\') {
++ src; // skip this (and next)
}
else if (*src == '"') {
in_dquote = ! in_dquote;
}
else if (*src == '\'') {
in_squote = ! in_squote;
}
else if (in_dquote || in_squote) {
// take everything literally
}

// find another opener inside?
else if (exactly<beg>(src)) {
++ level; // increase counter
}

// look for the closer (maybe final, maybe not)
else if (const char* stop = exactly<end>(src)) {
// only close one level?
if (level > 0) -- level;
// return position at end of stop
// delimiter may be multiple chars
else return stop;
}

// next
++ src;

}
}

// Match any single character.
const char* any_char(const char* src);
// Match any single character except the supplied one.
Expand Down