sassc 1.9.0 → 1.10.0

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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +30 -3
  3. data/ext/libsass/.gitignore +3 -0
  4. data/ext/libsass/.travis.yml +1 -1
  5. data/ext/libsass/GNUmakefile.am +7 -7
  6. data/ext/libsass/Makefile +7 -4
  7. data/ext/libsass/Makefile.conf +0 -1
  8. data/ext/libsass/appveyor.yml +6 -2
  9. data/ext/libsass/docs/api-context.md +4 -4
  10. data/ext/libsass/docs/api-doc.md +29 -11
  11. data/ext/libsass/docs/api-importer-example.md +5 -5
  12. data/ext/libsass/docs/build-on-windows.md +1 -1
  13. data/ext/libsass/include/sass/base.h +10 -0
  14. data/ext/libsass/include/sass/version.h +4 -0
  15. data/ext/libsass/include/sass/version.h.in +4 -0
  16. data/ext/libsass/include/sass2scss.h +1 -1
  17. data/ext/libsass/script/ci-build-libsass +15 -3
  18. data/ext/libsass/src/ast.cpp +161 -6
  19. data/ext/libsass/src/ast.hpp +71 -44
  20. data/ext/libsass/src/ast_factory.hpp +1 -1
  21. data/ext/libsass/src/ast_fwd_decl.hpp +2 -2
  22. data/ext/libsass/src/constants.cpp +2 -4
  23. data/ext/libsass/src/constants.hpp +3 -4
  24. data/ext/libsass/src/context.cpp +16 -17
  25. data/ext/libsass/src/context.hpp +2 -2
  26. data/ext/libsass/src/cssize.cpp +19 -8
  27. data/ext/libsass/src/cssize.hpp +5 -2
  28. data/ext/libsass/src/debugger.hpp +6 -3
  29. data/ext/libsass/src/emitter.cpp +1 -1
  30. data/ext/libsass/src/environment.cpp +1 -1
  31. data/ext/libsass/src/eval.cpp +42 -14
  32. data/ext/libsass/src/eval.hpp +1 -1
  33. data/ext/libsass/src/expand.cpp +24 -8
  34. data/ext/libsass/src/expand.hpp +2 -1
  35. data/ext/libsass/src/extend.cpp +55 -15
  36. data/ext/libsass/src/extend.hpp +5 -1
  37. data/ext/libsass/src/functions.cpp +10 -5
  38. data/ext/libsass/src/inspect.cpp +25 -19
  39. data/ext/libsass/src/inspect.hpp +2 -2
  40. data/ext/libsass/src/json.cpp +20 -9
  41. data/ext/libsass/src/json.hpp +5 -5
  42. data/ext/libsass/src/lexer.cpp +4 -1
  43. data/ext/libsass/src/lexer.hpp +21 -0
  44. data/ext/libsass/src/listize.cpp +2 -1
  45. data/ext/libsass/src/operation.hpp +4 -4
  46. data/ext/libsass/src/output.cpp +1 -1
  47. data/ext/libsass/src/output.hpp +1 -1
  48. data/ext/libsass/src/parser.cpp +189 -90
  49. data/ext/libsass/src/parser.hpp +42 -2
  50. data/ext/libsass/src/prelexer.cpp +474 -7
  51. data/ext/libsass/src/prelexer.hpp +15 -2
  52. data/ext/libsass/src/remove_placeholders.cpp +5 -5
  53. data/ext/libsass/src/remove_placeholders.hpp +3 -2
  54. data/ext/libsass/src/sass.cpp +33 -3
  55. data/ext/libsass/src/sass2scss.cpp +7 -0
  56. data/ext/libsass/src/sass_context.cpp +32 -62
  57. data/ext/libsass/src/sass_functions.cpp +3 -3
  58. data/ext/libsass/src/sass_values.cpp +5 -5
  59. data/ext/libsass/src/utf8/unchecked.h +16 -16
  60. data/ext/libsass/src/util.cpp +51 -30
  61. data/ext/libsass/src/util.hpp +6 -1
  62. data/ext/libsass/win/libsass.targets +0 -2
  63. data/ext/libsass/win/libsass.vcxproj.filters +0 -6
  64. data/lib/sassc/engine.rb +4 -1
  65. data/lib/sassc/error.rb +23 -1
  66. data/lib/sassc/version.rb +1 -1
  67. data/test/error_test.rb +27 -0
  68. data/test/native_test.rb +1 -1
  69. metadata +5 -5
  70. data/ext/libsass/include/sass/interface.h +0 -105
  71. data/ext/libsass/src/sass_interface.cpp +0 -215
