Skip to content
This repository was archived by the owner on Jun 14, 2019. It is now read-only.

Commit efb5a2d

Browse files
author
Aaron Leung
committed
Adding infrastructure for pure Sass functions. Necessary to pass more contextual data around during parsing, since the set of expressions permitted in functions is relatively limited.
1 parent 8840431 commit efb5a2d

File tree

9 files changed

+105
-56
lines changed

9 files changed

+105
-56
lines changed

context.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@ namespace Sass {
6565
// cerr << "Deallocated " << i << " source string(s)." << endl;
6666
}
6767

68-
inline void Context::register_function(Function_Descriptor d, Implementation ip)
68+
inline void Context::register_function(Function_Descriptor d, Primitive ip)
6969
{
7070
Function f(d, ip);
7171
function_env[pair<string, size_t>(f.name, f.parameters.size())] = f;
7272
}
7373

74-
inline void Context::register_function(Function_Descriptor d, Implementation ip, size_t arity)
74+
inline void Context::register_function(Function_Descriptor d, Primitive ip, size_t arity)
7575
{
7676
Function f(d, ip);
7777
function_env[pair<string, size_t>(f.name, arity)] = f;

context.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ namespace Sass {
5757
Context(const char* paths_str = 0);
5858
~Context();
5959

60-
void register_function(Function_Descriptor d, Implementation ip);
61-
void register_function(Function_Descriptor d, Implementation ip, size_t arity);
60+
void register_function(Function_Descriptor d, Primitive ip);
61+
void register_function(Function_Descriptor d, Primitive ip, size_t arity);
6262
void register_functions();
6363
};
6464

document.hpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,15 @@ namespace Sass {
128128
Node parse_import();
129129
Node parse_include();
130130
Node parse_mixin_definition();
131-
Node parse_mixin_parameters();
131+
Node parse_function_definition();
132+
Node parse_parameters();
132133
Node parse_parameter();
133134
Node parse_mixin_call();
134135
Node parse_arguments();
135136
Node parse_argument();
136137
Node parse_assignment();
137138
Node parse_propset();
138-
Node parse_ruleset(Selector_Lookahead lookahead, bool in_definition = false);
139+
Node parse_ruleset(Selector_Lookahead lookahead, Node::Type inside_of = Node::none);
139140
Node parse_selector_schema(const char* end_of_selector);
140141
Node parse_selector_group();
141142
Node parse_selector();
@@ -144,7 +145,7 @@ namespace Sass {
144145
Node parse_simple_selector();
145146
Node parse_pseudo();
146147
Node parse_attribute_selector();
147-
Node parse_block(Node surrounding_rulesetbool, bool in_definition = false);
148+
Node parse_block(Node surrounding_rulesetbool, Node::Type inside_of = Node::none);
148149
Node parse_rule();
149150
Node parse_values();
150151
Node parse_list();
@@ -160,10 +161,10 @@ namespace Sass {
160161
Node parse_function_call();
161162
Node parse_string();
162163
Node parse_value_schema();
163-
Node parse_if_directive(Node surrounding_ruleset);
164-
Node parse_for_directive(Node surrounding_ruleset);
165-
Node parse_each_directive(Node surrounding_ruleset);
166-
Node parse_while_directive(Node surrounding_ruleset);
164+
Node parse_if_directive(Node surrounding_ruleset, Node::Type inside_of);
165+
Node parse_for_directive(Node surrounding_ruleset, Node::Type inside_of);
166+
Node parse_each_directive(Node surrounding_ruleset, Node::Type inside_of);
167+
Node parse_while_directive(Node surrounding_ruleset, Node::Type inside_of);
167168

168169
Selector_Lookahead lookahead_for_selector(const char* start = 0);
169170

document_parser.cpp

Lines changed: 60 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ namespace Sass {
2222
else if (peek< mixin >() || peek< exactly<'='> >()) {
2323
root << parse_mixin_definition();
2424
}
25+
else if (peek< function >()) {
26+
root << parse_function_definition();
27+
}
2528
else if (peek< variable >()) {
2629
root << parse_assignment();
2730
if (!lex< exactly<';'> >()) throw_syntax_error("top-level variable binding must be terminated by ';'");
@@ -37,16 +40,16 @@ namespace Sass {
3740
if (!lex< exactly<';'> >()) throw_syntax_error("top-level @include directive must be terminated by ';'");
3841
}
3942
else if (peek< if_directive >()) {
40-
root << parse_if_directive(Node());
43+
root << parse_if_directive(Node(), Node::none);
4144
}
4245
else if (peek< for_directive >()) {
43-
root << parse_for_directive(Node());
46+
root << parse_for_directive(Node(), Node::none);
4447
}
4548
else if (peek< each_directive >()) {
46-
root << parse_each_directive(Node());
49+
root << parse_each_directive(Node(), Node::none);
4750
}
4851
else if (peek< while_directive >()) {
49-
root << parse_while_directive(Node());
52+
root << parse_while_directive(Node(), Node::none);
5053
}
5154
else {
5255
lex< spaces_and_comments >();
@@ -103,15 +106,30 @@ namespace Sass {
103106
lex< mixin >() || lex< exactly<'='> >();
104107
if (!lex< identifier >()) throw_syntax_error("invalid name in @mixin directive");
105108
Node name(context.new_Node(Node::identifier, path, line, lexed));
106-
Node params(parse_mixin_parameters());
109+
Node params(parse_parameters());
107110
if (!peek< exactly<'{'> >()) throw_syntax_error("body for mixin " + name.token().to_string() + " must begin with a '{'");
108-
Node body(parse_block(Node(), true));
111+
Node body(parse_block(Node(), Node::mixin));
109112
Node the_mixin(context.new_Node(Node::mixin, path, line, 3));
110113
the_mixin << name << params << body;
111114
return the_mixin;
112115
}
113116

114-
Node Document::parse_mixin_parameters()
117+
Node Document::parse_function_definition()
118+
{
119+
120+
lex< function >();
121+
size_t func_line = line;
122+
if (!lex< identifier >()) throw_syntax_error("name required for function definition");
123+
Node name(context.new_Node(Node::identifier, path, line, lexed));
124+
Node params(parse_parameters());
125+
if (!peek< exactly<'{'> >()) throw_syntax_error("body for function " + name.to_string() + " must begin with a '{'");
126+
Node body(parse_block(Node(), Node::function));
127+
Node func(context.new_Node(Node::function, path, func_line, 3));
128+
func << name << params << body;
129+
return func;
130+
}
131+
132+
Node Document::parse_parameters()
115133
{
116134
Node params(context.new_Node(Node::parameters, path, line, 0));
117135
Token name(lexed);
@@ -226,7 +244,7 @@ namespace Sass {
226244
return propset;
227245
}
228246

229-
Node Document::parse_ruleset(Selector_Lookahead lookahead, bool in_definition)
247+
Node Document::parse_ruleset(Selector_Lookahead lookahead, Node::Type inside_of)
230248
{
231249
Node ruleset(context.new_Node(Node::ruleset, path, line, 2));
232250
if (lookahead.has_interpolants) {
@@ -236,7 +254,7 @@ namespace Sass {
236254
ruleset << parse_selector_group();
237255
}
238256
if (!peek< exactly<'{'> >()) throw_syntax_error("expected a '{' after the selector");
239-
ruleset << parse_block(ruleset, in_definition);
257+
ruleset << parse_block(ruleset, inside_of);
240258
return ruleset;
241259
}
242260

@@ -436,7 +454,7 @@ namespace Sass {
436454
return attr_sel;
437455
}
438456

439-
Node Document::parse_block(Node surrounding_ruleset, bool in_definition)
457+
Node Document::parse_block(Node surrounding_ruleset, Node::Type inside_of)
440458
{
441459
lex< exactly<'{'> >();
442460
bool semicolon = false;
@@ -455,9 +473,9 @@ namespace Sass {
455473
block << context.new_Node(Node::comment, path, line, lexed);
456474
}
457475
else if (peek< import >(position)) {
458-
if (in_definition) {
476+
if (inside_of == Node::mixin || inside_of == Node::function) {
459477
lex< import >(); // to adjust the line number
460-
throw_syntax_error("@import directive not allowed inside mixin definition");
478+
throw_syntax_error("@import directive not allowed inside definition of mixin or function");
461479
}
462480
Node imported_tree(parse_import());
463481
if (imported_tree.type() == Node::css_import) {
@@ -470,19 +488,34 @@ namespace Sass {
470488
semicolon = true;
471489
}
472490
}
473-
else if (peek< include >(position)) {
474-
block << parse_mixin_call();
475-
semicolon = true;
476-
}
477491
else if (lex< variable >()) {
478492
block << parse_assignment();
479493
semicolon = true;
480494
}
495+
else if (peek< if_directive >()) {
496+
block << parse_if_directive(surrounding_ruleset, inside_of);
497+
}
498+
else if (peek< for_directive >()) {
499+
block << parse_for_directive(surrounding_ruleset, inside_of);
500+
}
501+
else if (peek< each_directive >()) {
502+
block << parse_each_directive(surrounding_ruleset, inside_of);
503+
}
504+
else if (peek < while_directive >()) {
505+
block << parse_while_directive(surrounding_ruleset, inside_of);
506+
}
507+
else if (inside_of == Node::function) {
508+
throw_syntax_error("only variable declarations and control directives are allowed inside functions");
509+
}
510+
else if (peek< include >(position)) {
511+
block << parse_mixin_call();
512+
semicolon = true;
513+
}
481514
else if (peek< sequence< identifier, optional_spaces, exactly<':'>, optional_spaces, exactly<'{'> > >(position)) {
482515
block << parse_propset();
483516
}
484517
else if ((lookahead_result = lookahead_for_selector(position)).found) {
485-
block << parse_ruleset(lookahead_result, in_definition);
518+
block << parse_ruleset(lookahead_result, inside_of);
486519
}
487520
else if (peek< exactly<'+'> >()) {
488521
block << parse_mixin_call();
@@ -492,22 +525,9 @@ namespace Sass {
492525
if (surrounding_ruleset.is_null_ptr()) throw_syntax_error("@extend directive may only be used within rules");
493526
Node extendee(parse_simple_selector_sequence());
494527
context.extensions.insert(pair<Node, Node>(extendee, surrounding_ruleset));
495-
cerr << "PARSED EXTENSION REQUEST: " << surrounding_ruleset[0].to_string() << " EXTENDS " << extendee.to_string() << endl;
496528
context.has_extensions = true;
497529
semicolon = true;
498530
}
499-
else if (peek< if_directive >()) {
500-
block << parse_if_directive(surrounding_ruleset);
501-
}
502-
else if (peek< for_directive >()) {
503-
block << parse_for_directive(surrounding_ruleset);
504-
}
505-
else if (peek< each_directive >()) {
506-
block << parse_each_directive(surrounding_ruleset);
507-
}
508-
else if (peek < while_directive >()) {
509-
block << parse_while_directive(surrounding_ruleset);
510-
}
511531
else if (!peek< exactly<';'> >()) {
512532
Node rule(parse_rule());
513533
// check for lbrace; if it's there, we have a namespace property with a value
@@ -896,28 +916,28 @@ namespace Sass {
896916
return call;
897917
}
898918

899-
Node Document::parse_if_directive(Node surrounding_ruleset)
919+
Node Document::parse_if_directive(Node surrounding_ruleset, Node::Type inside_of)
900920
{
901921
lex< if_directive >();
902922
Node conditional(context.new_Node(Node::if_directive, path, line, 2));
903923
conditional << parse_list(); // the predicate
904924
if (!lex< exactly<'{'> >()) throw_syntax_error("expected '{' after the predicate for @if");
905-
conditional << parse_block(surrounding_ruleset); // the consequent
925+
conditional << parse_block(surrounding_ruleset, inside_of); // the consequent
906926
// collect all "@else if"s
907927
while (lex< elseif_directive >()) {
908928
conditional << parse_list(); // the next predicate
909929
if (!lex< exactly<'{'> >()) throw_syntax_error("expected '{' after the predicate for @else if");
910-
conditional << parse_block(surrounding_ruleset); // the next consequent
930+
conditional << parse_block(surrounding_ruleset, inside_of); // the next consequent
911931
}
912932
// parse the "@else" if present
913933
if (lex< else_directive >()) {
914934
if (!lex< exactly<'{'> >()) throw_syntax_error("expected '{' after @else");
915-
conditional << parse_block(surrounding_ruleset); // the alternative
935+
conditional << parse_block(surrounding_ruleset, inside_of); // the alternative
916936
}
917937
return conditional;
918938
}
919939

920-
Node Document::parse_for_directive(Node surrounding_ruleset)
940+
Node Document::parse_for_directive(Node surrounding_ruleset, Node::Type inside_of)
921941
{
922942
lex< for_directive >();
923943
size_t for_line = line;
@@ -931,13 +951,13 @@ namespace Sass {
931951
else throw_syntax_error("expected 'through' or 'to' keywod in @for directive");
932952
Node upper_bound(parse_expression());
933953
if (!peek< exactly<'{'> >()) throw_syntax_error("expected '{' after the upper bound in @for directive");
934-
Node body(parse_block(surrounding_ruleset));
954+
Node body(parse_block(surrounding_ruleset, inside_of));
935955
Node loop(context.new_Node(for_type, path, for_line, 4));
936956
loop << var << lower_bound << upper_bound << body;
937957
return loop;
938958
}
939959

940-
Node Document::parse_each_directive(Node surrounding_ruleset)
960+
Node Document::parse_each_directive(Node surrounding_ruleset, Node::Type inside_of)
941961
{
942962
lex < each_directive >();
943963
size_t each_line = line;
@@ -946,18 +966,18 @@ namespace Sass {
946966
if (!lex< in >()) throw_syntax_error("expected 'in' keyword in @each directive");
947967
Node list(parse_list());
948968
if (!peek< exactly<'{'> >()) throw_syntax_error("expected '{' after the upper bound in @each directive");
949-
Node body(parse_block(surrounding_ruleset));
969+
Node body(parse_block(surrounding_ruleset, inside_of));
950970
Node each(context.new_Node(Node::each_directive, path, each_line, 3));
951971
each << var << list << body;
952972
return each;
953973
}
954974

955-
Node Document::parse_while_directive(Node surrounding_ruleset)
975+
Node Document::parse_while_directive(Node surrounding_ruleset, Node::Type inside_of)
956976
{
957977
lex< while_directive >();
958978
size_t while_line = line;
959979
Node predicate(parse_list());
960-
Node body(parse_block(surrounding_ruleset));
980+
Node body(parse_block(surrounding_ruleset, inside_of));
961981
Node loop(context.new_Node(Node::while_directive, path, while_line, 2));
962982
loop << predicate << body;
963983
return loop;

eval_apply.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ namespace Sass {
2727
env[expr[0].token()] = expr;
2828
return expr;
2929
} break;
30+
31+
case Node::function: {
32+
f_env[pair<string, size_t>(expr[0].to_string(), expr[1].size())] = Function(expr);
33+
return expr;
34+
} break;
3035

3136
case Node::expansion: {
3237
Token name(expr[0].token());

functions.hpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,32 @@
88
namespace Sass {
99
using std::map;
1010

11-
typedef Node (*Implementation)(const vector<Token>&, map<Token, Node>&, Node_Factory& new_Node);
11+
typedef Node (*Primitive)(const vector<Token>&, map<Token, Node>&, Node_Factory& new_Node);
1212
typedef const char* str;
1313
typedef str Function_Descriptor[];
1414

1515
struct Function {
1616

1717
string name;
1818
vector<Token> parameters;
19-
Implementation implementation;
19+
Node definition;
20+
Primitive primitive;
2021

2122
Function()
2223
{ /* TO DO: set up the generic callback here */ }
24+
25+
Function(Node def)
26+
: name(def[0].to_string()),
27+
parameters(vector<Token>()),
28+
definition(def),
29+
primitive(0)
30+
{ }
2331

24-
Function(Function_Descriptor d, Implementation ip)
32+
Function(Function_Descriptor d, Primitive ip)
2533
: name(d[0]),
2634
parameters(vector<Token>()),
27-
implementation(ip)
35+
definition(Node()),
36+
primitive(ip)
2837
{
2938
size_t len = 0;
3039
while (d[len+1]) ++len;
@@ -38,7 +47,10 @@ namespace Sass {
3847
}
3948

4049
Node operator()(map<Token, Node>& bindings, Node_Factory& new_Node) const
41-
{ return implementation(parameters, bindings, new_Node); }
50+
{
51+
if (primitive) return primitive(parameters, bindings, new_Node);
52+
else return Node();
53+
}
4254

4355
};
4456

node.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ namespace Sass {
143143
css_import,
144144
function_call,
145145
mixin,
146+
function,
146147
parameters,
147148
expansion,
148149
arguments,

prelexer.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,14 @@ namespace Sass {
107107
const char* mixin(const char* src) {
108108
return exactly<mixin_kwd>(src);
109109
}
110+
extern const char function_kwd[] = "@function";
111+
const char* function(const char* src) {
112+
return exactly<function_kwd>(src);
113+
}
114+
extern const char return_kwd[] = "@return";
115+
const char* ret(const char* src) {
116+
return exactly<return_kwd>(src);
117+
}
110118
extern const char include_kwd[] = "@include";
111119
const char* include(const char* src) {
112120
return exactly<include_kwd>(src);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy