sassc 1.8.3 → 1.8.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/ext/libsass/.editorconfig +1 -1
  4. data/ext/libsass/.gitignore +1 -0
  5. data/ext/libsass/LICENSE +1 -1
  6. data/ext/libsass/Makefile +20 -14
  7. data/ext/libsass/Makefile.conf +0 -1
  8. data/ext/libsass/Readme.md +3 -1
  9. data/ext/libsass/appveyor.yml +19 -11
  10. data/ext/libsass/docs/api-importer-example.md +2 -1235
  11. data/ext/libsass/docs/build-with-autotools.md +10 -0
  12. data/ext/libsass/docs/build-with-makefiles.md +18 -0
  13. data/ext/libsass/include/sass/base.h +4 -1
  14. data/ext/libsass/include/sass/values.h +2 -1
  15. data/ext/libsass/src/ast.cpp +279 -346
  16. data/ext/libsass/src/ast.hpp +234 -60
  17. data/ext/libsass/src/base64vlq.cpp +1 -0
  18. data/ext/libsass/src/bind.cpp +35 -45
  19. data/ext/libsass/src/bind.hpp +1 -0
  20. data/ext/libsass/src/color_maps.cpp +1 -0
  21. data/ext/libsass/src/constants.cpp +4 -1
  22. data/ext/libsass/src/constants.hpp +2 -1
  23. data/ext/libsass/src/context.cpp +41 -31
  24. data/ext/libsass/src/context.hpp +10 -10
  25. data/ext/libsass/src/cssize.cpp +7 -4
  26. data/ext/libsass/src/cssize.hpp +1 -3
  27. data/ext/libsass/src/debugger.hpp +73 -14
  28. data/ext/libsass/src/emitter.cpp +37 -25
  29. data/ext/libsass/src/emitter.hpp +10 -9
  30. data/ext/libsass/src/environment.cpp +16 -5
  31. data/ext/libsass/src/environment.hpp +5 -3
  32. data/ext/libsass/src/error_handling.cpp +91 -14
  33. data/ext/libsass/src/error_handling.hpp +105 -4
  34. data/ext/libsass/src/eval.cpp +519 -330
  35. data/ext/libsass/src/eval.hpp +12 -13
  36. data/ext/libsass/src/expand.cpp +92 -56
  37. data/ext/libsass/src/expand.hpp +5 -3
  38. data/ext/libsass/src/extend.cpp +60 -51
  39. data/ext/libsass/src/extend.hpp +1 -3
  40. data/ext/libsass/src/file.cpp +37 -27
  41. data/ext/libsass/src/functions.cpp +78 -62
  42. data/ext/libsass/src/functions.hpp +1 -0
  43. data/ext/libsass/src/inspect.cpp +293 -64
  44. data/ext/libsass/src/inspect.hpp +2 -0
  45. data/ext/libsass/src/lexer.cpp +1 -0
  46. data/ext/libsass/src/listize.cpp +14 -15
  47. data/ext/libsass/src/listize.hpp +3 -5
  48. data/ext/libsass/src/memory_manager.cpp +1 -0
  49. data/ext/libsass/src/node.cpp +2 -3
  50. data/ext/libsass/src/operation.hpp +70 -71
  51. data/ext/libsass/src/output.cpp +28 -32
  52. data/ext/libsass/src/output.hpp +1 -2
  53. data/ext/libsass/src/parser.cpp +402 -183
  54. data/ext/libsass/src/parser.hpp +19 -9
  55. data/ext/libsass/src/plugins.cpp +1 -0
  56. data/ext/libsass/src/position.cpp +1 -0
  57. data/ext/libsass/src/prelexer.cpp +134 -56
  58. data/ext/libsass/src/prelexer.hpp +51 -3
  59. data/ext/libsass/src/remove_placeholders.cpp +35 -9
  60. data/ext/libsass/src/remove_placeholders.hpp +4 -3
  61. data/ext/libsass/src/sass.cpp +1 -0
  62. data/ext/libsass/src/sass.hpp +129 -0
  63. data/ext/libsass/src/sass_context.cpp +31 -14
  64. data/ext/libsass/src/sass_context.hpp +2 -31
  65. data/ext/libsass/src/sass_functions.cpp +1 -0
  66. data/ext/libsass/src/sass_interface.cpp +5 -6
  67. data/ext/libsass/src/sass_util.cpp +1 -2
  68. data/ext/libsass/src/sass_util.hpp +5 -5
  69. data/ext/libsass/src/sass_values.cpp +13 -10
  70. data/ext/libsass/src/source_map.cpp +4 -3
  71. data/ext/libsass/src/source_map.hpp +2 -2
  72. data/ext/libsass/src/subset_map.hpp +0 -1
  73. data/ext/libsass/src/to_c.cpp +1 -0
  74. data/ext/libsass/src/to_c.hpp +1 -3
  75. data/ext/libsass/src/to_value.cpp +3 -5
  76. data/ext/libsass/src/to_value.hpp +1 -1
  77. data/ext/libsass/src/units.cpp +96 -59
  78. data/ext/libsass/src/units.hpp +10 -8
  79. data/ext/libsass/src/utf8_string.cpp +5 -0
  80. data/ext/libsass/src/util.cpp +23 -156
  81. data/ext/libsass/src/util.hpp +10 -14
  82. data/ext/libsass/src/values.cpp +1 -0
  83. data/ext/libsass/test/test_node.cpp +2 -6
  84. data/ext/libsass/test/test_selector_difference.cpp +1 -3
  85. data/ext/libsass/test/test_specificity.cpp +0 -2
  86. data/ext/libsass/test/test_superselector.cpp +0 -2
  87. data/ext/libsass/test/test_unification.cpp +1 -3
  88. data/ext/libsass/win/libsass.targets +18 -5
  89. data/ext/libsass/win/libsass.vcxproj +9 -7
  90. data/ext/libsass/win/libsass.vcxproj.filters +148 -106
  91. data/lib/sassc/version.rb +1 -1
  92. data/test/engine_test.rb +12 -0
  93. data/test/native_test.rb +1 -1
  94. metadata +3 -4
  95. data/ext/libsass/src/to_string.cpp +0 -48
  96. data/ext/libsass/src/to_string.hpp +0 -38
@@ -2,6 +2,7 @@
2
2
  #define SASS_EMITTER_H
3
3
 
4
4
  #include <string>
5
+ #include "sass.hpp"
5
6
  #include "sass/base.h"
6
7
  #include "source_map.hpp"
7
8
  #include "ast_fwd_decl.hpp"
