/* Copyright 2005-2006 Garrett Rooney. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ %include { #include #include #include "parser.h" #include "common.h" #include "lexer.h" static list_entry_t * get_entry(etl_template_parser_state_t *state) { list_entry_t *le = state->spare; if (le) { state->spare = le->next; le->next = NULL; } else { le = calloc(1, sizeof(*le)); } return le; } static etl_expression_t * build_expression_node(etl_expression_op_t op, etl_expression_t *left, etl_expression_t *right) { etl_expression_t *e = calloc(1, sizeof(*e)); e->type = ETL_EXPRESSION_NODE; e->contents.n = calloc(1, sizeof(*e->contents.n)); e->contents.n->op = op; e->contents.n->left = left; e->contents.n->right = right; return e; } } %name etl_template_parser_parse %token_prefix ETL_TMPL_TOK_ %token_type { etl_token_t * } %extra_argument { etl_template_parser_state_t *state } %type node { etl_template_node_t * } %type node_list { etl_template_node_t * } %type if { etl_if_contents_t * } %type unless { etl_if_contents_t * } %type for { etl_for_contents_t * } %type macro { etl_macro_contents_t * } %type template { etl_template_t * } %type expression { etl_expression_t * } %type args { etl_array_t * } %type expression_list { etl_array_t * } %type expressions { list_entry_t * } %type expressions_elt { list_entry_t * } %type var_list { list_entry_t * } %nonassoc EQ NEQ GT GTEQ LT LTEQ. %left PLUS MINUS. %left TIMES DIV MOD. %syntax_error { if (! state->err) { state->err = etl_error_createf(ETL_EINVAL, "syntax error near line %d", TOKEN->line); } } template ::= node_list(E). { if (! state->err) { state->tmpl = calloc(1, sizeof(*state->tmpl)); state->tmpl->tree = E; } } node_list(R) ::= node_list(L) node(E). { etl_template_node_t *itr = L; while (itr->next != NULL) itr = itr->next; itr->next = E; R = L; } node_list(L) ::= node(E). { L = E; } node(E) ::= TEXT(T). { E = calloc(1, sizeof(*E)); E->type = ETL_NODE_LITERAL; E->contents.l.data = strdup(T->data); E->next = NULL; } expression(E) ::= VAR(V). { E = calloc(1, sizeof(*E)); E->type = ETL_EXPRESSION_VAR; E->contents.v = strdup(V->data); } expression(E) ::= INTEGER(I). { E = calloc(1, sizeof(*E)); E->type = ETL_EXPRESSION_LITERAL; E->contents.lit = etl_variable_make_int(atoi(I->data)); } expression(E) ::= STRING(S). { E = calloc(1, sizeof(*E)); E->type = ETL_EXPRESSION_LITERAL; E->contents.lit = etl_variable_make_str(S->data); } expression(E) ::= expression(L) EQ expression(R). { E = build_expression_node(ETL_EXPRESSION_OP_EQ, L, R); } expression(E) ::= expression(L) NEQ expression(R). { E = build_expression_node(ETL_EXPRESSION_OP_NEQ, L, R); } expression(E) ::= expression(L) LT expression(R). { E = build_expression_node(ETL_EXPRESSION_OP_LT, L, R); } expression(E) ::= expression(L) GT expression(R). { E = build_expression_node(ETL_EXPRESSION_OP_GT, L, R); } expression(E) ::= expression(L) LTEQ expression(R). { E = build_expression_node(ETL_EXPRESSION_OP_LTEQ, L, R); } expression(E) ::= expression(L) GTEQ expression(R). { E = build_expression_node(ETL_EXPRESSION_OP_GTEQ, L, R); } expression(E) ::= expression(E1) DOT VAR(V). { E = calloc(1, sizeof(*E)); E->type = ETL_EXPRESSION_INDEX; E->contents.idx = calloc(1, sizeof(*E->contents.idx)); E->contents.idx->first = E1; E->contents.idx->second = calloc(1, sizeof(*E->contents.idx->second)); E->contents.idx->second->type = ETL_EXPRESSION_LITERAL; E->contents.idx->second->contents.lit = etl_variable_make_str(V->data); } expression(E) ::= expression(L) MOD expression(R). { E = build_expression_node(ETL_EXPRESSION_OP_MOD, L, R); } expression(E) ::= expression(L) PLUS expression(R). { E = build_expression_node(ETL_EXPRESSION_OP_PLUS, L, R); } expression(E) ::= expression(L) MINUS expression(R). { E = build_expression_node(ETL_EXPRESSION_OP_MINUS, L, R); } expression(E) ::= expression(L) TIMES expression(R). { E = build_expression_node(ETL_EXPRESSION_OP_TIMES, L, R); } expression(E) ::= expression(L) DIV expression(R). { E = build_expression_node(ETL_EXPRESSION_OP_DIV, L, R); } expression(E) ::= expression(E1) OPEN_BRACKET expression(E2) CLOSE_BRACKET. { E = calloc(1, sizeof(*E)); E->type = ETL_EXPRESSION_INDEX; E->contents.idx = calloc(1, sizeof(*E->contents.idx)); E->contents.idx->first = E1; E->contents.idx->second = E2; } expression(E) ::= expression(E1) DOT VAR(M) expression_list(A). { E = calloc(1, sizeof(*E)); E->type = ETL_EXPRESSION_METHOD; E->contents.method.invocant = E1; E->contents.method.name = strdup(M->data); E->contents.method.args = A; } expressions_elt(EE) ::= expression(E). { EE = get_entry(state); EE->data = E; } expressions(S1) ::= expressions_elt(E) COMMA expressions(S). { E->next = S; S1 = E; } expressions(S) ::= expressions_elt(E). { S = E; } expression_list(E) ::= OPEN_PAREN expressions(S) CLOSE_PAREN. { list_entry_t *itr = S; E = etl_array_create(5); while (itr) { list_entry_t *spare; etl_array_push(E, itr->data); spare = itr; itr = itr->next; spare->next = state->spare; state->spare = spare; } } expression_list(E) ::= OPEN_PAREN CLOSE_PAREN. { E = etl_array_create(0); } if(I) ::= START_DIR IF expression(V) END_DIR. { I = calloc(1, sizeof(*I)); I->var = V; } unless(U) ::= START_DIR UNLESS expression(V) END_DIR. { U = calloc(1, sizeof(*U)); U->var = V; } node(E) ::= if(I) node_list(Y) end. { E = calloc(1, sizeof(*E)); E->type = ETL_NODE_IF; I->yes_task = Y; I->no_task = NULL; E->contents.i = I; E->next = NULL; } node(E) ::= unless(U) node_list(Y) end. { E = calloc(1, sizeof(*E)); E->type = ETL_NODE_UNLESS; U->yes_task = Y; U->no_task = NULL; E->contents.i = U; E->next = NULL; } else ::= START_DIR ELSE END_DIR. node(E) ::= if(I) node_list(Y) else node_list(N) end. { E = calloc(1, sizeof(*E)); E->type = ETL_NODE_IF; I->yes_task = Y; I->no_task = N; E->contents.i = I; E->next = NULL; } node(E) ::= unless(U) node_list(Y) else node_list(N) end. { E = calloc(1, sizeof(*E)); E->type = ETL_NODE_UNLESS; U->yes_task = Y; U->no_task = N; E->contents.i = U; E->next = NULL; } for(F) ::= START_DIR FOR VAR(V) IN expression(A) END_DIR. { F = calloc(1, sizeof(*F)); F->var = strdup(V->data); F->type = ETL_FOR_VAR; F->over.arr = A; } for(F) ::= START_DIR FOR VAR(V1) COMMA VAR(V2) IN expression(A) END_DIR. { F = calloc(1, sizeof(*F)); F->var = strdup(V1->data); F->var2 = strdup(V2->data); F->type = ETL_FOR_VAR; F->over.arr = A; } for(F) ::= START_DIR FOR VAR(V) IN expression(A) TO expression(B) END_DIR. { F = calloc(1, sizeof(*F)); F->var = strdup(V->data); F->type = ETL_FOR_RANGE; F->over.range.from = A; F->over.range.to = B; } var_list(VL) ::= VAR(V) COMMA var_list(VL2). { VL = get_entry(state); VL->data = V; VL->next = VL2; } var_list(VL) ::= VAR(V). { VL = get_entry(state); VL->data = V; } args(A) ::= OPEN_PAREN var_list(VL) CLOSE_PAREN. { list_entry_t *itr = VL; A = etl_array_create(0); while (itr) { list_entry_t *spare; etl_array_push(A, strdup(((etl_token_t *) itr->data)->data)); spare = itr; itr = itr->next; spare->next = state->spare; state->spare = spare; } } args(A) ::= OPEN_PAREN CLOSE_PAREN. { A = etl_array_create(0); } macro(M) ::= START_DIR MACRO VAR(V) args(A) END_DIR. { M = calloc(1, sizeof(*M)); M->name = strdup(V->data); M->arg_names = A; M->refcount = 1; } end ::= START_DIR END END_DIR. node(E) ::= macro(M) node_list(B) end. { E = calloc(1, sizeof(*E)); E->type = ETL_NODE_DEFMACRO; M->body = B; E->contents.macro = M; } node(E) ::= START_DIR VAR(V) expression_list(EL) END_DIR. { E = calloc(1, sizeof(*E)); E->type = ETL_NODE_MACRO; E->contents.macro = calloc(1, sizeof(*E->contents.macro)); E->contents.macro->refcount = 1; E->contents.macro->name = strdup(V->data); E->contents.macro->arg_values = EL; } node(E) ::= for(F) node_list(X) end. { E = calloc(1, sizeof(*E)); E->type = ETL_NODE_FOR; F->task = X; E->contents.f = F; E->next = NULL; } node(E) ::= START_DIR PRINT expression(V) END_DIR. { E = calloc(1, sizeof(*E)); E->type = ETL_NODE_PRINT; E->contents.p.var = V; E->next = NULL; } node(E) ::= START_DIR PRINT expression(V) PIPE VAR(F) END_DIR. { E = calloc(1, sizeof(*E)); E->type = ETL_NODE_FILTER; E->contents.flt = calloc(1, sizeof(*E->contents.flt)); E->contents.flt->var = V; if (strcmp(F->data, "html") == 0) E->contents.flt->filter = etl_template_filter_html; else if (strcmp(F->data, "xml") == 0) E->contents.flt->filter = etl_template_filter_xml; else if (strcmp(F->data, "url") == 0) E->contents.flt->filter = etl_template_filter_url; else if (! state->err) state->err = etl_error_createf(ETL_EINVAL, "Unknown filter '%s'", F->data); E->next = NULL; } node(E) ::= START_DIR INCLUDE expression(E1) END_DIR. { E = calloc(1, sizeof(*E)); E->type = ETL_NODE_INCLUDE; E->contents.inc.file = E1; }