sassc 1.8.3 → 1.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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()));