@@ -133,6 +133,8 @@ namespace Sass {
133
133
  const char* lex(bool lazy = true, bool force = false)
134
134
  {
135
135
 
136
+ if (*position == 0) return 0;
137
+
136
138
  // position considered before lexed token
137
139
  // we can skip whitespace or comments for
138
140
  // lazy developers (but we need control)
@@ -298,12 +300,22 @@ namespace Sass {
298
300
  Supports_Condition* parse_supports_declaration();
299
301
  Supports_Condition* parse_supports_condition_in_parens();
300
302
  At_Root_Block* parse_at_root_block();
301
- At_Root_Expression* parse_at_root_expression();
302
- At_Rule* parse_at_rule();
303
+ At_Root_Query* parse_at_root_query();
304
+ String_Schema* parse_almost_any_value();
305
+ Directive* parse_special_directive();
306
+ Directive* parse_prefixed_directive();
307
+ Directive* parse_directive();
303
308
  Warning* parse_warning();
304
309
  Error* parse_error();
305
310
  Debug* parse_debug();
306
311
 
312
+ // be more like ruby sass
313
+ Expression* lex_almost_any_value_token();
314
+ Expression* lex_almost_any_value_chars();
315
+ Expression* lex_interp_string();
316
+ Expression* lex_interp_uri();
317
+ Expression* lex_interpolation();
318
+
307
319
  // these will throw errors
308
320
  Token lex_variable();
309
321
  Token lex_identifier();
@@ -319,6 +331,34 @@ namespace Sass {
319
331
 
320
332
  void throw_syntax_error(std::string message, size_t ln = 0);
321
333
  void throw_read_error(std::string message, size_t ln = 0);
334
+
335
+
336
+ template <Prelexer::prelexer open, Prelexer::prelexer close>
337
+ Expression* lex_interp()
338
+ {
339
+ if (lex < open >(false)) {
340
+ String_Schema* schema = SASS_MEMORY_NEW(ctx.mem, String_Schema, pstate);
341
+ // std::cerr << "LEX [[" << std::string(lexed) << "]]\n";
342
+ *schema << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);
343
+ if (position[0] == '#' && position[1] == '{') {
344
+ Expression* itpl = lex_interpolation();
345
+ if (itpl) *schema << itpl;
346
+ while (lex < close >(false)) {
347
+ // std::cerr << "LEX [[" << std::string(lexed) << "]]\n";
348
+ *schema << SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);
349
+ if (position[0] == '#' && position[1] == '{') {
350
+ Expression* itpl = lex_interpolation();
351
+ if (itpl) *schema << itpl;
352
+ } else {
353
+ return schema;
354
+ }
355
+ }
356
+ } else {
357
+ return SASS_MEMORY_NEW(ctx.mem, String_Constant, pstate, lexed);
358
+ }
359
+ }
360
+ return 0;
361
+ }
322
362
  };
323
363
 
324
364
  size_t check_bom_chars(const char* src, const char *end, const unsigned char* bom, size_t len);
@@ -16,6 +16,248 @@ namespace Sass {
16
16
  namespace Prelexer {
17
17
 
18
18
 
19
+ /*
20
+
21
+ def string_re(open, close)
22
+ /#{open}((?:\\.|\#(?!\{)|[^#{close}\\#])*)(#{close}|#\{)/m
23
+ end
24
+ end
25
+
26
+ # A hash of regular expressions that are used for tokenizing strings.
27
+ #
28
+ # The key is a `[Symbol, Boolean]` pair.
29
+ # The symbol represents which style of quotation to use,
30
+ # while the boolean represents whether or not the string
31
+ # is following an interpolated segment.
32
+ STRING_REGULAR_EXPRESSIONS = {
33
+ :double => {
34
+ /#{open}((?:\\.|\#(?!\{)|[^#{close}\\#])*)(#{close}|#\{)/m
35
+ false => string_re('"', '"'),
36
+ true => string_re('', '"')
37
+ },
38
+ :single => {
39
+ false => string_re("'", "'"),
40
+ true => string_re('', "'")
41
+ },
42
+ :uri => {
43
+ false => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/,
44
+ true => /(#{URLCHAR}*?)(#{W}\)|#\{)/
45
+ },
46
+ # Defined in https://developer.mozilla.org/en/CSS/@-moz-document as a
47
+ # non-standard version of http://www.w3.org/TR/css3-conditional/
48
+ :url_prefix => {
49
+ false => /url-prefix\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/,
50
+ true => /(#{URLCHAR}*?)(#{W}\)|#\{)/
51
+ },
52
+ :domain => {
53
+ false => /domain\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/,
54
+ true => /(#{URLCHAR}*?)(#{W}\)|#\{)/
55
+ }
56
+ }
57
+ */
58
+
59
+ /*
60
+ /#{open}
61
+ (
62
+ \\.
63
+ |
64
+ \# (?!\{)
65
+ |
66
+ [^#{close}\\#]
67
+ )*
68
+ (#{close}|#\{)
69
+ /m
70
+ false => string_re('"', '"'),
71
+ true => string_re('', '"')
72
+ */
73
+ extern const char string_double_negates[] = "\"\\#";
74
+ const char* re_string_double_close(const char* src)
75
+ {
76
+ return sequence <
77
+ // valid chars
78
+ zero_plus <
79
+ alternatives <
80
+ // escaped char
81
+ sequence <
82
+ exactly <'\\'>,
83
+ any_char
84
+ >,
85
+ // non interpolate hash
86
+ sequence <
87
+ exactly <'#'>,
88
+ negate <
89
+ exactly <'{'>
90
+ >
91
+ >,
92
+ // other valid chars
93
+ neg_class_char <
94
+ string_double_negates
95
+ >
96
+ >
97
+ >,
98
+ // quoted string closer
99
+ // or interpolate opening
100
+ alternatives <
101
+ exactly <'"'>,
102
+ lookahead < exactly< hash_lbrace > >
103
+ >
104
+ >(src);
105
+ }
106
+
107
+ const char* re_string_double_open(const char* src)
108
+ {
109
+ return sequence <
110
+ // quoted string opener
111
+ exactly <'"'>,
112
+ // valid chars
113
+ zero_plus <
114
+ alternatives <
115
+ // escaped char
116
+ sequence <
117
+ exactly <'\\'>,
118
+ any_char
119
+ >,
120
+ // non interpolate hash
121
+ sequence <
122
+ exactly <'#'>,
123
+ negate <
124
+ exactly <'{'>
125
+ >
126
+ >,
127
+ // other valid chars
128
+ neg_class_char <
129
+ string_double_negates
130
+ >
131
+ >
132
+ >,
133
+ // quoted string closer
134
+ // or interpolate opening
135
+ alternatives <
136
+ exactly <'"'>,
137
+ lookahead < exactly< hash_lbrace > >
138
+ >
139
+ >(src);
140
+ }
141
+
142
+ extern const char string_single_negates[] = "'\\#";
143
+ const char* re_string_single_close(const char* src)
144
+ {
145
+ return sequence <
146
+ // valid chars
147
+ zero_plus <
148
+ alternatives <
149
+ // escaped char
150
+ sequence <
151
+ exactly <'\\'>,
152
+ any_char
153
+ >,
154
+ // non interpolate hash
155
+ sequence <
156
+ exactly <'#'>,
157
+ negate <
158
+ exactly <'{'>
159
+ >
160
+ >,
161
+ // other valid chars
162
+ neg_class_char <
163
+ string_single_negates
164
+ >
165
+ >
166
+ >,
167
+ // quoted string closer
168
+ // or interpolate opening
169
+ alternatives <
170
+ exactly <'\''>,
171
+ lookahead < exactly< hash_lbrace > >
172
+ >
173
+ >(src);
174
+ }
175
+
176
+ const char* re_string_single_open(const char* src)
177
+ {
178
+ return sequence <
179
+ // quoted string opener
180
+ exactly <'\''>,
181
+ // valid chars
182
+ zero_plus <
183
+ alternatives <
184
+ // escaped char
185
+ sequence <
186
+ exactly <'\\'>,
187
+ any_char
188
+ >,
189
+ // non interpolate hash
190
+ sequence <
191
+ exactly <'#'>,
192
+ negate <
193
+ exactly <'{'>
194
+ >
195
+ >,
196
+ // other valid chars
197
+ neg_class_char <
198
+ string_single_negates
199
+ >
200
+ >
201
+ >,
202
+ // quoted string closer
203
+ // or interpolate opening
204
+ alternatives <
205
+ exactly <'\''>,
206
+ lookahead < exactly< hash_lbrace > >
207
+ >
208
+ >(src);
209
+ }
210
+
211
+ /*
212
+ :uri => {
213
+ false => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/,
214
+ true => /(#{URLCHAR}*?)(#{W}\)|#\{)/
215
+ },
216
+ */
217
+ const char* re_string_uri_close(const char* src)
218
+ {
219
+ return sequence <
220
+ non_greedy<
221
+ alternatives<
222
+ class_char< real_uri_chars >,
223
+ uri_character,
224
+ NONASCII,
225
+ ESCAPE
226
+ >,
227
+ alternatives<
228
+ sequence < optional < W >, exactly <')'> >,
229
+ lookahead < exactly< hash_lbrace > >
230
+ >
231
+ >,
232
+ optional <
233
+ sequence < optional < W >, exactly <')'> >
234
+ >
235
+ >(src);
236
+ }
237
+
238
+ const char* re_string_uri_open(const char* src)
239
+ {
240
+ return sequence <
241
+ exactly <'u'>,
242
+ exactly <'r'>,
243
+ exactly <'l'>,
244
+ exactly <'('>,
245
+ W,
246
+ non_greedy<
247
+ alternatives<
248
+ class_char< real_uri_chars >,
249
+ uri_character,
250
+ NONASCII,
251
+ ESCAPE
252
+ >,
253
+ alternatives<
254
+ sequence < W, exactly <')'> >,
255
+ exactly< hash_lbrace >
256
+ >
257
+ >
258
+ >(src);
259
+ }
260
+
19
261
  // Match a line comment (/.*?(?=\n|\r\n?|\Z)/.
20
262
  const char* line_comment(const char* src)
21
263
  {
@@ -370,6 +612,133 @@ namespace Sass {
370
612
  return sequence<exactly<'@'>, identifier>(src);
371
613
  }
372
614
 
615
+ /*
616
+ tok(%r{
617
+ (
618
+ \\.
619
+ |
620
+ (?!url\()
621
+ [^"'/\#!;\{\}] # "
622
+ |
623
+ /(?![\*\/])
624
+ |
625
+ \#(?!\{)
626
+ |
627
+ !(?![a-z]) # TODO: never consume "!" when issue 1126 is fixed.
628
+ )+
629
+ }xi) || tok(COMMENT) || tok(SINGLE_LINE_COMMENT) || interp_string || interp_uri ||
630
+ interpolation(:warn_for_color)
631
+ */
632
+ const char* re_almost_any_value_token(const char* src) {
633
+
634
+ return alternatives <
635
+ one_plus <
636
+ alternatives <
637
+ sequence <
638
+ exactly <'\\'>,
639
+ any_char
640
+ >,
641
+ sequence <
642
+ negate <
643
+ sequence <
644
+ exactly < url_kwd >,
645
+ exactly <'('>
646
+ >
647
+ >,
648
+ neg_class_char <
649
+ almost_any_value_class
650
+ >
651
+ >,
652
+ sequence <
653
+ exactly <'/'>,
654
+ negate <
655
+ alternatives <
656
+ exactly <'/'>,
657
+ exactly <'*'>
658
+ >
659
+ >
660
+ >,
661
+ sequence <
662
+ exactly <'\\'>,
663
+ exactly <'#'>,
664
+ negate <
665
+ exactly <'{'>
666
+ >
667
+ >,
668
+ sequence <
669
+ exactly <'!'>,
670
+ negate <
671
+ alpha
672
+ >
673
+ >
674
+ >
675
+ >,
676
+ block_comment,
677
+ line_comment,
678
+ interpolant,
679
+ space,
680
+ sequence <
681
+ exactly<'u'>,
682
+ exactly<'r'>,
683
+ exactly<'l'>,
684
+ exactly<'('>,
685
+ zero_plus <
686
+ alternatives <
687
+ class_char< real_uri_chars >,
688
+ uri_character,
689
+ NONASCII,
690
+ ESCAPE
691
+ >
692
+ >,
693
+ // false => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/,
694
+ // true => /(#{URLCHAR}*?)(#{W}\)|#\{)/
695
+ exactly<')'>
696
+ >
697
+ >(src);
698
+ }
699
+
700
+ /*
701
+ DIRECTIVES = Set[:mixin, :include, :function, :return, :debug, :warn, :for,
702
+ :each, :while, :if, :else, :extend, :import, :media, :charset, :content,
703
+ :_moz_document, :at_root, :error]
704
+ */
705
+ const char* re_special_directive(const char* src) {
706
+ return alternatives <
707
+ word < mixin_kwd >,
708
+ word < include_kwd >,
709
+ word < function_kwd >,
710
+ word < return_kwd >,
711
+ word < debug_kwd >,
712
+ word < warn_kwd >,
713
+ word < for_kwd >,
714
+ word < each_kwd >,
715
+ word < while_kwd >,
716
+ word < if_kwd >,
717
+ word < else_kwd >,
718
+ word < extend_kwd >,
719
+ word < import_kwd >,
720
+ word < media_kwd >,
721
+ word < charset_kwd >,
722
+ word < content_kwd >,
723
+ // exactly < moz_document_kwd >,
724
+ word < at_root_kwd >,
725
+ word < error_kwd >
726
+ >(src);
727
+ }
728
+
729
+ const char* re_prefixed_directive(const char* src) {
730
+ return sequence <
731
+ optional <
732
+ sequence <
733
+ exactly <'-'>,
734
+ one_plus < alnum >,
735
+ exactly <'-'>
736
+ >
737
+ >,
738
+ exactly < supports_kwd >
739
+ >(src);
740
+ }
741
+
373
742
  const char* re_reference_combinator(const char* src) {
374
743
  return sequence <
375
744
  optional <
@@ -508,7 +877,7 @@ namespace Sass {
508
877
  return one_plus< alternatives< alnum,
509
878
  exactly<'-'>,
510
879
  exactly<'_'>,
511
- exactly<'\\'> > >(src);
880
+ escape_seq > >(src);
512
881
  }
513
882
 
514
883
  const char* kwd_warn(const char* src) {
@@ -586,7 +955,7 @@ namespace Sass {
586
955
  }
587
956
 
588
957
  const char* hyphens_and_identifier(const char* src) {
589
- return sequence< zero_plus< exactly< '-' > >, identifier >(src);
958
+ return sequence< zero_plus< exactly< '-' > >, identifier_alnums >(src);
590
959
  }
591
960
  const char* hyphens_and_name(const char* src) {
592
961
  return sequence< zero_plus< exactly< '-' > >, name >(src);
@@ -596,11 +965,11 @@ namespace Sass {
596
965
  }
597
966
  // Match CSS id names.
598
967
  const char* id_name(const char* src) {
599
- return sequence<exactly<'#'>, name>(src);
968
+ return sequence<exactly<'#'>, identifier_alnums >(src);
600
969
  }
601
970
  // Match CSS class names.
602
971
  const char* class_name(const char* src) {
603
- return sequence<exactly<'.'>, identifier>(src);
972
+ return sequence<exactly<'.'>, identifier >(src);
604
973
  }
605
974
  // Attribute name in an attribute selector.
606
975
  const char* attribute_name(const char* src) {
@@ -609,7 +978,7 @@ namespace Sass {
609
978
  }
610
979
  // match placeholder selectors
611
980
  const char* placeholder(const char* src) {
612
- return sequence<exactly<'%'>, identifier>(src);
981
+ return sequence<exactly<'%'>, identifier_alnums >(src);
613
982
  }
614
983
  // Match CSS numeric constants.
615
984
 
@@ -825,6 +1194,22 @@ namespace Sass {
825
1194
  return sequence<exactly<'$'>, identifier>(src);
826
1195
  }
827
1196
 
1197
+ // parse `calc`, `-a-calc` and `--b-c-calc`
1198
+ // but do not parse `foocalc` or `foo-calc`
1199
+ const char* calc_fn_call(const char* src) {
1200
+ return sequence <
1201
+ optional < sequence <
1202
+ hyphens,
1203
+ one_plus < sequence <
1204
+ strict_identifier,
1205
+ hyphens
1206
+ > >
1207
+ > >,
1208
+ exactly < calc_fn_kwd >,
1209
+ word_boundary
1210
+ >(src);
1211
+ }
1212
+
828
1213
  // Match Sass boolean keywords.
829
1214
  const char* kwd_true(const char* src) {
830
1215
  return word<true_kwd>(src);
@@ -1167,6 +1552,83 @@ namespace Sass {
1167
1552
  >(src);
1168
1553
  }
1169
1554
 
1555
+ const char* re_selector_list(const char* src) {
1556
+ return alternatives <
1557
+ // partial bem selector
1558
+ sequence <
1559
+ ampersand,
1560
+ one_plus <
1561
+ exactly < '-' >
1562
+ >,
1563
+ word_boundary,
1564
+ optional_spaces
1565
+ >,
1566
+ // main selector matching
1567
+ one_plus <
1568
+ alternatives <
1569
+ // consume whitespace and comments
1570
+ spaces, block_comment, line_comment,
1571
+ // match `/deep/` selector (pass-trough)
1572
+ // there is no functionality for it yet
1573
+ schema_reference_combinator,
1574
+ // match selector ops /[*&%,\[\]]/
1575
+ class_char < selector_lookahead_ops >,
1576
+ // match selector combinators /[>+~]/
1577
+ class_char < selector_combinator_ops >,
1578
+ // match attribute compare operators
1579
+ sequence <
1580
+ exactly <'('>,
1581
+ optional_spaces,
1582
+ optional <re_selector_list>,
1583
+ optional_spaces,
1584
+ exactly <')'>
1585
+ >,
1586
+ alternatives <
1587
+ exact_match, class_match, dash_match,
1588
+ prefix_match, suffix_match, substring_match
1589
+ >,
1590
+ // main selector match
1591
+ sequence <
1592
+ // allow namespace prefix
1593
+ optional < namespace_schema >,
1594
+ // modifiers prefixes
1595
+ alternatives <
1596
+ sequence <
1597
+ exactly <'#'>,
1598
+ // not for interpolation
1599
+ negate < exactly <'{'> >
1600
+ >,
1601
+ // class match
1602
+ exactly <'.'>,
1603
+ // single or double colon
1604
+ optional < pseudo_prefix >
1605
+ >,
1606
+ // accept hypens in token
1607
+ one_plus < sequence <
1608
+ // can start with hyphens
1609
+ zero_plus < exactly<'-'> >,
1610
+ // now the main token
1611
+ alternatives <
1612
+ kwd_optional,
1613
+ exactly <'*'>,
1614
+ quoted_string,
1615
+ interpolant,
1616
+ identifier,
1617
+ variable,
1618
+ percentage,
1619
+ binomial,
1620
+ dimension,
1621
+ alnum
1622
+ >
1623
+ > >,
1624
+ // can also end with hyphens
1625
+ zero_plus < exactly<'-'> >
1626
+ >
1627
+ >
1628
+ >
1629
+ >(src);
1630
+ }
1631
+
1170
1632
  const char* type_selector(const char* src) {
1171
1633
  return sequence< optional<namespace_schema>, identifier>(src);
1172
1634
  }
@@ -1183,6 +1645,12 @@ namespace Sass {
1183
1645
  // lexer special_fn: these functions cannot be overloaded
1184
1646
  // (/((-[\w-]+-)?(calc|element)|expression|progid:[a-z\.]*)\(/i)
1185
1647
  const char* re_special_fun(const char* src) {
1648
+
1649
+ // match this first as we test prefix hyphens
1650
+ if (const char* calc = calc_fn_call(src)) {
1651
+ return calc;
1652
+ }
1653
+
1186
1654
  return sequence <
1187
1655
  optional <
1188
1656
  sequence <
@@ -1197,8 +1665,7 @@ namespace Sass {
1197
1665
  >
1198
1666
  >,
1199
1667
  alternatives <
1200
- exactly < calc_fn_kwd >,
1201
- exactly < expression_kwd >,
1668
+ word < expression_kwd >,
1202
1669
  sequence <
1203
1670
  sequence <
1204
1671
  exactly < progid_kwd >,