stash 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ext/stash/build.c +267 -0
- data/ext/stash/execute.c +248 -0
- data/ext/stash/extconf.rb +3 -0
- data/ext/stash/intern.h +79 -0
- data/ext/stash/lex.yy.c +2143 -0
- data/ext/stash/lex.yy.h +333 -0
- data/ext/stash/mustache.c +50 -0
- data/ext/stash/mustache.h +124 -0
- data/ext/stash/optimize.c +208 -0
- data/ext/stash/stash.c +177 -0
- data/ext/stash/stash.h +17 -0
- data/ext/stash/tokens.c +151 -0
- data/ext/stash/types.c +181 -0
- data/lib/stash.rb +2 -34
- data/lib/stash/debug.rb +46 -0
- metadata +30 -88
- data/.document +0 -5
- data/LICENSE +0 -20
- data/README.markdown +0 -92
- data/Rakefile +0 -46
- data/VERSION +0 -1
- data/lib/stash/class_methods.rb +0 -59
- data/lib/stash/hash.rb +0 -62
- data/lib/stash/list.rb +0 -91
- data/lib/stash/redis_adapter.rb +0 -152
- data/lib/stash/string.rb +0 -16
- data/spec/hash_spec.rb +0 -29
- data/spec/list_spec.rb +0 -65
- data/spec/spec.opts +0 -1
- data/spec/spec_helper.rb +0 -12
- data/spec/string_spec.rb +0 -9
- data/stash.gemspec +0 -68
data/ext/stash/build.c
ADDED
@@ -0,0 +1,267 @@
|
|
1
|
+
#include "intern.h"
|
2
|
+
|
3
|
+
/* forward declare helper functions */
|
4
|
+
static void append_node(mustache_node_t* parent, mustache_node_t* src);
|
5
|
+
static mustache_error_t* create_error(mustache_token_t* last_token, enum mustache_token_type expected_token, char* error_text);
|
6
|
+
|
7
|
+
/*
|
8
|
+
|
9
|
+
template := block
|
10
|
+
block := statement | block
|
11
|
+
statement := [text] | [newline] | command
|
12
|
+
command := variable | section | inverse_section | partial | comment
|
13
|
+
variable := [raw] [identifier] | [identifier]
|
14
|
+
section := [section_start] [identifier] block [section_end]
|
15
|
+
inverse_section := [inverse_section_start] [identifier] block [section_end]
|
16
|
+
partial := [partial] [identifier]
|
17
|
+
|
18
|
+
*/
|
19
|
+
|
20
|
+
void build_template(mustache_token_t* tokens, mustache_node_t* root, mustache_error_t** error)
|
21
|
+
{
|
22
|
+
root->type = NODE_BLOCK;
|
23
|
+
root->execute = execute_block;
|
24
|
+
root->optimize = optimize_block;
|
25
|
+
append_node(root, build_block(&tokens, error));
|
26
|
+
|
27
|
+
if (tokens) {
|
28
|
+
*error = create_error(tokens, TOKEN_ERROR, tokens->text);
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
mustache_node_t* build_block(mustache_token_t** tokens, mustache_error_t** error)
|
33
|
+
{
|
34
|
+
mustache_node_t* block;
|
35
|
+
mustache_node_t* next;
|
36
|
+
|
37
|
+
block = mustache_create_node();
|
38
|
+
block->type = NODE_BLOCK;
|
39
|
+
block->execute = execute_block;
|
40
|
+
block->optimize = optimize_block;
|
41
|
+
|
42
|
+
while ((next = build_statement(tokens, error))) {
|
43
|
+
append_node(block, next);
|
44
|
+
}
|
45
|
+
|
46
|
+
return block;
|
47
|
+
}
|
48
|
+
|
49
|
+
mustache_node_t* build_statement(mustache_token_t** tokens, mustache_error_t** error)
|
50
|
+
{
|
51
|
+
mustache_node_t* next = build_text(tokens, error);
|
52
|
+
|
53
|
+
if (!next) {
|
54
|
+
next = build_newline(tokens, error);
|
55
|
+
}
|
56
|
+
|
57
|
+
if (!next) {
|
58
|
+
next = build_command(tokens, error);
|
59
|
+
}
|
60
|
+
|
61
|
+
return next;
|
62
|
+
}
|
63
|
+
|
64
|
+
mustache_node_t* build_text(mustache_token_t** tokens, mustache_error_t** error)
|
65
|
+
{
|
66
|
+
mustache_node_t* next;
|
67
|
+
|
68
|
+
if (!is_token(*tokens, TOKEN_TEXT)) {
|
69
|
+
return NULL;
|
70
|
+
}
|
71
|
+
|
72
|
+
next = mustache_create_node();
|
73
|
+
next->type = NODE_TEXT;
|
74
|
+
next->token = pop_token(tokens);
|
75
|
+
next->execute = execute_text;
|
76
|
+
next->optimize = optimize_text;
|
77
|
+
return next;
|
78
|
+
}
|
79
|
+
|
80
|
+
mustache_node_t* build_newline(mustache_token_t** tokens, mustache_error_t** error)
|
81
|
+
{
|
82
|
+
mustache_node_t* next;
|
83
|
+
|
84
|
+
if (!is_token(*tokens, TOKEN_NEWLINE)) {
|
85
|
+
return NULL;
|
86
|
+
}
|
87
|
+
|
88
|
+
next = mustache_create_node();
|
89
|
+
next->type = NODE_NEWLINE;
|
90
|
+
next->token = pop_token(tokens);
|
91
|
+
next->execute = execute_newline;
|
92
|
+
next->optimize = optimize_newline;
|
93
|
+
return next;
|
94
|
+
}
|
95
|
+
|
96
|
+
mustache_node_t* build_command(mustache_token_t** tokens, mustache_error_t** error)
|
97
|
+
{
|
98
|
+
mustache_node_t* next = build_raw_variable(tokens, error);
|
99
|
+
|
100
|
+
if (!next) {
|
101
|
+
next = build_variable(tokens, error);
|
102
|
+
}
|
103
|
+
|
104
|
+
if (!next) {
|
105
|
+
next = build_section(tokens, error);
|
106
|
+
}
|
107
|
+
|
108
|
+
if (!next) {
|
109
|
+
next = build_partial(tokens, error);
|
110
|
+
}
|
111
|
+
|
112
|
+
if (!next) {
|
113
|
+
next = build_comment(tokens, error);
|
114
|
+
}
|
115
|
+
|
116
|
+
return next;
|
117
|
+
}
|
118
|
+
|
119
|
+
mustache_node_t* build_raw_variable(mustache_token_t** tokens, mustache_error_t** error)
|
120
|
+
{
|
121
|
+
mustache_node_t* next;
|
122
|
+
|
123
|
+
if (!is_token(*tokens, TOKEN_RAW)) {
|
124
|
+
return NULL;
|
125
|
+
}
|
126
|
+
|
127
|
+
free_token(pop_token(tokens));
|
128
|
+
|
129
|
+
if (!is_token(*tokens, TOKEN_IDENTIFIER)) {
|
130
|
+
*error = create_error(*tokens, TOKEN_IDENTIFIER, "Expected identifier");
|
131
|
+
}
|
132
|
+
|
133
|
+
next = mustache_create_node();
|
134
|
+
next->type = NODE_RAW_VARIABLE;
|
135
|
+
next->token = pop_token(tokens);
|
136
|
+
next->execute = execute_raw_variable;
|
137
|
+
next->optimize = optimize_variable;
|
138
|
+
return next;
|
139
|
+
}
|
140
|
+
|
141
|
+
mustache_node_t* build_variable(mustache_token_t** tokens, mustache_error_t** error)
|
142
|
+
{
|
143
|
+
mustache_node_t* next;
|
144
|
+
|
145
|
+
if (!is_token(*tokens, TOKEN_IDENTIFIER)) {
|
146
|
+
return NULL;
|
147
|
+
}
|
148
|
+
|
149
|
+
next = mustache_create_node();
|
150
|
+
next->type = NODE_VARIABLE;
|
151
|
+
next->token = pop_token(tokens);
|
152
|
+
next->execute = execute_variable;
|
153
|
+
next->optimize = optimize_variable;
|
154
|
+
return next;
|
155
|
+
}
|
156
|
+
|
157
|
+
mustache_node_t* build_section(mustache_token_t** tokens, mustache_error_t** error)
|
158
|
+
{
|
159
|
+
mustache_node_t* section;
|
160
|
+
mustache_token_t* token;
|
161
|
+
bool is_inverted;
|
162
|
+
|
163
|
+
if (!is_token(*tokens, TOKEN_SECTION_START) &&
|
164
|
+
!is_token(*tokens, TOKEN_ISECTION_START)) {
|
165
|
+
return NULL;
|
166
|
+
}
|
167
|
+
|
168
|
+
token = pop_token(tokens);
|
169
|
+
is_inverted = token->type == TOKEN_ISECTION_START;
|
170
|
+
free_token(token);
|
171
|
+
|
172
|
+
if (!is_token(*tokens, TOKEN_IDENTIFIER)) {
|
173
|
+
*error = create_error(*tokens, TOKEN_IDENTIFIER, "Expected identifier");
|
174
|
+
return NULL;
|
175
|
+
}
|
176
|
+
|
177
|
+
section = mustache_create_node();
|
178
|
+
section->type = is_inverted ? NODE_INVERTED_SECTION : NODE_SECTION;
|
179
|
+
section->token = pop_token(tokens);
|
180
|
+
section->execute = execute_section;
|
181
|
+
section->optimize = optimize_section;
|
182
|
+
append_node(section, build_block(tokens, error));
|
183
|
+
|
184
|
+
if (!is_token(*tokens, TOKEN_SECTION_END)) {
|
185
|
+
*error = create_error(*tokens, TOKEN_SECTION_END, "Expected section end");
|
186
|
+
return NULL;
|
187
|
+
}
|
188
|
+
|
189
|
+
free_token(pop_token(tokens));
|
190
|
+
|
191
|
+
if (!is_token(*tokens, TOKEN_IDENTIFIER)) {
|
192
|
+
*error = create_error(*tokens, TOKEN_IDENTIFIER, "Expected identifier");
|
193
|
+
return NULL;
|
194
|
+
}
|
195
|
+
|
196
|
+
free_token(pop_token(tokens));
|
197
|
+
|
198
|
+
return section;
|
199
|
+
}
|
200
|
+
|
201
|
+
mustache_node_t* build_partial(mustache_token_t** tokens, mustache_error_t** error)
|
202
|
+
{
|
203
|
+
mustache_node_t* node;
|
204
|
+
|
205
|
+
if (!is_token(*tokens, TOKEN_PARTIAL)) {
|
206
|
+
return NULL;
|
207
|
+
}
|
208
|
+
|
209
|
+
free_token(pop_token(tokens));
|
210
|
+
|
211
|
+
if (!is_token(*tokens, TOKEN_IDENTIFIER)) {
|
212
|
+
*error = create_error(*tokens, TOKEN_IDENTIFIER, "Expected identifier");
|
213
|
+
return NULL;
|
214
|
+
}
|
215
|
+
|
216
|
+
node = mustache_create_node();
|
217
|
+
node->type = NODE_PARTIAL;
|
218
|
+
node->token = pop_token(tokens);
|
219
|
+
node->execute = execute_partial;
|
220
|
+
node->optimize = optimize_partial;
|
221
|
+
return node;
|
222
|
+
}
|
223
|
+
|
224
|
+
mustache_node_t* build_comment(mustache_token_t** tokens, mustache_error_t** error)
|
225
|
+
{
|
226
|
+
mustache_node_t* node;
|
227
|
+
|
228
|
+
if (!is_token(*tokens, TOKEN_COMMENT)) {
|
229
|
+
return NULL;
|
230
|
+
}
|
231
|
+
|
232
|
+
node = mustache_create_node();
|
233
|
+
node->type = NODE_COMMENT;
|
234
|
+
node->token = pop_token(tokens);
|
235
|
+
node->execute = execute_comment;
|
236
|
+
node->optimize = optimize_comment;
|
237
|
+
return node;
|
238
|
+
}
|
239
|
+
|
240
|
+
|
241
|
+
/* Helper functions */
|
242
|
+
|
243
|
+
static void append_node(mustache_node_t* parent, mustache_node_t* src)
|
244
|
+
{
|
245
|
+
mustache_node_t* child = parent->first_child;
|
246
|
+
|
247
|
+
if (!child) {
|
248
|
+
parent->first_child = src;
|
249
|
+
}
|
250
|
+
else {
|
251
|
+
while (child->next_sibling) {
|
252
|
+
child = child->next_sibling;
|
253
|
+
}
|
254
|
+
child->next_sibling = src;
|
255
|
+
src->prv_sibling = child;
|
256
|
+
}
|
257
|
+
|
258
|
+
src->parent = parent;
|
259
|
+
}
|
260
|
+
|
261
|
+
static mustache_error_t* create_error(mustache_token_t* last_token, enum mustache_token_type expected_token, char* error_text)
|
262
|
+
{
|
263
|
+
mustache_error_t* error = mustache_malloc(sizeof(mustache_error_t));
|
264
|
+
error->last_token = last_token;
|
265
|
+
error->error_text = error_text;
|
266
|
+
return error;
|
267
|
+
}
|
data/ext/stash/execute.c
ADDED
@@ -0,0 +1,248 @@
|
|
1
|
+
#include "intern.h"
|
2
|
+
|
3
|
+
/* forward declare helper functions */
|
4
|
+
static bool html_encode(char* non_terminated_input, size_t length, mustache_context_t* ctx);
|
5
|
+
static mustache_value_t* get_variable_value(char* key, mustache_context_t* ctx);
|
6
|
+
static mustache_value_t* get_value(char* key, mustache_context_t* ctx);
|
7
|
+
bool is_whitespace(mustache_node_t* text_node);
|
8
|
+
|
9
|
+
bool execute_block(mustache_node_t* node, mustache_context_t* ctx)
|
10
|
+
{
|
11
|
+
mustache_node_t* child = node->first_child;
|
12
|
+
|
13
|
+
while (child) {
|
14
|
+
if (!child->execute(child, ctx)) {
|
15
|
+
return false;
|
16
|
+
}
|
17
|
+
child = child->next_sibling;
|
18
|
+
}
|
19
|
+
return true;
|
20
|
+
}
|
21
|
+
|
22
|
+
bool execute_text(mustache_node_t* node, mustache_context_t* ctx)
|
23
|
+
{
|
24
|
+
return mustache_write_to_buffer(ctx, node->token->text, strlen(node->token->text));
|
25
|
+
}
|
26
|
+
|
27
|
+
bool execute_newline(mustache_node_t* node, mustache_context_t* ctx)
|
28
|
+
{
|
29
|
+
if (!mustache_write_to_buffer(ctx, node->token->text, strlen(node->token->text))) {
|
30
|
+
return false;
|
31
|
+
}
|
32
|
+
|
33
|
+
if (*ctx->prefix && node->next_sibling) {
|
34
|
+
if (!mustache_write_to_buffer(ctx, ctx->prefix, strlen(ctx->prefix))) {
|
35
|
+
return false;
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
return true;
|
40
|
+
}
|
41
|
+
|
42
|
+
bool execute_raw_variable(mustache_node_t* node, mustache_context_t* ctx)
|
43
|
+
{
|
44
|
+
mustache_value_t* value = get_variable_value(node->token->text, ctx);
|
45
|
+
if (value) {
|
46
|
+
if (value->type == VALUE_VALUE) {
|
47
|
+
if (!mustache_write_to_buffer(ctx, value->data.value, value->length)) {
|
48
|
+
value->destroy(value);
|
49
|
+
return false;
|
50
|
+
}
|
51
|
+
}
|
52
|
+
value->destroy(value);
|
53
|
+
}
|
54
|
+
|
55
|
+
return true;
|
56
|
+
}
|
57
|
+
|
58
|
+
bool execute_variable(mustache_node_t* node, mustache_context_t* ctx)
|
59
|
+
{
|
60
|
+
mustache_value_t* value = get_variable_value(node->token->text, ctx);
|
61
|
+
if (value) {
|
62
|
+
if (value->type == VALUE_VALUE) {
|
63
|
+
if (!html_encode(value->data.value, value->length, ctx)) {
|
64
|
+
printf("html_encode failed\n");
|
65
|
+
value->destroy(value);
|
66
|
+
return false;
|
67
|
+
}
|
68
|
+
}
|
69
|
+
value->destroy(value);
|
70
|
+
}
|
71
|
+
|
72
|
+
return true;
|
73
|
+
}
|
74
|
+
size_t foo = 0;
|
75
|
+
|
76
|
+
bool execute_section(mustache_node_t* node, mustache_context_t* ctx)
|
77
|
+
{
|
78
|
+
mustache_value_t* value = get_variable_value(node->token->text, ctx);
|
79
|
+
mustache_context_t** list_contexts = NULL;
|
80
|
+
mustache_context_t* list_context = NULL;
|
81
|
+
bool response = true;
|
82
|
+
|
83
|
+
bool is_true;
|
84
|
+
|
85
|
+
// If the variable doesn't exist, don't execute the template
|
86
|
+
if (!value && node->type == NODE_SECTION) {
|
87
|
+
return true;
|
88
|
+
} else if (!value && node->type == NODE_INVERTED_SECTION) {
|
89
|
+
return node->first_child->execute(node->first_child, ctx);
|
90
|
+
}
|
91
|
+
|
92
|
+
switch (value->type) {
|
93
|
+
case VALUE_LIST:
|
94
|
+
list_contexts = value->data.list;
|
95
|
+
|
96
|
+
if (node->type == NODE_INVERTED_SECTION) {
|
97
|
+
if (value->length) {
|
98
|
+
return true;
|
99
|
+
} else {
|
100
|
+
return node->first_child->execute(node->first_child, ctx);
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
for (size_t i = 0; i < value->length; i++) {
|
105
|
+
list_context = list_contexts[i];
|
106
|
+
list_context->parent = ctx;
|
107
|
+
list_context->partials = ctx->partials;
|
108
|
+
list_context->get_partial = ctx->get_partial;
|
109
|
+
response = node->first_child->execute(node->first_child, list_context);
|
110
|
+
|
111
|
+
if (!response) {
|
112
|
+
break;
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
break;
|
117
|
+
case VALUE_OBJECT:
|
118
|
+
is_true = value->data.object != NULL;
|
119
|
+
if (is_true == (node->type == NODE_SECTION)) {
|
120
|
+
value->data.object->parent = ctx;
|
121
|
+
response = node->first_child->execute(node->first_child, value->data.object);
|
122
|
+
value->data.object->parent = NULL;
|
123
|
+
}
|
124
|
+
break;
|
125
|
+
case VALUE_VALUE:
|
126
|
+
is_true = value->length > 0;
|
127
|
+
if (is_true == (node->type == NODE_SECTION)) {
|
128
|
+
response = node->first_child->execute(node->first_child, ctx);
|
129
|
+
} else {
|
130
|
+
response = true;
|
131
|
+
}
|
132
|
+
break;
|
133
|
+
}
|
134
|
+
|
135
|
+
value->destroy(value);
|
136
|
+
|
137
|
+
if (!response) {
|
138
|
+
// TODO: truncate
|
139
|
+
}
|
140
|
+
return true;
|
141
|
+
}
|
142
|
+
|
143
|
+
bool execute_partial(mustache_node_t* node, mustache_context_t* ctx)
|
144
|
+
{
|
145
|
+
mustache_node_t* partial = ctx->get_partial(node->token->text, ctx->partials);
|
146
|
+
|
147
|
+
// add indentation
|
148
|
+
char* indentation = NULL;
|
149
|
+
|
150
|
+
if (node->prv_sibling &&
|
151
|
+
node->prv_sibling->type == NODE_TEXT &&
|
152
|
+
is_whitespace(node->prv_sibling) &&
|
153
|
+
(!node->prv_sibling->prv_sibling || node->prv_sibling->prv_sibling->type == NODE_NEWLINE)) {
|
154
|
+
|
155
|
+
// Save existing indentation, it will be replaced after partial execution
|
156
|
+
indentation = ctx->prefix;
|
157
|
+
size_t current_length = *ctx->prefix ? strlen(ctx->prefix) : 0;
|
158
|
+
size_t sibling_length = strlen(node->prv_sibling->token->text);
|
159
|
+
|
160
|
+
ctx->prefix = malloc(sizeof(char) * (current_length + sibling_length + 1));
|
161
|
+
|
162
|
+
if (current_length) {
|
163
|
+
strncpy(ctx->prefix, ctx->prefix, current_length);
|
164
|
+
}
|
165
|
+
strncpy(ctx->prefix + current_length, node->prv_sibling->token->text, sibling_length);
|
166
|
+
}
|
167
|
+
|
168
|
+
bool ret_val = true;
|
169
|
+
if (partial) {
|
170
|
+
ret_val = partial->execute(partial, ctx);
|
171
|
+
}
|
172
|
+
|
173
|
+
// restore indentation
|
174
|
+
if (indentation) {
|
175
|
+
free(ctx->prefix);
|
176
|
+
ctx->prefix = indentation;
|
177
|
+
}
|
178
|
+
|
179
|
+
return ret_val;
|
180
|
+
}
|
181
|
+
|
182
|
+
bool execute_comment(mustache_node_t* node, mustache_context_t* ctx)
|
183
|
+
{
|
184
|
+
return true;
|
185
|
+
}
|
186
|
+
|
187
|
+
/* helper functions */
|
188
|
+
|
189
|
+
static bool html_encode(
|
190
|
+
char* non_terminated_input,
|
191
|
+
size_t length,
|
192
|
+
mustache_context_t* ctx)
|
193
|
+
{
|
194
|
+
char current;
|
195
|
+
char* replacement;
|
196
|
+
size_t replacement_length;
|
197
|
+
|
198
|
+
for (int i = 0; i < length; i++) {
|
199
|
+
current = non_terminated_input[i];
|
200
|
+
|
201
|
+
switch (current) {
|
202
|
+
case '&': replacement = "&"; replacement_length = 5; break;
|
203
|
+
case '"': replacement = """; replacement_length = 6; break;
|
204
|
+
case '<': replacement = "<"; replacement_length = 4; break;
|
205
|
+
case '>': replacement = ">"; replacement_length = 4; break;
|
206
|
+
default:
|
207
|
+
replacement = ¤t;
|
208
|
+
replacement_length = 1;
|
209
|
+
break;
|
210
|
+
}
|
211
|
+
|
212
|
+
if (!mustache_write_to_buffer(ctx, replacement, replacement_length)) {
|
213
|
+
return false;
|
214
|
+
}
|
215
|
+
}
|
216
|
+
|
217
|
+
return true;
|
218
|
+
}
|
219
|
+
|
220
|
+
static mustache_value_t* get_variable_value(char* key, mustache_context_t* ctx)
|
221
|
+
{
|
222
|
+
// special case for implicit variables
|
223
|
+
if (key[0] == '.') {
|
224
|
+
return ctx->get_data(key, ctx->data, ctx, ctx);
|
225
|
+
}
|
226
|
+
|
227
|
+
char* context_identifier = strtok(key, ".");
|
228
|
+
mustache_value_t* result = NULL;
|
229
|
+
while (context_identifier) {
|
230
|
+
result = get_value(context_identifier, ctx);
|
231
|
+
if (result && result->type == VALUE_OBJECT) {
|
232
|
+
ctx = result->data.object;
|
233
|
+
}
|
234
|
+
context_identifier = strtok(NULL, ".");
|
235
|
+
}
|
236
|
+
return result;
|
237
|
+
}
|
238
|
+
|
239
|
+
static mustache_value_t* get_value(char* key, mustache_context_t* ctx)
|
240
|
+
{
|
241
|
+
mustache_value_t* value = ctx->get_data(key, ctx->data, ctx, ctx);
|
242
|
+
mustache_context_t* current_context = ctx;
|
243
|
+
while (!value && current_context->parent) {
|
244
|
+
current_context = current_context->parent;
|
245
|
+
value = current_context->get_data(key, current_context->data, current_context, ctx);
|
246
|
+
}
|
247
|
+
return value;
|
248
|
+
}
|