--- parser3/src/main/compile.y 2004/04/06 14:12:45 1.211 +++ parser3/src/main/compile.y 2008/07/23 09:54:17 1.228 @@ -2,10 +2,10 @@ /** @file Parser: compiler(lexical parser and grammar). - Copyright (c) 2001, 2003 ArtLebedev Group (http://www.artlebedev.com) + Copyright (c) 2001-2005 ArtLebedev Group (http://www.artlebedev.com) Author: Alexander Petrosyan (http://design.ru/paf) - $Id: compile.y,v 1.211 2004/04/06 14:12:45 paf Exp $ + $Id: compile.y,v 1.228 2008/07/23 09:54:17 misha Exp $ */ /** @@ -41,6 +41,9 @@ // defines #define USE_CONTROL_METHOD_NAME "USE" +#define OPTIONS_CONTROL_METHOD_NAME "OPTIONS" +#define OPTION_ALL_VARS_LOCAL_NAME "locals" +#define OPTION_APPEND_METHODS "append" // forwards @@ -48,6 +51,10 @@ static int real_yyerror(Parse_control* p static void yyprint(FILE* file, int type, YYSTYPE value); static int yylex(YYSTYPE* lvalp, void* pc); +static const VBool vfalse(false); +static const VBool vtrue(true); +static const VVoid vvoid; + // local convinient inplace typecast & var #undef PC #define PC (*(Parse_control *)pc) @@ -93,6 +100,9 @@ static int yylex(YYSTYPE* lvalp, void* p %token DEXISTS "-d" %token IS "is" +%token LITERAL_TRUE "true" +%token LITERAL_FALSE "false" + /* logical */ %left "!||" %left "||" @@ -143,21 +153,13 @@ control_method: '@' STRING '\n' YYERROR; } if(command==CLASS_NAME) { - if(PC.cclass->base_class()) { // already changed from default? - strcpy(PC.error, "class already have a name '"); - strncat(PC.error, PC.cclass->name().cstr(), 100); - strcat(PC.error, "'"); - YYERROR; - } if(strings_code->count()==1*OPERATIONS_PER_OPVALUE) { // new class' name const String& name=*LA2S(*strings_code); // creating the class VStateless_class* cclass=new VClass; - PC.cclass=cclass; - PC.cclass->set_name(name); - // append to request's classes - PC.request.classes().put(name, cclass); + PC.cclass_new=cclass; + PC.cclass_new->set_name(name); } else { strcpy(PC.error, "@"CLASS_NAME" must contain only one line with class name (contains more then one)"); YYERROR; @@ -166,9 +168,16 @@ control_method: '@' STRING '\n' for(size_t i=0; icount(); i+=OPERATIONS_PER_OPVALUE) PC.request.use_file(PC.request.main_class, *LA2S(*strings_code, i)); } else if(command==BASE_NAME) { + if(PC.append){ + strcpy(PC.error, "can't set base while appending methods to class '"); + strncat(PC.error, PC.cclass->name().cstr(), MAX_STRING/2); + strcat(PC.error, "'"); + YYERROR; + } + PC.class_add(); if(PC.cclass->base_class()) { // already changed from default? strcpy(PC.error, "class already have a base '"); - strncat(PC.error, PC.cclass->base_class()->name().cstr(), 100); + strncat(PC.error, PC.cclass->base_class()->name().cstr(), MAX_STRING/2); strcat(PC.error, "'"); YYERROR; } @@ -196,11 +205,35 @@ control_method: '@' STRING '\n' strcpy(PC.error, "@"BASE_NAME" must contain sole name"); YYERROR; } + } else if(command==OPTIONS_CONTROL_METHOD_NAME) { + for(size_t i=0; icount(); i+=OPERATIONS_PER_OPVALUE) { + const String& option=*LA2S(*strings_code, i); + if(option==OPTION_ALL_VARS_LOCAL_NAME){ + PC.set_all_vars_local(); + } else if(option==OPTION_APPEND_METHODS) { + if(!PC.class_reuse()){ + if(PC.cclass_new){ + strcpy(PC.error, "can't append methods to '"); + strncat(PC.error, PC.cclass_new->name().cstr(), MAX_STRING/2); + strcat(PC.error, "' - the class doesn't exist"); + } else { + strcpy(PC.error, "'"OPTION_APPEND_METHODS"' option should be used straight after @"CLASS_NAME); + } + YYERROR; + } + } else { + strcpy(PC.error, "'"); + strncat(PC.error, option.cstr(), MAX_STRING/2); + strcat(PC.error, "' invalid option. valid options are " + "'"OPTION_APPEND_METHODS"' and '"OPTION_ALL_VARS_LOCAL_NAME"'"); + YYERROR; + } + } } else { strcpy(PC.error, "'"); strncat(PC.error, command.cstr(), MAX_STRING/2); strcat(PC.error, "' invalid special name. valid names are " - "'"CLASS_NAME"', '"USE_CONTROL_METHOD_NAME"' and '"BASE_NAME"'"); + "'"CLASS_NAME"', '"USE_CONTROL_METHOD_NAME"', '"BASE_NAME"' and '"OPTIONS_CONTROL_METHOD_NAME"'."); YYERROR; } }; @@ -210,8 +243,8 @@ control_string: maybe_string '\n'; maybe_string: empty | STRING; code_method: '@' STRING bracketed_maybe_strings maybe_bracketed_strings maybe_comment '\n' { + PC.class_add(); PC.explicit_result=false; - const String& name=*LA2S(*$2); YYSTYPE params_names_code=$3; ArrayString* params_names=0; @@ -223,30 +256,40 @@ code_method: '@' STRING bracketed_maybe_ YYSTYPE locals_names_code=$4; ArrayString* locals_names=0; + bool all_vars_local=false; if(int size=locals_names_code->count()) { locals_names=new ArrayString; for(int i=0; iis_vars_local()){ + all_vars_local=true; + } Method* method=new Method( //name, Method::CT_ANY, 0, 0/*min,max numbered_params_count*/, params_names, locals_names, - 0/*to be filled later in next {} */, 0); - PC.cclass->add_method(PC.alias_method(name), *method); + 0/*to be filled later in next {} */, 0, all_vars_local); + *reinterpret_cast(&$$)=method; // todo: check [][;result;] } maybe_codes { - // fill in the code - reinterpret_cast($7)->parser_code=$8; + Method& method=*reinterpret_cast($7); + // fill in the code + method.parser_code=$8; + // register in class + const String& name=*LA2S(*$2); + PC.cclass->add_method(PC.alias_method(name), method); }; maybe_bracketed_strings: empty | bracketed_maybe_strings; @@ -347,7 +390,7 @@ construct_square: '[' { // allow $result_or_other_variable[ letters here any time ] *reinterpret_cast(&$$)=PC.explicit_result; PC.explicit_result=false; } any_constructor_code_value { - PC.explicit_result=reinterpret_cast($2); + PC.explicit_result=*reinterpret_cast(&$2); } ']' { // stack: context, name $$=$3; // stack: context, name, value @@ -402,7 +445,7 @@ call_value: '^' { YYSTYPE params_code=$5; if(params_code->count()==4) { // probably [] case. [OP_VALUE+origin+Void+STORE_PARAM] if(Value* value=LA2V(*params_code)) // it is OP_VALUE+origin+value? - if(!value->is_defined()) // value is VVoid? + if(value->is_void()) // value is VVoid? params_code=0; // ^zzz[] case. don't append lone empty param. } /* stack: context, method_junction */ @@ -421,7 +464,7 @@ store_square_param: '[' { // allow ^call[ letters here any time ] *reinterpret_cast(&$$)=PC.explicit_result; PC.explicit_result=false; } store_code_param_parts { - PC.explicit_result=reinterpret_cast($2); + PC.explicit_result=*reinterpret_cast(&$2); } ']' {$$=$3}; store_round_param: '(' store_expr_param_parts ')' {$$=$2}; store_curly_param: '{' store_curly_param_parts '}' {$$=$2}; @@ -441,9 +484,20 @@ store_code_param_part: code_param_value $$=$1; O(*$$, OP_STORE_PARAM); }; -store_expr_param_part: write_expr_value { - $$=N(); - OA(*$$, OP_EXPR_CODE__STORE_PARAM, $1); +store_expr_param_part: expr_value { + YYSTYPE expr_code=$1; + if(expr_code->count()==3 + && (*expr_code)[0].code==OP_VALUE) { // optimizing (double/bool/incidently 'string' too) case. [OP_VALUE+origin+Double] + $$=expr_code; + O(*$$, OP_STORE_PARAM); // no evaluating + } else { + ArrayOperation* code=N(); + O(*code, OP_PREPARE_TO_EXPRESSION); + P(*code, *expr_code); + O(*code, OP_WRITE_EXPR_RESULT); + $$=N(); + OA(*$$, OP_EXPR_CODE__STORE_PARAM, code); + } }; store_curly_param_part: maybe_codes { $$=N(); @@ -454,12 +508,6 @@ code_param_value: | STRING /* optimized [STRING] case */ | constructor_code_value /* [something complex] */ ; -write_expr_value: expr_value { - $$=N(); - O(*$$, OP_PREPARE_TO_EXPRESSION); - P(*$$, *$1); - O(*$$, OP_WRITE_EXPR_RESULT); -}; /* name */ @@ -501,9 +549,14 @@ name_expr_with_subvar_value: STRING subv $$=N(); OA(*$$, OP_STRING_POOL, code); }; -name_square_code_value: '[' codes ']' { +name_square_code_value: '[' { + // allow $result_or_other_variable[ letters here any time ] + *reinterpret_cast(&$$)=PC.explicit_result; PC.explicit_result=false; +} codes { + PC.explicit_result=*reinterpret_cast(&$2); +} ']' { $$=N(); - OA(*$$, OP_OBJECT_POOL, $2); /* stack: empty write context */ + OA(*$$, OP_OBJECT_POOL, $3); /* stack: empty write context */ /* some code that writes to that context */ /* context=pop; stack: context.value() */ }; @@ -546,17 +599,15 @@ class_constructor_prefix: class_static_p /* expr */ -expr_value: expr { - // see OP_PREPARE_TO_EXPRESSION!! - if(($$=$1)->count()==2) // only one string literal in there? - change_string_literal_to_double_literal(*$$); // make that string literal Double -}; +expr_value: expr; expr: - STRING + double_or_STRING +| true_value +| false_value | get_value | call_value -| '"' string_inside_quotes_value '"' { $$ = $2; } -| '\'' string_inside_quotes_value '\'' { $$ = $2; } +| '"' string_inside_quotes_value '"' { $$ = $2 } +| '\'' string_inside_quotes_value '\'' { $$ = $2 } | '(' expr ')' { $$ = $2; } /* stack: operand // stack: @operand */ | '-' expr %prec NUNARY { $$=$2; O(*$$, OP_NEG) } @@ -597,6 +648,11 @@ expr: | expr "is" expr { $$=$1; P(*$$, *$3); O(*$$, OP_IS) } ; +double_or_STRING: STRING { + // optimized from OP_STRING->OP_VALUE for doubles + maybe_change_string_literal_to_double_literal(*($$=$1)); +}; + string_inside_quotes_value: maybe_codes { $$=N(); OA(*$$, OP_STRING_POOL, $1); /* stack: empty write context */ @@ -611,7 +667,10 @@ write_string: STRING { change_string_literal_to_write_string_literal(*($$=$1)) }; -void_value: /* empty */ { $$=VL(new VVoid(), 0, 0, 0) }; +void_value: /* empty */ { $$=VL(/*we know that we will not change it*/const_cast(&vvoid), 0, 0, 0) } +true_value: "true" { $$ = VL(/*we know that we will not change it*/const_cast(&vtrue), 0, 0, 0) } +false_value: "false" { $$ = VL(/*we know that we will not change it*/const_cast(&vfalse), 0, 0, 0) } + empty: /* empty */ { $$=N() }; %% @@ -706,7 +765,7 @@ case LS_DEF_COMMENT: default: switch(*pc.source) { // ^escaping some punctuators - case '^': case '$': case ';': + case '^': case '$': case ';': case '@': case '(': case ')': case '[': case ']': case '{': case '}': @@ -734,10 +793,10 @@ default: pc.string.append_strdup_know_length(begin, end-begin); } // #HH ? - if(pc.source[0]=='#' && pc.source[1] && pc.source[2]) { - char c= + if(pc.source[1] && isxdigit(pc.source[1]) && pc.source[2] && isxdigit(pc.source[2])) { + char c=(char)( hex_value[(unsigned char)pc.source[1]]*0x10+ - hex_value[(unsigned char)pc.source[2]]; + hex_value[(unsigned char)pc.source[2]]); if(c==0) { result=BAD_HEX_LITERAL; goto break2; // wrong hex value[no ^#00 chars allowed]: bail out @@ -753,7 +812,14 @@ default: // skip analysis = forced literal continue; } - break; + // just escaped char + // reset piece 'begin' position & line + begin=pc.source; + begin_pos=pc.pos; + // skip over _ after ^ + pc.source++; pc.pos.col++; + // skip analysis = forced literal + continue; } break; } @@ -1050,8 +1116,28 @@ default: case 'd': if(end==begin) // right after whitespace if(pc.source[0]=='e' && pc.source[1]=='f') { // def - skip_analized=2; - result=DEF; + switch(pc.source[2]){ + case ' ': case '\t': case '\n': case '"': case '\'': case '^': case '$': // non-quoted string without whitespace after 'def' is not allowed + skip_analized=2; + result=DEF; + goto break2; + } + // error: incorrect char after 'def' + } + break; + case 't': + if(end==begin) // right after whitespace + if(pc.source[0]=='r' && pc.source[1]=='u' && pc.source[2]=='e') { // true + skip_analized=3; + result=LITERAL_TRUE; + goto break2; + } + break; + case 'f': + if(end==begin) // right after whitespace + if(pc.source[0]=='a' && pc.source[1]=='l' && pc.source[2]=='s' && pc.source[3]=='e') { // false + skip_analized=4; + result=LITERAL_FALSE; goto break2; } break; @@ -1131,6 +1217,8 @@ default: case '=': case '!': // common delimiters case ',': case '?': case '#': + // mysql column separators + case '`': // before call case '^': pop_LS(pc);