int check_do_while(is_do_while* node, is_label* label) { int errors = 0; int mylabel = ++label_counter; /* setting label for use with loops and break/continue */ if (label) node->scope = scope_new(symbol_new_loop(label->name, node->line, mylabel), false); else node->scope = scope_new(symbol_new_loop(NULL, node->line, mylabel), false); scope_push(node->scope); errors += check_stmt(node->body); if (errors == 0) node->terminates = node->body->terminates; errors += check_expr(node->cond); if (errors == 0) { if (!type_native_assign_able(t_type_native_bool, node->cond->s_type)) { errors++; pretty_error(node->line, "invalid do..while condition (must be boolean)"); } } scope_pop(); return errors; }
int check_while(is_while* node, is_label* label) { int errors = 0; char *string; int mylabel = ++label_counter; /* setting label for use with loops and break/continue */ if (label) node->scope = scope_new(symbol_new_loop(label->name, node->line, mylabel), false); else node->scope = scope_new(symbol_new_loop(NULL, node->line, mylabel), false); scope_push(node->scope); errors += check_expr(node->cond); if (errors == 0) { if (!type_native_assign_able(t_type_native_bool, node->cond->s_type)) { errors++; pretty_error(node->line, "invalid while condition: expected boolean, got %s", string = string_type_decl(node->cond->s_type)); free(string); } } errors += check_stmt(node->body); node->terminates = node->body->terminates; scope_pop(); return errors; }
void test_search_on_parent_scope(CuTest *tc) { /* parent */ scope_new(); scope_sym_table_insert("a", "A", NULL); /* new scope */ scope_new(); CuAssertIntEquals(tc, 2, scope_numbers()); CuAssertPtrNotNull(tc, scope_search_identifier("a")); scope_reset(); }
void test_insert_and_retrieve_entry_to_scope(CuTest *tc) { scope_new(); scope_sym_table_insert("a", "A", NULL); CuAssertPtrNotNull(tc, scope_search_identifier("a")); scope_reset(); }
static void _define_builtin_function(Scope *builtin_scope, Type rtype, char *name, builtin_function fn, int nparams, ...) { Symbol *fn_symbol = symbol_new(S_BUILTIN, name); fn_symbol->eval_type = rtype; fn_symbol->fn = fn; scope_define(builtin_scope, fn_symbol); fn_symbol->args = scope_new(NULL); va_list params; va_start(params, nparams); for(int i = 0; i < nparams; i++) { Type t = va_arg(params, Type); /* the n-th parameter is called "n" */ char buf[32]; snprintf(buf, 32, "%d", i); Symbol *sy = symbol_new(S_VARIABLE, buf); sy->eval_type = t; scope_define(fn_symbol->args, sy); } va_end(params); }
void test_create_scope(CuTest *tc) { CuAssertIntEquals(tc, 0, scope_numbers()); scope_new(); CuAssertIntEquals(tc, 1, scope_numbers()); scope_reset(); }
void test_reset_should_have_no_scope(CuTest *tc) { scope_new(); CuAssertIntEquals(tc, 1, scope_numbers()); scope_reset(); CuAssertIntEquals(tc, 0, scope_numbers()); scope_reset(); }
void test_do_not_find_entry_scope(CuTest *tc) { scope_new(); CuAssertIntEquals(tc, 1, scope_numbers()); CuAssertFalse(tc, is_identifier_declared("a")); CuAssertPtrNull(tc, scope_search_identifier("a")); scope_reset(); }
int check_for(is_for* node, is_label* label) { int errors = 0, cond_errors; char* typeA; int mylabel = ++label_counter; /* setting label for use with loops and break/continue */ if (label) node->scope = scope_new(symbol_new_loop(label->name, node->line, mylabel), false); else node->scope = scope_new(symbol_new_loop(NULL, node->line, mylabel), false); scope_push(node->scope); if (node->init) errors += check_for_init(node->init); if (node->cond) { cond_errors = check_for_cond(node->cond); if (cond_errors == 0) { if (!type_native_assign_able(t_type_native_bool, node->cond->s_type)) { cond_errors++; pretty_error(node->line, "for conditional is not boolean (is of type %s)", typeA = string_type_decl(node->cond->s_type)); free(typeA); } } errors += cond_errors; } if (node->inc) errors += check_for_inc(node->inc); errors += check_stmt(node->body); if (errors == 0) node->terminates = (node->body ? node->body->terminates : true); scope_pop(); return errors; }
int check_switch(is_switch* node) { int errors = 0; int mylabel = ++label_counter; /* setting label for use with break */ char *type; if (node->label) errors += check_label(node->label); errors += check_expr(node->expr); if (errors == 0) { if (node->expr->s_type->type == t_type_decl_array_decl) { errors++; type = string_type_decl(node->expr->s_type); pretty_error(node->line, "switch statement expression must be of object type (got %s)", type); free(type); } } if (errors == 0) { if (node->label) node->scope = scope_new(symbol_new_switch(node->label->name, node->line, mylabel, node->expr->s_type->data.type_object, symtab->framepos++), false); else node->scope = scope_new(symbol_new_switch(NULL, node->line, mylabel, node->expr->s_type->data.type_object, symtab->framepos++), false); scope_push(node->scope); check_switch_stmt_list(node->list, node); if (errors == 0) node->terminates = (node->list ? node->list->terminates : true); scope_pop(); } return errors; }
/* json = (_ value)+ _ _ = [ \t\n\r\b\f]* */ OOP json_grammar_new() { OOP scope = scope_new(o_empty_scope); OOP g_ws = star_pattern_new( if_pattern_new(charset_p_new(" \t\n\r\b\f"))); object_call(scope, s_bind, s_ws, g_ws); OOP g_value = value_grammar_new(scope); OOP g_json = and_pattern_new( plus_pattern_new( and_pattern_new( g_ws, g_value)), g_ws); object_call(scope, s_bind, s_json, g_json); return g_json; }
int generate_for_item(Pool *pool, FILE *out, AstFor *p, ListNode *item, int *need_comma) { Scope *scope = scope_new(pool, p->scope); ListBuilder code = list_builder_init(pool); if (dynamic_ok(p->filter) && !test_filter(p->filter, ast_to_outline_item(item->d))) return 1; if (p->list && *need_comma) CHECK(file_putc(out, ',')); *need_comma = 1; scope_add(scope, pool, p->item, item->d); CHECK(parse_code(pool, &p->code, scope, out_list_builder(&code))); CHECK(generate_code(pool, out, code.first)); return 1; }
struct parser_state *cli_js_init(void) { struct parser_state *state = cli_calloc(1, sizeof(*state)); if(!state) return NULL; if(!scope_new(state)) { free(state); return NULL; } state->global = state->current; if(yylex_init(&state->scanner)) { scope_done(state->global); free(state); return NULL; } cli_dbgmsg(MODULE "cli_js_init() done\n"); return state; }
int generate_macro_call(Pool *pool, FILE *out, AstMacroCall *p) { ListNode *call_input; ListNode *macro_input; Scope *scope = scope_new(pool, p->macro->scope); ListBuilder code = list_builder_init(pool); /* Assign values to all inputs: */ macro_input = p->macro->inputs; call_input = p->inputs; while (macro_input && call_input) { AstCodeText *name = macro_input->d.p; scope_add(scope, pool, name->code, call_input->d); macro_input = macro_input->next; call_input = call_input->next; } CHECK(parse_code(pool, &p->macro->code, scope, out_list_builder(&code))); CHECK(generate_code(pool, out, code.first)); return 1; }
int check_class_def(is_class_def* node) { int errors = 0; SYMBOL* symbol; symbol = scope_lookup(symtab, node->id->name, t_symbol_class); if (symbol) { errors++; pretty_error(node->line, "class \"%s\" is already defined (previous declaration was here: %d)", node->id->name, symbol->line); } else scope_insert(symtab, symbol = symbol_new_class(node->id->name, node->line)); node->scope = scope_new(symbol, true); /* FIXME: this should be in application and not in class def, because theoretically class should be able to access other classes */ scope_push(node->scope); errors += check_class_stmt_list(node->body, true); if (errors == 0) { symbol = scope_lookup(symtab, "main", t_symbol_func); if (!symbol) { errors++; pretty_error(node->line, "missing main entry point"); } } errors += check_class_stmt_list(node->body, false); scope_pop(); return errors; }
int check_func_def(is_func_def* node, bool first_pass) { SYMBOL* symbol; SCOPE* tempscope; int label; int errors = 0; is_type_decl *tmpType = NULL; if (first_pass) { errors += check_type_decl(node->type); symbol = scope_lookup(symtab, node->id->name, t_symbol_func); if (symbol) { pretty_error(node->line, "symbol \"%s\" is already defined (previous declaration was here: %d)", node->id->name, symbol->line); errors++; } else { tempscope = scope_new(NULL, false); scope_push(tempscope); errors += check_func_def_args(node->args); scope_pop(); if (strcmp(node->id->name, "main") == 0) { label = 0; if (node->args) /* no args is valid */ { if (node->args->length == 1) /* main may must not have more than two arguments */ { tmpType = insert_type_decl_array(insert_array_decl(insert_type_object(t_type_native_string), new_dims_empty_list(0, 1))); /* must be an array of Strings */ if (!type_type_equal(tmpType,node->args->node->type)) { errors++; pretty_error(node->line, "main function arguments do not match any valid prototypes"); } free_type_decl(tmpType); } else { errors++; pretty_error(node->line, "main function arguments do not match any valid prototypes"); } } tmpType = insert_type_decl_object(insert_type_object(t_type_native_int)); /* return value must be int or void */ if (!type_type_equal(tmpType,node->type)) { free_type_decl(tmpType); tmpType = new_type_decl_void(0); /* check for void */ if (!type_type_equal(tmpType,node->type)) /* none; errors occurred */ { errors++; pretty_error(node->line, "main function return type does not match any valid types (nor int nor void)"); } } free_type_decl(tmpType); } else label = ++label_counter; symbol = symbol_new_func(node->id->name, node->line, node->type, node->args, label); scope_delete(tempscope); scope_insert(symtab, symbol); node->scope = scope_new(symbol, false); } } else { scope_push(node->scope); errors += check_func_def_args(node->args); /* this will not give errors */ errors += check_stmt_list(node->body); if ((!node->body || !node->body->terminated) && !(node->type->type == t_type_decl_type_object && node->type->data.type_object->type == t_type_native_void)) { pretty_error(node->line, "reached end of non void function"); errors++; } scope_pop(); } return errors; }
/** * Program entry point. Constructs and launches the main program object. */ int main(int argc, char *argv[]) { Pool pool = pool_init(0x10000); /* 64K block size */ Options opt = options_init(); Source *in; Scope *scope = scope_new(&pool, 0); ListBuilder code = list_builder_init(&pool); /* Read the options: */ if (!options_parse(&opt, argc, argv)) { options_usage(argv[0]); goto error; } /* Determine output file name: */ if (!string_size(opt.name_out)) { if (string_rmatch(opt.name_in, string_from_k(".ol")) != 3) { fprintf(stderr, "error: If no output file is specified, the input file name must end with \".ol\".\n"); goto error; } opt.name_out = string(opt.name_in.p, opt.name_in.end - 3); } /* Input stream: */ in = source_load(&pool, opt.name_in); if (!in) { fprintf(stderr, "error: Could not open source file \"%s\"\n", opt.name_in.p); goto error; } /* Keywords: */ scope_add(scope, &pool, string_from_k("macro"), dynamic(type_keyword, keyword_new(&pool, parse_macro))); scope_add(scope, &pool, string_from_k("outline"), dynamic(type_keyword, keyword_new(&pool, parse_outline))); scope_add(scope, &pool, string_from_k("union"), dynamic(type_keyword, keyword_new(&pool, parse_union))); scope_add(scope, &pool, string_from_k("map"), dynamic(type_keyword, keyword_new(&pool, parse_map))); scope_add(scope, &pool, string_from_k("for"), dynamic(type_keyword, keyword_new(&pool, parse_for))); scope_add(scope, &pool, string_from_k("include"), dynamic(type_keyword, keyword_new(&pool, parse_include))); /* Do outline2c stuff: */ if (!parse_code(&pool, in, scope, out_list_builder(&code))) goto error; if (opt.debug) { printf("--- AST: ---\n"); dump_code(code.first, 0); printf("\n"); } if (!main_generate(&pool, code.first, &opt)) goto error; /* Clean up: */ pool_free(&pool); return 0; error: pool_free(&pool); return 1; }
/* buffer is html-normlike "chunk", if original file is bigger than buffer, * we rewind to a space, so we'll know that tokens won't be broken in half at * the end of a buffer. All tokens except string-literals of course. * So we can assume that after the buffer there is either a space, EOF, or a * chunk of text not containing whitespace at all (for which we care only if its * a stringliteral)*/ void cli_js_process_buffer(struct parser_state *state, const char *buf, size_t n) { struct scope* current = state->current; YYSTYPE val; int yv; YY_BUFFER_STATE yyb; if(!state->global) { /* this state has either not been initialized, * or cli_js_parse_done() was already called on it */ cli_warnmsg(MODULE "invalid state\n"); return; } yyb = yy_scan_bytes(buf, n, state->scanner); memset(&val, 0, sizeof(val)); val.vtype = vtype_undefined; /* on EOF yylex will return 0 */ while( (yv=yylex(&val, state->scanner)) != 0) { const char *text; size_t leng; val.type = yv; switch(yv) { case TOK_VAR: current->fsm_state = InsideVar; break; case TOK_IDENTIFIER_NAME: text = yyget_text(state->scanner); leng = yyget_leng(state->scanner); if(current->last_token == TOK_DOT) { /* this is a member name, don't normalize */ TOKEN_SET(&val, string, cli_strdup(text)); val.type = TOK_UNNORM_IDENTIFIER; } else { switch(current->fsm_state) { case WaitParameterList: state->syntax_errors++; /* fall through */ case Base: case InsideInitializer: TOKEN_SET(&val, cstring, scope_use(current, text, leng)); break; case InsideVar: case InsideFunctionDecl: TOKEN_SET(&val, cstring, scope_declare(current, text, leng, state)); current->fsm_state = InsideInitializer; current->brackets = 0; break; case WaitFunctionName: TOKEN_SET(&val, cstring, scope_declare(current, text, leng, state)); current->fsm_state = WaitParameterList; break; } } break; case TOK_PAR_OPEN: switch(current->fsm_state) { case WaitFunctionName: /* fallthrough */ case WaitParameterList: current->fsm_state = InsideFunctionDecl; break; default: /* noop */ break; } break; case TOK_PAR_CLOSE: switch(current->fsm_state) { case WaitFunctionName: state->syntax_errors++; break; case WaitParameterList: current->fsm_state = Base; break; default: /* noop */ break; } break; case TOK_CURLY_BRACE_OPEN: switch(current->fsm_state) { case WaitFunctionName: /* fallthrough */ case WaitParameterList: case InsideFunctionDecl: /* in a syntactically correct * file, we would already be in * the Base state when we see a { */ current->fsm_state = Base; /* fall-through */ case InsideVar: case InsideInitializer: state->syntax_errors++; /* fall-through */ case Base: default: current->blocks++; break; } break; case TOK_CURLY_BRACE_CLOSE: if(current->blocks > 0) current->blocks--; else state->syntax_errors++; if(!current->blocks) { if(current->parent) { /* add dummy FUNCTION token to * mark function end */ TOKEN_SET(&val, cstring, "}"); add_token(state, &val); TOKEN_SET(&val, scope, NULL); val.type = TOK_FUNCTION; state->current = current = current->parent; } else{ /* extra } */ state->syntax_errors++; } } break; case TOK_BRACKET_OPEN: current->brackets++; break; case TOK_BRACKET_CLOSE: if(current->brackets > 0) current->brackets--; else state->syntax_errors++; break; case TOK_COMMA: if (current->fsm_state == InsideInitializer && current->brackets == 0 && current->blocks == 0) { /* initializer ended only if we * encountered a comma, and [] are * balanced. * This avoids switching state on: * var x = [4,y,u];*/ current->fsm_state = InsideVar; } break; case TOK_SEMICOLON: if (current->brackets == 0 && current->blocks == 0) { /* avoid switching state on unbalanced []: * var x = [test;testi]; */ current->fsm_state = Base; } break; case TOK_FUNCTION: current = scope_new(state); current->fsm_state = WaitFunctionName; TOKEN_SET(&val, scope, state->current); break; case TOK_StringLiteral: if(state->tokens.cnt > 1 && state->tokens.data[state->tokens.cnt-1].type == TOK_PLUS) { /* see if can fold */ yystype *prev_string = &state->tokens.data[state->tokens.cnt-2]; if(prev_string->type == TOK_StringLiteral) { char *str = TOKEN_GET(prev_string, string); size_t str_len = strlen(str); text = yyget_text(state->scanner); leng = yyget_leng(state->scanner); /* delete TOK_PLUS */ free_token(&state->tokens.data[--state->tokens.cnt]); str = cli_realloc(str, str_len + leng + 1); if (!str) break; strncpy(str+str_len, text, leng); str[str_len + leng] = '\0'; TOKEN_SET(prev_string, string, str); free(val.val.string); memset(&val, 0, sizeof(val)); val.vtype = vtype_undefined; continue; } } break; } if(val.vtype == vtype_undefined) { text = yyget_text(state->scanner); TOKEN_SET(&val, string, cli_strdup(text)); abort(); } add_token(state, &val); current->last_token = yv; memset(&val, 0, sizeof(val)); val.vtype = vtype_undefined; } }
int check_stmt(is_stmt* node) { int errors = 0; /* ; empty statement */ if (!node) return 0; switch (node->type) { case t_stmt_stmt_list: node->data.stmt_list.scope = scope_new(NULL, false); scope_push(node->data.stmt_list.scope); errors += check_stmt_list(node->data.stmt_list.list); scope_pop(); if (errors == 0) node->terminates = node->data.stmt_list.list->terminated; break; case t_stmt_var_stmt: errors += check_var_stmt(node->data.var, false); break; case t_stmt_assign: errors += check_assign_op(node->data.assign); break; case t_stmt_incr: errors += check_incr_op(node->data.incr); break; case t_stmt_if: errors += check_if(node->data.if_stmt); if (errors == 0) node->terminates = node->data.if_stmt->terminates; break; case t_stmt_loop: errors += check_loop_stmt(node->data.loop); if (errors == 0) node->terminates = node->data.loop->terminates; break; case t_stmt_func_call: errors += check_func_call(node->data.func_call); break; case t_stmt_switch: errors += check_switch(node->data.switch_stmt); if (errors == 0) node->terminates = node->data.switch_stmt->terminates; break; case t_stmt_break: errors += check_break(node->data.break_stmt); node->terminates = true; break; case t_stmt_continue: errors += check_continue(node->data.continue_stmt); node->terminates = true; break; case t_stmt_return: errors += check_return(node->data.return_stmt); node->terminates = true; break; } return errors; }