@@ -12,7 +13,7 @@ namespace Sass {
12
13
  class Emitter {
13
14
 
14
15
  public:
15
- Emitter(Context* ctx);
16
+ Emitter(struct Sass_Output_Options& opt);
16
17
  virtual ~Emitter() { }
17
18
 
18
19
  protected:
@@ -24,17 +25,19 @@ namespace Sass {
24
25
  // proxy methods for source maps
25
26
  void add_source_index(size_t idx);
26
27
  void set_filename(const std::string& str);
27
- void add_open_mapping(AST_Node* node);
28
- void add_close_mapping(AST_Node* node);
28
+ void add_open_mapping(const AST_Node* node);
29
+ void add_close_mapping(const AST_Node* node);
30
+ void schedule_mapping(const AST_Node* node);
29
31
  std::string render_srcmap(Context &ctx);
30
32
  ParserState remap(const ParserState& pstate);
31
33
 
32
34
  public:
33
- Context* ctx;
35
+ struct Sass_Output_Options& opt;
34
36
  size_t indentation;
35
37
  size_t scheduled_space;
36
38
  size_t scheduled_linefeed;
37
39
  bool scheduled_delimiter;
40
+ const AST_Node* scheduled_mapping;
38
41
 
39
42
  public:
40
43
  // output strings different in comments
@@ -48,16 +51,14 @@ namespace Sass {
48
51
  // nested lists need parentheses
49
52
  bool in_space_array;
50
53
  bool in_comma_array;
51
- // list separators don't get compressed in @debug
52
- bool in_debug;
53
54
 
54
55
  public:
55
56
  // return buffer as std::string
56
57
  std::string get_buffer(void);
57
58
  // flush scheduled space/linefeed
58
- Sass_Output_Style output_style(void);
59
+ Sass_Output_Style output_style(void) const;
59
60
  // add outstanding linefeed
60
- void finalize(void);
61
+ void finalize(bool final = true);
61
62
  // flush scheduled space/linefeed
62
63
  void flush_schedules(void);
63
64
  // prepend some text or token to the buffer
@@ -69,7 +70,7 @@ namespace Sass {
69
70
  void append_wspace(const std::string& text);
70
71
  // append some text or token to the buffer
71
72
  // this adds source-mappings for node start and end
72
- void append_token(const std::string& text, AST_Node* node);
73
+ void append_token(const std::string& text, const AST_Node* node);
73
74
 
74
75
  public: // syntax sugar
75
76
  void append_indentation();
@@ -1,14 +1,24 @@
1
+ #include "sass.hpp"
1
2
  #include "ast.hpp"
2
3
  #include "environment.hpp"
3
4
 
4
5
  namespace Sass {
5
6
 
6
7
  template <typename T>
7
- Environment<T>::Environment() : local_frame_(std::map<std::string, T>()), parent_(0) { }
8
+ Environment<T>::Environment(bool is_shadow)
9
+ : local_frame_(std::map<std::string, T>()),
10
+ parent_(0), is_shadow_(false)
11
+ { }
8
12
  template <typename T>
9
- Environment<T>::Environment(Environment<T>* env) : local_frame_(std::map<std::string, T>()), parent_(env) { }
13
+ Environment<T>::Environment(Environment<T>* env, bool is_shadow)
14
+ : local_frame_(std::map<std::string, T>()),
15
+ parent_(env), is_shadow_(is_shadow)
16
+ { }
10
17
  template <typename T>
11
- Environment<T>::Environment(Environment<T>& env) : local_frame_(std::map<std::string, T>()), parent_(&env) { }
18
+ Environment<T>::Environment(Environment<T>& env, bool is_shadow)
19
+ : local_frame_(std::map<std::string, T>()),
20
+ parent_(&env), is_shadow_(is_shadow)
21
+ { }
12
22
 
13
23
  // link parent to create a stack
14
24
  template <typename T>
@@ -118,12 +128,13 @@ namespace Sass {
118
128
  template <typename T>
119
129
  void Environment<T>::set_lexical(const std::string& key, T val)
120
130
  {
121
- auto cur = this;
122
- while (cur->is_lexical()) {
131
+ auto cur = this; bool shadow = false;
132
+ while (cur->is_lexical() || shadow) {
123
133
  if (cur->has_local(key)) {
124
134
  cur->set_local(key, val);
125
135
  return;
126
136
  }
137
+ shadow = cur->is_shadow();
127
138
  cur = cur->parent_;
128
139
  }
129
140
  set_local(key, val);
@@ -15,12 +15,13 @@ namespace Sass {
15
15
  // TODO: test with map
16
16
  std::map<std::string, T> local_frame_;
17
17
  ADD_PROPERTY(Environment*, parent)
18
+ ADD_PROPERTY(bool, is_shadow)
18
19
 
19
20
  public:
20
21
  Memory_Manager mem;
21
- Environment();
22
- Environment(Environment* env);
23
- Environment(Environment& env);
22
+ Environment(bool is_shadow = false);
23
+ Environment(Environment* env, bool is_shadow = false);
24
+ Environment(Environment& env, bool is_shadow = false);
24
25
 
25
26
  // link parent to create a stack
26
27
  void link(Environment& env);
@@ -87,6 +88,7 @@ namespace Sass {
87
88
  #endif
88
89
 
89
90
  };
91
+
90
92
  }
91
93
 
92
94
  #endif
@@ -1,7 +1,7 @@
1
+ #include "sass.hpp"
1
2
  #include "ast.hpp"
2
3
  #include "prelexer.hpp"
3
4
  #include "backtrace.hpp"
4
- #include "to_string.hpp"
5
5
  #include "error_handling.hpp"
6
6
 
7
7
  #include <iostream>
@@ -10,16 +10,12 @@ namespace Sass {
10
10
 
11
11
  namespace Exception {
12
12
 
13
- Base::Base(ParserState pstate, std::string msg)
14
- : std::runtime_error(msg),
15
- msg(msg), pstate(pstate)
13
+ Base::Base(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack)
14
+ : std::runtime_error(msg), msg(msg),
15
+ prefix("Error"), pstate(pstate),
16
+ import_stack(import_stack)
16
17
  { }
17
18
 
18
- const char* Base::what() const throw()
19
- {
20
- return msg.c_str();
21
- }
22
-
23
19
  InvalidSass::InvalidSass(ParserState pstate, std::string msg)
24
20
  : Base(pstate, msg)
25
21
  { }
@@ -29,9 +25,9 @@ namespace Sass {
29
25
  : Base(selector->pstate()), parent(parent), selector(selector)
30
26
  {
31
27
  msg = "Invalid parent selector for \"";
32
- msg += selector->to_string(false);
28
+ msg += selector->to_string(Sass_Inspect_Options());
33
29
  msg += "\": \"";
34
- msg += parent->to_string(false);;
30
+ msg += parent->to_string(Sass_Inspect_Options());
35
31
  msg += "\"";
36
32
  }
37
33
 
@@ -39,15 +35,96 @@ namespace Sass {
39
35
  : Base(pstate), fn(fn), arg(arg), type(type), value(value)
40
36
  {
41
37
  msg = arg + ": \"";
42
- msg += value->to_string(true, 5);
38
+ msg += value->to_string(Sass_Inspect_Options());
43
39
  msg += "\" is not a " + type;
44
40
  msg += " for `" + fn + "'";
45
41
  }
46
42
 
47
- InvalidSyntax::InvalidSyntax(ParserState pstate, std::string msg)
48
- : Base(pstate, msg)
43
+ InvalidSyntax::InvalidSyntax(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack)
44
+ : Base(pstate, msg, import_stack)
49
45
  { }
50
46
 
47
+ UndefinedOperation::UndefinedOperation(const Expression* lhs, const Expression* rhs, const std::string& op)
48
+ : lhs(lhs), rhs(rhs), op(op)
49
+ {
50
+ msg = def_op_msg + ": \"";
51
+ msg += lhs->to_string({ NESTED, 5 });
52
+ msg += " " + op + " ";
53
+ msg += rhs->to_string({ TO_SASS, 5 });
54
+ msg += "\".";
55
+ }
56
+
57
+ InvalidNullOperation::InvalidNullOperation(const Expression* lhs, const Expression* rhs, const std::string& op)
58
+ : UndefinedOperation(lhs, rhs, op)
59
+ {
60
+ msg = def_op_null_msg + ": \"";
61
+ msg += lhs->inspect();
62
+ msg += " " + op + " ";
63
+ msg += rhs->inspect();
64
+ msg += "\".";
65
+ }
66
+
67
+ ZeroDivisionError::ZeroDivisionError(const Expression& lhs, const Expression& rhs)
68
+ : lhs(lhs), rhs(rhs)
69
+ {
70
+ msg = "divided by 0";
71
+ }
72
+
73
+ DuplicateKeyError::DuplicateKeyError(const Map& dup, const Expression& org)
74
+ : Base(org.pstate()), dup(dup), org(org)
75
+ {
76
+ msg = "Duplicate key ";
77
+ dup.get_duplicate_key()->is_delayed(false);
78
+ msg += dup.get_duplicate_key()->inspect();
79
+ msg += " in map (";
80
+ msg += org.inspect();
81
+ msg += ").";
82
+ }
83
+
84
+ TypeMismatch::TypeMismatch(const Expression& var, const std::string type)
85
+ : Base(var.pstate()), var(var), type(type)
86
+ {
87
+ msg = var.to_string();
88
+ msg += " is not an ";
89
+ msg += type;
90
+ msg += ".";
91
+ }
92
+
93
+ InvalidValue::InvalidValue(const Expression& val)
94
+ : Base(val.pstate()), val(val)
95
+ {
96
+ msg = val.to_string();
97
+ msg += " isn't a valid CSS value.";
98
+ }
99
+
100
+ IncompatibleUnits::IncompatibleUnits(const Number& lhs, const Number& rhs)
101
+ : lhs(lhs), rhs(rhs)
102
+ {
103
+ msg = "Incompatible units: '";
104
+ msg += rhs.unit();
105
+ msg += "' and '";
106
+ msg += lhs.unit();
107
+ msg += "'.";
108
+ }
109
+
110
+ AlphaChannelsNotEqual::AlphaChannelsNotEqual(const Expression* lhs, const Expression* rhs, const std::string& op)
111
+ : lhs(lhs), rhs(rhs), op(op)
112
+ {
113
+ msg = "Alpha channels must be equal: ";
114
+ msg += lhs->to_string({ NESTED, 5 });
115
+ msg += " " + op + " ";
116
+ msg += rhs->to_string({ NESTED, 5 });
117
+ msg += ".";
118
+ }
119
+
120
+
121
+ SassValueError::SassValueError(ParserState pstate, OperationError& err)
122
+ : Base(pstate, err.what())
123
+ {
124
+ msg = err.what();
125
+ prefix = err.errtype();
126
+ }
127
+
51
128
  }
52
129
 
53
130
 
@@ -12,16 +12,21 @@ namespace Sass {
12
12
 
13
13
  namespace Exception {
14
14
 
15
- const std::string def_msg = "Invalid sass";
15
+ const std::string def_msg = "Invalid sass detected";
16
+ const std::string def_op_msg = "Undefined operation";
17
+ const std::string def_op_null_msg = "Invalid null operation";
16
18
 
17
19
  class Base : public std::runtime_error {
18
20
  protected:
19
21
  std::string msg;
22
+ std::string prefix;
20
23
  public:
21
24
  ParserState pstate;
25
+ std::vector<Sass_Import_Entry>* import_stack;
22
26
  public:
23
- Base(ParserState pstate, std::string msg = def_msg);
24
- virtual const char* what() const throw();
27
+ Base(ParserState pstate, std::string msg = def_msg, std::vector<Sass_Import_Entry>* import_stack = 0);
28
+ virtual const char* errtype() const { return prefix.c_str(); }
29
+ virtual const char* what() const throw() { return msg.c_str(); }
25
30
  virtual ~Base() throw() {};
26
31
  };
27
32
 
@@ -53,10 +58,106 @@ namespace Sass {
53
58
 
54
59
  class InvalidSyntax : public Base {
55
60
  public:
56
- InvalidSyntax(ParserState pstate, std::string msg);
61
+ InvalidSyntax(ParserState pstate, std::string msg, std::vector<Sass_Import_Entry>* import_stack = 0);
57
62
  virtual ~InvalidSyntax() throw() {};
58
63
  };
59
64
 
65
+ /* common virtual base class (has no pstate) */
66
+ class OperationError : public std::runtime_error {
67
+ protected:
68
+ std::string msg;
69
+ public:
70
+ OperationError(std::string msg = def_op_msg)
71
+ : std::runtime_error(msg), msg(msg)
72
+ {};
73
+ public:
74
+ virtual const char* errtype() const { return "Error"; }
75
+ virtual const char* what() const throw() { return msg.c_str(); }
76
+ virtual ~OperationError() throw() {};
77
+ };
78
+
79
+ class ZeroDivisionError : public OperationError {
80
+ protected:
81
+ const Expression& lhs;
82
+ const Expression& rhs;
83
+ public:
84
+ ZeroDivisionError(const Expression& lhs, const Expression& rhs);
85
+ virtual const char* errtype() const { return "ZeroDivisionError"; }
86
+ virtual ~ZeroDivisionError() throw() {};
87
+ };
88
+
89
+ class DuplicateKeyError : public Base {
90
+ protected:
91
+ const Map& dup;
92
+ const Expression& org;
93
+ public:
94
+ DuplicateKeyError(const Map& dup, const Expression& org);
95
+ virtual const char* errtype() const { return "Error"; }
96
+ virtual ~DuplicateKeyError() throw() {};
97
+ };
98
+
99
+ class TypeMismatch : public Base {
100
+ protected:
101
+ const Expression& var;
102
+ const std::string type;
103
+ public:
104
+ TypeMismatch(const Expression& var, const std::string type);
105
+ virtual const char* errtype() const { return "Error"; }
106
+ virtual ~TypeMismatch() throw() {};
107
+ };
108
+
109
+ class InvalidValue : public Base {
110
+ protected:
111
+ const Expression& val;
112
+ public:
113
+ InvalidValue(const Expression& val);
114
+ virtual const char* errtype() const { return "Error"; }
115
+ virtual ~InvalidValue() throw() {};
116
+ };
117
+
118
+ class IncompatibleUnits : public OperationError {
119
+ protected:
120
+ const Number& lhs;
121
+ const Number& rhs;
122
+ public:
123
+ IncompatibleUnits(const Number& lhs, const Number& rhs);
124
+ virtual ~IncompatibleUnits() throw() {};
125
+ };
126
+
127
+ class UndefinedOperation : public OperationError {
128
+ protected:
129
+ const Expression* lhs;
130
+ const Expression* rhs;
131
+ const std::string op;
132
+ public:
133
+ UndefinedOperation(const Expression* lhs, const Expression* rhs, const std::string& op);
134
+ // virtual const char* errtype() const { return "Error"; }
135
+ virtual ~UndefinedOperation() throw() {};
136
+ };
137
+
138
+ class InvalidNullOperation : public UndefinedOperation {
139
+ public:
140
+ InvalidNullOperation(const Expression* lhs, const Expression* rhs, const std::string& op);
141
+ virtual ~InvalidNullOperation() throw() {};
142
+ };
143
+
144
+ class AlphaChannelsNotEqual : public OperationError {
145
+ protected:
146
+ const Expression* lhs;
147
+ const Expression* rhs;
148
+ const std::string op;
149
+ public:
150
+ AlphaChannelsNotEqual(const Expression* lhs, const Expression* rhs, const std::string& op);
151
+ // virtual const char* errtype() const { return "Error"; }
152
+ virtual ~AlphaChannelsNotEqual() throw() {};
153
+ };
154
+
155
+ class SassValueError : public Base {
156
+ public:
157
+ SassValueError(ParserState pstate, OperationError& err);
158
+ virtual ~SassValueError() throw() {};
159
+ };
160
+
60
161
  }
61
162
 
62
163
  void warn(std::string msg, ParserState pstate);
@@ -1,3 +1,4 @@
1
+ #include "sass.hpp"
1
2
  #include <cstdlib>
2
3
  #include <cmath>
3
4
  #include <iostream>
@@ -10,7 +11,6 @@
10
11
  #include "ast.hpp"
11
12
  #include "bind.hpp"
12
13
  #include "util.hpp"
13
- #include "to_string.hpp"
14
14
  #include "inspect.hpp"
15
15
  #include "environment.hpp"
16
16
  #include "position.hpp"
@@ -41,8 +41,7 @@ namespace Sass {
41
41
 
42
42
  Eval::Eval(Expand& exp)
43
43
  : exp(exp),
44
- ctx(exp.ctx),
45
- listize(exp.ctx)
44
+ ctx(exp.ctx)
46
45
  { }
47
46
  Eval::~Eval() { }
48
47
 
@@ -160,11 +159,11 @@ namespace Sass {
160
159
  std::string variable(f->variable());
161
160
  Expression* low = f->lower_bound()->perform(this);
162
161
  if (low->concrete_type() != Expression::NUMBER) {
163
- error("lower bound of `@for` directive must be numeric", low->pstate());
162
+ throw Exception::TypeMismatch(*low, "integer");
164
163
  }
165
164
  Expression* high = f->upper_bound()->perform(this);
166
165
  if (high->concrete_type() != Expression::NUMBER) {
167
- error("upper bound of `@for` directive must be numeric", high->pstate());
166
+ throw Exception::TypeMismatch(*high, "integer");
168
167
  }
169
168
  Number* sass_start = static_cast<Number*>(low);
170
169
  Number* sass_end = static_cast<Number*>(high);
@@ -178,10 +177,10 @@ namespace Sass {
178
177
  double start = sass_start->value();
179
178
  double end = sass_end->value();
180
179
  // only create iterator once in this environment
181
- Env* env = exp.environment();
182
- Number* it = SASS_MEMORY_NEW(env->mem, Number, low->pstate(), start, sass_end->unit());
183
- AST_Node* old_var = env->has_local(variable) ? env->get_local(variable) : 0;
184
- env->set_local(variable, it);
180
+ Env env(environment(), true);
181
+ exp.env_stack.push_back(&env);
182
+ Number* it = SASS_MEMORY_NEW(env.mem, Number, low->pstate(), start, sass_end->unit());
183
+ env.set_local(variable, it);
185
184
  Block* body = f->block();
186
185
  Expression* val = 0;
187
186
  if (start < end) {
@@ -190,7 +189,7 @@ namespace Sass {
190
189
  i < end;
191
190
  ++i) {
192
191
  it->value(i);
193
- env->set_local(variable, it);
192
+ env.set_local(variable, it);
194
193
  val = body->perform(this);
195
194
  if (val) break;
196
195
  }
@@ -200,14 +199,12 @@ namespace Sass {
200
199
  i > end;
201
200
  --i) {
202
201
  it->value(i);
203
- env->set_local(variable, it);
202
+ env.set_local(variable, it);
204
203
  val = body->perform(this);
205
204
  if (val) break;
206
205
  }
207
206
  }
208
- // restore original environment
209
- if (!old_var) env->del_local(variable);
210
- else env->set_local(variable, old_var);
207
+ exp.env_stack.pop_back();
211
208
  return val;
212
209
  }
213
210
 
@@ -217,12 +214,17 @@ namespace Sass {
217
214
  {
218
215
  std::vector<std::string> variables(e->variables());
219
216
  Expression* expr = e->list()->perform(this);
220
- Env* env = exp.environment();
221
- List* list = 0;
217
+ Env env(environment(), true);
218
+ exp.env_stack.push_back(&env);
219
+ Vectorized<Expression*>* list = 0;
222
220
  Map* map = 0;
223
221
  if (expr->concrete_type() == Expression::MAP) {
224
222
  map = static_cast<Map*>(expr);
225
223
  }
224
+ else if (Selector_List* ls = dynamic_cast<Selector_List*>(expr)) {
225
+ Listize listize(ctx.mem);
226
+ list = dynamic_cast<List*>(ls->perform(&listize));
227
+ }
226
228
  else if (expr->concrete_type() != Expression::LIST) {
227
229
  list = SASS_MEMORY_NEW(ctx.mem, List, expr->pstate(), 1, SASS_COMMA);
228
230
  *list << expr;
@@ -230,12 +232,7 @@ namespace Sass {
230
232
  else {
231
233
  list = static_cast<List*>(expr);
232
234
  }
233
- // remember variables and then reset them
234
- std::vector<AST_Node*> old_vars(variables.size());
235
- for (size_t i = 0, L = variables.size(); i < L; ++i) {
236
- old_vars[i] = env->has_local(variables[i]) ? env->get_local(variables[i]) : 0;
237
- env->set_local(variables[i], 0);
238
- }
235
+
239
236
  Block* body = e->block();
240
237
  Expression* val = 0;
241
238
 
@@ -247,10 +244,10 @@ namespace Sass {
247
244
  List* variable = SASS_MEMORY_NEW(ctx.mem, List, map->pstate(), 2, SASS_SPACE);
248
245
  *variable << key;
249
246
  *variable << value;
250
- env->set_local(variables[0], variable);
247
+ env.set_local(variables[0], variable);
251
248
  } else {
252
- env->set_local(variables[0], key);
253
- env->set_local(variables[1], value);
249
+ env.set_local(variables[0], key);
250
+ env.set_local(variables[1], value);
254
251
  }
255
252
 
256
253
  val = body->perform(this);
@@ -258,6 +255,9 @@ namespace Sass {
258
255
  }
259
256
  }
260
257
  else {
258
+ if (list->length() == 1 && dynamic_cast<Selector_List*>(list)) {
259
+ list = dynamic_cast<Vectorized<Expression*>*>(list);
260
+ }
261
261
  for (size_t i = 0, L = list->length(); i < L; ++i) {
262
262
  Expression* e = (*list)[i];
263
263
  // unwrap value if the expression is an argument
@@ -266,21 +266,23 @@ namespace Sass {
266
266
  if (List* scalars = dynamic_cast<List*>(e)) {
267
267
  if (variables.size() == 1) {
268
268
  Expression* var = scalars;
269
- env->set_local(variables[0], var);
269
+ env.set_local(variables[0], var);
270
270
  } else {
271
+ // XXX: this is never hit via spec tests
271
272
  for (size_t j = 0, K = variables.size(); j < K; ++j) {
272
273
  Expression* res = j >= scalars->length()
273
274
  ? SASS_MEMORY_NEW(ctx.mem, Null, expr->pstate())
274
275
  : (*scalars)[j];
275
- env->set_local(variables[j], res);
276
+ env.set_local(variables[j], res);
276
277
  }
277
278
  }
278
279
  } else {
279
280
  if (variables.size() > 0) {
280
- env->set_local(variables[0], e);
281
+ env.set_local(variables[0], e);
281
282
  for (size_t j = 1, K = variables.size(); j < K; ++j) {
283
+ // XXX: this is never hit via spec tests
282
284
  Expression* res = SASS_MEMORY_NEW(ctx.mem, Null, expr->pstate());
283
- env->set_local(variables[j], res);
285
+ env.set_local(variables[j], res);
284
286
  }
285
287
  }
286
288
  }
@@ -288,11 +290,7 @@ namespace Sass {
288
290
  if (val) break;
289
291
  }
290
292
  }
291
- // restore original environment
292
- for (size_t j = 0, K = variables.size(); j < K; ++j) {
293
- if(!old_vars[j]) env->del_local(variables[j]);
294
- else env->set_local(variables[j], old_vars[j]);
295
- }
293
+ exp.env_stack.pop_back();
296
294
  return val;
297
295
  }
298
296
 
@@ -300,10 +298,16 @@ namespace Sass {
300
298
  {
301
299
  Expression* pred = w->predicate();
302
300
  Block* body = w->block();
301
+ Env env(environment(), true);
302
+ exp.env_stack.push_back(&env);
303
303
  while (*pred->perform(this)) {
304
304
  Expression* val = body->perform(this);
305
- if (val) return val;
305
+ if (val) {
306
+ exp.env_stack.pop_back();
307
+ return val;
308
+ }
306
309
  }
310
+ exp.env_stack.pop_back();
307
311
  return 0;
308
312
  }
309
313
 
@@ -314,8 +318,9 @@ namespace Sass {
314
318
 
315
319
  Expression* Eval::operator()(Warning* w)
316
320
  {
321
+ Sass_Output_Style outstyle = ctx.c_options.output_style;
322
+ ctx.c_options.output_style = NESTED;
317
323
  Expression* message = w->message()->perform(this);
318
- To_String to_string(&ctx);
319
324
  Env* env = exp.environment();
320
325
 
321
326
  // try to use generic function
@@ -331,24 +336,27 @@ namespace Sass {
331
336
  union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);
332
337
  sass_list_set_value(c_args, 0, message->perform(&to_c));
333
338
  union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);
339
+ ctx.c_options.output_style = outstyle;
334
340
  sass_delete_value(c_args);
335
341
  sass_delete_value(c_val);
336
342
  return 0;
337
343
 
338
344
  }
339
345
 
340
- std::string result(unquote(message->perform(&to_string)));
346
+ std::string result(unquote(message->to_sass()));
341
347
  Backtrace top(backtrace(), w->pstate(), "");
342
348
  std::cerr << "WARNING: " << result;
343
- std::cerr << top.to_string(true);
349
+ std::cerr << top.to_string();
344
350
  std::cerr << std::endl << std::endl;
351
+ ctx.c_options.output_style = outstyle;
345
352
  return 0;
346
353
  }
347
354
 
348
355
  Expression* Eval::operator()(Error* e)
349
356
  {
357
+ Sass_Output_Style outstyle = ctx.c_options.output_style;
358
+ ctx.c_options.output_style = NESTED;
350
359
  Expression* message = e->message()->perform(this);
351
- To_String to_string(&ctx);
352
360
  Env* env = exp.environment();
353
361
 
354
362
  // try to use generic function
@@ -364,21 +372,24 @@ namespace Sass {
364
372
  union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);
365
373
  sass_list_set_value(c_args, 0, message->perform(&to_c));
366
374
  union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);
375
+ ctx.c_options.output_style = outstyle;
367
376
  sass_delete_value(c_args);
368
377
  sass_delete_value(c_val);
369
378
  return 0;
370
379
 
371
380
  }
372
381
 
373
- std::string result(unquote(message->perform(&to_string)));
382
+ std::string result(unquote(message->to_sass()));
383
+ ctx.c_options.output_style = outstyle;
374
384
  error(result, e->pstate());
375
385
  return 0;
376
386
  }
377
387
 
378
388
  Expression* Eval::operator()(Debug* d)
379
389
  {
390
+ Sass_Output_Style outstyle = ctx.c_options.output_style;
391
+ ctx.c_options.output_style = NESTED;
380
392
  Expression* message = d->value()->perform(this);
381
- To_String to_string(&ctx, false, true);
382
393
  Env* env = exp.environment();
383
394
 
384
395
  // try to use generic function
@@ -394,6 +405,7 @@ namespace Sass {
394
405
  union Sass_Value* c_args = sass_make_list(1, SASS_COMMA);
395
406
  sass_list_set_value(c_args, 0, message->perform(&to_c));
396
407
  union Sass_Value* c_val = c_func(c_args, c_function, ctx.c_compiler);
408
+ ctx.c_options.output_style = outstyle;
397
409
  sass_delete_value(c_args);
398
410
  sass_delete_value(c_val);
399
411
  return 0;
@@ -401,10 +413,11 @@ namespace Sass {
401
413
  }
402
414
 
403
415
  std::string cwd(ctx.cwd());
404
- std::string result(unquote(message->perform(&to_string)));
416
+ std::string result(unquote(message->to_sass()));
405
417
  std::string abs_path(Sass::File::rel2abs(d->pstate().path, cwd, cwd));
406
418
  std::string rel_path(Sass::File::abs2rel(d->pstate().path, cwd, cwd));
407
419
  std::string output_path(Sass::File::path_for_console(rel_path, abs_path, d->pstate().path));
420
+ ctx.c_options.output_style = outstyle;
408
421
 
409
422
  std::cerr << output_path << ":" << d->pstate().line+1 << " DEBUG: " << result;
410
423
  std::cerr << std::endl;
@@ -413,7 +426,29 @@ namespace Sass {
413
426
 
414
427
  Expression* Eval::operator()(List* l)
415
428
  {
429
+ // special case for unevaluated map
430
+ if (l->separator() == SASS_HASH) {
431
+ Map* lm = SASS_MEMORY_NEW(ctx.mem, Map,
432
+ l->pstate(),
433
+ l->length() / 2);
434
+ for (size_t i = 0, L = l->length(); i < L; i += 2)
435
+ {
436
+ Expression* key = (*l)[i+0]->perform(this);
437
+ Expression* val = (*l)[i+1]->perform(this);
438
+ // make sure the color key never displays its real name
439
+ key->is_delayed(true);
440
+ *lm << std::make_pair(key, val);
441
+ }
442
+ if (lm->has_duplicate_key()) {
443
+ throw Exception::DuplicateKeyError(*lm, *l);
444
+ }
445
+
446
+ lm->is_interpolant(l->is_interpolant());
447
+ return lm->perform(this);
448
+ }
449
+ // check if we should expand it
416
450
  if (l->is_expanded()) return l;
451
+ // regular case for unevaluated lists
417
452
  List* ll = SASS_MEMORY_NEW(ctx.mem, List,
418
453
  l->pstate(),
419
454
  l->length(),
@@ -422,6 +457,7 @@ namespace Sass {
422
457
  for (size_t i = 0, L = l->length(); i < L; ++i) {
423
458
  *ll << (*l)[i]->perform(this);
424
459
  }
460
+ ll->is_interpolant(l->is_interpolant());
425
461
  ll->is_expanded(true);
426
462
  return ll;
427
463
  }
@@ -433,8 +469,7 @@ namespace Sass {
433
469
  // make sure we're not starting with duplicate keys.
434
470
  // the duplicate key state will have been set in the parser phase.
435
471
  if (m->has_duplicate_key()) {
436
- To_String to_string(&ctx);
437
- error("Duplicate key \"" + m->get_duplicate_key()->perform(&to_string) + "\" in map " + m->perform(&to_string) + ".", m->pstate());
472
+ throw Exception::DuplicateKeyError(*m, *m);
438
473
  }
439
474
 
440
475
  Map* mm = SASS_MEMORY_NEW(ctx.mem, Map,
@@ -448,8 +483,7 @@ namespace Sass {
448
483
 
449
484
  // check the evaluated keys aren't duplicates.
450
485
  if (mm->has_duplicate_key()) {
451
- To_String to_string(&ctx);
452
- error("Duplicate key \"" + mm->get_duplicate_key()->perform(&to_string) + "\" in map " + m->perform(&to_string) + ".", mm->pstate());
486
+ throw Exception::DuplicateKeyError(*mm, *m);
453
487
  }
454
488
 
455
489
  mm->is_expanded(true);
@@ -458,33 +492,75 @@ namespace Sass {
458
492
 
459
493
  Expression* Eval::operator()(Binary_Expression* b)
460
494
  {
495
+
496
+ String_Schema* ret_schema = 0;
461
497
  enum Sass_OP op_type = b->type();
498
+
462
499
  // don't eval delayed expressions (the '/' when used as a separator)
463
- if (op_type == Sass_OP::DIV && b->is_delayed()) return b;
464
- b->is_delayed(false);
465
- // if one of the operands is a '/' then make sure it's evaluated
466
- Expression* lhs = b->left()->perform(this);
467
- lhs->is_delayed(false);
468
- while (typeid(*lhs) == typeid(Binary_Expression)) {
469
- Binary_Expression* lhs_ex = static_cast<Binary_Expression*>(lhs);
470
- if (lhs_ex->type() == Sass_OP::DIV && lhs_ex->is_delayed()) break;
471
- lhs = Eval::operator()(lhs_ex);
500
+ if (op_type == Sass_OP::DIV && b->is_delayed()) {
501
+ b->right(b->right()->perform(this));
502
+ b->left(b->left()->perform(this));
503
+ return b;
504
+ }
505
+
506
+ // only the last item will be used to eval the binary expression
507
+ if (String_Schema* s_1 = dynamic_cast<String_Schema*>(b->left())) {
508
+ if (!s_1->is_right_interpolant()) {
509
+ ret_schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, s_1->pstate());
510
+ Binary_Expression* bin_ex = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, b->pstate(),
511
+ b->op(), s_1->last(), b->right());
512
+ bin_ex->is_delayed(b->left()->is_delayed() || b->right()->is_delayed());
513
+ // bin_ex->is_interpolant(b->left()->is_interpolant());
514
+ for (size_t i = 0; i < s_1->length() - 1; ++i) {
515
+ *ret_schema << s_1->at(i)->perform(this);
516
+ }
517
+ *ret_schema << bin_ex->perform(this);
518
+ return ret_schema->perform(this);
519
+ }
520
+ }
521
+ if (String_Schema* s_r = dynamic_cast<String_Schema*>(b->right())) {
522
+ if (!s_r->is_left_interpolant() || op_type == Sass_OP::DIV) {
523
+ ret_schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, s_r->pstate());
524
+ Binary_Expression* bin_ex = SASS_MEMORY_NEW(ctx.mem, Binary_Expression, b->pstate(),
525
+ b->op(), b->left(), s_r->first());
526
+ bin_ex->is_delayed(b->left()->is_delayed() || b->right()->is_delayed());
527
+ // if (op_type == Sass_OP::SUB && b->is_right_interpolant()) bin_ex->is_interpolant(true);
528
+ *ret_schema << bin_ex->perform(this);
529
+ for (size_t i = 1; i < s_r->length(); ++i) {
530
+ *ret_schema << s_r->at(i)->perform(this);
531
+ }
532
+ return ret_schema->perform(this);
533
+ }
472
534
  }
473
535
 
474
- switch (op_type) {
475
- case Sass_OP::AND:
476
- return *lhs ? b->right()->perform(this) : lhs;
477
- break;
478
536
 
479
- case Sass_OP::OR:
480
- return *lhs ? lhs : b->right()->perform(this);
481
- break;
537
+ // don't eval delayed expressions (the '/' when used as a separator)
538
+ if (op_type == Sass_OP::DIV && b->is_delayed()) {
539
+ b->right(b->right()->perform(this));
540
+ b->left(b->left()->perform(this));
541
+ return b;
542
+ }
482
543
 
483
- default:
484
- break;
544
+ // b->is_delayed(false);
545
+ Expression* lhs = b->left();
546
+ Expression* rhs = b->right();
547
+
548
+ // bool delay_lhs = false;
549
+ // bool delay_rhs = false;
550
+
551
+ if (String_Schema* schema = dynamic_cast<String_Schema*>(lhs)) {
552
+ if (schema->is_right_interpolant()) {
553
+ b->is_delayed(true);
554
+ // delay_lhs = true;
555
+ }
485
556
  }
486
- // not a logical connective, so go ahead and eval the rhs
487
- Expression* rhs = b->right()->perform(this);
557
+ if (String_Schema* schema = dynamic_cast<String_Schema*>(rhs)) {
558
+ if (schema->is_left_interpolant()) {
559
+ b->is_delayed(true);
560
+ // delay_rhs = true;
561
+ }
562
+ }
563
+
488
564
  // maybe fully evaluate structure
489
565
  if (op_type == Sass_OP::EQ ||
490
566
  op_type == Sass_OP::NEQ ||
@@ -493,6 +569,26 @@ namespace Sass {
493
569
  op_type == Sass_OP::LT ||
494
570
  op_type == Sass_OP::LTE)
495
571
  {
572
+
573
+ if (String_Schema* schema = dynamic_cast<String_Schema*>(lhs)) {
574
+ if (schema->has_interpolants()) {
575
+ b->is_delayed(true);
576
+ }
577
+ }
578
+ if (String_Schema* schema = dynamic_cast<String_Schema*>(rhs)) {
579
+ if (schema->has_interpolants()) {
580
+ b->is_delayed(true);
581
+ }
582
+ }
583
+ lhs->is_expanded(false);
584
+ lhs->set_delayed(false);
585
+ lhs = lhs->perform(this);
586
+ lhs->is_expanded(false);
587
+ lhs->set_delayed(false);
588
+ lhs = lhs->perform(this);
589
+ rhs->is_expanded(false);
590
+ rhs->set_delayed(false);
591
+ rhs = rhs->perform(this);
496
592
  rhs->is_expanded(false);
497
593
  rhs->set_delayed(false);
498
594
  rhs = rhs->perform(this);
@@ -503,6 +599,30 @@ namespace Sass {
503
599
  // rhs = rhs->perform(this);
504
600
  }
505
601
 
602
+ // if one of the operands is a '/' then make sure it's evaluated
603
+ lhs = lhs->perform(this);
604
+ lhs->is_delayed(false);
605
+ while (typeid(*lhs) == typeid(Binary_Expression)) {
606
+ Binary_Expression* lhs_ex = static_cast<Binary_Expression*>(lhs);
607
+ if (lhs_ex->type() == Sass_OP::DIV && lhs_ex->is_delayed()) break;
608
+ lhs = Eval::operator()(lhs_ex);
609
+ }
610
+
611
+ switch (op_type) {
612
+ case Sass_OP::AND:
613
+ return *lhs ? b->right()->perform(this) : lhs;
614
+ break;
615
+
616
+ case Sass_OP::OR:
617
+ return *lhs ? lhs : b->right()->perform(this);
618
+ break;
619
+
620
+ default:
621
+ break;
622
+ }
623
+ // not a logical connective, so go ahead and eval the rhs
624
+ rhs = rhs->perform(this);
625
+
506
626
  // upgrade string to number if possible (issue #948)
507
627
  if (op_type == Sass_OP::DIV || op_type == Sass_OP::MUL) {
508
628
  if (String_Constant* str = dynamic_cast<String_Constant*>(rhs)) {
@@ -515,18 +635,6 @@ namespace Sass {
515
635
  }
516
636
  }
517
637
 
518
- // see if it's a relational expression
519
- switch(op_type) {
520
- case Sass_OP::EQ: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), eq(lhs, rhs));
521
- case Sass_OP::NEQ: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !eq(lhs, rhs));
522
- case Sass_OP::GT: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !lt(lhs, rhs) && !eq(lhs, rhs));
523
- case Sass_OP::GTE: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !lt(lhs, rhs));
524
- case Sass_OP::LT: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), lt(lhs, rhs));
525
- case Sass_OP::LTE: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), lt(lhs, rhs) || eq(lhs, rhs));
526
-
527
- default: break;
528
- }
529
-
530
638
 
531
639
  Expression::Concrete_Type l_type = lhs->concrete_type();
532
640
  Expression::Concrete_Type r_type = rhs->concrete_type();
@@ -534,23 +642,25 @@ namespace Sass {
534
642
  // Is one of the operands an interpolant?
535
643
  String_Schema* s1 = dynamic_cast<String_Schema*>(b->left());
536
644
  String_Schema* s2 = dynamic_cast<String_Schema*>(b->right());
645
+ Binary_Expression* b1 = dynamic_cast<Binary_Expression*>(b->left());
646
+ Binary_Expression* b2 = dynamic_cast<Binary_Expression*>(b->right());
537
647
 
538
- if ((s1 && s1->has_interpolants()) || (s2 && s2->has_interpolants())) {
539
- std::string sep;
540
- switch (op_type) {
541
- case Sass_OP::SUB: sep = "-"; break;
542
- case Sass_OP::DIV: sep = "/"; break;
543
- case Sass_OP::ADD: sep = "+"; break;
544
- case Sass_OP::MUL: sep = "*"; break;
545
- default: break;
546
- }
648
+ bool schema_op = false;
649
+
650
+ bool force_delay = (s2 && s2->is_left_interpolant()) ||
651
+ (s1 && s1->is_right_interpolant()) ||
652
+ (b1 && b1->is_right_interpolant()) ||
653
+ (b2 && b2->is_left_interpolant());
547
654
 
655
+ if ((s1 && s1->has_interpolants()) || (s2 && s2->has_interpolants()) || force_delay)
656
+ {
548
657
  // If possible upgrade LHS to a number
549
- if (op_type == Sass_OP::DIV || op_type == Sass_OP::MUL || op_type == Sass_OP::ADD || op_type == Sass_OP::SUB) {
658
+ if (op_type == Sass_OP::DIV || op_type == Sass_OP::MUL || op_type == Sass_OP::MOD || op_type == Sass_OP::ADD || op_type == Sass_OP::SUB ||
659
+ op_type == Sass_OP::EQ) {
550
660
  if (String_Constant* str = dynamic_cast<String_Constant*>(lhs)) {
551
661
  std::string value(str->value());
552
662
  const char* start = value.c_str();
553
- if (Prelexer::sequence < Prelexer::number >(start) != 0) {
663
+ if (Prelexer::sequence < Prelexer::dimension, Prelexer::end_of_file >(start) != 0) {
554
664
  lhs = SASS_MEMORY_NEW(ctx.mem, Textual, lhs->pstate(), Textual::DIMENSION, str->value());
555
665
  lhs->is_delayed(false); lhs = lhs->perform(this);
556
666
  }
@@ -568,54 +678,118 @@ namespace Sass {
568
678
  To_Value to_value(ctx, ctx.mem);
569
679
  Value* v_l = dynamic_cast<Value*>(lhs->perform(&to_value));
570
680
  Value* v_r = dynamic_cast<Value*>(rhs->perform(&to_value));
571
- Expression::Concrete_Type l_type = lhs->concrete_type();
572
- Expression::Concrete_Type r_type = rhs->concrete_type();
681
+ l_type = lhs->concrete_type();
682
+ r_type = rhs->concrete_type();
683
+
684
+ if (s2 && s2->has_interpolants() && s2->length()) {
685
+ Textual* front = dynamic_cast<Textual*>(s2->elements().front());
686
+ if (front && !front->is_interpolant())
687
+ {
688
+ // XXX: this is never hit via spec tests
689
+ schema_op = true;
690
+ rhs = front->perform(this);
691
+ }
692
+ }
573
693
 
574
- if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) {
575
- return SASS_MEMORY_NEW(ctx.mem, String_Constant, lhs->pstate(),
576
- v_l->to_string() + " " + sep + " " + v_r->to_string());
694
+ if (force_delay) {
695
+ std::string str("");
696
+ str += v_l->to_string(ctx.c_options);
697
+ if (b->op().ws_before) str += " ";
698
+ str += b->separator();
699
+ if (b->op().ws_after) str += " ";
700
+ str += v_r->to_string(ctx.c_options);
701
+ String_Constant* val = SASS_MEMORY_NEW(ctx.mem, String_Constant, lhs->pstate(), str);
702
+ val->is_interpolant(b->left()->has_interpolant());
703
+ return val;
704
+ }
705
+ }
706
+
707
+ // see if it's a relational expression
708
+ try {
709
+ switch(op_type) {
710
+ case Sass_OP::EQ: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), eq(lhs, rhs));
711
+ case Sass_OP::NEQ: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !eq(lhs, rhs));
712
+ case Sass_OP::GT: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !lt(lhs, rhs, "gt") && !eq(lhs, rhs));
713
+ case Sass_OP::GTE: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), !lt(lhs, rhs, "gte"));
714
+ case Sass_OP::LT: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), lt(lhs, rhs, "lt"));
715
+ case Sass_OP::LTE: return SASS_MEMORY_NEW(ctx.mem, Boolean, b->pstate(), lt(lhs, rhs, "lte") || eq(lhs, rhs));
716
+ default: break;
577
717
  }
578
718
  }
719
+ catch (Exception::OperationError& err)
720
+ {
721
+ // throw Exception::Base(b->pstate(), err.what());
722
+ throw Exception::SassValueError(b->pstate(), err);
723
+ }
724
+
725
+ l_type = lhs->concrete_type();
726
+ r_type = rhs->concrete_type();
579
727
 
580
728
  // ToDo: throw error in op functions
581
729
  // ToDo: then catch and re-throw them
582
- ParserState pstate(b->pstate());
583
- int precision = (int)ctx.c_options->precision;
584
- bool compressed = ctx.output_style() == SASS_STYLE_COMPRESSED;
585
- if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) {
586
- const Number* l_n = dynamic_cast<const Number*>(lhs);
587
- const Number* r_n = dynamic_cast<const Number*>(rhs);
588
- return op_numbers(ctx.mem, op_type, *l_n, *r_n, compressed, precision, &pstate);
589
- }
590
- if (l_type == Expression::NUMBER && r_type == Expression::COLOR) {
591
- const Number* l_n = dynamic_cast<const Number*>(lhs);
592
- const Color* r_c = dynamic_cast<const Color*>(rhs);
593
- return op_number_color(ctx.mem, op_type, *l_n, *r_c, compressed, precision, &pstate);
594
- }
595
- if (l_type == Expression::COLOR && r_type == Expression::NUMBER) {
596
- const Color* l_c = dynamic_cast<const Color*>(lhs);
597
- const Number* r_n = dynamic_cast<const Number*>(rhs);
598
- return op_color_number(ctx.mem, op_type, *l_c, *r_n, compressed, precision, &pstate);
599
- }
600
- if (l_type == Expression::COLOR && r_type == Expression::COLOR) {
601
- const Color* l_c = dynamic_cast<const Color*>(lhs);
602
- const Color* r_c = dynamic_cast<const Color*>(rhs);
603
- return op_colors(ctx.mem, op_type, *l_c, *r_c, compressed, precision, &pstate);
604
- }
605
-
606
- To_Value to_value(ctx, ctx.mem);
607
- Value* v_l = dynamic_cast<Value*>(lhs->perform(&to_value));
608
- Value* v_r = dynamic_cast<Value*>(rhs->perform(&to_value));
609
- Value* ex = op_strings(ctx.mem, op_type, *v_l, *v_r, compressed, precision, &pstate);
610
- if (String_Constant* str = dynamic_cast<String_Constant*>(ex))
730
+ Expression* rv = 0;
731
+ try {
732
+ ParserState pstate(b->pstate());
733
+ if (l_type == Expression::NUMBER && r_type == Expression::NUMBER) {
734
+ const Number* l_n = dynamic_cast<const Number*>(lhs);
735
+ const Number* r_n = dynamic_cast<const Number*>(rhs);
736
+ rv = op_numbers(ctx.mem, op_type, *l_n, *r_n, ctx.c_options, &pstate);
737
+ }
738
+ else if (l_type == Expression::NUMBER && r_type == Expression::COLOR) {
739
+ const Number* l_n = dynamic_cast<const Number*>(lhs);
740
+ const Color* r_c = dynamic_cast<const Color*>(rhs);
741
+ rv = op_number_color(ctx.mem, op_type, *l_n, *r_c, ctx.c_options, &pstate);
742
+ }
743
+ else if (l_type == Expression::COLOR && r_type == Expression::NUMBER) {
744
+ const Color* l_c = dynamic_cast<const Color*>(lhs);
745
+ const Number* r_n = dynamic_cast<const Number*>(rhs);
746
+ rv = op_color_number(ctx.mem, op_type, *l_c, *r_n, ctx.c_options, &pstate);
747
+ }
748
+ else if (l_type == Expression::COLOR && r_type == Expression::COLOR) {
749
+ const Color* l_c = dynamic_cast<const Color*>(lhs);
750
+ const Color* r_c = dynamic_cast<const Color*>(rhs);
751
+ rv = op_colors(ctx.mem, op_type, *l_c, *r_c, ctx.c_options, &pstate);
752
+ }
753
+ else {
754
+ To_Value to_value(ctx, ctx.mem);
755
+ Value* v_l = dynamic_cast<Value*>(lhs->perform(&to_value));
756
+ Value* v_r = dynamic_cast<Value*>(rhs->perform(&to_value));
757
+ bool interpolant = b->is_right_interpolant() ||
758
+ b->is_left_interpolant() ||
759
+ b->is_interpolant();
760
+ if (op_type == Sass_OP::SUB) interpolant = false;
761
+ // if (op_type == Sass_OP::DIV) interpolant = true;
762
+ Value* ex = op_strings(ctx.mem, b->op(), *v_l, *v_r, ctx.c_options, &pstate, !interpolant); // pass true to compress
763
+ if (String_Constant* str = dynamic_cast<String_Constant*>(ex))
764
+ {
765
+ if (str->concrete_type() == Expression::STRING)
766
+ {
767
+ String_Constant* lstr = dynamic_cast<String_Constant*>(lhs);
768
+ String_Constant* rstr = dynamic_cast<String_Constant*>(rhs);
769
+ if (op_type != Sass_OP::SUB) {
770
+ if (String_Constant* org = lstr ? lstr : rstr)
771
+ { str->quote_mark(org->quote_mark()); }
772
+ }
773
+ }
774
+ }
775
+ ex->is_interpolant(b->is_interpolant());
776
+ rv = ex;
777
+ }
778
+ }
779
+ catch (Exception::OperationError& err)
611
780
  {
612
- if (str->concrete_type() != Expression::STRING) return ex;
613
- String_Constant* lstr = dynamic_cast<String_Constant*>(lhs);
614
- String_Constant* rstr = dynamic_cast<String_Constant*>(rhs);
615
- if (String_Constant* org = lstr ? lstr : rstr)
616
- { str->quote_mark(org->quote_mark()); }
781
+ // throw Exception::Base(b->pstate(), err.what());
782
+ throw Exception::SassValueError(b->pstate(), err);
617
783
  }
618
- return ex;
784
+
785
+ if (rv) {
786
+ if (schema_op) {
787
+ // XXX: this is never hit via spec tests
788
+ (*s2)[0] = rv;
789
+ rv = s2->perform(this);
790
+ }
791
+ }
792
+ return rv;
619
793
 
620
794
  }
621
795
 
@@ -635,16 +809,15 @@ namespace Sass {
635
809
  return result;
636
810
  }
637
811
  else {
638
- To_String to_string(&ctx);
639
812
  // Special cases: +/- variables which evaluate to null ouput just +/-,
640
813
  // but +/- null itself outputs the string
641
- if (operand->concrete_type() == Expression::NULL_VAL && typeid(*(u->operand())) == typeid(Variable)) {
814
+ if (operand->concrete_type() == Expression::NULL_VAL && dynamic_cast<Variable*>(u->operand())) {
642
815
  u->operand(SASS_MEMORY_NEW(ctx.mem, String_Quoted, u->pstate(), ""));
643
816
  }
644
817
  else u->operand(operand);
645
818
  String_Constant* result = SASS_MEMORY_NEW(ctx.mem, String_Quoted,
646
819
  u->pstate(),
647
- u->perform(&to_string));
820
+ u->inspect());
648
821
  return result;
649
822
  }
650
823
  // unreachable
@@ -654,6 +827,7 @@ namespace Sass {
654
827
  Expression* Eval::operator()(Function_Call* c)
655
828
  {
656
829
  if (backtrace()->parent != NULL && backtrace()->depth() > Constants::MaxCallStack) {
830
+ // XXX: this is never hit via spec tests
657
831
  std::ostringstream stm;
658
832
  stm << "Stack depth exceeded max of " << Constants::MaxCallStack;
659
833
  error(stm.str(), c->pstate(), backtrace());
@@ -673,13 +847,14 @@ namespace Sass {
673
847
  c->pstate(),
674
848
  c->name(),
675
849
  args);
676
- To_String to_string(&ctx);
677
850
  if (args->has_named_arguments()) {
678
851
  error("Function " + c->name() + " doesn't support keyword arguments", c->pstate());
679
852
  }
680
- return SASS_MEMORY_NEW(ctx.mem, String_Quoted,
681
- c->pstate(),
682
- lit->perform(&to_string));
853
+ String_Quoted* str = SASS_MEMORY_NEW(ctx.mem, String_Quoted,
854
+ c->pstate(),
855
+ lit->to_string(ctx.c_options));
856
+ str->is_interpolant(c->is_interpolant());
857
+ return str;
683
858
  } else {
684
859
  // call generic function
685
860
  full_name = "*[f]";
@@ -711,10 +886,9 @@ namespace Sass {
711
886
  bind(std::string("Function"), c->name(), params, args, &ctx, &fn_env, this);
712
887
  Backtrace here(backtrace(), c->pstate(), ", in function `" + c->name() + "`");
713
888
  exp.backtrace_stack.push_back(&here);
714
- // if it's user-defined, eval the body
715
- if (body) result = body->perform(this);
716
- // if it's native, invoke the underlying CPP function
717
- else result = func(fn_env, *env, ctx, def->signature(), c->pstate(), backtrace());
889
+ // eval the body if user-defined or special, invoke underlying CPP function if native
890
+ if (body && !Prelexer::re_special_fun(c->name().c_str())) { result = body->perform(this); }
891
+ else if (func) { result = func(fn_env, *env, ctx, def->signature(), c->pstate(), backtrace()); }
718
892
  if (!result) error(std::string("Function ") + c->name() + " did not return a value", c->pstate());
719
893
  exp.backtrace_stack.pop_back();
720
894
  }
@@ -767,6 +941,7 @@ namespace Sass {
767
941
 
768
942
  result->is_delayed(result->concrete_type() == Expression::STRING);
769
943
  if (!result->is_delayed()) result = result->perform(this);
944
+ result->is_interpolant(c->is_interpolant());
770
945
  exp.env_stack.pop_back();
771
946
  return result;
772
947
  }
@@ -782,13 +957,11 @@ namespace Sass {
782
957
 
783
958
  Expression* Eval::operator()(Variable* v)
784
959
  {
785
- To_String to_string(&ctx);
786
960
  std::string name(v->name());
787
961
  Expression* value = 0;
788
962
  Env* env = environment();
789
963
  if (env->has(name)) value = static_cast<Expression*>((*env)[name]);
790
964
  else error("Undefined variable: \"" + v->name() + "\".", v->pstate());
791
- // std::cerr << "name: " << v->name() << "; type: " << typeid(*value).name() << "; value: " << value->perform(&to_string) << std::endl;
792
965
  if (typeid(*value) == typeid(Argument)) value = static_cast<Argument*>(value)->value();
793
966
 
794
967
  // behave according to as ruby sass (add leading zero)
@@ -800,7 +973,7 @@ namespace Sass {
800
973
  if (auto str = dynamic_cast<String_Quoted*>(value)) {
801
974
  value = SASS_MEMORY_NEW(ctx.mem, String_Quoted, *str);
802
975
  } else if (auto str = dynamic_cast<String_Constant*>(value)) {
803
- value = SASS_MEMORY_NEW(ctx.mem, String_Quoted, str->pstate(), str->perform(&to_string));
976
+ value = SASS_MEMORY_NEW(ctx.mem, String_Quoted, str->pstate(), str->value());
804
977
  }
805
978
  }
806
979
  else if (value->concrete_type() == Expression::LIST) {
@@ -822,16 +995,21 @@ namespace Sass {
822
995
  value = value->perform(this); // ->perform(&listize);
823
996
  }
824
997
 
825
- // std::cerr << "\ttype is now: " << typeid(*value).name() << std::endl << std::endl;
826
- return value;
998
+ value->is_interpolant(v->is_interpolant());
999
+ value->is_expanded(false);
1000
+ return value->perform(this);
827
1001
  }
828
1002
 
829
1003
  Expression* Eval::operator()(Textual* t)
830
1004
  {
831
1005
  using Prelexer::number;
832
1006
  Expression* result = 0;
833
- bool zero = !( t->value().substr(0, 1) == "." ||
834
- t->value().substr(0, 2) == "-." );
1007
+ size_t L = t->value().length();
1008
+ bool zero = !( (L > 0 && t->value().substr(0, 1) == ".") ||
1009
+ (L > 1 && t->value().substr(0, 2) == "0.") ||
1010
+ (L > 1 && t->value().substr(0, 2) == "-.") ||
1011
+ (L > 2 && t->value().substr(0, 3) == "-0.")
1012
+ );
835
1013
 
836
1014
  const std::string& text = t->value();
837
1015
  size_t num_pos = text.find_first_not_of(" \n\r\t");
@@ -854,7 +1032,7 @@ namespace Sass {
854
1032
  t->pstate(),
855
1033
  sass_atof(num.c_str()),
856
1034
  "%",
857
- zero);
1035
+ true);
858
1036
  break;
859
1037
  case Textual::DIMENSION:
860
1038
  result = SASS_MEMORY_NEW(ctx.mem, Number,
@@ -878,7 +1056,7 @@ namespace Sass {
878
1056
  static_cast<double>(strtol(r.c_str(), NULL, 16)),
879
1057
  static_cast<double>(strtol(g.c_str(), NULL, 16)),
880
1058
  static_cast<double>(strtol(b.c_str(), NULL, 16)),
881
- 1, true,
1059
+ 1, // alpha channel
882
1060
  t->value());
883
1061
  }
884
1062
  else {
@@ -887,11 +1065,12 @@ namespace Sass {
887
1065
  static_cast<double>(strtol(std::string(2,hext[0]).c_str(), NULL, 16)),
888
1066
  static_cast<double>(strtol(std::string(2,hext[1]).c_str(), NULL, 16)),
889
1067
  static_cast<double>(strtol(std::string(2,hext[2]).c_str(), NULL, 16)),
890
- 1, false,
1068
+ 1, // alpha channel
891
1069
  t->value());
892
1070
  }
893
1071
  } break;
894
1072
  }
1073
+ result->is_interpolant(t->is_interpolant());
895
1074
  return result;
896
1075
  }
897
1076
 
@@ -905,139 +1084,113 @@ namespace Sass {
905
1084
  return b;
906
1085
  }
907
1086
 
908
- char is_quoted(std::string str)
909
- {
910
- size_t len = str.length();
911
- if (len < 2) return 0;
912
- if ((str[0] == '"' && str[len-1] == '"') || (str[0] == '\'' && str[len-1] == '\'')) {
913
- return str[0];
1087
+ void Eval::interpolation(Context& ctx, std::string& res, Expression* ex, bool into_quotes, bool was_itpl) {
1088
+
1089
+ bool needs_closing_brace = false;
1090
+ //debug_ast(ex);
1091
+ if (Arguments* args = dynamic_cast<Arguments*>(ex)) {
1092
+ List* ll = SASS_MEMORY_NEW(ctx.mem, List, args->pstate(), 0, SASS_COMMA);
1093
+ for(auto arg : *args) {
1094
+ *ll << arg->value();
1095
+ }
1096
+ ll->is_interpolant(args->is_interpolant());
1097
+ needs_closing_brace = true;
1098
+ res += "(";
1099
+ ex = ll;
1100
+ }
1101
+ if (Number* nr = dynamic_cast<Number*>(ex)) {
1102
+ if (!nr->is_valid_css_unit()) {
1103
+ throw Exception::InvalidValue(*nr);
1104
+ }
914
1105
  }
915
- else {
916
- return 0;
1106
+ if (Argument* arg = dynamic_cast<Argument*>(ex)) {
1107
+ ex = arg->value();
1108
+ }
1109
+ if (String_Quoted* sq = dynamic_cast<String_Quoted*>(ex)) {
1110
+ if (was_itpl) {
1111
+ bool was_interpolant = ex->is_interpolant();
1112
+ ex = SASS_MEMORY_NEW(ctx.mem, String_Constant, sq->pstate(), sq->value());
1113
+ ex->is_interpolant(was_interpolant);
1114
+ }
917
1115
  }
918
- }
919
1116
 
920
- std::string Eval::interpolation(Expression* s, bool into_quotes) {
921
- Env* env = environment();
922
- if (String_Quoted* str_quoted = dynamic_cast<String_Quoted*>(s)) {
923
- if (str_quoted->quote_mark()) {
924
- if (str_quoted->quote_mark() == '*' || str_quoted->is_delayed()) {
925
- return evacuate_escapes(str_quoted->value());
926
- } else {
927
- return string_escape(quote(str_quoted->value(), str_quoted->quote_mark()));
928
- }
929
- } else {
930
- return evacuate_escapes(str_quoted->value());
1117
+ if (dynamic_cast<Null*>(ex)) { return; }
1118
+
1119
+ // parent selector needs another go
1120
+ if (dynamic_cast<Parent_Selector*>(ex)) {
1121
+ // XXX: this is never hit via spec tests
1122
+ ex = ex->perform(this);
1123
+ }
1124
+
1125
+ if (List* l = dynamic_cast<List*>(ex)) {
1126
+ List* ll = SASS_MEMORY_NEW(ctx.mem, List, l->pstate(), 0, l->separator());
1127
+ // this fixes an issue with bourbon sample, not really sure why
1128
+ if (l->size() && dynamic_cast<Null*>((*l)[0])) { res += " "; }
1129
+ for(auto item : *l) {
1130
+ item->is_interpolant(l->is_interpolant());
1131
+ std::string rl(""); interpolation(ctx, rl, item, into_quotes, l->is_interpolant());
1132
+ if (rl != "") *ll << SASS_MEMORY_NEW(ctx.mem, String_Quoted, item->pstate(), rl);
931
1133
  }
932
- } else if (String_Constant* str_constant = dynamic_cast<String_Constant*>(s)) {
933
- if (into_quotes && !str_constant->is_interpolant()) return str_constant->value();
934
- return evacuate_escapes(str_constant->value());
935
- } else if (dynamic_cast<Parent_Selector*>(s)) {
936
- To_String to_string(&ctx);
937
- Expression* sel = s->perform(this);
938
- return evacuate_quotes(sel ? sel->perform(&to_string) : "");
939
-
940
- } else if (String_Schema* str_schema = dynamic_cast<String_Schema*>(s)) {
941
- // To_String to_string(&ctx);
942
- // return evacuate_quotes(str_schema->perform(&to_string));
943
-
944
- std::string res = "";
945
- for(auto i : str_schema->elements())
946
- res += (interpolation(i));
947
- //ToDo: do this in one step
948
- auto esc = evacuate_escapes(res);
949
- auto unq = unquote(esc);
950
- if (unq == esc) {
951
- return string_to_output(res);
1134
+ res += (ll->to_string(ctx.c_options));
1135
+ ll->is_interpolant(l->is_interpolant());
1136
+ }
1137
+
1138
+ // Value
1139
+ // Textual
1140
+ // Function_Call
1141
+ // Selector_List
1142
+ // String_Quoted
1143
+ // String_Constant
1144
+ // Parent_Selector
1145
+ // Binary_Expression
1146
+ else {
1147
+ // ex = ex->perform(this);
1148
+ if (into_quotes && ex->is_interpolant()) {
1149
+ res += evacuate_escapes(ex ? ex->to_string(ctx.c_options) : "");
952
1150
  } else {
953
- return evacuate_quotes(unq);
1151
+ res += ex ? ex->to_string(ctx.c_options) : "";
954
1152
  }
955
- } else if (List* list = dynamic_cast<List*>(s)) {
956
- std::string acc = ""; // ToDo: different output styles
957
- std::string sep = list->separator() == SASS_COMMA ? "," : " ";
958
- if (ctx.output_style() != SASS_STYLE_COMPRESSED && sep == ",") sep += " ";
959
- bool initial = false;
960
- for(auto item : list->elements()) {
961
- if (item->concrete_type() != Expression::NULL_VAL) {
962
- if (initial) acc += sep;
963
- acc += interpolation(item);
964
- initial = true;
965
- }
966
- }
967
- return evacuate_quotes(acc);
968
- } else if (Variable* var = dynamic_cast<Variable*>(s)) {
969
- std::string name(var->name());
970
- if (!env->has(name)) error("Undefined variable: \"" + var->name() + "\".", var->pstate());
971
- Expression* value = static_cast<Expression*>((*env)[name]);
972
- return evacuate_quotes(interpolation(value));
973
- } else if (dynamic_cast<Binary_Expression*>(s)) {
974
- Expression* ex = s->perform(this);
975
- // avoid recursive calls if same object gets returned
976
- // since we will call interpolate again for the result
977
- if (ex == s) {
978
- To_String to_string(&ctx);
979
- return evacuate_quotes(s->perform(&to_string));
980
- }
981
- return evacuate_quotes(interpolation(ex));
982
- } else if (dynamic_cast<Function_Call*>(s)) {
983
- Expression* ex = s->perform(this);
984
- return evacuate_quotes(unquote(interpolation(ex)));
985
- } else if (dynamic_cast<Unary_Expression*>(s)) {
986
- Expression* ex = s->perform(this);
987
- return evacuate_quotes(interpolation(ex));
988
- } else if (dynamic_cast<Map*>(s)) {
989
- To_String to_string(&ctx);
990
- std::string dbg(s->perform(&to_string));
991
- error(dbg + " isn't a valid CSS value.", s->pstate());
992
- return dbg;
993
- } else {
994
- To_String to_string(&ctx);
995
- return evacuate_quotes(s->perform(&to_string));
996
1153
  }
1154
+
1155
+ if (needs_closing_brace) res += ")";
1156
+
997
1157
  }
998
1158
 
999
1159
  Expression* Eval::operator()(String_Schema* s)
1000
1160
  {
1001
- std::string acc;
1002
- bool into_quotes = false;
1003
1161
  size_t L = s->length();
1162
+ bool into_quotes = false;
1004
1163
  if (L > 1) {
1164
+ if (!dynamic_cast<String_Quoted*>((*s)[0]) && !dynamic_cast<String_Quoted*>((*s)[L - 1])) {
1005
1165
  if (String_Constant* l = dynamic_cast<String_Constant*>((*s)[0])) {
1006
1166
  if (String_Constant* r = dynamic_cast<String_Constant*>((*s)[L - 1])) {
1007
1167
  if (l->value()[0] == '"' && r->value()[r->value().size() - 1] == '"') into_quotes = true;
1008
1168
  if (l->value()[0] == '\'' && r->value()[r->value().size() - 1] == '\'') into_quotes = true;
1009
1169
  }
1010
1170
  }
1171
+ }
1011
1172
  }
1173
+ std::string res("");
1012
1174
  for (size_t i = 0; i < L; ++i) {
1013
- // really a very special fix, but this is the logic I got from
1014
- // analyzing the ruby sass behavior and it actually seems to work
1015
- // https://github.com/sass/libsass/issues/1333
1016
- if (i == 0 && L > 1 && dynamic_cast<Function_Call*>((*s)[i])) {
1017
- Expression* ex = (*s)[i]->perform(this);
1018
- if (auto sq = dynamic_cast<String_Quoted*>(ex)) {
1019
- if (sq->is_delayed() && ! s->has_interpolants()) {
1020
- acc += string_escape(quote(sq->value(), sq->quote_mark()));
1021
- } else {
1022
- acc += interpolation((*s)[i], into_quotes);
1023
- }
1024
- } else if (ex) {
1025
- acc += interpolation((*s)[i], into_quotes);
1026
- }
1027
- } else if ((*s)[i]) {
1028
- acc += interpolation((*s)[i], into_quotes);
1029
- }
1175
+ (*s)[i]->perform(this);
1176
+ Expression* ex = (*s)[i]->is_delayed() ? (*s)[i] : (*s)[i]->perform(this);
1177
+ interpolation(ctx, res, ex, into_quotes, ex->is_interpolant());
1178
+
1030
1179
  }
1031
- String_Quoted* str = SASS_MEMORY_NEW(ctx.mem, String_Quoted, s->pstate(), acc);
1032
- if (!str->quote_mark()) {
1033
- str->value(string_unescape(str->value()));
1034
- } else if (str->quote_mark()) {
1035
- str->quote_mark('*');
1180
+ if (!s->is_interpolant()) {
1181
+ if (res == "") return SASS_MEMORY_NEW(ctx.mem, Null, s->pstate());
1182
+ return SASS_MEMORY_NEW(ctx.mem, String_Constant, s->pstate(), res);
1036
1183
  }
1037
- str->is_delayed(true);
1184
+ String_Quoted* str = SASS_MEMORY_NEW(ctx.mem, String_Quoted, s->pstate(), res);
1185
+ // if (s->is_interpolant()) str->quote_mark(0);
1186
+ // String_Constant* str = SASS_MEMORY_NEW(ctx.mem, String_Constant, s->pstate(), res);
1187
+ if (str->quote_mark()) str->quote_mark('*');
1188
+ else if (!is_in_comment) str->value(string_to_output(str->value()));
1189
+ str->is_interpolant(s->is_interpolant());
1038
1190
  return str;
1039
1191
  }
1040
1192
 
1193
+
1041
1194
  Expression* Eval::operator()(String_Constant* s)
1042
1195
  {
1043
1196
  if (!s->is_delayed() && name_to_color(s->value())) {
@@ -1051,7 +1204,11 @@ namespace Sass {
1051
1204
 
1052
1205
  Expression* Eval::operator()(String_Quoted* s)
1053
1206
  {
1054
- return s;
1207
+ String_Quoted* str = SASS_MEMORY_NEW(ctx.mem, String_Quoted, s->pstate(), "");
1208
+ str->value(s->value());
1209
+ str->quote_mark(s->quote_mark());
1210
+ str->is_interpolant(s->is_interpolant());
1211
+ return str;
1055
1212
  }
1056
1213
 
1057
1214
  Expression* Eval::operator()(Supports_Operator* c)
@@ -1111,7 +1268,6 @@ namespace Sass {
1111
1268
 
1112
1269
  Expression* Eval::operator()(Media_Query* q)
1113
1270
  {
1114
- To_String to_string(&ctx);
1115
1271
  String* t = q->media_type();
1116
1272
  t = static_cast<String*>(t ? t->perform(this) : 0);
1117
1273
  Media_Query* qq = SASS_MEMORY_NEW(ctx.mem, Media_Query,
@@ -1138,6 +1294,7 @@ namespace Sass {
1138
1294
  Expression* value = e->value();
1139
1295
  value = (value ? value->perform(this) : 0);
1140
1296
  if (value && dynamic_cast<String_Quoted*>(value)) {
1297
+ // XXX: this is never hit via spec tests
1141
1298
  value = SASS_MEMORY_NEW(ctx.mem, String_Quoted,
1142
1299
  value->pstate(),
1143
1300
  dynamic_cast<String_Quoted*>(value)->value());
@@ -1191,8 +1348,49 @@ namespace Sass {
1191
1348
  {
1192
1349
  Arguments* aa = SASS_MEMORY_NEW(ctx.mem, Arguments, a->pstate());
1193
1350
  for (size_t i = 0, L = a->length(); i < L; ++i) {
1194
- *aa << static_cast<Argument*>((*a)[i]->perform(this));
1351
+ Argument* arg = static_cast<Argument*>((*a)[i]->perform(this));
1352
+ if (!(arg->is_rest_argument() || arg->is_keyword_argument())) {
1353
+ *aa << arg;
1354
+ }
1355
+ }
1356
+
1357
+ if (a->has_rest_argument()) {
1358
+ Expression* splat = static_cast<Argument*>(
1359
+ a->get_rest_argument()->perform(this)
1360
+ )->value()->perform(this);
1361
+
1362
+ Sass_Separator separator = SASS_COMMA;
1363
+ List* ls = dynamic_cast<List*>(splat);
1364
+ Map* ms = dynamic_cast<Map*>(splat);
1365
+
1366
+ List* arglist = SASS_MEMORY_NEW(ctx.mem, List,
1367
+ splat->pstate(),
1368
+ 0,
1369
+ ls ? ls->separator() : separator,
1370
+ true);
1371
+
1372
+ if (ls && ls->is_arglist()) {
1373
+ for (auto as : *ls) *arglist << as;
1374
+ } else if (ms) {
1375
+ *aa << SASS_MEMORY_NEW(ctx.mem, Argument, splat->pstate(), ms, "", false, true);
1376
+ } else if (ls) {
1377
+ for (auto as : *ls) *arglist << as;
1378
+ } else {
1379
+ *arglist << splat;
1380
+ }
1381
+ if (arglist->length()) {
1382
+ *aa << SASS_MEMORY_NEW(ctx.mem, Argument, splat->pstate(), arglist, "", true);
1383
+ }
1384
+ }
1385
+
1386
+ if (a->has_keyword_argument()) {
1387
+ Expression* kwarg = static_cast<Argument*>(
1388
+ a->get_keyword_argument()->perform(this)
1389
+ )->value()->perform(this);
1390
+
1391
+ *aa << SASS_MEMORY_NEW(ctx.mem, Argument, kwarg->pstate(), kwarg, "", false, true);
1195
1392
  }
1393
+
1196
1394
  return aa;
1197
1395
  }
1198
1396
 
@@ -1214,25 +1412,27 @@ namespace Sass {
1214
1412
  return lhs && rhs && *lhs == *rhs;
1215
1413
  }
1216
1414
 
1217
- bool Eval::lt(Expression* lhs, Expression* rhs)
1415
+ bool Eval::lt(Expression* lhs, Expression* rhs, std::string op)
1218
1416
  {
1219
1417
  Number* l = dynamic_cast<Number*>(lhs);
1220
1418
  Number* r = dynamic_cast<Number*>(rhs);
1221
- if (!l) error("may only compare numbers", lhs->pstate());
1222
- if (!r) error("may only compare numbers", rhs->pstate());
1419
+ // use compare operator from ast node
1420
+ if (!l || !r) throw Exception::UndefinedOperation(lhs, rhs, op);
1223
1421
  // use compare operator from ast node
1224
1422
  return *l < *r;
1225
1423
  }
1226
1424
 
1227
- Value* Eval::op_numbers(Memory_Manager& mem, enum Sass_OP op, const Number& l, const Number& r, bool compressed, int precision, ParserState* pstate)
1425
+ Value* Eval::op_numbers(Memory_Manager& mem, enum Sass_OP op, const Number& l, const Number& r, struct Sass_Inspect_Options opt, ParserState* pstate)
1228
1426
  {
1229
1427
  double lv = l.value();
1230
1428
  double rv = r.value();
1231
- if (op == Sass_OP::DIV && !rv) {
1232
- return SASS_MEMORY_NEW(mem, String_Quoted, pstate ? *pstate : l.pstate(), "Infinity");
1429
+ if (op == Sass_OP::DIV && rv == 0) {
1430
+ // XXX: this is never hit via spec tests
1431
+ return SASS_MEMORY_NEW(mem, String_Quoted, pstate ? *pstate : l.pstate(), lv ? "Infinity" : "NaN");
1233
1432
  }
1234
1433
  if (op == Sass_OP::MOD && !rv) {
1235
- error("division by zero", pstate ? *pstate : r.pstate());
1434
+ // XXX: this is never hit via spec tests
1435
+ throw Exception::ZeroDivisionError(l, r);
1236
1436
  }
1237
1437
 
1238
1438
  Number tmp(r);
@@ -1240,10 +1440,6 @@ namespace Sass {
1240
1440
  tmp.normalize(l.find_convertible_unit(), strict);
1241
1441
  std::string l_unit(l.unit());
1242
1442
  std::string r_unit(tmp.unit());
1243
- if (l_unit != r_unit && !l_unit.empty() && !r_unit.empty() &&
1244
- (op == Sass_OP::ADD || op == Sass_OP::SUB)) {
1245
- error("Incompatible units: '"+r_unit+"' and '"+l_unit+"'.", pstate ? *pstate : r.pstate());
1246
- }
1247
1443
  Number* v = SASS_MEMORY_NEW(mem, Number, l);
1248
1444
  v->pstate(pstate ? *pstate : l.pstate());
1249
1445
  if (l_unit.empty() && (op == Sass_OP::ADD || op == Sass_OP::SUB || op == Sass_OP::MOD)) {
@@ -1269,16 +1465,20 @@ namespace Sass {
1269
1465
  v->numerator_units().push_back(r.denominator_units()[i]);
1270
1466
  }
1271
1467
  } else {
1468
+ Number rh(r);
1469
+ v->value(ops[op](lv, rh.value() * r.convert_factor(l)));
1470
+ // v->normalize();
1471
+ return v;
1472
+
1272
1473
  v->value(ops[op](lv, tmp.value()));
1273
1474
  }
1274
1475
  v->normalize();
1275
1476
  return v;
1276
1477
  }
1277
1478
 
1278
- Value* Eval::op_number_color(Memory_Manager& mem, enum Sass_OP op, const Number& l, const Color& rh, bool compressed, int precision, ParserState* pstate)
1479
+ Value* Eval::op_number_color(Memory_Manager& mem, enum Sass_OP op, const Number& l, const Color& rh, struct Sass_Inspect_Options opt, ParserState* pstate)
1279
1480
  {
1280
1481
  Color r(rh);
1281
- r.disp("");
1282
1482
  double lv = l.value();
1283
1483
  switch (op) {
1284
1484
  case Sass_OP::ADD:
@@ -1293,15 +1493,15 @@ namespace Sass {
1293
1493
  case Sass_OP::SUB:
1294
1494
  case Sass_OP::DIV: {
1295
1495
  std::string sep(op == Sass_OP::SUB ? "-" : "/");
1296
- std::string color(r.to_string(compressed||!r.sixtuplet(), precision));
1496
+ std::string color(r.to_string(opt));
1297
1497
  return SASS_MEMORY_NEW(mem, String_Quoted,
1298
1498
  pstate ? *pstate : l.pstate(),
1299
- l.to_string(compressed, precision)
1499
+ l.to_string(opt)
1300
1500
  + sep
1301
1501
  + color);
1302
1502
  } break;
1303
1503
  case Sass_OP::MOD: {
1304
- error("cannot divide a number by a color", pstate ? *pstate : r.pstate());
1504
+ throw Exception::UndefinedOperation(&l, &r, sass_op_to_name(op));
1305
1505
  } break;
1306
1506
  default: break; // caller should ensure that we don't get here
1307
1507
  }
@@ -1309,10 +1509,13 @@ namespace Sass {
1309
1509
  return SASS_MEMORY_NEW(mem, Color, rh);
1310
1510
  }
1311
1511
 
1312
- Value* Eval::op_color_number(Memory_Manager& mem, enum Sass_OP op, const Color& l, const Number& r, bool compressed, int precision, ParserState* pstate)
1512
+ Value* Eval::op_color_number(Memory_Manager& mem, enum Sass_OP op, const Color& l, const Number& r, struct Sass_Inspect_Options opt, ParserState* pstate)
1313
1513
  {
1314
1514
  double rv = r.value();
1315
- if (op == Sass_OP::DIV && !rv) error("division by zero", pstate ? *pstate : r.pstate());
1515
+ if (op == Sass_OP::DIV && !rv) {
1516
+ // comparison of Fixnum with Float failed?
1517
+ throw Exception::ZeroDivisionError(l, r);
1518
+ }
1316
1519
  return SASS_MEMORY_NEW(mem, Color,
1317
1520
  pstate ? *pstate : l.pstate(),
1318
1521
  ops[op](l.r(), rv),
@@ -1321,13 +1524,14 @@ namespace Sass {
1321
1524
  l.a());
1322
1525
  }
1323
1526
 
1324
- Value* Eval::op_colors(Memory_Manager& mem, enum Sass_OP op, const Color& l, const Color& r, bool compressed, int precision, ParserState* pstate)
1527
+ Value* Eval::op_colors(Memory_Manager& mem, enum Sass_OP op, const Color& l, const Color& r, struct Sass_Inspect_Options opt, ParserState* pstate)
1325
1528
  {
1326
1529
  if (l.a() != r.a()) {
1327
- error("alpha channels must be equal when combining colors", pstate ? *pstate : r.pstate());
1530
+ throw Exception::AlphaChannelsNotEqual(&l, &r, "+");
1328
1531
  }
1329
1532
  if (op == Sass_OP::DIV && (!r.r() || !r.g() ||!r.b())) {
1330
- error("division by zero", pstate ? *pstate : r.pstate());
1533
+ // comparison of Fixnum with Float failed?
1534
+ throw Exception::ZeroDivisionError(l, r);
1331
1535
  }
1332
1536
  return SASS_MEMORY_NEW(mem, Color,
1333
1537
  pstate ? *pstate : l.pstate(),
@@ -1337,66 +1541,39 @@ namespace Sass {
1337
1541
  l.a());
1338
1542
  }
1339
1543
 
1340
- Value* Eval::op_strings(Memory_Manager& mem, enum Sass_OP op, Value& lhs, Value& rhs, bool compressed, int precision, ParserState* pstate)
1544
+ Value* Eval::op_strings(Memory_Manager& mem, Sass::Operand operand, Value& lhs, Value& rhs, struct Sass_Inspect_Options opt, ParserState* pstate, bool delayed)
1341
1545
  {
1342
1546
  Expression::Concrete_Type ltype = lhs.concrete_type();
1343
1547
  Expression::Concrete_Type rtype = rhs.concrete_type();
1548
+ enum Sass_OP op = operand.operand;
1344
1549
 
1345
1550
  String_Quoted* lqstr = dynamic_cast<String_Quoted*>(&lhs);
1346
1551
  String_Quoted* rqstr = dynamic_cast<String_Quoted*>(&rhs);
1347
1552
 
1348
- std::string lstr(lqstr ? lqstr->value() : lhs.to_string(compressed, precision));
1349
- std::string rstr(rqstr ? rqstr->value() : rhs.to_string(compressed, precision));
1350
-
1351
- bool l_str_quoted = ((Sass::String*)&lhs) && ((Sass::String*)&lhs)->sass_fix_1291();
1352
- bool r_str_quoted = ((Sass::String*)&rhs) && ((Sass::String*)&rhs)->sass_fix_1291();
1353
- bool l_str_color = ltype == Expression::STRING && name_to_color(lstr) && !l_str_quoted;
1354
- bool r_str_color = rtype == Expression::STRING && name_to_color(rstr) && !r_str_quoted;
1355
-
1356
- if (l_str_color && r_str_color) {
1357
- const Color* c_l = name_to_color(lstr);
1358
- const Color* c_r = name_to_color(rstr);
1359
- return op_colors(mem, op,*c_l, *c_r, compressed, precision);
1360
- }
1361
- else if (l_str_color && rtype == Expression::COLOR) {
1362
- const Color* c_l = name_to_color(lstr);
1363
- const Color* c_r = dynamic_cast<const Color*>(&rhs);
1364
- return op_colors(mem, op, *c_l, *c_r, compressed, precision);
1365
- }
1366
- else if (ltype == Expression::COLOR && r_str_color) {
1367
- const Color* c_l = dynamic_cast<const Color*>(&lhs);
1368
- const Color* c_r = name_to_color(rstr);
1369
- return op_colors(mem, op, *c_l, *c_r, compressed, precision);
1370
- }
1371
- else if (l_str_color && rtype == Expression::NUMBER) {
1372
- const Color* c_l = name_to_color(lstr);
1373
- const Number* n_r = dynamic_cast<const Number*>(&rhs);
1374
- return op_color_number(mem, op, *c_l, *n_r, compressed, precision);
1375
- }
1376
- else if (ltype == Expression::NUMBER && r_str_color) {
1377
- const Number* n_l = dynamic_cast<const Number*>(&lhs);
1378
- const Color* c_r = name_to_color(rstr);
1379
- return op_number_color(mem, op, *n_l, *c_r, compressed, precision);
1380
- }
1381
- if (op == Sass_OP::MUL) error("invalid operands for multiplication", lhs.pstate());
1382
- if (op == Sass_OP::MOD) error("invalid operands for modulo", lhs.pstate());
1553
+ std::string lstr(lqstr ? lqstr->value() : lhs.to_string(opt));
1554
+ std::string rstr(rqstr ? rqstr->value() : rhs.to_string(opt));
1555
+
1556
+ if (ltype == Expression::NULL_VAL) throw Exception::InvalidNullOperation(&lhs, &rhs, sass_op_to_name(op));
1557
+ if (rtype == Expression::NULL_VAL) throw Exception::InvalidNullOperation(&lhs, &rhs, sass_op_to_name(op));
1558
+ if (op == Sass_OP::MOD) throw Exception::UndefinedOperation(&lhs, &rhs, sass_op_to_name(op));
1559
+ if (op == Sass_OP::MUL) throw Exception::UndefinedOperation(&lhs, &rhs, sass_op_to_name(op));
1383
1560
  std::string sep;
1384
1561
  switch (op) {
1385
1562
  case Sass_OP::SUB: sep = "-"; break;
1386
1563
  case Sass_OP::DIV: sep = "/"; break;
1564
+ case Sass_OP::MUL: sep = "*"; break;
1565
+ case Sass_OP::MOD: sep = "%"; break;
1566
+ case Sass_OP::EQ: sep = "=="; break;
1567
+ case Sass_OP::NEQ: sep = "!="; break;
1568
+ case Sass_OP::LT: sep = "<"; break;
1569
+ case Sass_OP::GT: sep = ">"; break;
1570
+ case Sass_OP::LTE: sep = "<="; break;
1571
+ case Sass_OP::GTE: sep = ">="; break;
1387
1572
  default: break;
1388
1573
  }
1389
- if (ltype == Expression::NULL_VAL) error("Invalid null operation: \"null plus "+quote(unquote(rstr), '"')+"\".", lhs.pstate());
1390
- if (rtype == Expression::NULL_VAL) error("Invalid null operation: \""+quote(unquote(lstr), '"')+" plus null\".", rhs.pstate());
1391
-
1392
- if (ltype == Expression::NUMBER && sep == "/" && rtype == Expression::STRING)
1393
- {
1394
- return SASS_MEMORY_NEW(mem, String_Constant, lhs.pstate(),
1395
- lhs.to_string() + sep + rhs.to_string());
1396
- }
1397
1574
 
1398
- if ( (ltype == Expression::STRING || sep == "") &&
1399
- (sep != "/" || !rqstr || !rqstr->quote_mark())
1575
+ if ( (sep == "") /* &&
1576
+ (sep != "/" || !rqstr || !rqstr->quote_mark()) */
1400
1577
  ) {
1401
1578
  char quote_mark = 0;
1402
1579
  std::string unq(unquote(lstr + sep + rstr, &quote_mark, true));
@@ -1405,7 +1582,19 @@ namespace Sass {
1405
1582
  }
1406
1583
  return SASS_MEMORY_NEW(mem, String_Quoted, lhs.pstate(), lstr + sep + rstr);
1407
1584
  }
1408
- return SASS_MEMORY_NEW(mem, String_Constant, lhs.pstate(), (lstr) + sep + quote(rstr));
1585
+
1586
+ if (sep != "" && !delayed) {
1587
+ if (operand.ws_before) sep = " " + sep;
1588
+ if (operand.ws_after) sep = sep + " ";
1589
+ }
1590
+
1591
+ if (op == Sass_OP::SUB || op == Sass_OP::DIV) {
1592
+ if (lqstr && lqstr->quote_mark()) lstr = quote(lstr);
1593
+ if (rqstr && rqstr->quote_mark()) rstr = quote(rstr);
1594
+ return SASS_MEMORY_NEW(mem, String_Constant, lhs.pstate(), lstr + sep + rstr);
1595
+ }
1596
+
1597
+ return SASS_MEMORY_NEW(mem, String_Constant, lhs.pstate(), (lstr) + sep + (rstr));
1409
1598
  }
1410
1599
 
1411
1600
  Expression* cval_to_astnode(Memory_Manager& mem, union Sass_Value* v, Context& ctx, Backtrace* backtrace, ParserState pstate)
@@ -1496,6 +1685,7 @@ namespace Sass {
1496
1685
 
1497
1686
  }
1498
1687
 
1688
+ // XXX: this is never hit via spec tests
1499
1689
  Attribute_Selector* Eval::operator()(Attribute_Selector* s)
1500
1690
  {
1501
1691
  String* attr = s->value();
@@ -1507,9 +1697,8 @@ namespace Sass {
1507
1697
 
1508
1698
  Selector_List* Eval::operator()(Selector_Schema* s)
1509
1699
  {
1510
- To_String to_string;
1511
1700
  // the parser will look for a brace to end the selector
1512
- std::string result_str(s->contents()->perform(this)->perform(&to_string));
1701
+ std::string result_str(s->contents()->perform(this)->to_string(ctx.c_options));
1513
1702
  result_str = unquote(Util::rtrim(result_str)) + "{";
1514
1703
  Parser p = Parser::from_c_str(result_str.c_str(), ctx, s->pstate());
1515
1704
  return operator()(p.parse_selector_list(exp.block_stack.back()->is_root()));