stash 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,208 @@
1
+ #include "intern.h"
2
+
3
+ /* forward declare helper functions */
4
+ static bool contains_node_type(mustache_node_t* root, enum mustache_node_type type);
5
+ static void remove_node(mustache_node_t* node);
6
+ static bool is_posfix_standalone(mustache_node_t* node);
7
+ bool is_whitespace(mustache_node_t* text_node);
8
+
9
+ bool optimize_block(mustache_node_t* node)
10
+ {
11
+ mustache_node_t* child = node->first_child;
12
+ while (child) {
13
+ if (!child->optimize(child)) {
14
+ return false;
15
+ }
16
+ child = child->next_sibling;
17
+ }
18
+ return true;
19
+ }
20
+
21
+ bool optimize_text(mustache_node_t* node)
22
+ {
23
+ // Remove indentation from starting standalone tags
24
+ if ((!node->prv_sibling || node->prv_sibling->type == NODE_NEWLINE) &&
25
+ node->next_sibling &&
26
+ (node->next_sibling->type == NODE_SECTION || node->next_sibling->type == NODE_INVERTED_SECTION) &&
27
+ contains_node_type(node->next_sibling->first_child, NODE_NEWLINE) &&
28
+ is_whitespace(node)) {
29
+ remove_node(node);
30
+ }
31
+
32
+ // Remove indentation from ending standalone tags if beginning tag was intented
33
+ if (!node->next_sibling &&
34
+ node->prv_sibling &&
35
+ node->prv_sibling->type == NODE_NEWLINE &&
36
+ node->parent &&
37
+ node->parent->parent &&
38
+ (node->parent->parent->type == NODE_SECTION || node->parent->parent->type == NODE_INVERTED_SECTION) &&
39
+ (!node->parent->parent->next_sibling || (node->parent->parent->prv_sibling && is_whitespace(node->parent->parent->prv_sibling))) &&
40
+ is_whitespace(node)) {
41
+ remove_node(node);
42
+ }
43
+
44
+ return true;
45
+ }
46
+
47
+ bool optimize_newline(mustache_node_t* node)
48
+ {
49
+ // Remove newlines after starting standalone section tags
50
+ if (!node->prv_sibling &&
51
+ node->parent &&
52
+ node->parent->parent &&
53
+ (node->parent->parent->type == NODE_SECTION || node->parent->parent->type == NODE_INVERTED_SECTION)&&
54
+ (!node->parent->parent->prv_sibling || is_whitespace(node->parent->parent->prv_sibling))) {
55
+ remove_node(node);
56
+ }
57
+
58
+ // Remove newlines after ending standalone section tags
59
+ if (node->prv_sibling &&
60
+ (node->prv_sibling->type == NODE_SECTION || node->prv_sibling->type == NODE_INVERTED_SECTION) &&
61
+ (!node->prv_sibling->first_child->first_child || contains_node_type(node->prv_sibling->first_child, NODE_NEWLINE))) {
62
+ remove_node(node);
63
+ }
64
+ return true;
65
+ }
66
+
67
+ bool optimize_variable(mustache_node_t* node)
68
+ {
69
+ return true;
70
+ }
71
+
72
+ bool optimize_section(mustache_node_t* node)
73
+ {
74
+ return node->first_child->optimize(node->first_child);
75
+ }
76
+
77
+ bool optimize_comment(mustache_node_t* node)
78
+ {
79
+ // Remove whitespace from standalone comments.
80
+ // Comments that satisfy any of the following conditions are considered standalone:
81
+ // 1. "{{!comment}}"
82
+ // 2. "{{!comment}} "
83
+ // 3. "{{!comment}}\n"
84
+ // 4. "{{!comment}} \n"
85
+ // 5. " {{!comment}}"
86
+ // 6. " {{!comment}} "
87
+ // 7. " {{!comment}}\n"
88
+ // 8. " {{!comment}} \n"
89
+ // 9. "\n{{!comment}}"
90
+ // 10. "\n{{!comment}} "
91
+ // 11. "\n{{!comment}}\n"
92
+ // 12. "\n{{!comment}} \n"
93
+ // 13. "\n {{!comment}}"
94
+ // 14. "\n {{!comment}} "
95
+ // 15. "\n {{!comment}}\n"
96
+ // 16. "\n {{!comment}} \n"
97
+
98
+ bool is_standalone = false;
99
+ mustache_node_t* tmp = NULL;
100
+ mustache_node_t* prv = node->prv_sibling;
101
+ mustache_node_t* next = node->next_sibling;
102
+ if (!prv) {
103
+ is_standalone = is_posfix_standalone(node);
104
+ } else if (is_whitespace(prv)) {
105
+ if (!prv->prv_sibling &&
106
+ (!node->parent || !node->parent->parent || !(node->parent->parent->type == NODE_SECTION || node->parent->parent->type == NODE_INVERTED_SECTION))) {
107
+ is_standalone = is_posfix_standalone(node);
108
+ } else if (prv->type == NODE_NEWLINE) {
109
+ is_standalone = is_posfix_standalone(node);
110
+ } else if (prv->prv_sibling && prv->prv_sibling->type == NODE_NEWLINE) {
111
+ is_standalone = is_posfix_standalone(node);
112
+ }
113
+ }
114
+
115
+ // if the comment is standalone, remove the unnessesary nodes.
116
+ if (is_standalone) {
117
+ if (prv && prv->type == NODE_TEXT && node->type == NODE_COMMENT) {
118
+ remove_node(prv);
119
+ }
120
+ if (next && next->type == NODE_TEXT) {
121
+ tmp = next->next_sibling;
122
+ remove_node(next);
123
+ next = tmp;
124
+ }
125
+ while (next && next->type == NODE_NEWLINE) {
126
+ tmp = next->next_sibling;
127
+ remove_node(next);
128
+ next = tmp;
129
+ }
130
+ }
131
+ return true;
132
+ }
133
+
134
+ bool optimize_partial(mustache_node_t* node)
135
+ {
136
+ return optimize_comment(node);
137
+ }
138
+
139
+
140
+ /* helper functions */
141
+
142
+ static bool contains_node_type(mustache_node_t* root, enum mustache_node_type type)
143
+ {
144
+ if (root->type == type) {
145
+ return true;
146
+ }
147
+
148
+ if ((root->first_child && contains_node_type(root->first_child, type)) ||
149
+ (root->next_sibling && contains_node_type(root->next_sibling, type))) {
150
+ return true;
151
+ }
152
+
153
+ return false;
154
+ }
155
+
156
+ bool is_whitespace(mustache_node_t* text_node)
157
+ {
158
+ if (text_node->type == NODE_NEWLINE) {
159
+ return true;
160
+ }
161
+
162
+ if (text_node->type != NODE_TEXT) {
163
+ return false;
164
+ }
165
+
166
+ char* text = text_node->token->text;
167
+ while (text[0]) {
168
+ if (!isspace(text[0])) {
169
+ return false;
170
+ }
171
+ text++;
172
+ }
173
+ return true;
174
+ }
175
+
176
+ static void remove_node(mustache_node_t* node)
177
+ {
178
+ if (!node) {
179
+ return;
180
+ }
181
+
182
+ mustache_node_t* prv = node->prv_sibling;
183
+ mustache_node_t* next = node->next_sibling;
184
+
185
+ if (prv) {
186
+ prv->next_sibling = next;
187
+ } else {
188
+ node->parent->first_child = next;
189
+ }
190
+
191
+ if (next) {
192
+ next->prv_sibling = prv;
193
+ }
194
+
195
+ // TODO: free node
196
+ // TODO: free node's children
197
+ }
198
+
199
+ static bool is_posfix_standalone(mustache_node_t* node)
200
+ {
201
+ mustache_node_t* next = node->next_sibling;
202
+ if (!next || next->type == NODE_NEWLINE) {
203
+ return true;
204
+ } else if (next->type == NODE_TEXT && is_whitespace(next)) {
205
+ return (!next->next_sibling || next->next_sibling->type == NODE_NEWLINE);
206
+ }
207
+ return false;
208
+ }
@@ -0,0 +1,177 @@
1
+ #include <ruby.h>
2
+ #include <sys/time.h>
3
+ #include <sys/resource.h>
4
+ #include "mustache.h"
5
+ #include "stash.h"
6
+
7
+
8
+
9
+ extern void log_template_execution_time(mustache_context_t* m_ctx, double time);
10
+ extern double get_time();
11
+
12
+ VALUE cTemplate = Qnil;
13
+
14
+ static VALUE cTemplate_init(VALUE self, VALUE rb_template_string)
15
+ {
16
+ mustache_node_t* c_template;
17
+ Data_Get_Struct(self, mustache_node_t, c_template);
18
+
19
+ char* c_template_string;
20
+ long c_template_string_length;
21
+ mustache_error_t* error = NULL;
22
+
23
+ c_template_string = strdup(rb_str2cstr(rb_template_string, &c_template_string_length));
24
+ mustache_build_template(c_template_string, c_template, &error);
25
+
26
+ if (error) {
27
+ rb_raise(rb_eSyntaxError, error->error_text);
28
+ }
29
+
30
+ return self;
31
+ }
32
+
33
+ static VALUE cTemplate_render(int argc, VALUE* argv, VALUE self)
34
+ {
35
+ VALUE data = Qnil;
36
+ VALUE partials = Qnil;
37
+ VALUE debug = Qnil;
38
+
39
+ if (argc > 0) {
40
+ VALUE type = TYPE(argv[0]);
41
+ if (type != T_OBJECT && type != T_HASH) {
42
+ rb_raise(rb_eArgError, "Expected an object or hash as argument 1");
43
+ return Qnil;
44
+ }
45
+ data = argv[0];
46
+ }
47
+
48
+ if (argc > 1) {
49
+ VALUE type = TYPE(argv[1]);
50
+ if (type != T_HASH) {
51
+ rb_raise(rb_eArgError, "Expected a hash as argument 2");
52
+ return Qnil;
53
+ }
54
+ partials = argv[1];
55
+ }
56
+
57
+ if (argc > 2) {
58
+ if (strcmp(rb_obj_classname(argv[2]), "Template::Debug") != 0) {
59
+ rb_raise(rb_eArgError, "Expected an instance of Template::Debug as argument 3");
60
+ return Qnil;
61
+ }
62
+ debug = argv[2];
63
+ }
64
+
65
+ // Get the template
66
+ mustache_node_t* c_template;
67
+ Data_Get_Struct(self, mustache_node_t, c_template);
68
+
69
+ // Create the context
70
+ mustache_context_t* m_ctx = create_mustache_context(NULL);
71
+ mustache_ruby_context_t* r_ctx = (mustache_ruby_context_t*) m_ctx->custom;
72
+ m_ctx->data = (void*) data;
73
+ m_ctx->partials = (void*) partials;
74
+ m_ctx->debug_mode = debug != Qnil;
75
+
76
+ double start = 0;
77
+ if (m_ctx->debug_mode) {
78
+ start = get_time();
79
+ r_ctx->debugObject = debug;
80
+ }
81
+
82
+ // Execute the template
83
+ mustache_execute_template(c_template, m_ctx);
84
+
85
+ if (m_ctx->debug_mode) {
86
+ log_template_execution_time(m_ctx, get_time() - start);
87
+ }
88
+
89
+ // Return the buffer
90
+ VALUE result = rb_str_resize(r_ctx->buffer, r_ctx->buffer_length);
91
+ m_ctx->destroy(m_ctx);
92
+ return result;
93
+ }
94
+
95
+ static void template_mark(mustache_node_t* node)
96
+ {
97
+ }
98
+
99
+ static void template_free(mustache_node_t* node)
100
+ {
101
+ }
102
+
103
+ static VALUE template_allocate(VALUE klass)
104
+ {
105
+ mustache_node_t* c_template = mustache_create_node();
106
+ return Data_Wrap_Struct(cTemplate, template_mark, template_free, c_template);
107
+ }
108
+
109
+ void Init_stash()
110
+ {
111
+ // Define the Template class
112
+ cTemplate = rb_define_class("Template", rb_cObject);
113
+ rb_define_alloc_func(cTemplate, template_allocate);
114
+ rb_define_method(cTemplate, "initialize", cTemplate_init, 1);
115
+ rb_define_method(cTemplate, "render", cTemplate_render, -1);
116
+ }
117
+
118
+ bool mustache_write_to_buffer(mustache_context_t* m_ctx, char* data, size_t data_length)
119
+ {
120
+ if (data_length == 0) {
121
+ return true;
122
+ }
123
+
124
+ mustache_ruby_context_t* r_ctx = (mustache_ruby_context_t*) m_ctx->custom;
125
+ rb_str_buf_cat(r_ctx->buffer, data, data_length);
126
+ r_ctx->buffer_length += data_length;
127
+ return true;
128
+ }
129
+
130
+ void* mustache_malloc(long size)
131
+ {
132
+ return ruby_xmalloc(size);
133
+ }
134
+
135
+ void mustache_free(void* ptr)
136
+ {
137
+ ruby_xfree(ptr);
138
+ }
139
+
140
+ mustache_node_t* get_partial(char* key, void* partials)
141
+ {
142
+ VALUE rb_partials = (VALUE) partials;
143
+ Check_Type(rb_partials, T_HASH);
144
+
145
+ VALUE rb_value;
146
+ if (NIL_P(rb_value = rb_hash_aref(rb_partials, ID2SYM(rb_intern(key))))) {
147
+ return NULL;
148
+ }
149
+
150
+ if (rb_class_of(rb_value) != cTemplate) {
151
+ rb_raise(rb_eTypeError, "Expected Template");
152
+ }
153
+
154
+ mustache_node_t* template = NULL;
155
+ Data_Get_Struct(rb_value, mustache_node_t, template);
156
+ return template;
157
+ }
158
+
159
+ double get_time()
160
+ {
161
+ struct timeval t;
162
+ struct timezone tzp;
163
+ gettimeofday(&t, &tzp);
164
+ return t.tv_sec + t.tv_usec*1e-6;
165
+ }
166
+
167
+ void log_template_execution_time(mustache_context_t* m_ctx, double time)
168
+ {
169
+ mustache_ruby_context_t* r_ctx = (mustache_ruby_context_t*) m_ctx->custom;
170
+ rb_ivar_set(r_ctx->debugObject, rb_intern("@total_time"), rb_float_new(time));
171
+ }
172
+
173
+ void log_ruby_callback_execution_time(mustache_context_t* m_ctx, ID callback_name, double time)
174
+ {
175
+ mustache_ruby_context_t* r_ctx = (mustache_ruby_context_t*) m_ctx->custom;
176
+ rb_funcall(r_ctx->debugObject, rb_intern("log_callback"), 2, ID2SYM(callback_name), rb_float_new(time));
177
+ }
@@ -0,0 +1,17 @@
1
+ typedef struct mustache_ruby_context {
2
+ VALUE buffer;
3
+ long buffer_length;
4
+ VALUE debugObject;
5
+ } mustache_ruby_context_t;
6
+
7
+ mustache_context_t* create_mustache_context(mustache_context_t* parent);
8
+
9
+ mustache_value_t* convert_type(
10
+ char* key,
11
+ void* context,
12
+ mustache_context_t* m_lookup_context,
13
+ mustache_context_t* m_exection_context);
14
+
15
+ mustache_node_t* get_partial(char* key, void* partials);
16
+ void log_ruby_callback_execution_time(mustache_context_t* m_ctx, ID callback_name, double time);
17
+ double get_time();
@@ -0,0 +1,151 @@
1
+ #include "intern.h"
2
+
3
+ void set_start_delimiter(char* delimiter, scanner_data_t* data)
4
+ {
5
+ data->statement_start = strdup(delimiter);
6
+ }
7
+
8
+ void use_next_end_delimiter(scanner_data_t* data)
9
+ {
10
+ data->statement_end = data->next_statement_end;
11
+ data->next_statement_end = "";
12
+ }
13
+
14
+ void set_next_end_delimiter(char* delimiter, scanner_data_t* data)
15
+ {
16
+ data->statement_end = strdup(delimiter);
17
+ }
18
+
19
+ mustache_token_t* pop_token(mustache_token_t** head)
20
+ {
21
+ mustache_token_t* token = *head;
22
+ *head = token->next;
23
+ return token;
24
+ }
25
+
26
+ void reverse(mustache_token_t** head)
27
+ {
28
+ mustache_token_t* prv = NULL;
29
+ mustache_token_t* current = *head;
30
+
31
+ while (current) {
32
+ mustache_token_t* next = current->next;
33
+ current->next = prv;
34
+ prv = current;
35
+ current = next;
36
+ }
37
+
38
+ *head = prv;
39
+ }
40
+
41
+ bool is_token(mustache_token_t* head, enum mustache_token_type type)
42
+ {
43
+ if (!head) {
44
+ return false;
45
+ }
46
+
47
+ return head->type == type;
48
+ }
49
+
50
+ void free_token(mustache_token_t* token)
51
+ {
52
+ if (!token) {
53
+ return;
54
+ }
55
+
56
+ if (*token->text) {
57
+ free(token->text);
58
+ }
59
+
60
+ free(token);
61
+ }
62
+
63
+ bool is_string_in_buffer(char* string, int string_len, char* buffer, int buffer_len)
64
+ {
65
+ if (buffer_len < string_len) {
66
+ return false;
67
+ }
68
+
69
+ return strncmp(&buffer[buffer_len - string_len], string, string_len) == 0;
70
+ }
71
+
72
+ mustache_token_t* create_token(enum mustache_token_type type, char* text)
73
+ {
74
+ mustache_token_t* token = malloc(sizeof(mustache_token_t));
75
+ char* copied_text = strdup(text);
76
+
77
+ if (token) {
78
+ token->type = type;
79
+ token->text = copied_text;
80
+ token->next = NULL;
81
+ }
82
+
83
+ return token;
84
+ }
85
+
86
+ void push_token(enum mustache_token_type type, char* text, yyscan_t yyscanner)
87
+ {
88
+ scanner_data_t* data = (scanner_data_t*) yyget_extra(yyscanner);
89
+ mustache_token_t* token = create_token(type, text);
90
+
91
+ if (data->head_token) {
92
+ token->next = data->head_token;
93
+ }
94
+
95
+ data->head_token = token;
96
+ }
97
+
98
+ void push_text(yyscan_t yyscanner)
99
+ {
100
+ // Get the accumulated text
101
+ scanner_data_t* data = (scanner_data_t*) yyget_extra(yyscanner);
102
+
103
+ // If the buffer contains text, push the token and reset buffer
104
+ if (data->buffer[0]) {
105
+ push_token(TOKEN_TEXT, data->buffer, yyscanner);
106
+ memset(data->buffer, '\0', data->buffer_size);
107
+ data->buffer_index = 0;
108
+ }
109
+ }
110
+
111
+ void push_comment(yyscan_t yyscanner)
112
+ {
113
+ // Get the accumulated text
114
+ scanner_data_t* data = (scanner_data_t*) yyget_extra(yyscanner);
115
+
116
+ // If the buffer contains text, push the token and reset buffer
117
+ if (data->buffer[0]) {
118
+ push_token(TOKEN_COMMENT, data->buffer, yyscanner);
119
+ memset(data->buffer, '\0', data->buffer_size);
120
+ data->buffer_index = 0;
121
+ }
122
+ }
123
+
124
+ void push_error(yyscan_t yyscanner)
125
+ {
126
+ return;
127
+ }
128
+
129
+ int get_tokens(char* input, mustache_token_t** tokens)
130
+ {
131
+ yyscan_t scanner;
132
+ scanner_data_t data;
133
+ data.head_token = NULL;
134
+ data.buffer_size = sizeof(char) * strlen(input) + 1;
135
+ data.buffer = malloc(data.buffer_size);
136
+ memset(data.buffer, '\0', data.buffer_size);
137
+ data.buffer_index = 0;
138
+ data.statement_start = "{{";
139
+ data.statement_end = "}}";
140
+ data.next_statement_end = "";
141
+
142
+ yylex_init_extra(&data, &scanner);
143
+ yy_scan_string(input, scanner);
144
+ yylex(scanner);
145
+ yylex_destroy(scanner);
146
+ *tokens = data.head_token;
147
+ free(data.buffer);
148
+ reverse(tokens);
149
+
150
+ return true;
151
+ }