Parse-Yapp-1.21/0000755000175000017500000000000013141025035014176 5ustar wbraswellwbraswellParse-Yapp-1.21/t/0000755000175000017500000000000013141025035014441 5ustar wbraswellwbraswellParse-Yapp-1.21/t/stress.t0000755000175000017500000014000707241774323016175 0ustar wbraswellwbraswell#################################################### # # # *** WARNING *** # # # # This test will try to parse a full C++ grammar, # # so it will use a huge ammount of CPU and memory. # # # # So, be patient, specially for test #2... # # # #################################################### $^W=0; # Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl test.pl' ######################### We start with some black magic to print on failure. # Change 1..1 below to 1..last_test_to_print . # (It may become useful if the test is moved to ./t subdirectory.) BEGIN { $| = 1; print "1..10\n"; } END {print "not ok 1\n" unless $loaded;} use Parse::Yapp; $loaded = 1; print "ok 1\n"; ######################### End of black magic. # Insert your test code below (better if it prints "ok 13" # (correspondingly "not ok 13") depending on the success of chunk 13 # of the test code): use Parse::Yapp; my($testnum)=2; my($parser,$grammar); my($yapptxt); #Test 2 eval { $grammar=join('',); $parser=new Parse::Yapp(input => $grammar); }; $@ and do { print "not ok $testnum\n"; print "Object not created. Cannot continue test suite: aborting\n"; exit(1); }; print "ok ", $testnum++, "\n"; #Test 3 keys(%{$parser->{GRAMMAR}{NULLABLE}}) == 43 or print "not "; print "ok ", $testnum++, "\n"; #Test 4 keys(%{$parser->{GRAMMAR}{NTERM}}) == 233 or print "not "; print "ok ", $testnum++, "\n"; #Test 5 @{$parser->{GRAMMAR}{UUTERM}} == 3 or print "not "; print "ok ", $testnum++, "\n"; #Test 6 keys(%{$parser->{GRAMMAR}{TERM}}) == 108 or print "not "; print "ok ", $testnum++, "\n"; #Test 7 @{$parser->{GRAMMAR}{RULES}} == 825 or print "not "; print "ok ", $testnum++, "\n"; #Test 8 @{$parser->{STATES}} == 1611 or print "not "; print "ok ", $testnum++, "\n"; #Test 9 keys(%{$parser->{CONFLICTS}{SOLVED}}) == 115 or print "not "; print "ok ", $testnum++, "\n"; #Test 10 ( $parser->{CONFLICTS}{FORCED}{TOTAL}[0] == 30 and $parser->{CONFLICTS}{FORCED}{TOTAL}[1] == 42 ) or print "not "; print "ok ", $testnum++, "\n"; __DATA__ /* This grammar is a stripped form of the original C++ grammar from the GNU CC compiler : YACC parser for C++ syntax. Copyright (C) 1988, 89, 93-98, 1999 Free Software Foundation, Inc. Hacked by Michael Tiemann (tiemann@cygnus.com) The full gcc compiler an the original grammar file are freely available under the GPL license at : ftp://ftp.gnu.org/gnu/gcc/ */ %{ $language_string = "GNU C++"; %} %start program /* All identifiers that are not reserved words and are not declared typedefs in the current block */ %token IDENTIFIER /* All identifiers that are declared typedefs in the current block. In some contexts, they are treated just like IDENTIFIER, but they can also serve as typespecs in declarations. */ %token TYPENAME %token SELFNAME /* A template function. */ %token PFUNCNAME /* Reserved words that specify storage class. yylval contains an IDENTIFIER_NODE which indicates which one. */ %token SCSPEC /* Reserved words that specify type. yylval contains an IDENTIFIER_NODE which indicates which one. */ %token TYPESPEC /* Reserved words that qualify type: "const" or "volatile". yylval contains an IDENTIFIER_NODE which indicates which one. */ %token CV_QUALIFIER /* Character or numeric constants. yylval is the node for the constant. */ %token CONSTANT /* String constants in raw form. yylval is a STRING_CST node. */ %token STRING /* "...", used for functions with variable arglists. */ %token ELLIPSIS /* the reserved words */ /* SCO include files test "ASM", so use something else. */ %token SIZEOF ENUM /* STRUCT UNION */ IF ELSE WHILE DO FOR SWITCH CASE DEFAULT %token BREAK CONTINUE RETURN_KEYWORD GOTO ASM_KEYWORD TYPEOF ALIGNOF %token SIGOF %token ATTRIBUTE EXTENSION LABEL %token REALPART IMAGPART /* the reserved words... C++ extensions */ %token AGGR %token VISSPEC %token DELETE NEW THIS OPERATOR CXX_TRUE CXX_FALSE %token NAMESPACE TYPENAME_KEYWORD USING %token LEFT_RIGHT TEMPLATE %token TYPEID DYNAMIC_CAST STATIC_CAST REINTERPRET_CAST CONST_CAST %token SCOPE /* Define the operator tokens and their precedences. The value is an integer because, if used, it is the tree code to use in the expression made from the operator. */ %left EMPTY /* used to resolve s/r with epsilon */ %left error /* Add precedence rules to solve dangling else s/r conflict */ %nonassoc IF %nonassoc ELSE %left IDENTIFIER PFUNCNAME TYPENAME SELFNAME PTYPENAME SCSPEC TYPESPEC CV_QUALIFIER ENUM AGGR ELLIPSIS TYPEOF SIGOF OPERATOR NSNAME TYPENAME_KEYWORD %left '{' ',' ';' %nonassoc THROW %right ':' %right ASSIGN '=' %right '?' %left OROR %left ANDAND %left '|' %left '^' %left '&' %left MIN_MAX %left EQCOMPARE %left ARITHCOMPARE '<' '>' %left LSHIFT RSHIFT %left '+' '-' %left '*' '/' '%' %left POINTSAT_STAR DOT_STAR %right UNARY PLUSPLUS MINUSMINUS '~' %left HYPERUNARY %left PAREN_STAR_PAREN LEFT_RIGHT %left POINTSAT '.' '(' '[' %right SCOPE /* C++ extension */ %nonassoc NEW DELETE TRY CATCH /* C++ extensions */ /* Not needed by yapp : already defined in the first %left directive */ /* %token PTYPENAME */ %token PRE_PARSED_FUNCTION_DECL EXTERN_LANG_STRING ALL %token PRE_PARSED_CLASS_DECL DEFARG DEFARG_MARKER /* in order to recognize aggr tags as defining and thus shadowing. */ %token TYPENAME_DEFN IDENTIFIER_DEFN PTYPENAME_DEFN /* Not needed by yapp : already defined in the first %left directive */ /* %token NSNAME */ /* Used in lex.c for parsing pragmas. */ %token END_OF_LINE /* lex.c and pt.c depend on this being the last token. Define any new tokens before this one! */ %token END_OF_SAVED_INPUT %{ /* Deleted everything */ %} %% program: /* empty */ | extdefs ; /* the reason for the strange actions in this rule is so that notype_initdecls when reached via datadef can find a valid list of type and sc specs in $0. */ extdefs: lang_extdef | extdefs lang_extdef ; extdefs_opt: extdefs | /* empty */ ; dot_hush_warning: ; dot_warning_ok: ; extension: EXTENSION ; asm_keyword: ASM_KEYWORD ; lang_extdef: extdef ; extdef: fndef eat_saved_input | datadef | template_def | asm_keyword '(' string ')' ';' | extern_lang_string '{' extdefs_opt '}' | extern_lang_string dot_hush_warning fndef dot_warning_ok eat_saved_input | extern_lang_string dot_hush_warning datadef dot_warning_ok | NAMESPACE identifier '{' extdefs_opt '}' | NAMESPACE '{' extdefs_opt '}' | namespace_alias | using_decl ';' | using_directive | extension extdef ; namespace_alias: NAMESPACE identifier '=' any_id ';' ; using_decl: USING qualified_id | USING global_scope qualified_id | USING global_scope unqualified_id ; namespace_using_decl: USING namespace_qualifier identifier | USING global_scope identifier | USING global_scope namespace_qualifier identifier ; using_directive: USING NAMESPACE any_id ';' ; namespace_qualifier: NSNAME SCOPE | namespace_qualifier NSNAME SCOPE ; any_id: unqualified_id | qualified_id | global_scope qualified_id | global_scope unqualified_id ; extern_lang_string: EXTERN_LANG_STRING | extern_lang_string EXTERN_LANG_STRING ; template_header: TEMPLATE '<' template_parm_list '>' | TEMPLATE '<' '>' ; template_parm_list: template_parm | template_parm_list ',' template_parm ; maybe_identifier: identifier | /* empty */ ; template_type_parm: aggr maybe_identifier | TYPENAME_KEYWORD maybe_identifier ; template_template_parm: template_header aggr maybe_identifier ; template_parm: /* The following rules introduce a new reduce/reduce conflict on the ',' and '>' input tokens: they are valid prefixes for a `structsp', which means they could match a nameless parameter. See 14.6, paragraph 3. By putting them before the `parm' rule, we get their match before considering them nameless parameter declarations. */ template_type_parm | template_type_parm '=' type_id | parm | parm '=' expr_no_commas %prec ARITHCOMPARE | template_template_parm | template_template_parm '=' template_arg ; template_def: template_header template_extdef | template_header error %prec EMPTY ; template_extdef: fndef eat_saved_input | template_datadef | template_def | extern_lang_string dot_hush_warning fndef dot_warning_ok eat_saved_input | extern_lang_string dot_hush_warning template_datadef dot_warning_ok | extension template_extdef ; template_datadef: nomods_initdecls ';' | declmods notype_initdecls ';' | typed_declspecs initdecls ';' | structsp ';' ; datadef: nomods_initdecls ';' | declmods notype_initdecls ';' | typed_declspecs initdecls ';' | declmods ';' | explicit_instantiation ';' | typed_declspecs ';' | error ';' | error '}' | ';' ; ctor_initializer_opt: nodecls | base_init ; maybe_return_init: /* empty */ | return_init | return_init ';' ; eat_saved_input: /* empty */ | END_OF_SAVED_INPUT ; fndef: fn_dot_def1 maybe_return_init ctor_initializer_opt compstmt_or_error | fn_dot_def1 maybe_return_init function_try_block | fn_dot_def1 maybe_return_init error ; constructor_declarator: nested_name_specifier SELFNAME '(' parmlist ')' cv_qualifiers exception_specification_opt | nested_name_specifier SELFNAME LEFT_RIGHT cv_qualifiers exception_specification_opt | global_scope nested_name_specifier SELFNAME '(' parmlist ')' cv_qualifiers exception_specification_opt | global_scope nested_name_specifier SELFNAME LEFT_RIGHT cv_qualifiers exception_specification_opt | nested_name_specifier self_template_type '(' parmlist ')' cv_qualifiers exception_specification_opt | nested_name_specifier self_template_type LEFT_RIGHT cv_qualifiers exception_specification_opt | global_scope nested_name_specifier self_template_type '(' parmlist ')' cv_qualifiers exception_specification_opt | global_scope nested_name_specifier self_template_type LEFT_RIGHT cv_qualifiers exception_specification_opt ; fn_dot_def1: typed_declspecs declarator | declmods notype_declarator | notype_declarator | declmods constructor_declarator | constructor_declarator ; component_constructor_declarator: SELFNAME '(' parmlist ')' cv_qualifiers exception_specification_opt | SELFNAME LEFT_RIGHT cv_qualifiers exception_specification_opt | self_template_type '(' parmlist ')' cv_qualifiers exception_specification_opt | self_template_type LEFT_RIGHT cv_qualifiers exception_specification_opt ; /* more C++ complexity. See component_decl for a comment on the reduce/reduce conflict introduced by these rules. */ fn_dot_def2: declmods component_constructor_declarator | component_constructor_declarator | typed_declspecs declarator | declmods notype_declarator | notype_declarator | declmods constructor_declarator | constructor_declarator ; return_id: RETURN_KEYWORD IDENTIFIER ; return_init: return_id maybe_init | return_id '(' nonnull_exprlist ')' | return_id LEFT_RIGHT ; base_init: ':' dot_set_base_init member_init_list ; dot_set_base_init: /* empty */ ; member_init_list: /* empty */ | member_init | member_init_list ',' member_init | member_init_list error ; member_init: '(' nonnull_exprlist ')' | LEFT_RIGHT | notype_identifier '(' nonnull_exprlist ')' | notype_identifier LEFT_RIGHT | nonnested_type '(' nonnull_exprlist ')' | nonnested_type LEFT_RIGHT | typename_sub '(' nonnull_exprlist ')' | typename_sub LEFT_RIGHT ; identifier: IDENTIFIER | TYPENAME | SELFNAME | PTYPENAME | NSNAME ; notype_identifier: IDENTIFIER | PTYPENAME | NSNAME %prec EMPTY ; identifier_defn: IDENTIFIER_DEFN | TYPENAME_DEFN | PTYPENAME_DEFN ; explicit_instantiation: TEMPLATE begin_explicit_instantiation typespec ';' end_explicit_instantiation | TEMPLATE begin_explicit_instantiation typed_declspecs declarator end_explicit_instantiation | TEMPLATE begin_explicit_instantiation notype_declarator end_explicit_instantiation | TEMPLATE begin_explicit_instantiation constructor_declarator end_explicit_instantiation | SCSPEC TEMPLATE begin_explicit_instantiation typespec ';' end_explicit_instantiation | SCSPEC TEMPLATE begin_explicit_instantiation typed_declspecs declarator end_explicit_instantiation | SCSPEC TEMPLATE begin_explicit_instantiation notype_declarator end_explicit_instantiation | SCSPEC TEMPLATE begin_explicit_instantiation constructor_declarator end_explicit_instantiation ; begin_explicit_instantiation: ; end_explicit_instantiation: ; /* The TYPENAME expansions are to deal with use of a template class name as a template within the class itself, where the template decl is hidden by a type decl. Got all that? */ template_type: PTYPENAME '<' template_arg_list_opt template_close_bracket dot_finish_template_type | TYPENAME '<' template_arg_list_opt template_close_bracket dot_finish_template_type | self_template_type ; apparent_template_type: template_type | identifier '<' template_arg_list_opt '>' dot_finish_template_type ; self_template_type: SELFNAME '<' template_arg_list_opt template_close_bracket dot_finish_template_type ; dot_finish_template_type: ; template_close_bracket: '>' | RSHIFT ; template_arg_list_opt: /* empty */ | template_arg_list ; template_arg_list: template_arg | template_arg_list ',' template_arg ; template_arg: type_id | PTYPENAME | expr_no_commas %prec ARITHCOMPARE ; unop: '-' | '+' | PLUSPLUS | MINUSMINUS | '!' ; expr: nontrivial_exprlist | expr_no_commas ; paren_expr_or_null: LEFT_RIGHT | '(' expr ')' ; paren_cond_or_null: LEFT_RIGHT | '(' condition ')' ; xcond: /* empty */ | condition | error ; condition: type_specifier_seq declarator maybeasm maybe_attribute '=' | expr ; compstmtend: '}' | maybe_label_decls stmts '}' | maybe_label_decls stmts error '}' | maybe_label_decls error '}' ; already_scoped_stmt: '{' compstmtend | simple_stmt ; nontrivial_exprlist: expr_no_commas ',' expr_no_commas | expr_no_commas ',' error | nontrivial_exprlist ',' expr_no_commas | nontrivial_exprlist ',' error ; nonnull_exprlist: expr_no_commas | nontrivial_exprlist ; unary_expr: primary %prec UNARY /* __extension__ turns off -pedantic for following primary. */ | extension cast_expr %prec UNARY | '*' cast_expr %prec UNARY | '&' cast_expr %prec UNARY | '~' cast_expr | unop cast_expr %prec UNARY /* Refer to the address of a label as a pointer. */ | ANDAND identifier | SIZEOF unary_expr %prec UNARY | SIZEOF '(' type_id ')' %prec HYPERUNARY | ALIGNOF unary_expr %prec UNARY | ALIGNOF '(' type_id ')' %prec HYPERUNARY /* The %prec EMPTY's here are required by the = init initializer syntax extension; see below. */ | new new_type_id %prec EMPTY | new new_type_id new_initializer | new new_placement new_type_id %prec EMPTY | new new_placement new_type_id new_initializer /* The dot_begin_new_placement in the following rules is necessary to avoid shift/reduce conflicts that lead to mis-parsing some expressions. Of course, these constructs are not really new-placement and it is bogus to call begin_new_placement. But, the parser cannot always tell at this point whether the next thing is an expression or a type-id, so there is nothing we can do. Fortunately, begin_new_placement does nothing harmful. When we rewrite the parser, this lossage should be removed, of course. */ | new '(' dot_begin_new_placement type_id dot_finish_new_placement %prec EMPTY | new '(' dot_begin_new_placement type_id dot_finish_new_placement new_initializer | new new_placement '(' dot_begin_new_placement type_id dot_finish_new_placement %prec EMPTY | new new_placement '(' dot_begin_new_placement type_id dot_finish_new_placement new_initializer | delete cast_expr %prec UNARY | delete '[' ']' cast_expr %prec UNARY | delete '[' expr ']' cast_expr %prec UNARY | REALPART cast_expr %prec UNARY | IMAGPART cast_expr %prec UNARY ; /* Note this rule is not suitable for use in new_placement since it uses NULL_TREE as the argument to finish_new_placement. This rule serves only to avoid reduce/reduce conflicts in unary_expr. See the comments there on the use of begin/finish_new_placement. */ dot_finish_new_placement: ')' ; dot_begin_new_placement: ; new_placement: '(' dot_begin_new_placement nonnull_exprlist ')' | '{' dot_begin_new_placement nonnull_exprlist '}' ; new_initializer: '(' nonnull_exprlist ')' | LEFT_RIGHT | '(' typespec ')' /* GNU extension so people can use initializer lists. Note that this alters the meaning of `new int = 1', which was previously syntactically valid but semantically invalid. */ | '=' init ; /* This is necessary to postpone reduction of `int ((int)(int)(int))'. */ regcast_or_absdcl: '(' type_id ')' %prec EMPTY | regcast_or_absdcl '(' type_id ')' %prec EMPTY ; cast_expr: unary_expr | regcast_or_absdcl unary_expr %prec UNARY | regcast_or_absdcl '{' initlist maybecomma '}' %prec UNARY ; expr_no_commas: cast_expr /* Handle general members. */ | expr_no_commas POINTSAT_STAR expr_no_commas | expr_no_commas DOT_STAR expr_no_commas | expr_no_commas '+' expr_no_commas | expr_no_commas '-' expr_no_commas | expr_no_commas '*' expr_no_commas | expr_no_commas '/' expr_no_commas | expr_no_commas '%' expr_no_commas | expr_no_commas LSHIFT expr_no_commas | expr_no_commas RSHIFT expr_no_commas | expr_no_commas ARITHCOMPARE expr_no_commas | expr_no_commas '<' expr_no_commas | expr_no_commas '>' expr_no_commas | expr_no_commas EQCOMPARE expr_no_commas | expr_no_commas MIN_MAX expr_no_commas | expr_no_commas '&' expr_no_commas | expr_no_commas '|' expr_no_commas | expr_no_commas '^' expr_no_commas | expr_no_commas ANDAND expr_no_commas | expr_no_commas OROR expr_no_commas | expr_no_commas '?' xexpr ':' expr_no_commas | expr_no_commas '=' expr_no_commas | expr_no_commas ASSIGN expr_no_commas | THROW | THROW expr_no_commas /* These extensions are not defined. The second arg to build_m_component_ref is old, build_m_component_ref now does an implicit build_indirect_ref (x, NULL_PTR) on the second argument. | object '&' expr_no_commas %prec UNARY { $$ = build_m_component_ref ($$, build_x_unary_op (ADDR_EXPR, $3)); } | object unop expr_no_commas %prec UNARY { $$ = build_m_component_ref ($$, build_x_unary_op ($2, $3)); } | object '(' type_id ')' expr_no_commas %prec UNARY { tree type = groktypename ($3.t); $$ = build_m_component_ref ($$, build_c_cast (type, $5)); } | object primary_no_id %prec UNARY { $$ = build_m_component_ref ($$, $2); } */ ; notype_unqualified_id: '~' see_typename identifier | '~' see_typename template_type | template_id | operator_name | IDENTIFIER | PTYPENAME | NSNAME %prec EMPTY ; do_id: ; template_id: PFUNCNAME '<' do_id template_arg_list_opt template_close_bracket | operator_name '<' do_id template_arg_list_opt template_close_bracket ; object_template_id: TEMPLATE identifier '<' template_arg_list_opt template_close_bracket | TEMPLATE PFUNCNAME '<' template_arg_list_opt template_close_bracket | TEMPLATE operator_name '<' template_arg_list_opt template_close_bracket ; unqualified_id: notype_unqualified_id | TYPENAME | SELFNAME ; expr_or_declarator_intern: expr_or_declarator | attributes expr_or_declarator ; expr_or_declarator: notype_unqualified_id | '*' expr_or_declarator_intern %prec UNARY | '&' expr_or_declarator_intern %prec UNARY | '(' expr_or_declarator_intern ')' ; notype_template_declarator: IDENTIFIER '<' template_arg_list_opt template_close_bracket | NSNAME '<' template_arg_list template_close_bracket ; direct_notype_declarator: complex_direct_notype_declarator /* This precedence declaration is to prefer this reduce to the Koenig lookup shift in primary, below. I hate yacc. */ | notype_unqualified_id %prec '(' | notype_template_declarator | '(' expr_or_declarator_intern ')' ; primary: notype_unqualified_id | CONSTANT | boolean_dot_literal | string | '(' expr ')' | '(' expr_or_declarator_intern ')' | '(' error ')' | '(' compstmt ')' /* Koenig lookup support We could store lastiddecl in $1 to avoid another lookup, but that would result in many additional reduce/reduce conflicts. */ | notype_unqualified_id '(' nonnull_exprlist ')' | notype_unqualified_id LEFT_RIGHT | primary '(' nonnull_exprlist ')' | primary LEFT_RIGHT | primary '[' expr ']' | primary PLUSPLUS | primary MINUSMINUS /* C++ extensions */ | THIS | CV_QUALIFIER '(' nonnull_exprlist ')' | functional_cast | DYNAMIC_CAST '<' type_id '>' '(' expr ')' | STATIC_CAST '<' type_id '>' '(' expr ')' | REINTERPRET_CAST '<' type_id '>' '(' expr ')' | CONST_CAST '<' type_id '>' '(' expr ')' | TYPEID '(' expr ')' | TYPEID '(' type_id ')' | global_scope IDENTIFIER | global_scope template_id | global_scope operator_name | overqualified_id %prec HYPERUNARY | overqualified_id '(' nonnull_exprlist ')' | overqualified_id LEFT_RIGHT | object object_template_id %prec UNARY | object object_template_id '(' nonnull_exprlist ')' | object object_template_id LEFT_RIGHT | object unqualified_id %prec UNARY | object overqualified_id %prec UNARY | object unqualified_id '(' nonnull_exprlist ')' | object unqualified_id LEFT_RIGHT | object overqualified_id '(' nonnull_exprlist ')' | object overqualified_id LEFT_RIGHT /* p->int::~int() is valid -- 12.4 */ | object '~' TYPESPEC LEFT_RIGHT | object TYPESPEC SCOPE '~' TYPESPEC LEFT_RIGHT | object error ; /* Not needed for now. primary_no_id: '(' expr ')' | '(' error ')' | '(' | primary_no_id '(' nonnull_exprlist ')' | primary_no_id LEFT_RIGHT | primary_no_id '[' expr ']' | primary_no_id PLUSPLUS | primary_no_id MINUSMINUS | SCOPE IDENTIFIER | SCOPE operator_name ; */ new: NEW | global_scope NEW ; delete: DELETE | global_scope delete ; boolean_dot_literal: CXX_TRUE | CXX_FALSE ; /* Produces a STRING_CST with perhaps more STRING_CSTs chained onto it. */ string: STRING | string STRING ; nodecls: /* empty */ ; object: primary '.' | primary POINTSAT ; decl: typespec initdecls ';' | typed_declspecs initdecls ';' | declmods notype_initdecls ';' | typed_declspecs ';' | declmods ';' | extension decl ; /* Any kind of declarator (thus, all declarators allowed after an explicit typespec). */ declarator: after_type_declarator %prec EMPTY | notype_declarator %prec EMPTY ; /* This is necessary to postpone reduction of `int()()()()'. */ fcast_or_absdcl: LEFT_RIGHT %prec EMPTY | fcast_or_absdcl LEFT_RIGHT %prec EMPTY ; /* ANSI type-id (8.1) */ type_id: typed_typespecs absdcl | nonempty_cv_qualifiers absdcl | typespec absdcl | typed_typespecs %prec EMPTY | nonempty_cv_qualifiers %prec EMPTY ; /* Declspecs which contain at least one type specifier or typedef name. (Just `const' or `volatile' is not enough.) A typedef'd name following these is taken as a name to be declared. In the result, declspecs have a non-NULL TREE_VALUE, attributes do not. */ typed_declspecs: typed_typespecs %prec EMPTY | typed_declspecs1 ; typed_declspecs1: declmods typespec | typespec reserved_declspecs %prec HYPERUNARY | typespec reserved_typespecquals reserved_declspecs | declmods typespec reserved_declspecs | declmods typespec reserved_typespecquals | declmods typespec reserved_typespecquals reserved_declspecs ; reserved_declspecs: SCSPEC | reserved_declspecs typespecqual_reserved | reserved_declspecs SCSPEC | reserved_declspecs attributes | attributes ; /* List of just storage classes and type modifiers. A declaration can start with just this, but then it cannot be used to redeclare a typedef-name. In the result, declspecs have a non-NULL TREE_VALUE, attributes do not. */ /* We use hash_tree_cons for lists of typeless declspecs so that they end up on a persistent obstack. Otherwise, they could appear at the beginning of something like static const struct { int foo () { } } b; and would be discarded after we finish compiling foo. We don't need to worry once we see a type. */ declmods: nonempty_cv_qualifiers %prec EMPTY { $$ = $1.t; TREE_STATIC ($$) = 1; } | SCSPEC { $$ = hash_tree_cons (NULL_TREE, $$, NULL_TREE); } | declmods CV_QUALIFIER { $$ = hash_tree_cons (NULL_TREE, $2, $$); TREE_STATIC ($$) = 1; } | declmods SCSPEC { if (extra_warnings && TREE_STATIC ($$)) warning ("`%s' is not at beginning of declaration", IDENTIFIER_POINTER ($2)); $$ = hash_tree_cons (NULL_TREE, $2, $$); TREE_STATIC ($$) = TREE_STATIC ($1); } | declmods attributes { $$ = hash_tree_cons ($2, NULL_TREE, $1); } | attributes %prec EMPTY { $$ = hash_tree_cons ($1, NULL_TREE, NULL_TREE); } ; /* Used instead of declspecs where storage classes are not allowed (that is, for typenames and structure components). C++ can takes storage classes for structure components. Don't accept a typedef-name if anything but a modifier precedes it. */ typed_typespecs: typespec %prec EMPTY | nonempty_cv_qualifiers typespec | typespec reserved_typespecquals | nonempty_cv_qualifiers typespec reserved_typespecquals ; reserved_typespecquals: typespecqual_reserved | reserved_typespecquals typespecqual_reserved ; /* A typespec (but not a type qualifier). Once we have seen one of these in a declaration, if a typedef name appears then it is being redeclared. */ typespec: structsp | TYPESPEC %prec EMPTY | complete_type_name | TYPEOF '(' expr ')' | TYPEOF '(' type_id ')' | SIGOF '(' expr ')' | SIGOF '(' type_id ')' ; /* A typespec that is a reserved word, or a type qualifier. */ typespecqual_reserved: TYPESPEC | CV_QUALIFIER | structsp ; initdecls: initdcl0 | initdecls ',' initdcl ; notype_initdecls: notype_initdcl0 | notype_initdecls ',' initdcl ; nomods_initdecls: nomods_initdcl0 | nomods_initdecls ',' initdcl ; maybeasm: /* empty */ | asm_keyword '(' string ')' ; initdcl: declarator maybeasm maybe_attribute '=' init /* Note how the declaration of the variable is in effect while its init is parsed! */ | declarator maybeasm maybe_attribute ; /* This rule assumes a certain configuration of the parser stack. In particular, $0, the element directly before the beginning of this rule on the stack, must be a maybeasm. $-1 must be a declarator or notype_declarator. And $-2 must be some declmods or declspecs. We can't move the maybeasm into this rule because we need that reduce so we prefer fn_dot_def1 when appropriate. */ initdcl0_innards: maybe_attribute '=' /* Note how the declaration of the variable is in effect while its init is parsed! */ init | maybe_attribute ; initdcl0: declarator maybeasm initdcl0_innards ; notype_initdcl0: notype_declarator maybeasm initdcl0_innards ; nomods_initdcl0: notype_declarator maybeasm initdcl0_innards | constructor_declarator maybeasm maybe_attribute ; /* the * rules are dummies to accept the Apollo extended syntax so that the header files compile. */ maybe_attribute: /* empty */ | attributes ; attributes: attribute | attributes attribute ; attribute: ATTRIBUTE '(' '(' attribute_list ')' ')' ; attribute_list: attrib | attribute_list ',' attrib ; attrib: /* empty */ | any_word | any_word '(' IDENTIFIER ')' | any_word '(' IDENTIFIER ',' nonnull_exprlist ')' | any_word '(' nonnull_exprlist ')' ; /* This still leaves out most reserved keywords, shouldn't we include them? */ any_word: identifier | SCSPEC | TYPESPEC | CV_QUALIFIER ; /* A nonempty list of identifiers, including typenames. */ identifiers_or_typenames: identifier | identifiers_or_typenames ',' identifier ; maybe_init: /* empty */ %prec EMPTY | '=' init ; /* If we are processing a template, we don't want to expand this initializer yet. */ init: expr_no_commas %prec '=' | '{' '}' | '{' initlist '}' | '{' initlist ',' '}' | error ; /* This chain is built in reverse order, and put in forward order where initlist is used. */ initlist: init | initlist ',' init /* These are for labeled elements. */ | '[' expr_no_commas ']' init | identifier ':' init | initlist ',' identifier ':' init ; fn_dot_defpen: PRE_PARSED_FUNCTION_DECL ; pending_inline: fn_dot_defpen maybe_return_init ctor_initializer_opt compstmt_or_error | fn_dot_defpen maybe_return_init function_try_block | fn_dot_defpen maybe_return_init error ; pending_inlines: /* empty */ | pending_inlines pending_inline eat_saved_input ; /* A regurgitated default argument. The value of DEFARG_MARKER will be the TREE_LIST node for the parameter in question. */ defarg_again: DEFARG_MARKER expr_no_commas END_OF_SAVED_INPUT | DEFARG_MARKER error END_OF_SAVED_INPUT ; pending_defargs: /* empty */ %prec EMPTY | pending_defargs defarg_again | pending_defargs error ; structsp: ENUM identifier '{' enumlist maybecomma_warn '}' | ENUM identifier '{' '}' | ENUM '{' enumlist maybecomma_warn '}' | ENUM '{' '}' | ENUM identifier | ENUM complex_type_name | TYPENAME_KEYWORD typename_sub /* C++ extensions, merged with C to avoid shift/reduce conflicts */ | class_head '{' opt_dot_component_decl_list '}' maybe_attribute pending_defargs pending_inlines | class_head %prec EMPTY ; maybecomma: /* empty */ | ',' ; maybecomma_warn: /* empty */ | ',' ; aggr: AGGR | aggr SCSPEC | aggr TYPESPEC | aggr CV_QUALIFIER | aggr AGGR | aggr attributes ; named_class_head_sans_basetype: aggr identifier ; named_class_head_sans_basetype_defn: aggr identifier_defn %prec EMPTY | named_class_head_sans_basetype '{' | named_class_head_sans_basetype ':' ; named_complex_class_head_sans_basetype: aggr nested_name_specifier identifier | aggr global_scope nested_name_specifier identifier | aggr global_scope identifier | aggr apparent_template_type | aggr nested_name_specifier apparent_template_type ; named_class_head: named_class_head_sans_basetype %prec EMPTY | named_class_head_sans_basetype_defn /* Class name is unqualified, so we look for base classes in the current scope. */ maybe_base_class_list %prec EMPTY | named_complex_class_head_sans_basetype maybe_base_class_list ; unnamed_class_head: aggr '{' ; /* The tree output of this nonterminal a declarationf or the type named. If NEW_TYPE_FLAG is set, then the name used in this class-head was explicitly qualified, e.g.: `struct X::Y'. We have already called push_scope for X. */ class_head: unnamed_class_head | named_class_head ; maybe_base_class_list: /* empty */ %prec EMPTY | ':' see_typename %prec EMPTY | ':' see_typename base_class_list %prec EMPTY ; base_class_list: base_class | base_class_list ',' see_typename base_class ; base_class: base_class_dot_1 | base_class_access_list see_typename base_class_dot_1 ; base_class_dot_1: typename_sub | nonnested_type | SIGOF '(' expr ')' | SIGOF '(' type_id ')' ; base_class_access_list: VISSPEC see_typename | SCSPEC see_typename | base_class_access_list VISSPEC see_typename | base_class_access_list SCSPEC see_typename ; opt_dot_component_decl_list: | component_decl_list | opt_dot_component_decl_list access_specifier component_decl_list | opt_dot_component_decl_list access_specifier ; access_specifier: VISSPEC ':' ; /* Note: we no longer warn about the semicolon after a component_decl_list. ARM $9.2 says that the semicolon is optional, and therefore allowed. */ component_decl_list: component_decl | component_decl_list component_decl ; component_decl: component_decl_1 ';' | component_decl_1 '}' /* C++: handle constructors, destructors and inline functions */ /* note that INLINE is like a TYPESPEC */ | fn_dot_def2 ':' /* base_init compstmt */ | fn_dot_def2 TRY /* base_init compstmt */ | fn_dot_def2 RETURN_KEYWORD /* base_init compstmt */ | fn_dot_def2 '{' /* nodecls compstmt */ | ';' | extension component_decl | template_header component_decl | template_header typed_declspecs ';' ; component_decl_1: /* Do not add a "typed_declspecs declarator" rule here for speed; we need to call grok_x_components for enums, so the speedup would be insignificant. */ typed_declspecs components | declmods notype_components | notype_declarator maybeasm maybe_attribute maybe_init | constructor_declarator maybeasm maybe_attribute maybe_init | ':' expr_no_commas | error /* These rules introduce a reduce/reduce conflict; in typedef int foo, bar; class A { foo (bar); }; should "A::foo" be declared as a function or "A::bar" as a data member? In other words, is "bar" an after_type_declarator or a parmlist? */ | declmods component_constructor_declarator maybeasm maybe_attribute maybe_init | component_constructor_declarator maybeasm maybe_attribute maybe_init | using_decl ; /* The case of exactly one component is handled directly by component_decl. */ /* ??? Huh? ^^^ */ components: /* empty: possibly anonymous */ | component_declarator0 | components ',' component_declarator ; notype_components: /* empty: possibly anonymous */ | notype_component_declarator0 | notype_components ',' notype_component_declarator ; component_declarator0: after_type_component_declarator0 | notype_component_declarator0 ; component_declarator: after_type_component_declarator | notype_component_declarator ; after_type_component_declarator0: after_type_declarator maybeasm maybe_attribute maybe_init | TYPENAME ':' expr_no_commas maybe_attribute ; notype_component_declarator0: notype_declarator maybeasm maybe_attribute maybe_init | constructor_declarator maybeasm maybe_attribute maybe_init | IDENTIFIER ':' expr_no_commas maybe_attribute | ':' expr_no_commas maybe_attribute ; after_type_component_declarator: after_type_declarator maybeasm maybe_attribute maybe_init | TYPENAME ':' expr_no_commas maybe_attribute ; notype_component_declarator: notype_declarator maybeasm maybe_attribute maybe_init | IDENTIFIER ':' expr_no_commas maybe_attribute | ':' expr_no_commas maybe_attribute ; /* We chain the enumerators in reverse order. Because of the way enums are built, the order is insignificant. Take advantage of this fact. */ enumlist: enumerator | enumlist ',' enumerator ; enumerator: identifier | identifier '=' expr_no_commas ; /* ANSI new-type-id (5.3.4) */ new_type_id: type_specifier_seq new_declarator | type_specifier_seq %prec EMPTY /* GNU extension to allow arrays of arbitrary types with non-constant dimension. For the use of begin_new_placement here, see the comments in unary_expr above. */ | '(' dot_begin_new_placement type_id dot_finish_new_placement '[' expr ']' ; cv_qualifiers: /* empty */ %prec EMPTY | cv_qualifiers CV_QUALIFIER ; nonempty_cv_qualifiers: CV_QUALIFIER | nonempty_cv_qualifiers CV_QUALIFIER ; /* These rules must follow the rules for function declarations and component declarations. That way, longer rules are preferred. */ suspend_mom: /* empty */ ; /* An expression which will not live on the momentary obstack. */ nonmomentary_expr: suspend_mom expr ; /* An expression which will not live on the momentary obstack. */ maybe_parmlist: suspend_mom '(' nonnull_exprlist ')' | suspend_mom '(' parmlist ')' | suspend_mom LEFT_RIGHT | suspend_mom '(' error ')' ; /* A declarator that is allowed only after an explicit typespec. */ after_type_declarator_intern: after_type_declarator | attributes after_type_declarator ; /* may all be followed by prec '.' */ after_type_declarator: '*' nonempty_cv_qualifiers after_type_declarator_intern %prec UNARY | '&' nonempty_cv_qualifiers after_type_declarator_intern %prec UNARY | '*' after_type_declarator_intern %prec UNARY | '&' after_type_declarator_intern %prec UNARY | ptr_to_mem cv_qualifiers after_type_declarator_intern | direct_after_type_declarator ; direct_after_type_declarator: direct_after_type_declarator maybe_parmlist cv_qualifiers exception_specification_opt %prec '.' | direct_after_type_declarator '[' nonmomentary_expr ']' | direct_after_type_declarator '[' ']' | '(' after_type_declarator_intern ')' | nested_name_specifier type_name %prec EMPTY | type_name %prec EMPTY ; nonnested_type: type_name %prec EMPTY | global_scope type_name ; complete_type_name: nonnested_type | nested_type | global_scope nested_type ; nested_type: nested_name_specifier type_name %prec EMPTY ; /* A declarator allowed whether or not there has been an explicit typespec. These cannot redeclare a typedef-name. */ notype_declarator_intern: notype_declarator | attributes notype_declarator ; notype_declarator: '*' nonempty_cv_qualifiers notype_declarator_intern %prec UNARY | '&' nonempty_cv_qualifiers notype_declarator_intern %prec UNARY | '*' notype_declarator_intern %prec UNARY | '&' notype_declarator_intern %prec UNARY | ptr_to_mem cv_qualifiers notype_declarator_intern | direct_notype_declarator ; complex_notype_declarator: '*' nonempty_cv_qualifiers notype_declarator_intern %prec UNARY | '&' nonempty_cv_qualifiers notype_declarator_intern %prec UNARY | '*' complex_notype_declarator %prec UNARY | '&' complex_notype_declarator %prec UNARY | ptr_to_mem cv_qualifiers notype_declarator_intern | complex_direct_notype_declarator ; complex_direct_notype_declarator: direct_notype_declarator maybe_parmlist cv_qualifiers exception_specification_opt %prec '.' | '(' complex_notype_declarator ')' | direct_notype_declarator '[' nonmomentary_expr ']' | direct_notype_declarator '[' ']' | notype_qualified_id | nested_name_specifier notype_template_declarator ; qualified_id: nested_name_specifier unqualified_id | nested_name_specifier object_template_id ; notype_qualified_id: nested_name_specifier notype_unqualified_id | nested_name_specifier object_template_id ; overqualified_id: notype_qualified_id | global_scope notype_qualified_id ; functional_cast: typespec '(' nonnull_exprlist ')' | typespec '(' expr_or_declarator_intern ')' | typespec fcast_or_absdcl %prec EMPTY ; type_name: TYPENAME | SELFNAME | template_type %prec EMPTY ; nested_name_specifier: nested_name_specifier_1 | nested_name_specifier nested_name_specifier_1 | nested_name_specifier TEMPLATE explicit_template_type SCOPE ; /* Why the @#$%^& do type_name and notype_identifier need to be expanded inline here?!? (jason) */ nested_name_specifier_1: TYPENAME SCOPE | SELFNAME SCOPE | NSNAME SCOPE | template_type SCOPE /* These break 'const i;' | IDENTIFIER SCOPE { failed_scope: cp_error ("`%D' is not an aggregate typedef", lastiddecl ? lastiddecl : $$); $$ = error_mark_node; } | PTYPENAME SCOPE { goto failed_scope; } */ ; typename_sub: typename_sub0 | global_scope typename_sub0 ; typename_sub0: typename_sub1 identifier %prec EMPTY | typename_sub1 template_type %prec EMPTY | typename_sub1 explicit_template_type %prec EMPTY | typename_sub1 TEMPLATE explicit_template_type %prec EMPTY ; typename_sub1: typename_sub2 | typename_sub1 typename_sub2 | typename_sub1 explicit_template_type SCOPE | typename_sub1 TEMPLATE explicit_template_type SCOPE ; typename_sub2: TYPENAME SCOPE | SELFNAME SCOPE | template_type SCOPE | PTYPENAME SCOPE | IDENTIFIER SCOPE | NSNAME SCOPE ; explicit_template_type: identifier '<' template_arg_list_opt template_close_bracket ; complex_type_name: global_scope type_name | nested_type | global_scope nested_type ; ptr_to_mem: nested_name_specifier '*' | global_scope nested_name_specifier '*' ; /* All uses of explicit global scope must go through this nonterminal so that got_scope will be set before yylex is called to get the next token. */ global_scope: SCOPE ; /* ANSI new-declarator (5.3.4) */ new_declarator: '*' cv_qualifiers new_declarator | '*' cv_qualifiers %prec EMPTY | '&' cv_qualifiers new_declarator %prec EMPTY | '&' cv_qualifiers %prec EMPTY | ptr_to_mem cv_qualifiers %prec EMPTY | ptr_to_mem cv_qualifiers new_declarator | direct_new_declarator %prec EMPTY ; /* ANSI direct-new-declarator (5.3.4) */ direct_new_declarator: '[' expr ']' | direct_new_declarator '[' nonmomentary_expr ']' ; absdcl_intern: absdcl | attributes absdcl ; /* ANSI abstract-declarator (8.1) */ absdcl: '*' nonempty_cv_qualifiers absdcl_intern | '*' absdcl_intern | '*' nonempty_cv_qualifiers %prec EMPTY | '*' %prec EMPTY | '&' nonempty_cv_qualifiers absdcl_intern | '&' absdcl_intern | '&' nonempty_cv_qualifiers %prec EMPTY | '&' %prec EMPTY | ptr_to_mem cv_qualifiers %prec EMPTY | ptr_to_mem cv_qualifiers absdcl_intern | direct_abstract_declarator %prec EMPTY ; /* ANSI direct-abstract-declarator (8.1) */ direct_abstract_declarator: '(' absdcl_intern ')' /* `(typedef)1' is `int'. */ | PAREN_STAR_PAREN | direct_abstract_declarator '(' parmlist ')' cv_qualifiers exception_specification_opt %prec '.' | direct_abstract_declarator LEFT_RIGHT cv_qualifiers exception_specification_opt %prec '.' | direct_abstract_declarator '[' nonmomentary_expr ']' %prec '.' | direct_abstract_declarator '[' ']' %prec '.' | '(' complex_parmlist ')' cv_qualifiers exception_specification_opt %prec '.' | regcast_or_absdcl cv_qualifiers exception_specification_opt %prec '.' | fcast_or_absdcl cv_qualifiers exception_specification_opt %prec '.' | '[' nonmomentary_expr ']' %prec '.' | '[' ']' %prec '.' ; /* For C++, decls and stmts can be intermixed, so we don't need to have a special rule that won't start parsing the stmt section until we have a stmt that parses without errors. */ stmts: stmt | errstmt | stmts stmt | stmts errstmt ; errstmt: error ';' ; /* Read zero or more forward-declarations for labels that nested functions can jump to. */ maybe_label_decls: /* empty */ | label_decls ; label_decls: label_decl | label_decls label_decl ; label_decl: LABEL identifiers_or_typenames ';' ; /* This is the body of a function definition. It causes syntax errors to ignore to the next openbrace. */ compstmt_or_error: compstmt | error compstmt ; compstmt: '{' compstmtend ; simple_if: IF paren_cond_or_null implicitly_scoped_stmt ; implicitly_scoped_stmt: compstmt | simple_stmt ; stmt: compstmt | simple_stmt ; simple_stmt: decl | expr ';' | simple_if ELSE implicitly_scoped_stmt | simple_if %prec IF | WHILE paren_cond_or_null already_scoped_stmt | DO implicitly_scoped_stmt WHILE paren_expr_or_null ';' | FOR '(' for_dot_init_dot_statement xcond ';' xexpr ')' already_scoped_stmt | SWITCH '(' condition ')' implicitly_scoped_stmt | CASE expr_no_commas ':' stmt | CASE expr_no_commas ELLIPSIS expr_no_commas ':' stmt | DEFAULT ':' stmt | BREAK ';' | CONTINUE ';' | RETURN_KEYWORD ';' | RETURN_KEYWORD expr ';' | asm_keyword maybe_cv_qualifier '(' string ')' ';' /* This is the case with just output operands. */ | asm_keyword maybe_cv_qualifier '(' string ':' asm_operands ')' ';' /* This is the case with input operands as well. */ | asm_keyword maybe_cv_qualifier '(' string ':' asm_operands ':' asm_operands ')' ';' /* This is the case with clobbered registers as well. */ | asm_keyword maybe_cv_qualifier '(' string ':' asm_operands ':' asm_operands ':' asm_clobbers ')' ';' | GOTO '*' expr ';' | GOTO identifier ';' | label_colon stmt | label_colon '}' | ';' | try_block | using_directive | namespace_using_decl | namespace_alias ; function_try_block: TRY ctor_initializer_opt compstmt handler_seq ; try_block: TRY compstmt handler_seq ; handler_seq: handler | handler_seq handler ; handler: CATCH handler_args compstmt ; type_specifier_seq: typed_typespecs %prec EMPTY | nonempty_cv_qualifiers %prec EMPTY ; handler_args: '(' ELLIPSIS ')' /* This doesn't allow reference parameters, the below does. | '(' type_specifier_seq absdcl ')' | '(' type_specifier_seq ')' | '(' type_specifier_seq notype_declarator ')' | '(' typed_typespecs after_type_declarator ')' This allows reference parameters... */ | '(' parm ')' ; label_colon: IDENTIFIER ':' | PTYPENAME ':' | TYPENAME ':' | SELFNAME ':' ; for_dot_init_dot_statement: xexpr ';' | decl | '{' compstmtend ; /* Either a type-qualifier or nothing. First thing in an `asm' statement. */ maybe_cv_qualifier: /* empty */ | CV_QUALIFIER ; xexpr: /* empty */ | expr | error ; /* These are the operands other than the first string and colon in asm ("addextend %2,%1": "=dm" (x), "0" (y), "g" (*x)) */ asm_operands: /* empty */ | nonnull_asm_operands ; nonnull_asm_operands: asm_operand | nonnull_asm_operands ',' asm_operand ; asm_operand: STRING '(' expr ')' ; asm_clobbers: STRING | asm_clobbers ',' STRING ; /* This is what appears inside the parens in a function declarator. Its value is represented in the format that grokdeclarator expects. In C++, declaring a function with no parameters means that that function takes *no* parameters. */ parmlist: /* empty */ | complex_parmlist | type_id ; /* This nonterminal does not include the common sequence '(' type_id ')', as it is ambiguous and must be disambiguated elsewhere. */ complex_parmlist: parms | parms_comma ELLIPSIS /* C++ allows an ellipsis without a separating ',' */ | parms ELLIPSIS | type_id ELLIPSIS | ELLIPSIS | parms ':' | type_id ':' ; /* A default argument to a */ defarg: '=' defarg1 ; defarg1: DEFARG | init ; /* A nonempty list of parameter declarations or type names. */ parms: named_parm | parm defarg | parms_comma full_parm | parms_comma bad_parm | parms_comma bad_parm '=' init ; parms_comma: parms ',' | type_id ',' ; /* A single parameter declaration or parameter type name, as found in a parmlist. */ named_parm: /* Here we expand typed_declspecs inline to avoid mis-parsing of TYPESPEC IDENTIFIER. */ typed_declspecs1 declarator | typed_typespecs declarator | typespec declarator | typed_declspecs1 absdcl | typed_declspecs1 %prec EMPTY | declmods notype_declarator ; full_parm: parm | parm defarg ; parm: named_parm | type_id ; see_typename: /* empty */ %prec EMPTY ; bad_parm: /* empty */ %prec EMPTY | notype_declarator ; exception_specification_opt: /* empty */ %prec EMPTY | THROW '(' ansi_raise_identifiers ')' %prec EMPTY | THROW LEFT_RIGHT %prec EMPTY ; ansi_raise_identifier: type_id ; ansi_raise_identifiers: ansi_raise_identifier | ansi_raise_identifiers ',' ansi_raise_identifier ; conversion_declarator: /* empty */ %prec EMPTY | '*' cv_qualifiers conversion_declarator | '&' cv_qualifiers conversion_declarator | ptr_to_mem cv_qualifiers conversion_declarator ; operator: OPERATOR ; operator_name: operator '*' | operator '/' | operator '%' | operator '+' | operator '-' | operator '&' | operator '|' | operator '^' | operator '~' | operator ',' | operator ARITHCOMPARE | operator '<' | operator '>' | operator EQCOMPARE | operator ASSIGN | operator '=' | operator LSHIFT | operator RSHIFT | operator PLUSPLUS | operator MINUSMINUS | operator ANDAND | operator OROR | operator '!' | operator '?' ':' | operator MIN_MAX | operator POINTSAT %prec EMPTY | operator POINTSAT_STAR %prec EMPTY | operator LEFT_RIGHT | operator '[' ']' | operator NEW %prec EMPTY | operator DELETE %prec EMPTY | operator NEW '[' ']' | operator DELETE '[' ']' /* Names here should be looked up in class scope ALSO. */ | operator type_specifier_seq conversion_declarator | operator error ; %% Parse-Yapp-1.21/t/calc.t0000755000175000017500000001174306742144157015561 0ustar wbraswellwbraswell$^W=0; # Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl test.pl' ######################### We start with some black magic to print on failure. # Change 1..1 below to 1..last_test_to_print . # (It may become useful if the test is moved to ./t subdirectory.) BEGIN { $| = 1; print "1..15\n"; } END {print "not ok 1\n" unless $loaded;} use Parse::Yapp; $loaded = 1; print "ok 1\n"; ######################### End of black magic. # Insert your test code below (better if it prints "ok 13" # (correspondingly "not ok 13") depending on the success of chunk 13 # of the test code): use Parse::Yapp; my($testnum)=2; my($parser,$grammar); my($yapptxt); #Test 2 eval { $grammar=join('',); $parser=new Parse::Yapp(input => $grammar); }; $@ and do { print "not ok $testnum\n"; print "Object not created. Cannot continue test suite: aborting\n"; exit(1); }; print "ok $testnum\n"; ++$testnum; #Test 3 eval { $yapptxt=$parser->Output(classname => 'Calc'); }; $@ and do { print "not ok $testnum\n"; print "Parser not generated. Cannot continue test suite: aborting\n"; exit(1); }; print "ok $testnum\n"; ++$testnum; #Test 4 eval $yapptxt; $@ and do { print "not ok $testnum\n"; print "Parser not loaded. Cannot continue test suite: aborting\n"; exit(1); }; print "ok $testnum\n"; ++$testnum; #Test 5 my($calc); eval { $calc=new Calc(); }; $@ and do { print "not ok $testnum\n"; print "Parser not found. Cannot continue test suite: aborting\n"; exit(1); }; print "ok $testnum\n"; ++$testnum; #Test 6 eval { $calc->YYData->{INPUT}="13*2\n-(13*2)+3\n5^3+2\n"; @outcheck=((13*2),(-(13*2)+3),(5**3+2)); $output=$calc->YYParse(yylex => \&Calc::Lexer); }; print $@ ? "not ok $testnum\n" : "ok $testnum\n"; ++$testnum; #Test 7 print join(',',@$output) ne join(',',@outcheck) ? "not ok $testnum\n" : "ok $testnum\n"; ++$testnum; #Test 8 eval { delete($calc->YYData->{LINE}); $calc->YYData->{INPUT}="5+8\n-(13*2)+3--\n3*8\n**7-3(12*55)\n12*(5-2)\n"; @outcheck=((5+8), undef, (3*8), undef, (12*(5-2))); @errcheck=( 2, 4); $nberr=2; $output=$calc->YYParse(yylex => \&Calc::Lexer, yyerror => \&Calc::Error); }; print $@ ? "not ok $testnum\n" : "ok $testnum\n"; ++$testnum; #Test 9 print join(',',@$output) ne join(',',@outcheck) ? "not ok $testnum\n" : "ok $testnum\n"; ++$testnum; #Test 10 print join(',',@{$calc->YYData->{ERRLINES}}) ne join(',',@errcheck) ? "not ok $testnum\n" : "ok $testnum\n"; ++$testnum; #Test 11 print $calc->YYNberr != $nberr ? "not ok $testnum\n" : "ok $testnum\n"; ++$testnum; #Test 12 eval { $calc->YYData->{INPUT}="a=-(13*2)+3\nb=12*(5-2)\na*b\n"; @outcheck=((-(13*2)+3), (12*(5-2)), ((-(13*2)+3)*(12*(5-2)))); $output=$calc->YYParse(yylex => \&Calc::Lexer, yyerror => \&Calc::Error); }; print $@ ? "not ok $testnum\n" : "ok $testnum\n"; ++$testnum; #Test 13 print join(',',@$output) ne join(',',@outcheck) ? "not ok $testnum\n" : "ok $testnum\n"; ++$testnum; #Test 14 eval { local *STDERR; close(STDERR); #Supress debug output $calc->YYData->{INPUT}="a=-(13*2)+3\n-*12\nb=12*(5-2)\na*b\n"; @outcheck=((-(13*2)+3), undef, (12*(5-2)), ((-(13*2)+3)*(12*(5-2)))); $output=$calc->YYParse(yylex => \&Calc::Lexer, yyerror => \&Calc::Error, yydebug => 0xFF ); }; print $@ ? "not ok $testnum\n" : "ok $testnum\n"; ++$testnum; #Test 15 print join(',',@$output) ne join(',',@outcheck) ? "not ok $testnum\n" : "ok $testnum\n"; ++$testnum; __DATA__ %right '=' %left '-' '+' %left '*' '/' %left NEG %right '^' %% input: #empty | input line { push(@{$_[1]},$_[2]); $_[1] } ; line: '\n' { ++$_[0]->YYData->{LINE}; $_[1] } | exp '\n' { ++$_[0]->YYData->{LINE}; $_[1] } | error '\n' { ++$_[0]->YYData->{LINE}; $_[0]->YYErrok } ; exp: NUM | VAR { $_[0]->YYData->{VARS}{$_[1]} } | VAR '=' exp { $_[0]->YYData->{VARS}{$_[1]}=$_[3] } | exp '+' exp { $_[1] + $_[3] } | exp '-' exp { $_[1] - $_[3] } | exp '*' exp { $_[1] * $_[3] } | exp '/' exp { $_[1] / $_[3] } | '-' exp %prec NEG { -$_[2] } | exp '^' exp { $_[1] ** $_[3] } | '(' exp ')' { $_[2] } ; %% sub Error { my($parser)=shift; push(@{$parser->YYData->{ERRLINES}}, $parser->YYData->{LINE}); } sub Lexer { my($parser)=shift; exists($parser->YYData->{LINE}) or $parser->YYData->{LINE}=1; $parser->YYData->{INPUT} or return('',undef); $parser->YYData->{INPUT}=~s/^[ \t]//; for ($parser->YYData->{INPUT}) { s/^([0-9]+(?:\.[0-9]+)?)// and return('NUM',$1); s/^([A-Za-z][A-Za-z0-9_]*)// and return('VAR',$1); s/^(.)//s and return($1,$1); } } Parse-Yapp-1.21/t/base.t0000755000175000017500000000510407301724046015554 0ustar wbraswellwbraswell$DEBUG=0; $|=1; @tests=( [ #1 Basic test <<'EOT' %{ my $out; %} %% S: A { return($out) } ; A: 'a' 'b' 'c' D { $out=$_[1].$_[2].$_[3].$_[4]; undef } ; D: 'd' ; %% EOT , [ 'a','b','c','d' ], "abcd" ],[ #2 In rule actions <<'EOT' %{ my $out; %} %% S: A { return($out) } ; A: 'a' { $out=$_[1] } 'b' { $out.=$_[3]} 'c' { $out.=$_[5]} D { $out.=$_[7].$_[5].$_[3].$_[1] } ; D: 'd' ; %% EOT , [ 'a', 'b', 'c', 'd' ], "abcdcba" ],[ #3 YYSemval > 0 <<'EOT' %{ my $out; %} %% S: A { return($out) } ; A: 'a' 'b' 'c' D { $out.=$_[0]->YYSemval(1). $_[0]->YYSemval(2). $_[0]->YYSemval(3). $_[0]->YYSemval(4); undef } ; D: 'd' ; %% EOT , [ 'a', 'b', 'c', 'd' ], "abcd" ],[ #4 YYSemval < 0 <<'EOT' %{ my $out; %} %% S: A { return($out) } ; A: 'a' 'b' X ; X: 'c' 'd' { $out=$_[0]->YYSemval(-1).$_[0]->YYSemval(0).$_[1].$_[2] }; %% EOT , [ 'a', 'b', 'c', 'd' ], "abcd" ],[ #5 Left assoc <<'EOT' %{ my $out; %} %left '*' %% S: A { return($out) } ; A: A '*' A { $out="($_[1]$_[2]$_[3])" } | B ; B: 'a' | 'b' | 'c' | 'd' ; %% EOT , [ 'a', '*', 'b', '*', 'c', '*', 'd' ], "(((a*b)*c)*d)" ],[ #6 Right assoc <<'EOT' %{ my $out; %} %right '*' %% S: A { return($out) } ; A: A '*' A { $out="($_[1]$_[2]$_[3])" } | B ; B: 'a' | 'b' | 'c' | 'd' ; %% EOT , [ 'a', '*', 'b', '*', 'c', '*', 'd' ], "(a*(b*(c*d)))" ], [ #7 nonassoc <<'EOT' %{ my $out; %} %nonassoc '+' #%left '+' %% S: S '+' S { $out } | 'a' | error { $out="nonassoc" } ; %% EOT , [ 'a' , '+', 'a', '+', 'a' ], "nonassoc" ], [ #8 Left assoc with '\\' <<'EOT' %{ my $out; %} %left '\\' %% S: A { return($out) } ; A: A '\\' A { $out="($_[1]$_[2]$_[3])" } | B ; B: 'a' | 'b' | 'c' | 'd' ; %% EOT , [ 'a', '\\', 'b', '\\', 'c', '\\', 'd' ], '(((a\b)\c)\d)' ], ); use Parse::Yapp; my($count)=0; sub TestIt { my($g,$in,$chk)=@_; my($lex) = sub { my($t)=shift(@$in); defined($t) or $t=''; return($t,$t); }; ++$count; my($p)=new Parse::Yapp(input => $g); $p=$p->Output(classname => 'Test'); $DEBUG and print $p; eval $p; $@ and do { print "$@\n"; print "not ok $count\n"; return; }; $p=new Test(yylex => $lex, yyerror => sub {}); $out=$p->YYParse; undef $p; $out eq $chk or do { print "Got '$out' instead of '$chk'\n"; print 'not '; }; print 'ok'," $count\n"; undef(&Test::new); } print '1..'.@tests."\n"; for (@tests) { TestIt(@$_); } Parse-Yapp-1.21/Makefile.PL0000755000175000017500000000222613120173475016166 0ustar wbraswellwbraswelluse ExtUtils::MakeMaker; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. WriteMakefile( 'NAME' => 'Parse::Yapp', # CORRELATION #py001: $VERSION must be changed in both Parse::Yapp & Parse::Yapp::Driver 'VERSION_FROM' => 'lib/Parse/Yapp/Driver.pm', 'MAN3PODS' => { 'lib/Parse/Yapp.pm' => '$(INST_MAN3DIR)/Parse::Yapp.3' }, 'PM' => { 'lib/Parse/Yapp.pm' => '$(INST_LIBDIR)/Yapp.pm', 'lib/Parse/Yapp/Options.pm' => '$(INST_LIBDIR)/Yapp/Options.pm', 'lib/Parse/Yapp/Driver.pm' => '$(INST_LIBDIR)/Yapp/Driver.pm', 'lib/Parse/Yapp/Grammar.pm' => '$(INST_LIBDIR)/Yapp/Grammar.pm', 'lib/Parse/Yapp/Lalr.pm' => '$(INST_LIBDIR)/Yapp/Lalr.pm', 'lib/Parse/Yapp/Output.pm' => '$(INST_LIBDIR)/Yapp/Output.pm', 'lib/Parse/Yapp/Parse.pm' => '$(INST_LIBDIR)/Yapp/Parse.pm' }, 'EXE_FILES' => [ 'yapp' ], ); sub MY::postamble { <<'EOT'; YAPPPARSE = lib/Parse/Yapp/Parse.pm $(YAPPPARSE) :: YappParse.yp $(PERL) -I./lib yapp -m 'Parse::Yapp::Parse' -o 'lib/Parse/Yapp/Parse.pm' YappParse.yp EOT } Parse-Yapp-1.21/Calc.yp0000644000175000017500000000364707003362232015427 0ustar wbraswellwbraswell# # Calc.yp # # Parse::Yapp input grammar example. # # This file is PUBLIC DOMAIN # # %right '=' %left '-' '+' %left '*' '/' %left NEG %right '^' %% input: #empty | input line { push(@{$_[1]},$_[2]); $_[1] } ; line: '\n' { $_[1] } | exp '\n' { print "$_[1]\n" } | error '\n' { $_[0]->YYErrok } ; exp: NUM | VAR { $_[0]->YYData->{VARS}{$_[1]} } | VAR '=' exp { $_[0]->YYData->{VARS}{$_[1]}=$_[3] } | exp '+' exp { $_[1] + $_[3] } | exp '-' exp { $_[1] - $_[3] } | exp '*' exp { $_[1] * $_[3] } | exp '/' exp { $_[3] and return($_[1] / $_[3]); $_[0]->YYData->{ERRMSG} = "Illegal division by zero.\n"; $_[0]->YYError; undef } | '-' exp %prec NEG { -$_[2] } | exp '^' exp { $_[1] ** $_[3] } | '(' exp ')' { $_[2] } ; %% sub _Error { exists $_[0]->YYData->{ERRMSG} and do { print $_[0]->YYData->{ERRMSG}; delete $_[0]->YYData->{ERRMSG}; return; }; print "Syntax error.\n"; } sub _Lexer { my($parser)=shift; $parser->YYData->{INPUT} or $parser->YYData->{INPUT} = or return('',undef); $parser->YYData->{INPUT}=~s/^[ \t]//; for ($parser->YYData->{INPUT}) { s/^([0-9]+(?:\.[0-9]+)?)// and return('NUM',$1); s/^([A-Za-z][A-Za-z0-9_]*)// and return('VAR',$1); s/^(.)//s and return($1,$1); } } sub Run { my($self)=shift; $self->YYParse( yylex => \&_Lexer, yyerror => \&_Error ); } my($calc)=new Calc; $calc->Run; Parse-Yapp-1.21/docs/0000755000175000017500000000000013141025035015126 5ustar wbraswellwbraswellParse-Yapp-1.21/docs/fdesarmenien_20170307-parse_yapp_assignment__email.eml0000644000175000017500000000361513060003746027161 0ustar wbraswellwbraswellReturn-Path: f.desar@free.fr Received: from mip.hushmail.com (LHLO smtp1.hushmail.com) (65.39.178.78) by server with LMTP; Tue, 7 Mar 2017 17:55:44 +0000 (UTC) Received: from smtp1.hushmail.com (localhost [127.0.0.1]) by smtp1.hushmail.com (Postfix) with SMTP id 0F2EF40314 for ; Tue, 7 Mar 2017 17:55:44 +0000 (UTC) X-Hush-Verified-Domain: free.fr X-Hush-Real-Recipient: william.braswell@autoparallel.com Received: from smtp1-g21.free.fr (unknown [212.27.42.1]) by smtp1.hushmail.com (Postfix) with ESMTP for ; Tue, 7 Mar 2017 17:55:38 +0000 (UTC) Received: from [IPv6:2a01:e35:8a59:5440:e559:3978:edd5:c7d3] (unknown [IPv6:2a01:e35:8a59:5440:e559:3978:edd5:c7d3]) (Authenticated sender: f.desar) by smtp1-g21.free.fr (Postfix) with ESMTPSA id 57D94B00586 for ; Tue, 7 Mar 2017 18:55:32 +0100 (CET) From: =?utf-8?Q?Fran=C3=A7ois_D=C3=A9sarm=C3=A9nien?= Content-Type: multipart/alternative; boundary="Apple-Mail=_E77B7410-6B48-42C2-A2CA-7D2E9FC863BE" Subject: Parse::Yapp copyright transfer agreement Message-Id: <087E711D-4B20-491F-8932-BE4C471AE048@free.fr> Date: Tue, 7 Mar 2017 18:55:31 +0100 To: "William N. Braswell Jr." Mime-Version: 1.0 (Mac OS X Mail 9.3 \(3124\)) X-Mailer: Apple Mail (2.3124) --Apple-Mail=_E77B7410-6B48-42C2-A2CA-7D2E9FC863BE Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=utf-8 Hi, Here is the signed document. Congratulations : you are now officially the copyright holder for the = Parse::Yapp modules, although it would be nice of you not to forget to keep my name attached to the = copyright notices as the original creator and copyright holder of the original modules (mostly for = sentimental reasons :-)). Enjoy and best regards, Fran=C3=A7ois Parse-Yapp-1.21/docs/fdesarmenien_20170307-parse_yapp_assignment_redacted.png0000644000175000017500000015334413057777033027537 0ustar wbraswellwbraswellPNG  IHDRI/e0PLTE-+.OMPcaerpt"bKGDH pHYs  tIME 2# IDATxyp[Wz/I{\t9b(RʙnȒӖHl|tl*2%쏔,X-X,W'i-KzD"֛EnY$pϙs.(YΛUWMOHw;~X0,`mhFa;(0whߡ;4~Ch B! d { f(0  eH,@')c9`48Fʈ"*@_B"/ ȑR:t`έ&Eui!tX1o$Mx#"F̧/bR+Q? ,gX%D2MGXGۅ {)vh_ǻv3u}aסJd O/547ZnhL)C\&' gZ[>߶_hjki?ַmh0-6H0ؿUhƑ)ȡWm?ټ߰o*S|;77>}Qkߩ{~wd7ފ1[۷|[g|Iۦ߷4őf^%޹uyi}e;qHS@j} w* F.ǻm6C"֑}޷UU\Ӷw͕?o,y~"?;om"οYەW="[Ǟ3X?֮zsݟ9^ö'd-2Ǟ;[6rps5[75ǽ;w+o. w]6}* /7X0PWӖ|K4Irw sĭ?;K)פ{#o<6][ޝ͋R M7OAxTA}ƞrB~a1eW|Tsݺ}=wν a/w)ߝ~ZhfdvvnC?C<ЩHt&uMEш,6e<֔:;ܵʁH1TzKnh|~ɬ4H.:\}3.<,(őraY V` zp6+ΪWrB.Ѧ6EGzB&"Pݿai)ȤӹbeAE?hΦi.ʤcB,KRh*>өX4JѻïWkЀ#f9 EoUH<Ě_ )JiBbc8E4X`\UrTP$7G,ƧGЌϡ E/{1άnܻe,3LdD%RX& ,!<AOphW9GP KA4SW_ a a","j\/ƈY_/9heg= K \()d+KGUpHC+\Ȳ 2B`> :\%&1 2G|py10?I t&݃,Njʨzij?ˡl"Xayhs|*iKs!yT,XIٸ@GWwLCb?d}~DiE`1V.*bVp+q@s("mi2j99% ^iXFS~A.7w.+ϞJf*vdsO؈?ɻOfywbKňt&C b;uᇅ|Y`YܰpOO,H.J҈tidBTjF~E>Ca4h8ۮo{,99ͭ\jtbU<Sc7$Vu:V1jRIzS®c=ǽ=$m:zZ'~U+zmN&=M_2 -)co옩c׬ Udb񬨟Y4ԖAi[=v[FKpRéɝor@ӱseݲNpnh:ձ,Xt-'O ٙP$7leIKϿ9¸V4mTkgMRYRG&U5YaA٣mأ|+5)HgQK{p ˖dw2\ RtFq! 1L3kkFeUvYC|Ϊ;óFXT!&_egĸ^m݉)V;rB;еC>^jڎ::Y~}O t  or~${}.vVZqwNl-GOAiKn us`NtvPj^;#/ $$UM$VtQ#TcLtOk 8$V}0rS}} _Lk\9*:ԕr|Bӛ+pv\vM| ,pY(DTܢ!`1Kfuɛۡ(jwh s^\{~|&>ᜫcę/B{[^~>|Qv=`"57VG}vI, b~%Pׄ )fGvU!=ޗ JfdP1|ҹ-2Tds9t0,W5ݢdJY%Z3/Br*Hzّ:#`ԭct!Co0RU.Kډe !u2˼/`W#9q}!f*$Cr $I"Y$a5H@EJVU4殏>pMb$:a%H", 'a }hgcA ~eÏ| B/7H0GҗBP )1xSXab<Unzb 0#P 0.7M toT"$Ch#!M` yL D5T sb^0ˁDRxt:FQ`aSx#߿,1c_U \W5O]~xtU<pǪb//LFȠ=۠E~|_FTlvEе +$Yd}"><5rjFqXD S'yM (Mvb Wy9P?G%ФN[#pYMS4[h#Ah,MOLŗM,~Hl#t 89* F\hQ1,w9͒\}X_#KCAzaYTD:ђ"VA '+b2c q@E՝)jHR! )_|0hP7cP_@Px6?3|L X/rL"!4$ C7m2N"S5sL/enKAIR*BL%&hF6He![ 0mZKՊrY.8ڄ*[*A*ދf&@'Nz?_ 8S)>>7i X3oϻ_;Jdv KXvĭyebvs"_?֡^ntM-jSi̲ F uQ1.HE:$7Lw6;Gu֪Pk.b5 sZ~pvHJ0: =<> g$`x~7+ hv|0|?b[.g-9zY1F5 >Ԉ>t_—Una]DhXl]f\Xؕ5nw01ͫQOi 6EݫG-WiO jc*EWl HS u^9Txg jsSV [5K%e.} \tA?زl޿Y嫬IJ/ ˨Z—FR /xGaxYwm]^@av@vDsxwYW\8a32Q31JȮ| m:tZBGɵAL=6G%ف/Ut?,9ٿ!g}X !לEU|Bta޿+Ba IЏ:hXM4 81_?<\,N^ {&[l jA8-LQ_Ltj3 /j\jw<1lEX;*ZXr")-K=kY- U.u6f Uڽ}pY9(ɲq͐d|D| l?|!*Q{X |L[솿&zIT۳$ шc>U^FW}0 ؕ8)VѰЉTjcoRd%CYE/S.Ո f`1hXE#ހX"%H3TbKL1K 8+ud1}ʊ j>T2A䖕6/Tp6/8m.  1ǏK %JFoY$ߐA q">.񊢊F4'βsӐ]*}oyC y) ɳtFL¨E7bZ Z^\R_GK>yDZ 6ϧ9#rWG|Q_ӣL`t6h>ӹ*{qW‡-D'bSAGm+iUIH4%*=K@ k:<)B_xhz1E|]&)+r\(:]ֺmۏv+tB>AɅW64۱vbs8`fcȼ/?BB4A 9`jˣ0z[opUĺ=2Dc Ȣlj "iQvDޞKJ4q1̑dR+s+WRSj6˰~ʥSm5?`hTc_9$l=3tAۯSxvH1htZ(y$v]c*qs[# ,ZX!<#KtdJ_PJfQ3J3ؐUg vL!b33bYcM>~[fEh_#A#靀65575e^QHs`UDpf۳yjk.’};5Q"Au؆;'J.\)00tM=\:y0fv}<h|Ij39V46a~E{V?-d46n;ԟ ]6N-?5[,rNu Sv/7(#~@ns:LoX{4&v"[1")NZ<[^iDON[N^ ^688z.>ީMKҞ(ۡHLtYhބ O,Aa\q6 4>uͩ|Dh^2s I2{ibΘ ɔ&n=-;I 4zer-sğK^BC1u2ԑ`QKq5%;|ԃI`XNtZs5+/QSRTЏl(`"G>s[kjW<4)S&m x?7:,AYQzQAY~m=`HۃV;1^"B!s@sQ]hv6eeiw*grт`>m7Biٛ-fujsA2Jg!C:jR!Uy4J:tZh(`Ua+HMG%UDbW Y*Ll>~a9K>oO%U(APdXސHJu#8ag3 AYJW-t*QdåT5Xe+t#C4Gb7x`& >AbÆX"udUVEhbAs;Q Ej&.PQZ9aA8ɝm1p`M3֩D}dKHB. <1(d`yH}4eȬ;d; $ 9Ta$k ֧ —#X Ԋ݁}82Q|#FC4XJFrx2xۈOò յ .)>P%'\DM` t<ҩI" 庨SvɄ @))QϬ/Y^!a>\ufeFb2s*HQvMvT^nYj5dr[Xg{S#6@^uP;퍇+!x;ʶOt+G{)xSU@;"URH&OP&RX1+slD V/6,3"y]B4?OldMVmgꖭ5=sc0`<'ő}.|*`h֛}fD6%J- 1 OG;ޙe\4S*G9ɓׄH Glڸm@h@KX.嚰Zf)XCgbNkQʩ.}2:;3ѬOu96hU,(gr9݀,sj9@Ꭸ="qvV<#]MyL]K$&MfgA8 ":dryE&}ez)W0cڳV/ͳ %Ÿ\aWژL(y<>?s[dhD|@岈 ,JnD]8KX;*6kM' H |<1"[NT.?]D2i c2y6/d2d$Nân*+bG)"He}h. +Ԝ_J9p9\1XG$?ڝyȜ2iBF(yR vU_XL,T;.gސ]>BW=" iIPu%.>>MPA=yi7ҭs6.Qs95֫7iV7x<wQ8":qgް?I>|\.:kBg%T )Ecx )(b4ÑmQϠk!JE}fr^p!F]V0.8)L4(22 5 ҕy(dR:) #U=7vadNӑx$L6]Eb_+ \YS1sl':E;r:P,|^΁UGPθCEL 1X3yib 'S>?(9-`1aDǡuU>U:L<q+l$N[XUs*x؍çoH,L}I ϰ(կd~T>YT`эPi7&YUylOjyR'&kװ躕 Bt(7E3yx4ix4aB2[|8it !/Pcg4"Gy5*<@ad('؁c/5A|x`u` >J?_B-l]xϱWDG*PupX(E ~/5 3DP~PRX=j,G3 PT O>„HtHV$қxg\NDX<'óȨLitE )%nl}9T\S$-CeI0 БrW S]P<2lCVt>91箘*̇).]wk+t$$-0a2il$Hs\QĀsG.@Xr%teDRpxmT#LUƘ?"_AF<8$gaQxf(1c-]Mw̛kϜ鸨ʱ g!J:FV|va"*G'׎ &7fmG'w[;ݑ [YS.Yݐդ7!G,M,(n_ڒ8Ķ׈;ҚQpnVnvzTKX(\t[]y4Wڳȱ/uRbЉи#]]aO{i4ˮKy_D P%iKjɬpDIҏ(K6U+w|{3ñtO8,? 2ISdVd_M4bҷ_񾧫Q]:DNyɞyp@i։Īf{mK@ruS;_f]bŢQ 2MRE[ǬT*d7-9P3bOP20` [sqlfu@6됢@>f#jsjYoP% %*yT[ 4,c ڤ >]RҺ_#4fCZS؉4;I./3Q|ؠ YDN i>{ߖ>6 q~i@-YmAVi,86!h' Eڶ`nl1zUdyNvV2ŒD )ʐGQm9ݸAKޒҩ-@wbf*&v \ᄅSzDn;t54'EӏT`,מihL ^7NiO.N u8iNF5P )n|`olU֚oXg{A;#/F'FCU䦟O[pVrGCܤO:ApqĐө/[twTL.Ys vC~^nU,(~{ڝlhs_r:WzkEoϥ[;+W# bj ZovCuO^צ\Ac X} WW?SM_bÇϷ̺"U7uZUIavLODO_Sjh*$Smed|2w13;ߌ:L0Wq$S!CM#SBt JȁFMQX9 \]lS|L6%ILVG_T^,Õ`,ۈøF#q(wxDމL@p5S>),ŇWaBFL΃p"TIPōu?tәT<ξ$jfdzl51O0!HDPgR,El5מi$MmqIX  iG)5^8oaXAj.g! 93~ïP˘ף;H3%g2`q&׈sƒx|7JW<;L%ǧa*ɕcTQI< 3ˣ@ ÏqجB~ Ng*"^g'꣘|ԟ&sGQ(648]]r68 8IC~;%d>Tm Sîxz N@UiV n#71.koS}9X?Ċ ==\ 95<%l9B2HKwC$&:S:BdsbSH+jm6l 1c*` YsֆvBhY~4lssX]3 N$U VV* 8g+@8#g 3ű/9nT!88n\(G~2 2/Q:C<'*1)dct&(5(Ĩ\\ @qq;7O+4iȤD5{.qn{g,bE HjnHXrcӍ)^~L}Ƽkϧ5= HaϬ/nRtzNsS򀭩{fHv첤LmU5ӎsᲬ[(ga4pP9%Tu-H:Ú#S i|jsj'=;~S+7Xxq`P+5S n8H M[Zq 0]8mPdnTϞV.z/YS\g˶_ꔢ1siCC[S^=r}t,,&;^^kݳW RٝfFxG|FHMȕ{NԕHo#-S73zlopXy3JG{ޕ:VRklnorz4=e׵_oݟuom;:AM'.u߼p~੝tl~ /YZ^;{ڴ8A|2ڞMk&9CCԍWcmƏt~Ɍ_W뛗DSyk&#=yaiJ޹{" Ff$9J:Sn^nrrmAFd4`{'bl"b˻3'[s{?$߼AmyS g^ iXxXo7k 'R=3<]?dhO~NF9v `ݚm޹+~(A,Llj elKxbrVqӦs n^>x%ԟ'H+ ߘ?c DSv%OSTN8Socf?yyR&9aֳgǂ'8Mp~Tix^i\ؙ=SgQwm"}uғxxcu4ןGh x7]ټx  '9:^{o>Iwo5[5Wߛ ׇ䡃 jЫ]1VrKEE_X/'鉢*ʁR1VSWX ۢعDHի \q<[6&їPi:(K ZfC&qa@dkh+gTu䴯썎Bf_:vDr77M;{b֞ gBm/6v]/6de~mb 5|I?}pU IX~M} 6[4;Or,ެg1SA$*O9+dN &˔_!ExOP A|^S`*G4r9:S& plg@ޙ* OOr>`~g/YFdqz1E_\3Խށ:{ѐriܾp,)rn{9ɠc s,@CLg#>Jfqrh[D0!?:_(?RH8x)_9K8, .G"j 6ܑ?*Y_I|iD 9|jufH@(`ʅd-yVX#GŦ< X G_xsw (ݮϑN+yna,)$dNF#jݖ0T^ KQC;cAgdWۃ5W]L.u7M9[*rX7|J2!vd;U4HmyޟE熠$ S9?yD Cq+a\] ﻄGݘNN@XRzORgɥn2&vvSZvǶb˩w]yH{g_yɿz+ 4w^5gf|GnΙ:z}B9lx۠gܛߝ}^Wּ2^|C3-~K\ԻJϾIyk.`+A>Ş/Q-׿;ztepAxl{4{=$ (k/o=zxw͡k.:ښ ׅ{:MwڎL6{P>{U<3Utsaxs]qbOdzw/rYz>k[ww|䯞̝? IDATұh̓Go^ݿO'?}wZ6?C\oٱMvjr?9>mŸuoi>?mop{4o䍭[j9ºCGlqBf͗ Og|⺶?v`FvT?Vmrl~zwT7w<ՋNJ{ 5,Ů ѡ;r_~csM}O{$q]m]8*FP;cGNF-}f+B= ~RcCz[ݼSdۧZ'ۯs0P=^[-_{Z1ƃ}hͯ[2$q+pw'O|$p[o}Q"_3%e}OGwOr/DnxſB0rM7Q޴VϽe*55,~־ïjnvQ_N}$Ѷma[O6M._yw#Gg ԵNdݗ[??Ͼuo ? w߸C]zˇ_|hC>Sw޻ cT K}ogyؕ{ݱMoݨ=!~ոp`5o'||E*6^~ =/#]U2tm=|M {ɏgZ6Q ӭoi|5Ól}oܸ'{k:( ΕZH%nYxa/D:J~L+Ic.J._Zr}Aȭ8BT;+!5n)GTEzIfI)Yj~qA]WjqĝQ xvI3#^=?ol2j'Z/8Q;^es.̃=ƣYHOQ_ڶ+-G~߼6ʉ Gˇ LprϾ}R8%<ݓh&? (_MŁt~؜VYv#^.q StoTy]沰29ԍecq\sG`|T@16PEi*Q |3]8WN`C<@!6T2)cp5I+Zqᖍ܋*ʳh|p1G Tf93]gby_U\3XT|~ 9j'u\q>ȢvT5LTdQW3SC&%g 48IfVcy‡3/X >88/s9QU'B'Y熗7bK. T╀IwOGmRl~ԇMW*BqR]vTSyH)`iSUE 4^ΙgkJu9rbcӍЁ$ljg;NG刪 8Xa"Z9ũ|FOF gBesBõs /tsH˸d? j5$֟3UG U:M,XgVXpz!a*` ,DQe$?,|kK52~*._ %~muI)?jQEl LdF HB@yEA1 /}5h_6 _MKCݻqe颸t4v4f:e;A&$KF%>$OK|yr;HIql ʊ- ʌc/IlI$Nl`~Ӊ%nOǑD޻(ۙ3sq.m&U} osw<:l=\Q7H3\*UE?ovR$Ԭ>Kuф}]27:3[c86w 2J aj2 1Vt}V+pCK >#W0kp$͋5{Pdm5[ !kKf; 2Qٖߴa^nyǧ{8tm7͉z摣g@un^/[ 3d\ ޅ?1')Hw?Q1Ӎ)9ok~/j<<CgU[!a+ }U nHyi[p-%p' YKezldrkg<9Qai 0J} bmezg5h.xI͇Wz~w<;ٓ׿yf'?xUO#͒:;+Χ&>#F'-Ѯk}o_O_?r~ĮwG_6`{CG~zm79][ ^:p:L9='_:G:<;0)8_'_6ߞ?}[{9vC{WKI  $UONpɋWvI I(1[0==|tycE#צN.3?qxUqמrS۽/?4=ž^?ŧWOOY;\/?u=e' >yO}}On;8ܧC?~I-w_O뉣8Cor^9~S1pD'?<%;rz3_ȭ7ox~qמvӥ#zʋ'q|?5Lt{O>ӶNRu>Qkؾ#?w87/~Y'9޳rp>{޲x$Gޜ)6ˮ}_\vN{z?2j=nvax/+}tO>Q-]<7]o݃/(qę?tyJl^>qfdִOn>^NONڅdG.?~~م?_{O<goFEcywG={^Wg?~vdwʟzCe'vb>{s^d~߇{k3{a=/|{tiܴ?;ٞ1C^va?ۻ`هP4 Tf!׊Kln88**T;ΦjT:^Jo,%MwhBUM<췞Wu1-cR ƙeOv_2:澦iv-I7oURY=8;XatoٹЩw;0낡OS:;j߭#W]/%y/=_7qDHי>]G(JïFRH l}ӈKö5 f؎p_=-vNq09M^Y` ٞ3,#<c`k,Qr8sD|\0mݪgkD2-ZVbdm>J;gZx9!VpRtl{b\+ʹf8lk=-9Ha gv,RTufE6Md0[! Kvb1|9Ɋ X&xHF%V^6X  U!6 rDR dSJ *`% tbVXHHJ*=CgzHY,}Y2P!1E',Zэng8^d)Ѝ޸Zzj=̆i(R +TAHѿ])GƙS 8R&AMT5 To$ p/py A%6`UP!#a4 s$X \8Z9X|LR\AjY3C04w՞ʎ HyWa)̽!71H3> t.g} qv A5煃U ievB3Zt;8U8,$h@ >hZǙ6O遥} Pj"%E;gkҽL͋s$~m.ii,xY? \&EUZ lZ[ _uk%髥fwͷme7`%8}/G'sd QHGKB݆es Mn=A' V5A9QoV  e_ oU}0 rI*C-.>d ^'m$8 ,d5u4xK_}hx#ݮO1Y&c\ײ8Zi:1YsfGhƛ5޼~a߹7gϏBM'^ OFףw~:>H˟+,ts~'b/φ_0zx3);v]'w0D0籇⺡=!V |PT8o1x" Mi|M@0:*5.29շ-6g\kuZԦ[iHR#'@"Ĵ}_ 1 D/7~n~qΓA7o|~_"z}}SmpU;yȗ?Sh2l^s-i_^?;z]p+#>l{WzQ~ G47 T7k@47_}{bQ9|v g/n^9~tiʌ>YnI66^_onj{3dHӣؓbO^S?Ŵ߱< ~ɧ>TخݿTC_g vl硫_⧷z(|z{qg.Ǩ[W1qn8V[C nVg1Yy]!n DV\PzDٚ}/KSbup g3oGb/x2]mk<_o6]"n\S[56ؗs,JG}Xy't v\|SD[jش*_?7'NÁ8b\љbWAKD%6~T~jDW_?MT{C?2 n J/RDQ-gJelmqo 4B[FTf2RVU d*AI6.}ƲmݕKkp+ҪI)RnͲ.ULX<> 'k{բƺ&̊6Rt\284e>YVdW}%%Q#פ bpfc+8hQϗ4"`Za5:nixqEgQ$v(j/+S*O!Z&"2HDḿ+S+=dl<+ [<Ӂ L %AT~ 6fjZ&m8ad.TQj0d o,xbi|XS7~"f5,}xN H>8ȎC< DV~cKT~ H&ahs,Avǃ}x&"9H3fppbT+%Od 82HUZ?C#n9H&y{ ;C: !OIQ1BJ O#->jt#B&̠ 8Ӊlĥ6gUYza |5ǡEq*Z;m&_\w8eܱ㮇|U(/ֳxږ%mcZ#D1JxEcy# zT݀$<ՙ&XYc ع&;2 l7@xh)u/8?󉯭6^P{sۨ'#Rf]$S[P19 QtϬut \}]T{DjiG[ză\KEc v#O!1a㏜hu23;8Tے1mh2эXѽ~w SMWY;g Tgb(((˗]x cYA{##=FӲSX-aU?6@/ZN :q9XlkU3+k86\vgF+5e<a9G\vS,Ta"5dRHs깱k ZtYn8 J-w=¹kܚؼ$S^LdL|ѠnnƔ m |Ē؊%j&1J&=-ޯ ɤ!|Yv.-}Lݶ^M,q,1!KD4N/-s"Ht^?4KeqEU`-re:+}reiԆ2hhtp'Ƚ!o :Kg.=x9O/cS#`ީ˕k.}ӏP` u@P܎Ў)r1Gwٱ-x7| zw8h-i,6ďP5= v ˋЍhsV|;h@J?oW6"GP[9zq{d͏: it'{#sM78BviUxaӇ%)8H^* "oe;r\4r0;`q$O1h Jf8{.z?$L*QEx%TC%;7)!xa}hud^x/$HI؍%nC $Pjp\K$@$"\6 ^hM01h6fx YaPC5zh(C.t ]Fjtt]AN3}FxP,< hGY4W踍gbm~4.8 IDAT8DiGt"lW*bM'ؾt(KSBPXti NVQ ,ѶAROGL/ /eyIXJl\N*攪S+m̺+瘖{1j[h"7iK!ֺ;G,Uﶧ-䳡U=K/lr[S/Ydj)OPL!MJl#VXJ73iX1a͘4JoemX ׄ>Db5F Y0oLt,msY  pbrQ>គ $㽱 CLwH~@w`ɲEAuK7{GgQZ8|j4=6a佹Qn7dB?F'dlcIl#ɄΪ1j3s1ځS+y<\Ji2e-viuŸa[fy0Ҋqizհhؖ.?"m꓇QCa>Ks/|f|̎x8{c=fM87'y[-9BѫgBa٬4z NΐjuYwiܒOu"ǖB4+8MqJbZhF=ac "k5m8n8 Ӫ_:sjV.И" gM9Ph ЫRkjW'6{ξgԏ7Wڷ$W/Z[0p_|U YnKlb ;ǔЭVH% v˩%.Zet2$ yD (闕ghzlE8mcqK8A4А?j_sW8st8Yk1g쁕J3DgAL-Vȑ;8m^̪l2hcS;x7t>^bLpr[MixWT\(qyL͛d\u16/}=Rs]A.vy'X('X?RC%}88v2OBXTȂ\#t;nPLLB{V7R%aw.9&TM;31Ri_`1F2PΈ 8-N(FM5 B֑/ۙv7Yڒ*`2 \ N"I:pMjR4mx ΀Xa-z[ .d3 1`‡Uzc Lz/Dhr. \$2 ߮KS+Pۂ+ -z[pQħ|=MG>ς Z^5,'RvULN,<-q}@Oqm^s5Ňf:x-+:DuYKdiy&gpWgUNϦm} ( gGĚ +22Lhv΃XTL*ٶÆ^*t)|2^(KE䌖Ƌm)J h.LTzS12J=S[J0M2!4V5퍖zDsTtzbOQpJ^Tt}D%i/kN"dJ'b.|]qKqV_M1"4d2Sn`&wO6- kW g^ 7՛ʠY95QK%,1m:,9fOOzB^&\֗=Qd!?NhR[BҏsUaE÷5a_FmL\} j>ܲ%Ls&kvӼ4o20iXt16hz+]-E[bpfrk`RS &Ӹ֞ &U_R6_Dx *\"4aޒٖED4FS s\9Li4vmíJ*A^ eb@t ύ&kHjD\bPvYP& uq曣"SG%vMݨTÍEN<7t~Y{j9!t$0on6AБuWiUbA: ^`qhɱm9ƦlKKD"Z:gL͞GvO6|ktOSXUc%QjqpƩ &Ce8bЀhh`FR `7cwZݸ?l>L}*8avi.*v3ƅIM(sflc6?XRmx`ZQ̆ed r-8(Vtz(=΄VC#<$qPnWk}.& Jmfagq^JDvD"4I"U76%ajsl8]A~f$PMC@s>tfUp*DSF0Gj M G-7'ئ+{rgFɥ2i*δtqfޮbotx&_iG` ڹub2p,<0Wfr{9:s1, w.x<`Q] \5O*5 3(@yj Y(smK#N M< -yI-NcֱO&$3T!xVۄ 1U8Kr-'V&tf,BsKh.:g7ZP;[XnZXJWum lp>nyO)]uÚ]h|C$|lK,\˄rI_+o|j%+MpXN:u=@"\| Ϥ*)-kܡIzGzʯ E]̲Ta6iăNY(4%^)P/h-+[:-bjT*.ES ΔKJgT-JwtJ$眰bk-ܦTaYv 6n1,> @ z\6-38lNjp 03*54{%$bal[VSWS1wE&Q,Kܻ٪vDﰘx9 DuQv͹Yn*^lq]raaܖ ^f35sYmHFL0U6e5a2s8uK];D`o1[#9~WZzWR%fHcf,◈4wBlrZ%Ԟ9<ׂ jEUG!7h\]3m2b  qI/0pd.]Hr z+AvEL0/cq~.k|st9Vc>1g{CaQj бȤs,R㌺\ă.ǘ.9:n$Zq!-[UڛP|ؤݎi3k9N`"mg3:(U@ka=g4[?Tfqȕ?/-ϴe ?3EMXVGt\6Q1dng^ Wfd xXn(Ш"1(KRU" zTL1rDC16b@;XDw&޳q쫟Pif_{Ä q~P#~^Q ڱ1^rE+6j'߃aҙV:K{\q&dLkވk\K+GSn:@ Ubuա%l Dz1ՐԺ'dQo@S%e)od)&.SL gKcXEfj2뉬Gn6N0{s=Srj.CtL`T&d9L\ st0$r ,0RMat@* 44y57pnwB6<_;78ybbWictˢ;8o6,)QЛzL*ʪoNsDűih6IƞĤSog^=>D=>Ar54p! ("%đ=xvJ.o a*T5 p`aHYB Xzkr'T!^d=K[ zYi,6#t ^W6"D![˰`T)#,] ʵeKH3D,ݹw*NSn#Rɬ{ab=fӭ\ɤ( @iGTBo3Bqo B`Bhw<pNp7,iQāChYcw<@Q6`䖡m ->Vb!Api2pJ8Jl48B % E'֢XAu/hߕNqv+6[-+QLW0ťyK?EP+v8~^:Ъ;Yutcr;=`3&̺@RD,oMe {}vF[> \;A҅YR!$BEgA![*~fp\V=74QdzЗ48%DsۊD(\.лr8֜y-fK1۲1jAm+-R\Ў(4D 1Yaq ì Cm×wZCH=f7ImYgWKI޶ˢQbc sFI1~rZ0/go? WDfwpw2&J棅, :yty+i|g۽&]ܐ(eVW&򮻳t!-Dz=]I#kAdUl>_H[|7Z:UzJ+&:ape[oQbKsݸX33&F,Gm/vd crYBztG{uQz|s CnQm},}G"5C: G0V0*=/ v.w(F}[rxlИc[ [FG'@N58itI b;a-v8TM`BTR Fl f4w]+[o) h+%xԩS?0 ư\=4nRl 5piƩ]{y2.JFow)YX8K辙YtJ't*m Eb΀nj#N'dG]\t8{Gc5+dY%GԦ)ݑ3+32MQ,^ȱsT)Lol߫Qb-B mQiȮ9ޱ 8C~GP#y̚e M/JokZn>T#mO2{SkFU|TxK<վYcB] K5ؐ䦹qB|)~k m܃+E!r?#fT.̊ R WYѴѐј8i9Vbr-/u (2׸ɹ BXV-֤VMBce?xTMjKȾ ¤g:U{Zd96I1jwv)S^rUvcˆZO?? 9]mP\?7%-6k FjT*[NFMraruL,@4T}^nt)ѸňYԡ^)"0O %xp`oȂFkXtSmS# ETMXi+z@|O{XW )o` ԫlR"^ӕ"RxֵP IDATQRME:&2BfҚ,57zh{,˅ZIzlȶ8I枰l&񩣅yie5 X!]7 ˶4$QJyI3gB>עҕ=wZSe ,xZc"\ _](7hvGæ-T܁QXT?(qbC#HElJ=-ay1]H.83ށw $tAPmȆZ%>N[$َ-|ڞ@SX4e$Y0(ARqʭ5|ؚ J2IFJ6TI8KYU׶D4rH셜dp i8[?@U?-uķ+LE$ x;b3 ,LF$:M8TjxA#3(qE1(C4;85X%ѥ٠:eB&o9!;8T3J vgD\ _ Tه ~ GSPǡ4U@E~fgS퉱i(]9.!5Kd+ DmtĖghӨq/ʒx{:3GbRKMrԆ(_ l,P' /B;6s?da=J:4|2dmٙăjqI,.KIP}L;fm5@m7)V)_ȓeSzh>p)ˍUo;LC эZjEiUJ魴2qߙ)Tz;Rqr -I{=_YPW@22KZY:.ijgJ3a|ؕn~m_!ݠѩأb]1CY0%sĢ9PpWRI!1b9⚉ھ+f^x  ջ ON`n [$>n¡%PE4Iq5yEEҺjMZnX #F^C^ ;&_9'ɥ HH18ur1ЇI.\ܯ-jg +Ntl oǙh׽79Go &\=:GHKnb^.8-jFL]EE^㴎 zv3,k:0}5||,kQ+& 厱INpF3,Qh/m}">)8elM[uYlm: `h,dj vGR+ Z=yU~K̩`B Оscky8 4VGaS9݁E+p0CC0O֜ i&YIH tE{㞪_5-nU`O\0DypX'{1 4 W?i2Θ0dbjܜ#$%s" urş/v;&N4yaQpu ؖKB~%qEuъ+~8g_G&be4p6ik8mR&]/B=#t-J0>Q/ @[PI{!jâ|}#ZE W[xyU3n%qqczV4㩁aw m 0g2 sMr fE/LVeRi»WfG֑>m! ukABn.$hHl#i\!(PUR,"_LZ-thf>Y$$q|#gHr3C˅vQوF3BtX\>s^,f[p.2i"xWʪ9[Tkꉦ;.ەhҺEcHŀSf TR'6B%j-lmr+85G -f 9Xk u'K85K =8[!\Kl%hz DGF|w$FEJFOAB ˕baUMаM\?a LbYJMdFl |L <R'X"F$䥓BOH?鶴@#9sB|R;ƪQD`9XF#543ȏ zJª4/dtC8  ?XkS;\<bvjP]Js܎$jGz}h5U9?Zg݇T?dn@ 9Z_A 8͒؛aΌ?n$) d2AАsC="wN@ox9~|duI8bwG߸3 k$ 8:r\Ǩ/CMt3pU"}Õ _7 NeHWT%Ya*U7@Ʈ;M TTgfj;ʊi)\7k8xBei`LS I*xXΐ?ff~n֕a-92:um׺4`q |X{ Bt<ԾC&<37]E| _u">ZiYF^W}MyotFe=g1Azs^umρoQߞLRRCW S)mlC5\PN\^?4d;iѤ<ƽqc> $e dWcJra O >HVAdnφ;l,|kGկ;{6 e顿)(KTcw{̥zm"w赯S!ϋOsWgɀ|Ƿ3/ oD<4<4+?"DŽ홃MCbA>p{;!љiTעb8f5- h$|!;5ljoę9縬~L0d*mu.`/vM7nυۛ4m[ssZ|>JԪSxFqW}8_~xͥG;SElbnJ y2#n3/sAB\ϡ$4{zg0a+F?1H/lfFRgN>4$RveiMH G/4{՞Y%jhZTZ̞xޫ)Rᕊ˸n0Γ#)Xf}#GoDAU'Fgy|N8uУt+G#W}:f;-lU_'wOqgLQX_b\ܦ\;+n Da!XtQl9Akwީ\?h'Ts}yŔ% q=0W^YEҨ.`.ptPe " K Hȳy"e!r?4Pb_hoz1-=.Tj%Mk(Q3fFEzLqd͙ϖLiLP5WBنa8Vn.^2Z'kAuQ9֧A_n#O3/]w껢ry .F=r*0c=X6C+]˞.dhƣ?$K6M*x+ ^:t j=hpZwN$}Wy͓msV.|VBbC^P)qxtJokӥR˴Aua5mր5ޞUqؿ')oΚdˏe߫˓"Tv?es_{=m!!<]C=?pN7]mE5*O-q7CkOyDl8~/5KWLgq{Ovaƴk. }<=6K;!` I/\?&)2a@걄L򷄯D]jwh1[|Xsۿ2Nyی|!US Urx!@$蘿h_gqZd9esfȚ5ymί$ΥS _a&xT6ՂkXwMZ7:1Y:&~u0MONU#zUOW`fb\4]:]{ٌbI9x|ܐqw$;nt$Nk𙨷oLÒJnQa!y.^<RݧRs_ s~;_qIm.n[-1 6xBl`; = )_EL4F$CTAȉsesH/X :7PMl(ʪ(szf YφT g҅ߐz>qq4F2U)Z[H\%gՑʸX̅cD1l%ˤb˹T(S= \@:H1ac=RCu m^B@ @:ӖJ58J?(^``=,CP "^x r<uo`y->FI *b)f[-/V>&93,4] S V`*UO:,DШqo sC H+FLАuvlf `_:]?a'+,L. gY |Wp/q`,g4 c٩Z.$\$*Mcp10|l smgje|W&z<cXI\"xDi, Xkm[o~wndaqSdiHXf .; IXL21G3 s>`1$#2~^o )2D8G(3 &m ~ž?~PzGBO=.oEߣye}BltwTJn hTxO֟}oϾuo<{acXP IDATٱ!b~7K~LL͐ %*~C~i`>dlK\xilrq6B1e{u$:\'Rɱ:r &cZ޼: vDt&ҩ=pEip0;7R"{M"e{rۯ- ޚ-ͧZ܎>yM=4z7oHzduUVĉC%;sDK5OV)z[ӶrLjk!e[2]nk!hv+}3o*" ଊ_t#[2rXCS@ʡ3$}9SWŔz=|S컖N=i7Q፯qfoemj5/+%Uץf[';SyFplɐEyѓsV7o^Id"w.1ӺJ7և;vX̹Cߺ.mؐ*;g=5ЋI&wyZLd[q_eG.3i.kW,em5k\ Q5$Zq֠Q?m]sD87C۵3ŊoW<]WY9W@nm+˃ԯ]Fkꮑ_^V6"o׾Yv.y(uhcSЏ(kM8L=.Ŵfmݗ{z&C~Q~ͤ`t+=-?`J;,TIhھg~Ϟ؏Sr +6I>|kluf,_S7l6. 7ƙe16 ^O,kP$Bq3`$LsLzΆ6V1#/OfnLEƬ=@FoݪIƖTiscjfZkvƓ6K:Xyҿ(pY)ڪbVs=;.]z2u` g7,b#ǰJ%&M0_$t_QQAW$1s (\$X&Ubǡ>5Bxi.Vh-,Fx4yj08(V>6zDQ\a ơ GAM̓c P $,KN1[XKWT2.DJlD4-XÓ\A72 ߙMOA FQMMT'i& tMQuYli9裳1'fio_Md/a=XYSMY{U'`;cZ&Aw9[)/C4L} cI2^ @AXB,lLWS#*s \XJTGHF1)hо"{Zqh`wUm* ml$ʾw`@ߣ:['򫦋G϶]?svv9[wz1NRJ"5$WE=ȥTdeW #r=U.5agUՓU;0Y?dd̚1m5[(x=ܚ2ܮ[> ḏnw d{C+B2nu8R"w1e/Pi/M,qܰL8^3Y?ŕKm|?Ơ;fw{Μ6=b#ܨr ?hM=*Dx醠M^}[{F=|ҽ = e"]});fzS3S%ѩpCd2O^*=({tGo(yvЍ1AV]W1ܜꭾn![9:ef\X&:>B.*uwU;sR! \U#ŚKUj%yՈRi~Tg4?8~ Nrܪ n̈/){Ak737f< ˸yڳPIT n'@oj!b_qBvFP*6h aq(k:9AR,K#P:*YAp-*NpA0i`vШcA KaFQU)@CLx,^G[ܳ$/I8F3T%!#lHºnT[ `;B'DJ E'͒p`\຀pD0(hY`C y)!j6JQ f0Bgfp r,bz"{ ΂0LuĎ߷4&:Ic(.H)Zw8 ϸ[:'ɲ s^A?Vmkj, $|$K|SS& xJ 6!1F6`*` VZL&hP.;"ƳplGPfNy!cP*YO(k [{V*m'c۪e!k1u3؅qε)߄ aјMs6x]ʰY_Ywd۳;Yp?q`jzX6UTTbb{?Il* Y6P09CD2H)` uVEl,dgrsEE>lYNJ"نJ ~Ulّ Ί|[em)AX70 p3=bmz,GFtg XwWyb5_k4m4ZiK[-KSj&( 9⴩u@g~K/;TnSfQmt֮!E%S/Q$ivieꢑ`j]m{pt?94?3czGޏkZn\ux^1ֺK愐a"S!+Z%ϩ8 ,/7?LXVŃsab"UGP*Z ˝Niʭϱj ˆG1?}{0< D\ƹ' iϜ;Oeena<5 ^6nruBQȗ9zq{4mYbrr>ϼ"m,c)Zq=ѭ{.:r&^љ AEa CdqY-kwܮiǞg/녝҈.?>zT_SuHVW#uJi"w9o䠭GY1d8})h﷬*G􊹉0v"i7ѣX2%a6&Q|'|RFRe%@/H:<:VBxd8G{=nE,-NQ tf` e PwYRaAmgDhm0u;oey-.$ nQYh& =SyP*Y^F S z6, 0IH>̨LCUw 0?9o YL֎0$kYK,ñ5sQtoLUVWM5k;G`8Ix=R3ޜ>n'.=<5:|Aw$GV}c>p6kw\ <D oj;vzę0\#:ԧp"jY+|rwmlSxgDJ{Fj0|,ED0"o(&pYuևhڜ19r \d.i *X$]-&;؝ٌ#>B&PI,Qsb<Ի4 Y1$dEJ.4 $D~O5 yj'm]#MWr[e; ]ಅz Ҋ_.يx>s 2;Ʃ+gUͳ,o8g;>Tg" ,=5xDnٕ}y?xuUb^prF znqP/I[$ E%}|zVb0VWN[V-c=3LiLUszM|ʌ`<e>xU֡k=աzڲzP!: }A`,=cX>=Qn>֋rw4,guq}KZ^c'"aN$5ByѼܟHZAlH[ց47h/\ aā5DKƱe*1tҽ[8+rpM.KG'kS^? ]Itt+SJdE˲>c@lR&Y;E$5MMyJfFӰQ^ZBÈNi5#u u)iҖXt6٠h4kМ{8zz,+ C0HqUXHΟ&1\ pFkFIH[+lEk<.  X^ɬָ"*/ gGIDyK?j_ھIDATd6ev ,:Hâ6D',i}^6r RAӛL5<o*F<}6MKI5w( `XOWOFo(Dd|O?6)TI=C7L(lbO2= 6؂  K_86n&3p)!Sl0PK6CW/m Q7Dre OCoA?A _>cS5oBm"(Up.༚WaST yTaJvS:M:M[ g)`1Yo0EsOn ߡP/ 6w!7˓0m$QLKzň^Qin^ޢo@g#~BcJ'W3j2QFlf/5h[rfe"0SӜ%"2GF&k)]}IٚڸMoU6 S{lдG&e*FJXZn秈3%kd^-cNk֤5q.^uaNVUsGxd~x0bx{fƱ d a$jv{z.T_uu/Uz.%L`$k/G48v X7k8r{^^hX#kL{5ġ/*z[H۸ǥ4`' k)qQVrJF̱h{gkRc4m3:mk0, ohuMyp_taq\M5ӭ U2jDR$؀hDLzkmXlh}]>}ZOFU92Z`Y3AG a*fO 6j)],YT 9ZfhW|"C)m IZ_ABf_iv!F[]'S]h`6[Ʊ$[?z"|l|U**c0S&m3Pkp|~@ fe3̀GQ FOz9g᧿i&/U}5r~u%=51OFO4h꿄5Xy5G$í\`OLn) |. ͑$s5`!e z5p1F&)P!i X1? x|֠{'D5#m!}^*%A{S`Xi>l?yO?-!ր?;}oSض~5f %ƶcs5ۘxI{/_z赐5?kiMk>g "1,_I_4f K[ bs?g5 px́blFb?>6mB sd ㈭y /63~@&>EcC /޿$Ơӟ`m2g nDEQvn-?ٴ\y>vx_210 fIh2ulnͲdx+5Seqߍe?WnmﰧWGΗa/bre|ùmFk\ae/\ɿ>7}rlU0,W l>#z 68^%Enbkn&ϳY`IXd5# <7H 'Z7jp6EuQ6k>KT?#ꤟ@=e\nqgkZF&7\4}5ڜh/^xXCMׅmg>LQ }cy_&0̟tJ|}=/`r@J4xǦ$^m,.v [N@!xA7n5i#|2~'Ga CS5xkNN^"jNH|9<,G[x,_ m{aA@[cdxIA~f/20Jj?9Jd[;owiN8$rr'ВBo) Mk [?*2p5m@Q2 |&KZG a $io[7a~A ?F6]%|]DBQiTpfnh)yhblô?.k/I[* llSm͡Rl\.GH3DREʉR!DB O(Xˣ8F9m3!ԁ7|-/?Åɶ MGo]qήN`u0I8Cϱ?;E`F7/oi$q=+9G !rъ5`d+ݑcjBp? V`/GDUJ#s5p?P8~Qjpz' X1`ň'D '@|I,Y7oq:l¨1ߧ2Т*;O}}>XzpEdBe0>(0޿qQCW72JU6Fs{Ѻ|(Og}n)Po)#f(ÕXݘeUU:EE')l6S˖qY8FNe fn9`1|s"?D6FP+05W!luly,VwAJM?ȱ}ҏ{/'^+;wM?W ߽t᫝/[O={Y5ps5zM[BW/͟6z$ { & 64\1 gH\Rho"Spґ؃+~>UܿjhO;Zâ4;,< I bw۰%R藽J~qgMxG9!1X @ۺ#3V+ ` HmN>XZORܪ4׊p;5=m<{eѝvuÊ/8ˉ?ksww槾ʣ.aN2LrE" sKMacy1Ei{s(޳1 woCk]?ݹ1s!R$Ay x )_9@\pOoUqgǁ^2Lx2ʛ݉? /tϟڋJE݁AYz ג|>“0fWkbSQH{Hݦɏ.g^j771%):Lw]9Fqa}X~ g'}.G;_077ux+cߗd@SZJ`M'3%(^0C}[*Dv3ˁ5dvynqU?gyiY8ccio+)DQF|J_Ǿ?Ž*~w?%CߝyIEźݵ1Zeu>ְ+v_:jw ԆsٖZ]ghX됙jpo}q" jm2i{ΎRGc4%4WJ魳j wǷbqcg K?(@O/OQا0L" ) @ <,[![c(, _H vyAڧI-Uk"G(T160<($\/B5UO1?2rH / F၏^cvyHsOoytH|B7}&a]=.I ^C'KQ4]\sb'Ggg8+ {~ߜ;7J ~(O6P; Sp<4<-xA^H1@/CNT^a D*G-aSyF8GiDCi-a +z@,ԁrb,Tp&&V)Ko. A8Ӣe!R& hD1$w+DHԬ? jI" ϡeaM g, J@8@QHrfz r1A僐ɔ`(^@ꩡGP.^w/k"gH`ljd%8*B ~C1b;Vɼ08>0|qQx8?isM>Yc& X5vC*1aCB};cX4mbiiD c'ATeh̓ C|ff$eD& |"6rW[.yp] yapp I<-V> yapp I<-h> =head1 DESCRIPTION yapp is a frontend to the Parse::Yapp module, which lets you compile Parse::Yapp grammar input files into Perl LALR(1) OO parser modules. =head1 OPTIONS Options, as of today, are all optionals :-) =over 4 =item I<-v> Creates a file F.output describing your parser. It will show you a summary of conflicts, rules, the DFA (Deterministic Finite Automaton) states and overall usage of the parser. =item I<-s> Create a standalone module in which the driver is included. Note that if you have more than one parser module called from a program, to have it standalone, you need this option only for one of your parser module. =item I<-n> Disable source file line numbering embedded in your parser module. I don't know why one should need it, but it's there. =item I<-m module> Gives your parser module the package name (or name space or module name or class name or whatever-you-call-it) of F. It defaults to F =item I<-o outfile> The compiled output file will be named F for your parser module. It defaults to F.pm or, if you specified the option I<-m A::Module::Name> (see below), to F, in the current working directory. =item I<-t filename> The I<-t filename> option allows you to specify a file which should be used as template for generating the parser output. The default is to use the internal template defined in F. For how to write your own template and which substitutions are available, have a look to the module F : it should be obvious. =item I<-b shebang> If you work on systems that understand so called I, and your generated parser is directly an executable script, you can specifie one with the I<-b> option, ie: yapp -b '/usr/local/bin/perl -w' -o myscript.pl myscript.yp This will output a file called F whose very first line is: #!/usr/local/bin/perl -w The argument is mandatory, but if you specify an empty string, the value of I<$Config{perlpath}> will be used instead. =item I The input grammar file. If no suffix is given, and the file does not exists, an attempt to open the file with a suffix of F<.yp> is tried before exiting. =item I<-V> Display current version of Parse::Yapp and gracefully exits. =item I<-h> Display the usage screen. =back =head1 BUGS None known now :-) =head1 AUTHOR William N. Braswell, Jr. (Remove "NOSPAM".) =head1 COPYRIGHT Copyright © 1998, 1999, 2000, 2001, Francois Desarmenien. Copyright © 2017 William N. Braswell, Jr. See Parse::Yapp(3) for legal use and distribution rights =head1 SEE ALSO Parse::Yapp(3) Perl(1) yacc(1) bison(1) =cut require 5.004; use File::Basename; use Getopt::Std; use Config; use Parse::Yapp; use strict; use vars qw ( $opt_n $opt_m $opt_V $opt_v $opt_o $opt_h $opt_s $opt_t $opt_b); sub Usage { my($prog)=(fileparse($0,'\..*'))[0]; die < default is -v Create a file .output describing your parser -s Create a standalone module in which the driver is included -n Disable source file line numbering embedded in your parser -o outfile Create the file for your parser module Default is .pm or, if -m A::Module::Name is specified, Name.pm -t filename Uses the file as a template for creating the parser module file. Default is to use internal template defined in Parse::Yapp::Output -b shebang Adds '#!' as the very first line of the output file grammar The grammar file. If no suffix is given, and the file does not exists, .yp is added -V Display current version of Parse::Yapp and gracefully exits -h Display this help screen EOF } my($nbargs)=@ARGV; getopts('Vhvsnb:m:t:o:') or Usage; ( ($opt_V and $nbargs > 1) or $opt_h) and Usage; $opt_V and do { @ARGV == 0 or Usage; print "This is Parse::Yapp version $Parse::Yapp::Driver::VERSION.\n"; exit(0); }; # -t ($opt_t) option allows a file to be specified which # contains a 'template' to be used when generating the parser; # if defined, we open and read the file. $opt_t and do { local $/ = undef; local *TFILE; open(TFILE, $opt_t) or die "Cannot open template file $opt_t: $!\n"; $opt_t = ; close(TFILE); }; @ARGV == 1 or Usage; my($filename)=$ARGV[0]; my($base,$path,$sfx)=fileparse($filename,'\..*'); -r "$filename" or do { $sfx eq '.yp' or $filename.='.yp'; -r "$filename" or die "Cannot open $filename for reading.\n"; }; my($parser)=new Parse::Yapp(inputfile => $filename); my($warnings)=$parser->Warnings(); $warnings and print STDERR $warnings; $opt_v and do { my($output)="$path$base.output"; my($tmp); open(OUT,">$output") or die "Cannot create $base.output for writing.\n"; $tmp=$parser->Warnings() and print OUT "Warnings:\n---------\n$tmp\n"; $tmp=$parser->Conflicts() and print OUT "Conflicts:\n----------\n$tmp\n"; print OUT "Rules:\n------\n"; print OUT $parser->ShowRules()."\n"; print OUT "States:\n-------\n"; print OUT $parser->ShowDfa()."\n"; print OUT "Summary:\n--------\n"; print OUT $parser->Summary(); close(OUT); }; my($outfile)="$path$base.pm"; my($package)="$base"; $opt_m and do { $package=$opt_m; $package=~/^(?:(?:[^:]|:(?!:))*::)*(.*)$/; $outfile="$1.pm"; }; $opt_o and $outfile=$opt_o; $opt_s = $opt_s ? 1 : 0; $opt_n = $opt_n ? 0 : 1; open(OUT,">$outfile") or die "Cannot open $outfile for writing.\n"; defined($opt_b) and do { $opt_b or $opt_b = $Config{perlpath}; print OUT "#!$opt_b\n"; }; print OUT $parser->Output(classname => $package, standalone => $opt_s, linenumbers => $opt_n, template => $opt_t, ); close(OUT); Parse-Yapp-1.21/META.json0000664000175000017500000000143113141025035015620 0ustar wbraswellwbraswell{ "abstract" : "unknown", "author" : [ "unknown" ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 7.24, CPAN::Meta::Converter version 2.150005", "license" : [ "unknown" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Parse-Yapp", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } } }, "release_status" : "stable", "version" : "1.21", "x_serialization_backend" : "JSON::PP version 2.27300" } Parse-Yapp-1.21/README.md0000644000175000017500000000277513120143605015471 0ustar wbraswellwbraswell# parse-yapp Parse::Yapp, Yet Another Parser Parser For Perl Compiles yacc-like LALR grammars to generate Perl OO parser modules. COPYRIGHT Copyright © 1998, 1999, 2000, 2001, Francois Desarmenien. Copyright © 2017 William N. Braswell, Jr. (see the Copyright section in Yapp.pm for usage and distribution rights) IMPORTANT NOTES The Parse::Yapp pod section is the main documentation and it assumes you already have a good knowledge of yacc. If not, I suggest the GNU Bison manual which is a very good tutorial to LALR parsing and yacc grammar syntax. The yapp frontend has its own documentation using either 'perldoc yapp' or (on systems with man pages) 'man yapp'. Any help on improving those documentations is very welcome. DESCRIPTION This is the production release of the Parse::Yapp parser generator. It lets you create Perl OO fully reentrant LALR(1) parser modules (see the Yapp.pm pod pages for more details) and has been designed to be functionally as close as possible to yacc, but using the full power of Perl and opened for enhancements. REQUIREMENTS Requires perl5.004 or better :) It is written only in Perl, with standard distribution modules, so you don't need any compiler nor special modules. INSTALLATION perl Makefile.PL make make test make install WARRANTY This software comes with absolutly NO WARRANTY of any kind. I just hope it can be useful. FEEDBACK Send feedback, comments, bug reports, pizza and postcards to: Will Braswell (Remove "NOSPAM".) Parse-Yapp-1.21/YappParse.yp0000644000175000017500000003107613120132466016467 0ustar wbraswellwbraswell%{ # Copyright © 1998, 1999, 2000, 2001, Francois Desarmenien. # Copyright © 2017 William N. Braswell, Jr. # All Rights Reserved. # (see COPYRIGHT in Parse::Yapp.pm pod section for use and distribution rights) # # Parse/Yapp/Parser.yp: Parse::Yapp::Parser.pm source file # # Use: yapp -m 'Parse::Yapp::Parse' -o Parse/Yapp/Parse.pm YappParse.yp # # to generate the Parser module. # %} %{ require 5.004; use Carp; my($input,$lexlevel,@lineno,$nberr,$prec,$labelno); my($syms,$head,$tail,$token,$term,$nterm,$rules,$precterm,$start,$nullable); my($expect); %} %% # Main rule yapp: head body tail ; #Common rules: symbol: LITERAL { exists($$syms{$_[1][0]}) or do { $$syms{$_[1][0]} = $_[1][1]; $$term{$_[1][0]} = undef; }; $_[1] } | ident #default action ; ident: IDENT { exists($$syms{$_[1][0]}) or do { $$syms{$_[1][0]} = $_[1][1]; $$term{$_[1][0]} = undef; }; $_[1] } ; # Head section: head: headsec '%%' ; headsec: #empty #default action | decls #default action ; decls: decls decl #default action | decl #default action ; decl: '\n' #default action | TOKEN typedecl symlist '\n' { for (@{$_[3]}) { my($symbol,$lineno)=@$_; exists($$token{$symbol}) and do { _SyntaxError(0, "Token $symbol redefined: ". "Previously defined line $$syms{$symbol}", $lineno); next; }; $$token{$symbol}=$lineno; $$term{$symbol} = [ ]; } undef } | ASSOC typedecl symlist '\n' { for (@{$_[3]}) { my($symbol,$lineno)=@$_; defined($$term{$symbol}[0]) and do { _SyntaxError(1, "Precedence for symbol $symbol redefined: ". "Previously defined line $$syms{$symbol}", $lineno); next; }; $$token{$symbol}=$lineno; $$term{$symbol} = [ $_[1][0], $prec ]; } ++$prec; undef } | START ident '\n' { $start=$_[2][0]; undef } | HEADCODE '\n' { push(@$head,$_[1]); undef } | UNION CODE '\n' { undef } #ignore | TYPE typedecl identlist '\n' { for ( @{$_[3]} ) { my($symbol,$lineno)=@$_; exists($$nterm{$symbol}) and do { _SyntaxError(0, "Non-terminal $symbol redefined: ". "Previously defined line $$syms{$symbol}", $lineno); next; }; delete($$term{$symbol}); #not a terminal $$nterm{$symbol}=undef; #is a non-terminal } } | EXPECT NUMBER '\n' { $expect=$_[2][0]; undef } | error '\n' { $_[0]->YYErrok } ; typedecl: #empty | '<' IDENT '>' ; symlist: symlist symbol { push(@{$_[1]},$_[2]); $_[1] } | symbol { [ $_[1] ] } ; identlist: identlist ident { push(@{$_[1]},$_[2]); $_[1] } | ident { [ $_[1] ] } ; # Rule section body: rulesec '%%' { $start or $start=$$rules[1][0]; ref($$nterm{$start}) or _SyntaxError(2,"Start symbol $start not found ". "in rules section",$_[2][1]); $$rules[0]=[ '$start', [ $start, chr(0) ], undef, undef ]; } | '%%' { _SyntaxError(2,"No rules in input grammar",$_[1][1]); } ; rulesec: rulesec rules #default action | rules #default action ; rules: IDENT ':' rhss ';' { _AddRules($_[1],$_[3]); undef } | error ';' { $_[0]->YYErrok } ; rhss: rhss '|' rule { push(@{$_[1]},$_[3]); $_[1] } | rule { [ $_[1] ] } ; rule: rhs prec epscode { push(@{$_[1]}, $_[2], $_[3]); $_[1] } | rhs { my($code)=undef; defined($_[1]) and $_[1][-1][0] eq 'CODE' and $code = ${pop(@{$_[1]})}[1]; push(@{$_[1]}, undef, $code); $_[1] } ; rhs: #empty #default action (will return undef) | rhselts #default action ; rhselts: rhselts rhselt { push(@{$_[1]},$_[2]); $_[1] } | rhselt { [ $_[1] ] } ; rhselt: symbol { [ 'SYMB', $_[1] ] } | code { [ 'CODE', $_[1] ] } ; prec: PREC symbol { defined($$term{$_[2][0]}) or do { _SyntaxError(1,"No precedence for symbol $_[2][0]", $_[2][1]); return undef; }; ++$$precterm{$_[2][0]}; $$term{$_[2][0]}[1]; } ; epscode: { undef } | code { $_[1] } ; code: CODE { $_[1] } ; # Tail section: tail: /*empty*/ | TAILCODE { $tail=$_[1] } ; %% sub _Error { my($value)=$_[0]->YYCurval; my($what)= $token ? "input: '$$value[0]'" : "end of input"; _SyntaxError(1,"Unexpected $what",$$value[1]); } sub _Lexer { #At EOF pos($$input) >= length($$input) and return('',[ undef, -1 ]); #In TAIL section $lexlevel > 1 and do { my($pos)=pos($$input); $lineno[0]=$lineno[1]; $lineno[1]=-1; pos($$input)=length($$input); return('TAILCODE',[ substr($$input,$pos), $lineno[0] ]); }; #Skip blanks $lexlevel == 0 ? $$input=~m{\G((?: [\t\ ]+ # Any white space char but \n | \#[^\n]* # Perl like comments | /\*.*?\*/ # C like comments )+)}xsgc : $$input=~m{\G((?: \s+ # any white space char | \#[^\n]* # Perl like comments | /\*.*?\*/ # C like comments )+)}xsgc and do { my($blanks)=$1; #Maybe At EOF pos($$input) >= length($$input) and return('',[ undef, -1 ]); $lineno[1]+= $blanks=~tr/\n//; }; $lineno[0]=$lineno[1]; $$input=~/\G([A-Za-z_][A-Za-z0-9_]*)/gc and return('IDENT',[ $1, $lineno[0] ]); $$input=~/\G('(?:[^'\\]|\\\\|\\'|\\)+?')/gc and do { $1 eq "'error'" and do { _SyntaxError(0,"Literal 'error' ". "will be treated as error token",$lineno[0]); return('IDENT',[ 'error', $lineno[0] ]); }; return('LITERAL',[ $1, $lineno[0] ]); }; $$input=~/\G(%%)/gc and do { ++$lexlevel; return($1, [ $1, $lineno[0] ]); }; $$input=~/\G\{/gc and do { my($level,$from,$code); $from=pos($$input); $level=1; while($$input=~/([{}])/gc) { substr($$input,pos($$input)-1,1) eq '\\' #Quoted and next; $level += ($1 eq '{' ? 1 : -1) or last; } $level and _SyntaxError(2,"Unmatched { opened line $lineno[0]",-1); $code = substr($$input,$from,pos($$input)-$from-1); $lineno[1]+= $code=~tr/\n//; return('CODE',[ $code, $lineno[0] ]); }; if($lexlevel == 0) {# In head section $$input=~/\G%(left|right|nonassoc)/gc and return('ASSOC',[ uc($1), $lineno[0] ]); $$input=~/\G%(start)/gc and return('START',[ undef, $lineno[0] ]); $$input=~/\G%(expect)/gc and return('EXPECT',[ undef, $lineno[0] ]); $$input=~/\G%\{/gc and do { my($code); $$input=~/\G(.*?)%}/sgc or _SyntaxError(2,"Unmatched %{ opened line $lineno[0]",-1); $code=$1; $lineno[1]+= $code=~tr/\n//; return('HEADCODE',[ $code, $lineno[0] ]); }; $$input=~/\G%(token)/gc and return('TOKEN',[ undef, $lineno[0] ]); $$input=~/\G%(type)/gc and return('TYPE',[ undef, $lineno[0] ]); $$input=~/\G%(union)/gc and return('UNION',[ undef, $lineno[0] ]); $$input=~/\G([0-9]+)/gc and return('NUMBER',[ $1, $lineno[0] ]); } else {# In rule section $$input=~/\G%(prec)/gc and return('PREC',[ undef, $lineno[0] ]); } #Always return something $$input=~/\G(.)/sg or die "Parse::Yapp::Grammar::Parse: Match (.) failed: report as a BUG"; $1 eq "\n" and ++$lineno[1]; ( $1 ,[ $1, $lineno[0] ]); } sub _SyntaxError { my($level,$message,$lineno)=@_; $message= "*". [ 'Warning', 'Error', 'Fatal' ]->[$level]. "* $message, at ". ($lineno < 0 ? "eof" : "line $lineno"). ".\n"; $level > 1 and die $message; warn $message; $level > 0 and ++$nberr; $nberr == 20 and die "*Fatal* Too many errors detected.\n" } sub _AddRules { my($lhs,$lineno)=@{$_[0]}; my($rhss)=$_[1]; ref($$nterm{$lhs}) and do { _SyntaxError(1,"Non-terminal $lhs redefined: ". "Previously declared line $$syms{$lhs}",$lineno); return; }; ref($$term{$lhs}) and do { my($where) = exists($$token{$lhs}) ? $$token{$lhs} : $$syms{$lhs}; _SyntaxError(1,"Non-terminal $lhs previously ". "declared as token line $where",$lineno); return; }; ref($$nterm{$lhs}) #declared through %type or do { $$syms{$lhs}=$lineno; #Say it's declared here delete($$term{$lhs}); #No more a terminal }; $$nterm{$lhs}=[]; #It's a non-terminal now my($epsrules)=0; #To issue a warning if more than one epsilon rule for my $rhs (@$rhss) { my($tmprule)=[ $lhs, [ ], splice(@$rhs,-2) ]; #Init rule @$rhs or do { ++$$nullable{$lhs}; ++$epsrules; }; for (0..$#$rhs) { my($what,$value)=@{$$rhs[$_]}; $what eq 'CODE' and do { my($name)='@'.++$labelno."-$_"; push(@$rules,[ $name, [], undef, $value ]); push(@{$$tmprule[1]},$name); next; }; push(@{$$tmprule[1]},$$value[0]); } push(@$rules,$tmprule); push(@{$$nterm{$lhs}},$#$rules); } $epsrules > 1 and _SyntaxError(0,"More than one empty rule for symbol $lhs",$lineno); } sub Parse { my($self)=shift; @_ > 0 or croak("No input grammar\n"); my($parsed)={}; $input=\$_[0]; $lexlevel=0; @lineno=(1,1); $nberr=0; $prec=0; $labelno=0; $head=(); $tail=""; $syms={}; $token={}; $term={}; $nterm={}; $rules=[ undef ]; #reserve slot 0 for start rule $precterm={}; $start=""; $nullable={}; $expect=0; pos($$input)=0; $self->YYParse(yylex => \&_Lexer, yyerror => \&_Error); $nberr and _SyntaxError(2,"Errors detected: No output",-1); @$parsed{ 'HEAD', 'TAIL', 'RULES', 'NTERM', 'TERM', 'NULL', 'PREC', 'SYMS', 'START', 'EXPECT' } = ( $head, $tail, $rules, $nterm, $term, $nullable, $precterm, $syms, $start, $expect); undef($input); undef($lexlevel); undef(@lineno); undef($nberr); undef($prec); undef($labelno); undef($head); undef($tail); undef($syms); undef($token); undef($term); undef($nterm); undef($rules); undef($precterm); undef($start); undef($nullable); undef($expect); $parsed } Parse-Yapp-1.21/README0000755000175000017500000000276013120143626015072 0ustar wbraswellwbraswellParse::Yapp, Yet Another Parser Parser For Perl Compiles yacc-like LALR grammars to generate Perl OO parser modules. COPYRIGHT Copyright © 1998, 1999, 2000, 2001, Francois Desarmenien. Copyright © 2017 William N. Braswell, Jr. (see the Copyright section in Yapp.pm for usage and distribution rights) IMPORTANT NOTES The Parse::Yapp pod section is the main documentation and it assumes you already have a good knowledge of yacc. If not, I suggest the GNU Bison manual which is a very good tutorial to LALR parsing and yacc grammar syntax. The yapp frontend has its own documentation using either 'perldoc yapp' or (on systems with man pages) 'man yapp'. Any help on improving those documentations is very welcome. DESCRIPTION This is the production release of the Parse::Yapp parser generator. It lets you create Perl OO fully reentrant LALR(1) parser modules (see the Yapp.pm pod pages for more details) and has been designed to be functionally as close as possible to yacc, but using the full power of Perl and opened for enhancements. REQUIREMENTS Requires perl5.004 or better :) It is written only in Perl, with standard distribution modules, so you don't need any compiler nor special modules. INSTALLATION perl Makefile.PL make make test make install WARRANTY This software comes with absolutly NO WARRANTY of any kind. I just hope it can be useful. FEEDBACK Send feedback, comments, bug reports, pizza and postcards to: Will Braswell (Remove "NOSPAM".) Parse-Yapp-1.21/Changes0000644000175000017500000002234313141024663015503 0ustar wbraswellwbraswellRevision history for Perl extension Yapp. - original version; created by h2xs 1.18 0.01 Thu Jun 25 20:02:09 1998 0.02 Never released 0.03 Never released 0.04 Never released - Fix installation of yapp.pl to $INST_SCRIPT - $VERSION is now in Yapp/Driver to check compatibility - Add debugging driver and debug option - Bug in error recovery fixed: do not shift an $error reduction - Add LeftValue, Curtok and Curval methods - Add driver version compatibility check 0.05 Thu Jul 03 20:05:05 1998 - Add LeftValue, Curtok and Curval methods 0.06 Tue Jul 07 20:36:17 GMT 1998 - Error token '$error' becomes 'error' (like in yacc) - The '$end' token becomes '' 0.07 Never released - Default action become a separate entry in states hash - $COMPATIBLE value changed to 0.08 - The grammar parser is now a Yapp parser module - Comments can be either Perl (#...\n) or C /* ... */ style - The parser accepts %type, %union and constructs and almost ignore them, except checking consistency between token and non-terminal declarations (warnings) - The parser now has error recovery and consistent error line numbers - The parser now accepts "in rule" actions and generates pseudo empty rules to reduce, named @x-y, where x is a sequential number and y the 'dot position' in the rule for the driver to know how many parameters to pass to semantic action sub - Add "in rule" actions handling in Driver.pm - Empty rhs need not be the first one anymore - Warning if more than one empty rhs for a given lhs 0.08 Fri Jul 10 22:04:31 GMT 1998 - Changed 'print STDERR' to 'warn' in parser. - Use of literal 'error' produces a warning and is treated as the error token - Add prefix 'YY' before each parser object methods to avoid clashes with user written methods - Renamed YYUserData to YYData (shorter and more consistent with other methods names) - Renamed YYLeftVal to YYSemval for same reasons - Modified Driver.pm so Semval(-n..n) reacts like $-n..$n in yacc 0.09 Never released - Changed test suite to 't/' style and add base tests for semantic actions/values and associativity tests - Check code to be (almost) Perl's -w compatible - Updates to pod section in Yapp.pm reflecting most of those changes 0.10 Mon Jul 13 20:53:40 GMT 1998 - Cosmetic changes 0.11 Wed Jul 15 19:46:17 GMT 1998 - Renamed Yapp::Parse parameters with a leading yy - Updated Yapp.pm pod section 0.12 Tue Jul 21 22:34:00 GMT 1998 0.13 Never released. (I'm not supersticious, but who knows...8-)) - Renaming to Parse::Yapp for better CPAN entry... 0.14 Wed Jul 29 21:43:03 GMT 1998 - Doc change: empty token is '', not undef (perl -w complains otherwise) - Bug in _ReduceGrammar: used $ruleno instead of $#{$grammar{RULES}} making no-terminal pointing to wrong rhs if useless rules. 0.15 Mon Aug 17 11:39:01 CEST 1998 - YappParse.yp modified to allow empty tail section (not even \n) - YappParse.yp modified to diagnose lack of rules in grammar section - Driver.pm has been modified so there is no performance impact at loading when debugging is not used and to insure thread safety at runtime - Output.pm can now include driver code into the parser module to make it 'standalone' - Copyright notice in Driver has been changed to reflect its use if included in a standalone parser - A -s option has been added to yapp.pl to generate standalone parsers - Usage in yapp.pl reflects this new option - Updated Yapp.pm pod to add Standalone Parsers item 0.16 Sun Oct 25 12:36:05 CET 1998 - Output.pm modified not to use DATA handle, which seems broken on windows systems when Parse::Yapp module is untarred and Output.pm hasn't its \n converted to \r\n pairs. - Added the %expect declaration, a la bison - Updated Yapp.pm pod to reflect this new option - The core of Parse::Yapp seems very stable now so I change the status from alpha to beta and jump to version 0.20 directly. 0.20 Sun Dec 20 16:13:21 CET 1998 - Added YYExpect method in Parse/Yapp/Driver.pm - Updated Yapp.pm pod to reflect this new method - Modified Makefile.PL for using current Parse::Yapp version if recompiling Parse/Yapp/Parse.pm from YappParse.yp ( $(PERL) -I. ) - Modified yapp.pl to add -V option and make output default to final name of package, if -m option is specified. Usage updated. - Added missing $@ check after eval of debugging driver. 0.21 Thu Dec 24 17:55:47 GMT 1998 - Corrected a weird bug in Lalr.pm (_SolveConflicts & _SetDefaults) about shift/reduce conflicts on non-associative tokens - Added a test in base.t to check non-associative conflicts and error token handling - Added some doc to explain YYExpect can include YYCurtok when non-associ- ative errors happen 0.22 Wed Mar 10 17:03:39 CET 1999 - Moved Parse path tree under lib to be conformant with standard Perl modules distributions - Added Parse::Yapp::Options class as parent of Parse::Yapp::Grammar to handle various options. - As the Output method is not really 'public' yet, it now takes its arguments as hash list, like YYParse. Can break code of people not using the yapp.pl front-end. Of course, its arguments are handled by Parse::Yapp::Options - Added #line "inputfile" trace in generated grammar by default for input source line numbers (obvious reason for adding that :-). - Added language, linenumbers, inputfile, classname, standalone and input options which default to ( 'Perl', 1, undef, 'Parser', 0, undef ) - Modified yapp.pl for new parameter list - Idem for t/base.t and t/calc.t - Modified Grammar.pm so it uses linenumbers option to know it must output line numbers and inputfile as the filename. If inputfile is undef, then use 'unkown'. - Added a new flag -n to yapp.pl to disable line numbers in parser output - Renamed yapp.pl to yapp (bored of typing .pl) - Wrote pod for yapp frontend (so now there's a man 1 page !) - Added article in copyright notice when using the standalone parser option 0.30 Sat Apr 3 15:36:58 CEST 1999 - Corrected a silly bug in yapp front-end, calling Output method with inputfile parameter, which was already done with the constructor - Change to yapp frontend so the F<*.output> file goes to the same directory than the F<*.yp> source file - Corrected Head method in Parse::Yapp::Grammar so it returns '' if there is no header code, to avoid a warning with perl's -w switch - Same for Tail method, so no line number is output if there is no trailer code - Corrected a bug in Grammar.pm, to make useful rules useless if their lhs are not reachable. 0.31 Fri May 7 21:06:32 CEST 1999 - Won some milliseconds in Driver.pm by not calling an anonymous sub if there is no semantic action: just get the first semantic value as result - Added a patch from Andy Wardley (thanks) which allow people to specify their own template rather than the standard one from Output.pm - Added option C<-b> to yapp to specify a 'shebang'. If value is an empty string, $Config{perlpath} is used. 1.00 Wed Oct 20 17:52:38 CEST 1999 - Corrected a bug in Options.pm to have it run with Perl 5.6.0 1.01 Tue Mar 28 18:50:19 CEST 2000 - In YappParse.yp, if declaring a token with %left/%right/%nonassoc and later redefining it with token lost precedence/associativity. Now, it emits a warning - In Lalr.pm, _FirstSfx, incorrectly looped when epsilon was in firstset instead of beeing nullable (this one was weird) - In Driver.pm, check for a call to YYErrok after calling error routine to abort error recovery and continue normal parsing - New method YYLexer added in Driver.pm, to get a reference to the lexer routine - In Driver.pm, $check variable was not always cleaned up 1.02 Mon May 1 13:42:03 CEST 2000 - English corrections in README file (thanks to Donald Lancon) - New email address - Updated copyright boundaries - Various cleanups in Grammar.pm and Lalr.pm 1.03 Sun Nov 5 13:14:49 CEST 2000 - In Lalr.pm, _Preds, recursivity removed - English corrections in Parse::Yapp pod section (thanks to Julian Trudy) - Updated copyright boundaries - Stress test added (compile and check a full C++ grammar) 1.04 Mon Feb 12 16:46:37 CET 2001 - Bug correction in YappParse.yp _Lexer sub to accept '\\' literals (Thanks to Denis Barbier to catch this one) 1.05 Sun Nov 4 20:32:32 CET 2001 1.06 Wed Jun 14 2017 - First Release In Over 15 Years, How's That For Long-Term Stability?!? :-) - Bug Fix, Unescaped Left Curly Braces { In Regular Expressions, Fatal Since Perl v5.25.1, Minor Changes In YappParse.yp & Parse.pm - Typo Fixes, POD Spelling & Syntax - Copyright Assignment Documents, Desarmenien To Braswell 1.20 Wed Jun 14 2017 - Bug Fix, CPAN Version Dependencies Failure, Enable Module Version In Parse::Yapp, Not Just Distribution Version In Parse::Yapp::Driver - Bump Version 1.21 Fri Aug 04 2017 - Typo Fixes, POD Spelling (thanks to Gregor Herrmann) - Bug Fix, Missing POD Encoding (thanks to Gregor Herrmann) Parse-Yapp-1.21/lib/0000755000175000017500000000000013141025035014744 5ustar wbraswellwbraswellParse-Yapp-1.21/lib/Parse/0000755000175000017500000000000013141025035016016 5ustar wbraswellwbraswellParse-Yapp-1.21/lib/Parse/Yapp/0000755000175000017500000000000013141025035016727 5ustar wbraswellwbraswellParse-Yapp-1.21/lib/Parse/Yapp/Lalr.pm0000755000175000017500000006076613120142612020177 0ustar wbraswellwbraswell# # Module Parse::Yapp::Lalr # # Copyright © 1998, 1999, 2000, 2001, Francois Desarmenien. # Copyright © 2017 William N. Braswell, Jr. # (see the pod text in Parse::Yapp module for use and distribution rights) # package Parse::Yapp::Lalr; @ISA=qw( Parse::Yapp::Grammar ); require 5.004; use Parse::Yapp::Grammar; =for nobody Parse::Yapp::Compile Object Structure: -------------------------------------- { GRAMMAR => Parse::Yapp::Grammar, STATES => [ { CORE => [ items... ], ACTIONS => { term => action } GOTOS => { nterm => stateno } }... ] CONFLICTS=>{ SOLVED => { stateno => [ ruleno, token, solved ] }, FORCED => { TOTAL => [ nbsr, nbrr ], DETAIL => { stateno => { TOTAL => [ nbsr, nbrr ] } LIST => [ ruleno, token ] } } } } 'items' are of form: [ ruleno, dotpos ] 'term' in ACTIONS is '' means default action 'action' may be: undef: explicit error (nonassociativity) 0 : accept >0 : shift and go to state 'action' <0 : reduce using rule -'action' 'solved' may have values of: 'shift' if solved as Shift 'reduce' if solved as Reduce 'error' if solved by discarding both Shift and Reduce (nonassoc) SOLVED is a set of states containing Solved conflicts FORCED are forced conflict resolutions nbsr and nbrr are number of shift/reduce and reduce/reduce conflicts TOTAL is the total number of SR/RR conflicts for the parser DETAIL is the detail of conflicts for each state TOTAL is the total number of SR/RR conflicts for a state LIST is the list of discarded reductions (for display purpose only) =cut use strict; use Carp; ############### # Constructor # ############### sub new { my($class)=shift; ref($class) and $class=ref($class); my($self)=$class->SUPER::new(@_); $self->_Compile(); bless($self,$class); } ########### # Methods # ########### ########################### # Method To View Warnings # ########################### sub Warnings { my($self)=shift; my($text); my($nbsr,$nbrr)=@{$$self{CONFLICTS}{FORCED}{TOTAL}}; $text=$self->SUPER::Warnings(); $nbsr != $$self{GRAMMAR}{EXPECT} and $text.="$nbsr shift/reduce conflict".($nbsr > 1 ? "s" : ""); $nbrr and do { $nbsr and $text.=" and "; $text.="$nbrr reduce/reduce conflict".($nbrr > 1 ? "s" : ""); }; ( $nbsr != $$self{GRAMMAR}{EXPECT} or $nbrr) and $text.="\n"; $text; } ############################# # Method To View DFA States # ############################# sub ShowDfa { my($self)=shift; my($text); my($grammar,$states)=($$self{GRAMMAR}, $$self{STATES}); for my $stateno (0..$#$states) { my(@shifts,@reduces,@errors,$default); $text.="State $stateno:\n\n"; #Dump Kernel Items for (sort { $$a[0] <=> $$b[0] or $$a[1] <=> $$b[1] } @{$$states[$stateno]{'CORE'}}) { my($ruleno,$pos)=@$_; my($lhs,$rhs)=@{$$grammar{RULES}[$ruleno]}[0,1]; my(@rhscopy)=@$rhs; $ruleno or $rhscopy[-1] = '$end'; splice(@rhscopy,$pos,0,'.'); $text.= "\t$lhs -> ".join(' ',@rhscopy)."\t(Rule $ruleno)\n"; } #Prepare Actions for (keys(%{$$states[$stateno]{ACTIONS}})) { my($term,$action)=($_,$$states[$stateno]{ACTIONS}{$_}); $term eq chr(0) and $term = '$end'; not defined($action) and do { push(@errors,$term); next; }; $action > 0 and do { push(@shifts,[ $term, $action ]); next; }; $action = -$action; $term or do { $default= [ '$default', $action ]; next; }; push(@reduces,[ $term, $action ]); } #Dump shifts @shifts and do { $text.="\n"; for (sort { $$a[0] cmp $$b[0] } @shifts) { my($term,$shift)=@$_; $text.="\t$term\tshift, and go to state $shift\n"; } }; #Dump errors @errors and do { $text.="\n"; for my $term (sort { $a cmp $b } @errors) { $text.="\t$term\terror (nonassociative)\n"; } }; #Prepare reduces exists($$self{CONFLICTS}{FORCED}{DETAIL}{$stateno}) and push(@reduces,@{$$self{CONFLICTS}{FORCED}{DETAIL}{$stateno}{LIST}}); @reduces=sort { $$a[0] cmp $$b[0] or $$a[1] <=> $$b[1] } @reduces; defined($default) and push(@reduces,$default); #Dump reduces @reduces and do { $text.="\n"; for (@reduces) { my($term,$ruleno)=@$_; my($discard); $ruleno < 0 and do { ++$discard; $ruleno = -$ruleno; }; $text.= "\t$term\t".($discard ? "[" : ""); if($ruleno) { $text.= "reduce using rule $ruleno ". "($$grammar{RULES}[$ruleno][0])"; } else { $text.='accept'; } $text.=($discard ? "]" : "")."\n"; } }; #Dump gotos exists($$states[$stateno]{GOTOS}) and do { $text.= "\n"; for (keys(%{$$states[$stateno]{GOTOS}})) { $text.= "\t$_\tgo to state $$states[$stateno]{GOTOS}{$_}\n"; } }; $text.="\n"; } $text; } ###################################### # Method to get summary about parser # ###################################### sub Summary { my($self)=shift; my($text); $text=$self->SUPER::Summary(); $text.="Number of states : ". scalar(@{$$self{STATES}})."\n"; $text; } ####################################### # Method To Get Infos about conflicts # ####################################### sub Conflicts { my($self)=shift; my($states)=$$self{STATES}; my($conflicts)=$$self{CONFLICTS}; my($text); for my $stateno ( sort { $a <=> $b } keys(%{$$conflicts{SOLVED}})) { for (@{$$conflicts{SOLVED}{$stateno}}) { my($ruleno,$token,$how)=@$_; $token eq chr(0) and $token = '$end'; $text.="Conflict in state $stateno between rule ". "$ruleno and token $token resolved as $how.\n"; } }; for my $stateno ( sort { $a <=> $b } keys(%{$$conflicts{FORCED}{DETAIL}})) { my($nbsr,$nbrr)=@{$$conflicts{FORCED}{DETAIL}{$stateno}{TOTAL}}; $text.="State $stateno contains "; $nbsr and $text.="$nbsr shift/reduce conflict". ($nbsr > 1 ? "s" : ""); $nbrr and do { $nbsr and $text.=" and "; $text.="$nbrr reduce/reduce conflict". ($nbrr > 1 ? "s" : ""); }; $text.="\n"; }; $text; } ################################# # Method to dump parsing tables # ################################# sub DfaTable { my($self)=shift; my($states)=$$self{STATES}; my($stateno); my($text); $text="[\n\t{"; $text.=join("\n\t},\n\t{", map { my($state)=$_; my($text); $text="#State ".$stateno++."\n\t\t"; ( not exists($$state{ACTIONS}{''}) or keys(%{$$state{ACTIONS}}) > 1) and do { $text.="ACTIONS => {\n\t\t\t"; $text.=join(",\n\t\t\t", map { my($term,$action)=($_,$$state{ACTIONS}{$_}); my($text); if(substr($term,0,1) eq "'") { $term=~s/([\@\$\"])/\\$1/g; $term=~s/^'|'$/"/g; } else { $term= $term eq chr(0) ? "''" : "'$term'"; } if(defined($action)) { $action=int($action); } else { $action='undef'; } "$term => $action"; } grep { $_ } keys(%{$$state{ACTIONS}})); $text.="\n\t\t}"; }; exists($$state{ACTIONS}{''}) and do { keys(%{$$state{ACTIONS}}) > 1 and $text.=",\n\t\t"; $text.="DEFAULT => $$state{ACTIONS}{''}"; }; exists($$state{GOTOS}) and do { $text.=",\n\t\tGOTOS => {\n\t\t\t"; $text.=join(",\n\t\t\t", map { my($nterm,$stateno)=($_,$$state{GOTOS}{$_}); my($text); "'$nterm' => $stateno"; } keys(%{$$state{GOTOS}})); $text.="\n\t\t}"; }; $text; }@$states); $text.="\n\t}\n]"; $text; } #################################### # Method to build Dfa from Grammar # #################################### sub _Compile { my($self)=shift; my($grammar,$states); $grammar=$self->{GRAMMAR}; $states = _LR0($grammar); $self->{CONFLICTS} = _LALR($grammar,$states); $self->{STATES}=$states; } ######################### # LR0 States Generation # ######################### # ########################### # General digraph routine # ########################### sub _Digraph { my($rel,$F)=@_; my(%N,@S); my($infinity)=(~(1<<31)); my($Traverse); $Traverse = sub { my($x,$d)=@_; my($y); push(@S,$x); $N{$x}=$d; exists($$rel{$x}) and do { for $y (keys(%{$$rel{$x}})) { exists($N{$y}) or &$Traverse($y,$d+1); $N{$y} < $N{$x} and $N{$x} = $N{$y}; $$F{$x}|=$$F{$y}; } }; $N{$x} == $d and do { for(;;) { $y=pop(@S); $N{$y}=$infinity; $y eq $x and last; $$F{$y}=$$F{$x}; } }; }; for (keys(%$rel)) { exists($N{$_}) or &$Traverse($_,1); } } ####################### # Generate LR0 states # ####################### =for nobody Formula used for closures: CLOSE(A) = DCLOSE(A) u U (CLOSE(B) | A close B) where: DCLOSE(A) = { [ A -> alpha ] in P } A close B iff [ A -> B gamma ] in P =cut sub _SetClosures { my($grammar)=@_; my($rel,$closures); for my $symbol (keys(%{$$grammar{NTERM}})) { $closures->{$symbol}=pack('b'.@{$$grammar{RULES}}); for my $ruleno (@{$$grammar{NTERM}{$symbol}}) { my($rhs)=$$grammar{RULES}[$ruleno][1]; vec($closures->{$symbol},$ruleno,1)=1; @$rhs > 0 and exists($$grammar{NTERM}{$$rhs[0]}) and ++$rel->{$symbol}{$$rhs[0]}; } } _Digraph($rel,$closures); $closures } sub _Closures { my($grammar,$core,$closures)=@_; my($ruleset)=pack('b'.@{$$grammar{RULES}}); for (@$core) { my($ruleno,$pos)=@$_; my($rhs)=$$grammar{RULES}[$ruleno][1]; $pos < @$rhs and exists($closures->{$$rhs[$pos]}) and $ruleset|=$closures->{$$rhs[$pos]}; } [ @$core, map { [ $_, 0 ] } grep { vec($ruleset,$_,1) } 0..$#{$$grammar{RULES}} ]; } sub _Transitions { my($grammar,$cores,$closures,$states,$stateno)=@_; my($core)=$$states[$stateno]{'CORE'}; my(%transitions); for (@{_Closures($grammar,$core,$closures)}) { my($ruleno,$pos)=@$_; my($rhs)=$$grammar{RULES}[$ruleno][1]; $pos == @$rhs and do { push(@{$$states[$stateno]{ACTIONS}{''}},$ruleno); next; }; push(@{$transitions{$$rhs[$pos]}},[ $ruleno, $pos+1 ]); } for (keys(%transitions)) { my($symbol,$core)=($_,$transitions{$_}); my($corekey)=join(',',map { join('.',@$_) } sort { $$a[0] <=> $$b[0] or $$a[1] <=> $$b[1] } @$core); my($tostateno); exists($cores->{$corekey}) or do { push(@$states,{ 'CORE' => $core }); $cores->{$corekey}=$#$states; }; $tostateno=$cores->{$corekey}; push(@{$$states[$tostateno]{FROM}},$stateno); exists($$grammar{TERM}{$_}) and do { $$states[$stateno]{ACTIONS}{$_} = [ $tostateno ]; next; }; $$states[$stateno]{GOTOS}{$_} = $tostateno; } } sub _LR0 { my($grammar)=@_; my($states) = []; my($stateno); my($closures); #$closures={ nterm => ruleset,... } my($cores)={}; # { "itemlist" => stateno, ... } # where "itemlist" has the form: # "ruleno.pos,ruleno.pos" ordered by ruleno,pos $closures = _SetClosures($grammar); push(@$states,{ 'CORE' => [ [ 0, 0 ] ] }); for($stateno=0;$stateno<@$states;++$stateno) { _Transitions($grammar,$cores,$closures,$states,$stateno); } $states } ######################################################### # Add Lookahead tokens where needed to make LALR states # ######################################################### =for nobody Compute First sets for non-terminal using the following formula: FIRST(A) = { a in T u { epsilon } | A l a } u U { FIRST(B) | B in V and A l B } where: A l x iff [ A -> X1 X2 .. Xn x alpha ] in P and Xi =>* epsilon, 1 <= i <= n =cut sub _SetFirst { my($grammar,$termlst,$terminx)=@_; my($rel,$first)=( {}, {} ); for my $symbol (keys(%{$$grammar{NTERM}})) { $first->{$symbol}=pack('b'.@$termlst); RULE: for my $ruleno (@{$$grammar{NTERM}{$symbol}}) { my($rhs)=$$grammar{RULES}[$ruleno][1]; for (@$rhs) { exists($terminx->{$_}) and do { vec($first->{$symbol},$terminx->{$_},1)=1; next RULE; }; ++$rel->{$symbol}{$_}; exists($$grammar{NULLABLE}{$_}) or next RULE; } vec($first->{$symbol},0,1)=1; } } _Digraph($rel,$first); $first } sub _Preds { my($states,$stateno,$len)=@_; my($queue, $preds); $len or return [ $stateno ]; $queue=[ [ $stateno, $len ] ]; while(@$queue) { my($pred) = shift(@$queue); my($stateno, $len) = @$pred; $len == 1 and do { push(@$preds,@{$states->[$stateno]{FROM}}); next; }; push(@$queue, map { [ $_, $len - 1 ] } @{$states->[$stateno]{FROM}}); } # Pass @$preds through a hash to ensure unicity [ keys( %{ +{ map { ($_,1) } @$preds } } ) ]; } sub _FirstSfx { my($grammar,$firstset,$termlst,$terminx,$ruleno,$pos,$key)=@_; my($first)=pack('b'.@$termlst); my($rhs)=$$grammar{RULES}[$ruleno][1]; for (;$pos < @$rhs;++$pos) { exists($terminx->{$$rhs[$pos]}) and do { vec($first,$terminx->{$$rhs[$pos]},1)=1; return($first); }; $first|=$firstset->{$$rhs[$pos]}; vec($first,0,1) and vec($first,0,1)=0; exists($$grammar{NULLABLE}{$$rhs[$pos]}) or return($first); } vec($first,0,1)=1; $first; } =for noboby Compute Follow sets using following formula: FOLLOW(p,A) = READ(p,A) u U { FOLLOW(q,B) | (p,A) include (q,B) where: READ(p,A) = U { FIRST(beta) | [ A -> alpha A . beta ] in KERNEL(GOTO(p,A)) } - { epsilon } (p,a) include (q,B) iff [ B -> alpha A . beta ] in KERNEL(GOTO(p,A), epsilon in FIRST(beta) and q in PRED(p,alpha) =cut sub _ComputeFollows { my($grammar,$states,$termlst)=@_; my($firstset,$terminx); my($inconsistent, $rel, $follows, $sfx)= ( {}, {}, {}, {} ); %$terminx= map { ($termlst->[$_],$_) } 0..$#$termlst; $firstset=_SetFirst($grammar,$termlst,$terminx); for my $stateno (0..$#$states) { my($state)=$$states[$stateno]; exists($$state{ACTIONS}{''}) and ( @{$$state{ACTIONS}{''}} > 1 or keys(%{$$state{ACTIONS}}) > 1 ) and do { ++$inconsistent->{$stateno}; for my $ruleno (@{$$state{ACTIONS}{''}}) { my($lhs,$rhs)=@{$$grammar{RULES}[$ruleno]}[0,1]; for my $predno (@{_Preds($states,$stateno,scalar(@$rhs))}) { ++$rel->{"$stateno.$ruleno"}{"$predno.$lhs"}; } } }; exists($$state{GOTOS}) or next; for my $symbol (keys(%{$$state{GOTOS}})) { my($tostate)=$$states[$$state{GOTOS}{$symbol}]; my($goto)="$stateno.$symbol"; $follows->{$goto}=pack('b'.@$termlst); for my $item (@{$$tostate{'CORE'}}) { my($ruleno,$pos)=@$item; my($key)="$ruleno.$pos"; exists($sfx->{$key}) or $sfx->{$key} = _FirstSfx($grammar,$firstset, $termlst,$terminx, $ruleno,$pos,$key); $follows->{$goto}|=$sfx->{$key}; vec($follows->{$goto},0,1) and do { my($lhs)=$$grammar{RULES}[$ruleno][0]; vec($follows->{$goto},0,1)=0; for my $predno (@{_Preds($states,$stateno,$pos-1)}) { ++$rel->{$goto}{"$predno.$lhs"}; } }; } } } _Digraph($rel,$follows); ($follows,$inconsistent) } sub _ComputeLA { my($grammar,$states)=@_; my($termlst)= [ '',keys(%{$$grammar{TERM}}) ]; my($follows,$inconsistent) = _ComputeFollows($grammar,$states,$termlst); for my $stateno ( keys(%$inconsistent ) ) { my($state)=$$states[$stateno]; my($conflict); #NB the sort is VERY important for conflicts resolution order for my $ruleno (sort { $a <=> $b } @{$$state{ACTIONS}{''}}) { for my $term ( map { $termlst->[$_] } grep { vec($follows->{"$stateno.$ruleno"},$_,1) } 0..$#$termlst) { exists($$state{ACTIONS}{$term}) and ++$conflict; push(@{$$state{ACTIONS}{$term}},-$ruleno); } } delete($$state{ACTIONS}{''}); $conflict or delete($inconsistent->{$stateno}); } $inconsistent } ############################# # Solve remaining conflicts # ############################# sub _SolveConflicts { my($grammar,$states,$inconsistent)=@_; my(%rulesprec,$RulePrec); my($conflicts)={ SOLVED => {}, FORCED => { TOTAL => [ 0, 0 ], DETAIL => {} } }; $RulePrec = sub { my($ruleno)=@_; my($rhs,$rprec)=@{$$grammar{RULES}[$ruleno]}[1,2]; my($lastterm); defined($rprec) and return($rprec); exists($rulesprec{$ruleno}) and return($rulesprec{$ruleno}); $lastterm=(grep { exists($$grammar{TERM}{$_}) } @$rhs)[-1]; defined($lastterm) and ref($$grammar{TERM}{$lastterm}) and do { $rulesprec{$ruleno}=$$grammar{TERM}{$lastterm}[1]; return($rulesprec{$ruleno}); }; undef; }; for my $stateno (keys(%$inconsistent)) { my($state)=$$states[$stateno]; my($actions)=$$state{ACTIONS}; my($nbsr,$nbrr); for my $term ( keys(%$actions) ) { my($act)=$$actions{$term}; @$act > 1 or next; $$act[0] > 0 and ref($$grammar{TERM}{$term}) and do { my($assoc,$tprec)=@{$$grammar{TERM}{$term}}; my($k,$error); for ($k=1;$k<@$act;++$k) { my($ruleno)=-$$act[$k]; my($rprec)=&$RulePrec($ruleno); defined($rprec) or next; ( $tprec > $rprec or ( $tprec == $rprec and $assoc eq 'RIGHT')) and do { push(@{$$conflicts{SOLVED}{$stateno}}, [ $ruleno, $term, 'shift' ]); splice(@$act,$k--,1); next; }; ( $tprec < $rprec or $assoc eq 'LEFT') and do { push(@{$$conflicts{SOLVED}{$stateno}}, [ $ruleno, $term, 'reduce' ]); $$act[0] > 0 and do { splice(@$act,0,1); --$k; }; next; }; push(@{$$conflicts{SOLVED}{$stateno}}, [ $ruleno, $term, 'error' ]); splice(@$act,$k--,1); $$act[0] > 0 and do { splice(@$act,0,1); ++$error; --$k; }; } $error and unshift(@$act,undef); }; @$act > 1 and do { $nbrr += @$act - 2; ($$act[0] > 0 ? $nbsr : $nbrr) += 1; push(@{$$conflicts{FORCED}{DETAIL}{$stateno}{LIST}}, map { [ $term, $_ ] } splice(@$act,1)); }; } $nbsr and do { $$conflicts{FORCED}{TOTAL}[0]+=$nbsr; $$conflicts{FORCED}{DETAIL}{$stateno}{TOTAL}[0]+=$nbsr; }; $nbrr and do { $$conflicts{FORCED}{TOTAL}[1]+=$nbrr; $$conflicts{FORCED}{DETAIL}{$stateno}{TOTAL}[1]+=$nbrr; }; } $conflicts } ############################### # Make default reduce actions # ############################### sub _SetDefaults { my($states)=@_; for my $state (@$states) { my($actions)=$$state{ACTIONS}; my(%reduces,$default,$nodefault); exists($$actions{''}) and do { $$actions{''}[0] = -$$actions{''}[0]; ++$nodefault; }; #shift error token => no default exists($$actions{error}) and $$actions{error}[0] > 0 and ++$nodefault; for my $term (keys(%$actions)) { $$actions{$term}=$$actions{$term}[0]; ( not defined($$actions{$term}) or $$actions{$term} > 0 or $nodefault) and next; push(@{$reduces{$$actions{$term}}},$term); } keys(%reduces) > 0 or next; $default=( map { $$_[0] } sort { $$b[1] <=> $$a[1] or $$b[0] <=> $$a[0] } map { [ $_, scalar(@{$reduces{$_}}) ] } keys(%reduces))[0]; delete(@$actions{ @{$reduces{$default}} }); $$state{ACTIONS}{''}=$default; } } sub _LALR { my($grammar,$states) = @_; my($conflicts,$inconsistent); $inconsistent = _ComputeLA($grammar,$states); $conflicts = _SolveConflicts($grammar,$states,$inconsistent); _SetDefaults($states); $conflicts } 1; Parse-Yapp-1.21/lib/Parse/Yapp/Output.pm0000755000175000017500000000373613120142622020600 0ustar wbraswellwbraswell# # Module Parse::Yapp::Output # # Copyright © 1998, 1999, 2000, 2001, Francois Desarmenien. # Copyright © 2017 William N. Braswell, Jr. # (see the pod text in Parse::Yapp module for use and distribution rights) # package Parse::Yapp::Output; @ISA=qw ( Parse::Yapp::Lalr ); require 5.004; use Parse::Yapp::Lalr; use Parse::Yapp::Driver; use strict; use Carp; sub _CopyDriver { my($text)='#Included Parse/Yapp/Driver.pm file'.('-' x 40)."\n"; open(DRV,$Parse::Yapp::Driver::FILENAME) or die "BUG: could not open $Parse::Yapp::Driver::FILENAME"; $text.="{\n".join('',)."}\n"; close(DRV); $text.='#End of include'.('-' x 50)."\n"; } sub Output { my($self)=shift; $self->Options(@_); my($package)=$self->Option('classname'); my($head,$states,$rules,$tail,$driver); my($version)=$Parse::Yapp::Driver::VERSION; my($datapos); my($text)=$self->Option('template') ||<<'EOT'; #################################################################### # # This file was generated using Parse::Yapp version <<$version>>. # # Don't edit this file, use source file instead. # # ANY CHANGE MADE HERE WILL BE LOST ! # #################################################################### package <<$package>>; use vars qw ( @ISA ); use strict; @ISA= qw ( Parse::Yapp::Driver ); <<$driver>> <<$head>> sub new { my($class)=shift; ref($class) and $class=ref($class); my($self)=$class->SUPER::new( yyversion => '<<$version>>', yystates => <<$states>>, yyrules => <<$rules>>, @_); bless($self,$class); } <<$tail>> 1; EOT $driver='use Parse::Yapp::Driver;'; defined($package) or $package='Parse::Yapp::Default'; $head= $self->Head(); $rules=$self->RulesTable(); $states=$self->DfaTable(); $tail= $self->Tail(); $self->Option('standalone') and $driver=_CopyDriver(); $text=~s/<<(\$.+)>>/$1/gee; $text; } 1; Parse-Yapp-1.21/lib/Parse/Yapp/Driver.pm0000755000175000017500000002322413141024714020531 0ustar wbraswellwbraswell# # Module Parse::Yapp::Driver # # This module is part of the Parse::Yapp package available on your # nearest CPAN # # Any use of this module in a standalone parser make the included # text under the same copyright as the Parse::Yapp module itself. # # This notice should remain unchanged. # # Copyright © 1998, 1999, 2000, 2001, Francois Desarmenien. # Copyright © 2017 William N. Braswell, Jr. # (see the pod text in Parse::Yapp module for use and distribution rights) # package Parse::Yapp::Driver; require 5.004; use strict; use vars qw ( $VERSION $COMPATIBLE $FILENAME ); # CORRELATION #py001: $VERSION must be changed in both Parse::Yapp & Parse::Yapp::Driver $VERSION = '1.21'; $COMPATIBLE = '0.07'; $FILENAME=__FILE__; use Carp; #Known parameters, all starting with YY (leading YY will be discarded) my(%params)=(YYLEX => 'CODE', 'YYERROR' => 'CODE', YYVERSION => '', YYRULES => 'ARRAY', YYSTATES => 'ARRAY', YYDEBUG => ''); #Mandatory parameters my(@params)=('LEX','RULES','STATES'); sub new { my($class)=shift; my($errst,$nberr,$token,$value,$check,$dotpos); my($self)={ ERROR => \&_Error, ERRST => \$errst, NBERR => \$nberr, TOKEN => \$token, VALUE => \$value, DOTPOS => \$dotpos, STACK => [], DEBUG => 0, CHECK => \$check }; _CheckParams( [], \%params, \@_, $self ); exists($$self{VERSION}) and $$self{VERSION} < $COMPATIBLE and croak "Yapp driver version $VERSION ". "incompatible with version $$self{VERSION}:\n". "Please recompile parser module."; ref($class) and $class=ref($class); bless($self,$class); } sub YYParse { my($self)=shift; my($retval); _CheckParams( \@params, \%params, \@_, $self ); if($$self{DEBUG}) { _DBLoad(); $retval = eval '$self->_DBParse()';#Do not create stab entry on compile $@ and die $@; } else { $retval = $self->_Parse(); } $retval } sub YYData { my($self)=shift; exists($$self{USER}) or $$self{USER}={}; $$self{USER}; } sub YYErrok { my($self)=shift; ${$$self{ERRST}}=0; undef; } sub YYNberr { my($self)=shift; ${$$self{NBERR}}; } sub YYRecovering { my($self)=shift; ${$$self{ERRST}} != 0; } sub YYAbort { my($self)=shift; ${$$self{CHECK}}='ABORT'; undef; } sub YYAccept { my($self)=shift; ${$$self{CHECK}}='ACCEPT'; undef; } sub YYError { my($self)=shift; ${$$self{CHECK}}='ERROR'; undef; } sub YYSemval { my($self)=shift; my($index)= $_[0] - ${$$self{DOTPOS}} - 1; $index < 0 and -$index <= @{$$self{STACK}} and return $$self{STACK}[$index][1]; undef; #Invalid index } sub YYCurtok { my($self)=shift; @_ and ${$$self{TOKEN}}=$_[0]; ${$$self{TOKEN}}; } sub YYCurval { my($self)=shift; @_ and ${$$self{VALUE}}=$_[0]; ${$$self{VALUE}}; } sub YYExpect { my($self)=shift; keys %{$self->{STATES}[$self->{STACK}[-1][0]]{ACTIONS}} } sub YYLexer { my($self)=shift; $$self{LEX}; } ################# # Private stuff # ################# sub _CheckParams { my($mandatory,$checklist,$inarray,$outhash)=@_; my($prm,$value); my($prmlst)={}; while(($prm,$value)=splice(@$inarray,0,2)) { $prm=uc($prm); exists($$checklist{$prm}) or croak("Unknow parameter '$prm'"); ref($value) eq $$checklist{$prm} or croak("Invalid value for parameter '$prm'"); $prm=unpack('@2A*',$prm); $$outhash{$prm}=$value; } for (@$mandatory) { exists($$outhash{$_}) or croak("Missing mandatory parameter '".lc($_)."'"); } } sub _Error { print "Parse error.\n"; } sub _DBLoad { { no strict 'refs'; exists(${__PACKAGE__.'::'}{_DBParse})#Already loaded ? and return; } my($fname)=__FILE__; my(@drv); open(DRV,"<$fname") or die "Report this as a BUG: Cannot open $fname"; while() { /^\s*sub\s+_Parse\s*{\s*$/ .. /^\s*}\s*#\s*_Parse\s*$/ and do { s/^#DBG>//; push(@drv,$_); } } close(DRV); $drv[0]=~s/_P/_DBP/; eval join('',@drv); } #Note that for loading debugging version of the driver, #this file will be parsed from 'sub _Parse' up to '}#_Parse' inclusive. #So, DO NOT remove comment at end of sub !!! sub _Parse { my($self)=shift; my($rules,$states,$lex,$error) = @$self{ 'RULES', 'STATES', 'LEX', 'ERROR' }; my($errstatus,$nberror,$token,$value,$stack,$check,$dotpos) = @$self{ 'ERRST', 'NBERR', 'TOKEN', 'VALUE', 'STACK', 'CHECK', 'DOTPOS' }; #DBG> my($debug)=$$self{DEBUG}; #DBG> my($dbgerror)=0; #DBG> my($ShowCurToken) = sub { #DBG> my($tok)='>'; #DBG> for (split('',$$token)) { #DBG> $tok.= (ord($_) < 32 or ord($_) > 126) #DBG> ? sprintf('<%02X>',ord($_)) #DBG> : $_; #DBG> } #DBG> $tok.='<'; #DBG> }; $$errstatus=0; $$nberror=0; ($$token,$$value)=(undef,undef); @$stack=( [ 0, undef ] ); $$check=''; while(1) { my($actions,$act,$stateno); $stateno=$$stack[-1][0]; $actions=$$states[$stateno]; #DBG> print STDERR ('-' x 40),"\n"; #DBG> $debug & 0x2 #DBG> and print STDERR "In state $stateno:\n"; #DBG> $debug & 0x08 #DBG> and print STDERR "Stack:[". #DBG> join(',',map { $$_[0] } @$stack). #DBG> "]\n"; if (exists($$actions{ACTIONS})) { defined($$token) or do { ($$token,$$value)=&$lex($self); #DBG> $debug & 0x01 #DBG> and print STDERR "Need token. Got ".&$ShowCurToken."\n"; }; $act= exists($$actions{ACTIONS}{$$token}) ? $$actions{ACTIONS}{$$token} : exists($$actions{DEFAULT}) ? $$actions{DEFAULT} : undef; } else { $act=$$actions{DEFAULT}; #DBG> $debug & 0x01 #DBG> and print STDERR "Don't need token.\n"; } defined($act) and do { $act > 0 and do { #shift #DBG> $debug & 0x04 #DBG> and print STDERR "Shift and go to state $act.\n"; $$errstatus and do { --$$errstatus; #DBG> $debug & 0x10 #DBG> and $dbgerror #DBG> and $$errstatus == 0 #DBG> and do { #DBG> print STDERR "**End of Error recovery.\n"; #DBG> $dbgerror=0; #DBG> }; }; push(@$stack,[ $act, $$value ]); $$token ne '' #Don't eat the eof and $$token=$$value=undef; next; }; #reduce my($lhs,$len,$code,@sempar,$semval); ($lhs,$len,$code)=@{$$rules[-$act]}; #DBG> $debug & 0x04 #DBG> and $act #DBG> and print STDERR "Reduce using rule ".-$act." ($lhs,$len): "; $act or $self->YYAccept(); $$dotpos=$len; unpack('A1',$lhs) eq '@' #In line rule and do { $lhs =~ /^\@[0-9]+\-([0-9]+)$/ or die "In line rule name '$lhs' ill formed: ". "report it as a BUG.\n"; $$dotpos = $1; }; @sempar = $$dotpos ? map { $$_[1] } @$stack[ -$$dotpos .. -1 ] : (); $semval = $code ? &$code( $self, @sempar ) : @sempar ? $sempar[0] : undef; splice(@$stack,-$len,$len); $$check eq 'ACCEPT' and do { #DBG> $debug & 0x04 #DBG> and print STDERR "Accept.\n"; return($semval); }; $$check eq 'ABORT' and do { #DBG> $debug & 0x04 #DBG> and print STDERR "Abort.\n"; return(undef); }; #DBG> $debug & 0x04 #DBG> and print STDERR "Back to state $$stack[-1][0], then "; $$check eq 'ERROR' or do { #DBG> $debug & 0x04 #DBG> and print STDERR #DBG> "go to state $$states[$$stack[-1][0]]{GOTOS}{$lhs}.\n"; #DBG> $debug & 0x10 #DBG> and $dbgerror #DBG> and $$errstatus == 0 #DBG> and do { #DBG> print STDERR "**End of Error recovery.\n"; #DBG> $dbgerror=0; #DBG> }; push(@$stack, [ $$states[$$stack[-1][0]]{GOTOS}{$lhs}, $semval ]); $$check=''; next; }; #DBG> $debug & 0x04 #DBG> and print STDERR "Forced Error recovery.\n"; $$check=''; }; #Error $$errstatus or do { $$errstatus = 1; &$error($self); $$errstatus # if 0, then YYErrok has been called or next; # so continue parsing #DBG> $debug & 0x10 #DBG> and do { #DBG> print STDERR "**Entering Error recovery.\n"; #DBG> ++$dbgerror; #DBG> }; ++$$nberror; }; $$errstatus == 3 #The next token is not valid: discard it and do { $$token eq '' # End of input: no hope and do { #DBG> $debug & 0x10 #DBG> and print STDERR "**At eof: aborting.\n"; return(undef); }; #DBG> $debug & 0x10 #DBG> and print STDERR "**Dicard invalid token ".&$ShowCurToken.".\n"; $$token=$$value=undef; }; $$errstatus=3; while( @$stack and ( not exists($$states[$$stack[-1][0]]{ACTIONS}) or not exists($$states[$$stack[-1][0]]{ACTIONS}{error}) or $$states[$$stack[-1][0]]{ACTIONS}{error} <= 0)) { #DBG> $debug & 0x10 #DBG> and print STDERR "**Pop state $$stack[-1][0].\n"; pop(@$stack); } @$stack or do { #DBG> $debug & 0x10 #DBG> and print STDERR "**No state left on stack: aborting.\n"; return(undef); }; #shift the error token #DBG> $debug & 0x10 #DBG> and print STDERR "**Shift \$error token and go to state ". #DBG> $$states[$$stack[-1][0]]{ACTIONS}{error}. #DBG> ".\n"; push(@$stack, [ $$states[$$stack[-1][0]]{ACTIONS}{error}, undef ]); } #never reached croak("Error in driver logic. Please, report it as a BUG"); }#_Parse #DO NOT remove comment 1; Parse-Yapp-1.21/lib/Parse/Yapp/Parse.pm0000644000175000017500000004641213120143703020346 0ustar wbraswellwbraswell#################################################################### # # This file was generated using Parse::Yapp version 1.06. # # Don't edit this file, use source file instead. # # ANY CHANGE MADE HERE WILL BE LOST ! # #################################################################### package Parse::Yapp::Parse; use vars qw ( @ISA ); use strict; @ISA= qw ( Parse::Yapp::Driver ); use Parse::Yapp::Driver; #line 1 "YappParse.yp" # Copyright © 1998, 1999, 2000, 2001, Francois Desarmenien. # Copyright © 2017 William N. Braswell, Jr. # All Rights Reserved. # (see COPYRIGHT in Parse::Yapp.pm pod section for use and distribution rights) # # Parse/Yapp/Parser.yp: Parse::Yapp::Parser.pm source file # # Use: yapp -m 'Parse::Yapp::Parse' -o Parse/Yapp/Parse.pm YappParse.yp # # to generate the Parser module. # #line 15 "YappParse.yp" require 5.004; use Carp; my($input,$lexlevel,@lineno,$nberr,$prec,$labelno); my($syms,$head,$tail,$token,$term,$nterm,$rules,$precterm,$start,$nullable); my($expect); sub new { my($class)=shift; ref($class) and $class=ref($class); my($self)=$class->SUPER::new( yyversion => '1.06', yystates => [ {#State 0 ACTIONS => { "%%" => -6, 'UNION' => 1, 'START' => 3, 'ASSOC' => 8, 'EXPECT' => 9, "\n" => 7, 'TOKEN' => 14, 'error' => 13, 'HEADCODE' => 10, 'TYPE' => 11 }, GOTOS => { 'decls' => 12, 'decl' => 2, 'head' => 4, 'headsec' => 5, 'yapp' => 6 } }, {#State 1 ACTIONS => { 'CODE' => 15 } }, {#State 2 DEFAULT => -9 }, {#State 3 ACTIONS => { 'IDENT' => 16 }, GOTOS => { 'ident' => 17 } }, {#State 4 ACTIONS => { 'error' => 22, 'IDENT' => 23, "%%" => 21 }, GOTOS => { 'rulesec' => 20, 'body' => 19, 'rules' => 18 } }, {#State 5 ACTIONS => { "%%" => 24 } }, {#State 6 ACTIONS => { '' => 25 } }, {#State 7 DEFAULT => -10 }, {#State 8 ACTIONS => { "<" => 26 }, DEFAULT => -19, GOTOS => { 'typedecl' => 27 } }, {#State 9 ACTIONS => { 'NUMBER' => 28 } }, {#State 10 ACTIONS => { "\n" => 29 } }, {#State 11 ACTIONS => { "<" => 26 }, DEFAULT => -19, GOTOS => { 'typedecl' => 30 } }, {#State 12 ACTIONS => { 'UNION' => 1, "%%" => -7, 'START' => 3, "\n" => 7, 'EXPECT' => 9, 'ASSOC' => 8, 'TYPE' => 11, 'HEADCODE' => 10, 'TOKEN' => 14, 'error' => 13 }, GOTOS => { 'decl' => 31 } }, {#State 13 ACTIONS => { "\n" => 32 } }, {#State 14 ACTIONS => { "<" => 26 }, DEFAULT => -19, GOTOS => { 'typedecl' => 33 } }, {#State 15 ACTIONS => { "\n" => 34 } }, {#State 16 DEFAULT => -4 }, {#State 17 ACTIONS => { "\n" => 35 } }, {#State 18 DEFAULT => -28 }, {#State 19 ACTIONS => { 'TAILCODE' => 36 }, DEFAULT => -45, GOTOS => { 'tail' => 37 } }, {#State 20 ACTIONS => { 'error' => 22, 'IDENT' => 23, "%%" => 39 }, GOTOS => { 'rules' => 38 } }, {#State 21 DEFAULT => -26 }, {#State 22 ACTIONS => { ";" => 40 } }, {#State 23 ACTIONS => { ":" => 41 } }, {#State 24 DEFAULT => -5 }, {#State 25 DEFAULT => 0 }, {#State 26 ACTIONS => { 'IDENT' => 42 } }, {#State 27 ACTIONS => { 'LITERAL' => 44, 'IDENT' => 16 }, GOTOS => { 'symbol' => 43, 'symlist' => 45, 'ident' => 46 } }, {#State 28 ACTIONS => { "\n" => 47 } }, {#State 29 DEFAULT => -14 }, {#State 30 ACTIONS => { 'IDENT' => 16 }, GOTOS => { 'identlist' => 48, 'ident' => 49 } }, {#State 31 DEFAULT => -8 }, {#State 32 DEFAULT => -18 }, {#State 33 ACTIONS => { 'IDENT' => 16, 'LITERAL' => 44 }, GOTOS => { 'symbol' => 43, 'ident' => 46, 'symlist' => 50 } }, {#State 34 DEFAULT => -15 }, {#State 35 DEFAULT => -13 }, {#State 36 DEFAULT => -46 }, {#State 37 DEFAULT => -1 }, {#State 38 DEFAULT => -27 }, {#State 39 DEFAULT => -25 }, {#State 40 DEFAULT => -30 }, {#State 41 ACTIONS => { 'IDENT' => 16, 'CODE' => 52, 'LITERAL' => 44 }, DEFAULT => -35, GOTOS => { 'rule' => 54, 'rhselt' => 55, 'ident' => 46, 'code' => 53, 'rhselts' => 51, 'rhss' => 57, 'symbol' => 58, 'rhs' => 56 } }, {#State 42 ACTIONS => { ">" => 59 } }, {#State 43 DEFAULT => -22 }, {#State 44 DEFAULT => -2 }, {#State 45 ACTIONS => { "\n" => 60, 'IDENT' => 16, 'LITERAL' => 44 }, GOTOS => { 'ident' => 46, 'symbol' => 61 } }, {#State 46 DEFAULT => -3 }, {#State 47 DEFAULT => -17 }, {#State 48 ACTIONS => { 'IDENT' => 16, "\n" => 62 }, GOTOS => { 'ident' => 63 } }, {#State 49 DEFAULT => -24 }, {#State 50 ACTIONS => { 'LITERAL' => 44, "\n" => 64, 'IDENT' => 16 }, GOTOS => { 'ident' => 46, 'symbol' => 61 } }, {#State 51 ACTIONS => { 'LITERAL' => 44, 'IDENT' => 16, 'CODE' => 52 }, DEFAULT => -36, GOTOS => { 'ident' => 46, 'code' => 53, 'symbol' => 58, 'rhselt' => 65 } }, {#State 52 DEFAULT => -44 }, {#State 53 DEFAULT => -40 }, {#State 54 DEFAULT => -32 }, {#State 55 DEFAULT => -38 }, {#State 56 ACTIONS => { 'PREC' => 67 }, DEFAULT => -34, GOTOS => { 'prec' => 66 } }, {#State 57 ACTIONS => { ";" => 69, "|" => 68 } }, {#State 58 DEFAULT => -39 }, {#State 59 DEFAULT => -20 }, {#State 60 DEFAULT => -12 }, {#State 61 DEFAULT => -21 }, {#State 62 DEFAULT => -16 }, {#State 63 DEFAULT => -23 }, {#State 64 DEFAULT => -11 }, {#State 65 DEFAULT => -37 }, {#State 66 ACTIONS => { 'CODE' => 52 }, DEFAULT => -42, GOTOS => { 'epscode' => 71, 'code' => 70 } }, {#State 67 ACTIONS => { 'LITERAL' => 44, 'IDENT' => 16 }, GOTOS => { 'ident' => 46, 'symbol' => 72 } }, {#State 68 ACTIONS => { 'LITERAL' => 44, 'CODE' => 52, 'IDENT' => 16 }, DEFAULT => -35, GOTOS => { 'rhs' => 56, 'rhselts' => 51, 'code' => 53, 'ident' => 46, 'rhselt' => 55, 'symbol' => 58, 'rule' => 73 } }, {#State 69 DEFAULT => -29 }, {#State 70 DEFAULT => -43 }, {#State 71 DEFAULT => -33 }, {#State 72 DEFAULT => -41 }, {#State 73 DEFAULT => -31 } ], yyrules => [ [#Rule 0 '$start', 2, undef ], [#Rule 1 'yapp', 3, undef ], [#Rule 2 'symbol', 1, sub #line 33 "YappParse.yp" { exists($$syms{$_[1][0]}) or do { $$syms{$_[1][0]} = $_[1][1]; $$term{$_[1][0]} = undef; }; $_[1] } ], [#Rule 3 'symbol', 1, undef ], [#Rule 4 'ident', 1, sub #line 44 "YappParse.yp" { exists($$syms{$_[1][0]}) or do { $$syms{$_[1][0]} = $_[1][1]; $$term{$_[1][0]} = undef; }; $_[1] } ], [#Rule 5 'head', 2, undef ], [#Rule 6 'headsec', 0, undef ], [#Rule 7 'headsec', 1, undef ], [#Rule 8 'decls', 2, undef ], [#Rule 9 'decls', 1, undef ], [#Rule 10 'decl', 1, undef ], [#Rule 11 'decl', 4, sub #line 69 "YappParse.yp" { for (@{$_[3]}) { my($symbol,$lineno)=@$_; exists($$token{$symbol}) and do { _SyntaxError(0, "Token $symbol redefined: ". "Previously defined line $$syms{$symbol}", $lineno); next; }; $$token{$symbol}=$lineno; $$term{$symbol} = [ ]; } undef } ], [#Rule 12 'decl', 4, sub #line 87 "YappParse.yp" { for (@{$_[3]}) { my($symbol,$lineno)=@$_; defined($$term{$symbol}[0]) and do { _SyntaxError(1, "Precedence for symbol $symbol redefined: ". "Previously defined line $$syms{$symbol}", $lineno); next; }; $$token{$symbol}=$lineno; $$term{$symbol} = [ $_[1][0], $prec ]; } ++$prec; undef } ], [#Rule 13 'decl', 3, sub #line 105 "YappParse.yp" { $start=$_[2][0]; undef } ], [#Rule 14 'decl', 2, sub #line 106 "YappParse.yp" { push(@$head,$_[1]); undef } ], [#Rule 15 'decl', 3, sub #line 107 "YappParse.yp" { undef } ], [#Rule 16 'decl', 4, sub #line 109 "YappParse.yp" { for ( @{$_[3]} ) { my($symbol,$lineno)=@$_; exists($$nterm{$symbol}) and do { _SyntaxError(0, "Non-terminal $symbol redefined: ". "Previously defined line $$syms{$symbol}", $lineno); next; }; delete($$term{$symbol}); #not a terminal $$nterm{$symbol}=undef; #is a non-terminal } } ], [#Rule 17 'decl', 3, sub #line 125 "YappParse.yp" { $expect=$_[2][0]; undef } ], [#Rule 18 'decl', 2, sub #line 126 "YappParse.yp" { $_[0]->YYErrok } ], [#Rule 19 'typedecl', 0, undef ], [#Rule 20 'typedecl', 3, undef ], [#Rule 21 'symlist', 2, sub #line 133 "YappParse.yp" { push(@{$_[1]},$_[2]); $_[1] } ], [#Rule 22 'symlist', 1, sub #line 134 "YappParse.yp" { [ $_[1] ] } ], [#Rule 23 'identlist', 2, sub #line 137 "YappParse.yp" { push(@{$_[1]},$_[2]); $_[1] } ], [#Rule 24 'identlist', 1, sub #line 138 "YappParse.yp" { [ $_[1] ] } ], [#Rule 25 'body', 2, sub #line 143 "YappParse.yp" { $start or $start=$$rules[1][0]; ref($$nterm{$start}) or _SyntaxError(2,"Start symbol $start not found ". "in rules section",$_[2][1]); $$rules[0]=[ '$start', [ $start, chr(0) ], undef, undef ]; } ], [#Rule 26 'body', 1, sub #line 153 "YappParse.yp" { _SyntaxError(2,"No rules in input grammar",$_[1][1]); } ], [#Rule 27 'rulesec', 2, undef ], [#Rule 28 'rulesec', 1, undef ], [#Rule 29 'rules', 4, sub #line 160 "YappParse.yp" { _AddRules($_[1],$_[3]); undef } ], [#Rule 30 'rules', 2, sub #line 161 "YappParse.yp" { $_[0]->YYErrok } ], [#Rule 31 'rhss', 3, sub #line 164 "YappParse.yp" { push(@{$_[1]},$_[3]); $_[1] } ], [#Rule 32 'rhss', 1, sub #line 165 "YappParse.yp" { [ $_[1] ] } ], [#Rule 33 'rule', 3, sub #line 168 "YappParse.yp" { push(@{$_[1]}, $_[2], $_[3]); $_[1] } ], [#Rule 34 'rule', 1, sub #line 169 "YappParse.yp" { my($code)=undef; defined($_[1]) and $_[1][-1][0] eq 'CODE' and $code = ${pop(@{$_[1]})}[1]; push(@{$_[1]}, undef, $code); $_[1] } ], [#Rule 35 'rhs', 0, undef ], [#Rule 36 'rhs', 1, undef ], [#Rule 37 'rhselts', 2, sub #line 186 "YappParse.yp" { push(@{$_[1]},$_[2]); $_[1] } ], [#Rule 38 'rhselts', 1, sub #line 187 "YappParse.yp" { [ $_[1] ] } ], [#Rule 39 'rhselt', 1, sub #line 190 "YappParse.yp" { [ 'SYMB', $_[1] ] } ], [#Rule 40 'rhselt', 1, sub #line 191 "YappParse.yp" { [ 'CODE', $_[1] ] } ], [#Rule 41 'prec', 2, sub #line 195 "YappParse.yp" { defined($$term{$_[2][0]}) or do { _SyntaxError(1,"No precedence for symbol $_[2][0]", $_[2][1]); return undef; }; ++$$precterm{$_[2][0]}; $$term{$_[2][0]}[1]; } ], [#Rule 42 'epscode', 0, sub #line 208 "YappParse.yp" { undef } ], [#Rule 43 'epscode', 1, sub #line 209 "YappParse.yp" { $_[1] } ], [#Rule 44 'code', 1, sub #line 212 "YappParse.yp" { $_[1] } ], [#Rule 45 'tail', 0, undef ], [#Rule 46 'tail', 1, sub #line 218 "YappParse.yp" { $tail=$_[1] } ] ], @_); bless($self,$class); } #line 221 "YappParse.yp" sub _Error { my($value)=$_[0]->YYCurval; my($what)= $token ? "input: '$$value[0]'" : "end of input"; _SyntaxError(1,"Unexpected $what",$$value[1]); } sub _Lexer { #At EOF pos($$input) >= length($$input) and return('',[ undef, -1 ]); #In TAIL section $lexlevel > 1 and do { my($pos)=pos($$input); $lineno[0]=$lineno[1]; $lineno[1]=-1; pos($$input)=length($$input); return('TAILCODE',[ substr($$input,$pos), $lineno[0] ]); }; #Skip blanks $lexlevel == 0 ? $$input=~m{\G((?: [\t\ ]+ # Any white space char but \n | \#[^\n]* # Perl like comments | /\*.*?\*/ # C like comments )+)}xsgc : $$input=~m{\G((?: \s+ # any white space char | \#[^\n]* # Perl like comments | /\*.*?\*/ # C like comments )+)}xsgc and do { my($blanks)=$1; #Maybe At EOF pos($$input) >= length($$input) and return('',[ undef, -1 ]); $lineno[1]+= $blanks=~tr/\n//; }; $lineno[0]=$lineno[1]; $$input=~/\G([A-Za-z_][A-Za-z0-9_]*)/gc and return('IDENT',[ $1, $lineno[0] ]); $$input=~/\G('(?:[^'\\]|\\\\|\\'|\\)+?')/gc and do { $1 eq "'error'" and do { _SyntaxError(0,"Literal 'error' ". "will be treated as error token",$lineno[0]); return('IDENT',[ 'error', $lineno[0] ]); }; return('LITERAL',[ $1, $lineno[0] ]); }; $$input=~/\G(%%)/gc and do { ++$lexlevel; return($1, [ $1, $lineno[0] ]); }; $$input=~/\G\{/gc and do { my($level,$from,$code); $from=pos($$input); $level=1; while($$input=~/([{}])/gc) { substr($$input,pos($$input)-1,1) eq '\\' #Quoted and next; $level += ($1 eq '{' ? 1 : -1) or last; } $level and _SyntaxError(2,"Unmatched { opened line $lineno[0]",-1); $code = substr($$input,$from,pos($$input)-$from-1); $lineno[1]+= $code=~tr/\n//; return('CODE',[ $code, $lineno[0] ]); }; if($lexlevel == 0) {# In head section $$input=~/\G%(left|right|nonassoc)/gc and return('ASSOC',[ uc($1), $lineno[0] ]); $$input=~/\G%(start)/gc and return('START',[ undef, $lineno[0] ]); $$input=~/\G%(expect)/gc and return('EXPECT',[ undef, $lineno[0] ]); $$input=~/\G%\{/gc and do { my($code); $$input=~/\G(.*?)%}/sgc or _SyntaxError(2,"Unmatched %{ opened line $lineno[0]",-1); $code=$1; $lineno[1]+= $code=~tr/\n//; return('HEADCODE',[ $code, $lineno[0] ]); }; $$input=~/\G%(token)/gc and return('TOKEN',[ undef, $lineno[0] ]); $$input=~/\G%(type)/gc and return('TYPE',[ undef, $lineno[0] ]); $$input=~/\G%(union)/gc and return('UNION',[ undef, $lineno[0] ]); $$input=~/\G([0-9]+)/gc and return('NUMBER',[ $1, $lineno[0] ]); } else {# In rule section $$input=~/\G%(prec)/gc and return('PREC',[ undef, $lineno[0] ]); } #Always return something $$input=~/\G(.)/sg or die "Parse::Yapp::Grammar::Parse: Match (.) failed: report as a BUG"; $1 eq "\n" and ++$lineno[1]; ( $1 ,[ $1, $lineno[0] ]); } sub _SyntaxError { my($level,$message,$lineno)=@_; $message= "*". [ 'Warning', 'Error', 'Fatal' ]->[$level]. "* $message, at ". ($lineno < 0 ? "eof" : "line $lineno"). ".\n"; $level > 1 and die $message; warn $message; $level > 0 and ++$nberr; $nberr == 20 and die "*Fatal* Too many errors detected.\n" } sub _AddRules { my($lhs,$lineno)=@{$_[0]}; my($rhss)=$_[1]; ref($$nterm{$lhs}) and do { _SyntaxError(1,"Non-terminal $lhs redefined: ". "Previously declared line $$syms{$lhs}",$lineno); return; }; ref($$term{$lhs}) and do { my($where) = exists($$token{$lhs}) ? $$token{$lhs} : $$syms{$lhs}; _SyntaxError(1,"Non-terminal $lhs previously ". "declared as token line $where",$lineno); return; }; ref($$nterm{$lhs}) #declared through %type or do { $$syms{$lhs}=$lineno; #Say it's declared here delete($$term{$lhs}); #No more a terminal }; $$nterm{$lhs}=[]; #It's a non-terminal now my($epsrules)=0; #To issue a warning if more than one epsilon rule for my $rhs (@$rhss) { my($tmprule)=[ $lhs, [ ], splice(@$rhs,-2) ]; #Init rule @$rhs or do { ++$$nullable{$lhs}; ++$epsrules; }; for (0..$#$rhs) { my($what,$value)=@{$$rhs[$_]}; $what eq 'CODE' and do { my($name)='@'.++$labelno."-$_"; push(@$rules,[ $name, [], undef, $value ]); push(@{$$tmprule[1]},$name); next; }; push(@{$$tmprule[1]},$$value[0]); } push(@$rules,$tmprule); push(@{$$nterm{$lhs}},$#$rules); } $epsrules > 1 and _SyntaxError(0,"More than one empty rule for symbol $lhs",$lineno); } sub Parse { my($self)=shift; @_ > 0 or croak("No input grammar\n"); my($parsed)={}; $input=\$_[0]; $lexlevel=0; @lineno=(1,1); $nberr=0; $prec=0; $labelno=0; $head=(); $tail=""; $syms={}; $token={}; $term={}; $nterm={}; $rules=[ undef ]; #reserve slot 0 for start rule $precterm={}; $start=""; $nullable={}; $expect=0; pos($$input)=0; $self->YYParse(yylex => \&_Lexer, yyerror => \&_Error); $nberr and _SyntaxError(2,"Errors detected: No output",-1); @$parsed{ 'HEAD', 'TAIL', 'RULES', 'NTERM', 'TERM', 'NULL', 'PREC', 'SYMS', 'START', 'EXPECT' } = ( $head, $tail, $rules, $nterm, $term, $nullable, $precterm, $syms, $start, $expect); undef($input); undef($lexlevel); undef(@lineno); undef($nberr); undef($prec); undef($labelno); undef($head); undef($tail); undef($syms); undef($token); undef($term); undef($nterm); undef($rules); undef($precterm); undef($start); undef($nullable); undef($expect); $parsed } 1; Parse-Yapp-1.21/lib/Parse/Yapp/Options.pm0000644000175000017500000001020413120142664020722 0ustar wbraswellwbraswell# # Module Parse::Yapp::Options # # Copyright © 1998, 1999, 2000, 2001, Francois Desarmenien. # Copyright © 2017 William N. Braswell, Jr. # (see the pod text in Parse::Yapp module for use and distribution rights) # package Parse::Yapp::Options; use strict; use Carp; ############################################################################ #Definitions of options # # %known_options allowed options # # %default_options default # # %actions sub refs to execute if option is set with ($self,$value) # as parameters ############################################################################ # #A value of '' means any value can do # my(%known_options)= ( language => { perl => "Ouput parser for Perl language", # for future use... # 'c++' => "Output parser for C++ language", # c => "Output parser for C language" }, linenumbers => { 0 => "Don't embbed line numbers in parser", 1 => "Embbed source line numbers in parser" }, inputfile => { '' => "Input file name: will automagically fills input" }, classname => { '' => "Class name of parser object (Perl and C++)" }, standalone => { 0 => "Don't create a standalone parser (Perl and C++)", 1 => "Create a standalone parser" }, input => { '' => "Input text of grammar" }, template => { '' => "Template text for generating grammar file" }, ); my(%default_options)= ( language => 'perl', linenumbers => 1, inputfile => undef, classname => 'Parser', standalone => 0, input => undef, template => undef, shebang => undef, ); my(%actions)= ( inputfile => \&__LoadFile ); ############################################################################# # # Actions # # These are NOT a method, although they look like... # # They are super-private routines (that's why I prepend __ to their names) # ############################################################################# sub __LoadFile { my($self,$filename)=@_; open(IN,"<$filename") or croak "Cannot open input file '$filename' for reading"; $self->{OPTIONS}{input}=join('',); close(IN); } ############################################################################# # # Private methods # ############################################################################# sub _SetOption { my($self)=shift; my($key,$value)=@_; $key=lc($key); @_ == 2 or croak "Invalid number of arguments"; exists($known_options{$key}) or croak "Unknown option: '$key'"; if(exists($known_options{$key}{lc($value)})) { $value=lc($value); } elsif(not exists($known_options{$key}{''})) { croak "Invalid value '$value' for option '$key'"; } exists($actions{$key}) and &{$actions{$key}}($self,$value); $self->{OPTIONS}{$key}=$value; } sub _GetOption { my($self)=shift; my($key)=map { lc($_) } @_; @_ == 1 or croak "Invalid number of arguments"; exists($known_options{$key}) or croak "Unknown option: '$key'"; $self->{OPTIONS}{$key}; } ############################################################################# # # Public methods # ############################################################################# # # Constructor # sub new { my($class)=shift; my($self)={ OPTIONS => { %default_options } }; ref($class) and $class=ref($class); bless($self,$class); $self->Options(@_); $self; } # # Specify one or more options to set # sub Options { my($self)=shift; my($key,$value); @_ % 2 == 0 or croak "Invalid number of arguments"; while(($key,$value)=splice(@_,0,2)) { $self->_SetOption($key,$value); } } # # Set (2 parameters) or Get (1 parameter) values for one option # sub Option { my($self)=shift; my($key,$value)=@_; @_ == 1 and return $self->_GetOption($key); @_ == 2 and return $self->_SetOption($key,$value); croak "Invalid number of arguments"; } 1; Parse-Yapp-1.21/lib/Parse/Yapp/Grammar.pm0000755000175000017500000002142513120142674020670 0ustar wbraswellwbraswell# # Module Parse::Yapp::Grammar # # Copyright © 1998, 1999, 2000, 2001, Francois Desarmenien. # Copyright © 2017 William N. Braswell, Jr. # (see the pod text in Parse::Yapp module for use and distribution rights) # package Parse::Yapp::Grammar; @ISA=qw( Parse::Yapp::Options ); require 5.004; use Carp; use strict; use Parse::Yapp::Options; use Parse::Yapp::Parse; ############### # Constructor # ############### sub new { my($class)=shift; my($values); my($self)=$class->SUPER::new(@_); my($parser)=new Parse::Yapp::Parse; defined($self->Option('input')) or croak "No input grammar"; $values = $parser->Parse($self->Option('input')); undef($parser); $$self{GRAMMAR}=_ReduceGrammar($values); ref($class) and $class=ref($class); bless($self, $class); } ########### # Methods # ########### ########################## # Method To View Grammar # ########################## sub ShowRules { my($self)=shift; my($rules)=$$self{GRAMMAR}{RULES}; my($ruleno)=-1; my($text); for (@$rules) { my($lhs,$rhs)=@$_; $text.=++$ruleno.":\t".$lhs." -> "; if(@$rhs) { $text.=join(' ',map { $_ eq chr(0) ? '$end' : $_ } @$rhs); } else { $text.="/* empty */"; } $text.="\n"; } $text; } ########################### # Method To View Warnings # ########################### sub Warnings { my($self)=shift; my($text); my($grammar)=$$self{GRAMMAR}; exists($$grammar{UUTERM}) and do { $text="Unused terminals:\n\n"; for (@{$$grammar{UUTERM}}) { $text.="\t$$_[0], declared line $$_[1]\n"; } $text.="\n"; }; exists($$grammar{UUNTERM}) and do { $text.="Useless non-terminals:\n\n"; for (@{$$grammar{UUNTERM}}) { $text.="\t$$_[0], declared line $$_[1]\n"; } $text.="\n"; }; exists($$grammar{UURULES}) and do { $text.="Useless rules:\n\n"; for (@{$$grammar{UURULES}}) { $text.="\t$$_[0] -> ".join(' ',@{$$_[1]})."\n"; } $text.="\n"; }; $text; } ###################################### # Method to get summary about parser # ###################################### sub Summary { my($self)=shift; my($text); $text ="Number of rules : ". scalar(@{$$self{GRAMMAR}{RULES}})."\n"; $text.="Number of terminals : ". scalar(keys(%{$$self{GRAMMAR}{TERM}}))."\n"; $text.="Number of non-terminals : ". scalar(keys(%{$$self{GRAMMAR}{NTERM}}))."\n"; $text; } ############################### # Method to Ouput rules table # ############################### sub RulesTable { my($self)=shift; my($inputfile)=$self->Option('inputfile'); my($linenums)=$self->Option('linenumbers'); my($rules)=$$self{GRAMMAR}{RULES}; my($ruleno); my($text); defined($inputfile) or $inputfile = 'unkown'; $text="[\n\t"; $text.=join(",\n\t", map { my($lhs,$rhs,$code)=@$_[0,1,3]; my($len)=scalar(@$rhs); my($text); $text.="[#Rule ".$ruleno++."\n\t\t '$lhs', $len,"; if($code) { $text.= "\nsub". ( $linenums ? qq(\n#line $$code[1] "$inputfile"\n) : " "). "{$$code[0]}"; } else { $text.=' undef'; } $text.="\n\t]"; $text; } @$rules); $text.="\n]"; $text; } ################################ # Methods to get HEAD and TAIL # ################################ sub Head { my($self)=shift; my($inputfile)=$self->Option('inputfile'); my($linenums)=$self->Option('linenumbers'); my($text); $$self{GRAMMAR}{HEAD}[0] or return ''; defined($inputfile) or $inputfile = 'unkown'; for (@{$$self{GRAMMAR}{HEAD}}) { $linenums and $text.=qq(#line $$_[1] "$inputfile"\n); $text.=$$_[0]; } $text } sub Tail { my($self)=shift; my($inputfile)=$self->Option('inputfile'); my($linenums)=$self->Option('linenumbers'); my($text); $$self{GRAMMAR}{TAIL}[0] or return ''; defined($inputfile) or $inputfile = 'unkown'; $linenums and $text=qq(#line $$self{GRAMMAR}{TAIL}[1] "$inputfile"\n); $text.=$$self{GRAMMAR}{TAIL}[0]; $text } ################# # Private Stuff # ################# sub _UsefulRules { my($rules,$nterm) = @_; my($ufrules,$ufnterm); my($done); $ufrules=pack('b'.@$rules); $ufnterm={}; vec($ufrules,0,1)=1; #start rules IS always useful RULE: for (1..$#$rules) { # Ignore start rule for my $sym (@{$$rules[$_][1]}) { exists($$nterm{$sym}) and next RULE; } vec($ufrules,$_,1)=1; ++$$ufnterm{$$rules[$_][0]}; } do { $done=1; RULE: for (grep { vec($ufrules,$_,1) == 0 } 1..$#$rules) { for my $sym (@{$$rules[$_][1]}) { exists($$nterm{$sym}) and not exists($$ufnterm{$sym}) and next RULE; } vec($ufrules,$_,1)=1; exists($$ufnterm{$$rules[$_][0]}) or do { $done=0; ++$$ufnterm{$$rules[$_][0]}; }; } }until($done); ($ufrules,$ufnterm) }#_UsefulRules sub _Reachable { my($rules,$nterm,$term,$ufrules,$ufnterm)=@_; my($reachable); my(@fifo)=( 0 ); $reachable={ '$start' => 1 }; #$start is always reachable while(@fifo) { my($ruleno)=shift(@fifo); for my $sym (@{$$rules[$ruleno][1]}) { exists($$term{$sym}) and do { ++$$reachable{$sym}; next; }; ( not exists($$ufnterm{$sym}) or exists($$reachable{$sym}) ) and next; ++$$reachable{$sym}; push(@fifo, grep { vec($ufrules,$_,1) } @{$$nterm{$sym}}); } } $reachable }#_Reachable sub _SetNullable { my($rules,$term,$nullable) = @_; my(@nrules); my($done); RULE: for (@$rules) { my($lhs,$rhs)=@$_; exists($$nullable{$lhs}) and next; for (@$rhs) { exists($$term{$_}) and next RULE; } push(@nrules,[$lhs,$rhs]); } do { $done=1; RULE: for (@nrules) { my($lhs,$rhs)=@$_; exists($$nullable{$lhs}) and next; for (@$rhs) { exists($$nullable{$_}) or next RULE; } $done=0; ++$$nullable{$lhs}; } }until($done); } sub _ReduceGrammar { my($values)=@_; my($ufrules,$ufnterm,$reachable); my($grammar)={ HEAD => $values->{HEAD}, TAIL => $values->{TAIL}, EXPECT => $values->{EXPECT} }; my($rules,$nterm,$term) = @$values {'RULES', 'NTERM', 'TERM'}; ($ufrules,$ufnterm) = _UsefulRules($rules,$nterm); exists($$ufnterm{$values->{START}}) or die "*Fatal* Start symbol $values->{START} derives nothing, at eof\n"; $reachable = _Reachable($rules,$nterm,$term,$ufrules,$ufnterm); $$grammar{TERM}{chr(0)}=undef; for my $sym (keys %$term) { ( exists($$reachable{$sym}) or exists($values->{PREC}{$sym}) ) and do { $$grammar{TERM}{$sym} = defined($$term{$sym}[0]) ? $$term{$sym} : undef; next; }; push(@{$$grammar{UUTERM}},[ $sym, $values->{SYMS}{$sym} ]); } $$grammar{NTERM}{'$start'}=[]; for my $sym (keys %$nterm) { exists($$reachable{$sym}) and do { exists($values->{NULL}{$sym}) and ++$$grammar{NULLABLE}{$sym}; $$grammar{NTERM}{$sym}=[]; next; }; push(@{$$grammar{UUNTERM}},[ $sym, $values->{SYMS}{$sym} ]); } for my $ruleno (0..$#$rules) { vec($ufrules,$ruleno,1) and exists($$grammar{NTERM}{$$rules[$ruleno][0]}) and do { push(@{$$grammar{RULES}},$$rules[$ruleno]); push(@{$$grammar{NTERM}{$$rules[$ruleno][0]}},$#{$$grammar{RULES}}); next; }; push(@{$$grammar{UURULES}},[ @{$$rules[$ruleno]}[0,1] ]); } _SetNullable(@$grammar{'RULES', 'TERM', 'NULLABLE'}); $grammar; }#_ReduceGrammar 1; Parse-Yapp-1.21/lib/Parse/Yapp.pm0000755000175000017500000004020013141024700017262 0ustar wbraswellwbraswell# # Module Parse::Yapp.pm. # # Copyright © 1998, 1999, 2000, 2001, Francois Desarmenien. # Copyright © 2017 William N. Braswell, Jr. # All Rights Reserved. # # See the Copyright section at the end of the Parse/Yapp.pm pod section # for usage and distribution rights. # # package Parse::Yapp; use strict; use vars qw($VERSION @ISA); @ISA = qw(Parse::Yapp::Output); use Parse::Yapp::Output; # CORRELATION #py001: $VERSION must be changed in both Parse::Yapp & Parse::Yapp::Driver our $VERSION = '1.21'; 1; __END__ =encoding UTF-8 =head1 NAME Parse::Yapp - Perl extension for generating and using LALR parsers. =head1 SYNOPSIS yapp -m MyParser grammar_file.yp ... use MyParser; $parser=new MyParser(); $value=$parser->YYParse(yylex => \&lexer_sub, yyerror => \&error_sub); $nberr=$parser->YYNberr(); $parser->YYData->{DATA}= [ 'Anything', 'You Want' ]; $data=$parser->YYData->{DATA}[0]; =head1 DESCRIPTION Parse::Yapp (Yet Another Perl Parser compiler) is a collection of modules that let you generate and use yacc like thread safe (reentrant) parsers with perl object oriented interface. The script yapp is a front-end to the Parse::Yapp module and let you easily create a Perl OO parser from an input grammar file. =head2 The Grammar file =over 4 =item C Through all your files, comments are either Perl style, introduced by I<#> up to the end of line, or C style, enclosed between I and I<*/>. =item C Through all the grammar files, two kind of symbols may appear: I symbols, called also I symbols, which are the names of your rules, and I symbols, called also I. Tokens are the symbols your lexer function will feed your parser with (see below). They are of two flavours: symbolic tokens and string literals. Non-terminals and symbolic tokens share the same identifier syntax: [A-Za-z][A-Za-z0-9_]* String literals are enclosed in single quotes and can contain almost anything. They will be output to your parser file double-quoted, making any special character as such. '"', '$' and '@' will be automatically quoted with '\', making their writing more natural. On the other hand, if you need a single quote inside your literal, just quote it with '\'. You cannot have a literal I<'error'> in your grammar as it would confuse the driver with the I token. Use a symbolic token instead. In case you inadvertently use it, this will produce a warning telling you you should have written it I and will treat it as if it were the I token, which is certainly NOT what you meant. =item C It is very close to yacc syntax (in fact, I should compile a clean I grammar without any modification, whereas the opposite is not true). This file is divided in three sections, separated by C<%%>: header section %% rules section %% footer section =over 4 =item B section may optionally contain: =over =item * One or more code blocks enclosed inside C<%{> and C<%}> just like in yacc. They may contain any valid Perl code and will be copied verbatim at the very beginning of the parser module. They are not as useful as they are in yacc, but you can use them, for example, for global variable declarations, though you will notice later that such global variables can be avoided to make a reentrant parser module. =item * Precedence declarations, introduced by C<%left>, C<%right> and C<%nonassoc> specifying associativity, followed by the list of tokens or litterals having the same precedence and associativity. The precedence being the latter declared will be having the highest level. (see the yacc or bison manuals for a full explanation of how they work, as they are implemented exactly the same way in Parse::Yapp) =item * C<%start> followed by a rule's left hand side, declaring this rule to be the starting rule of your grammar. The default, when C<%start> is not used, is the first rule in your grammar section. =item * C<%token> followed by a list of symbols, forcing them to be recognized as tokens, generating a syntax error if used in the left hand side of a rule declaration. Note that in Parse::Yapp, you I need to declare tokens as in yacc: any symbol not appearing as a left hand side of a rule is considered to be a token. Other yacc declarations or constructs such as C<%type> and C<%union> are parsed but (almost) ignored. =item * C<%expect> followed by a number, suppress warnings about number of Shift/Reduce conflicts when both numbers match, a la bison. =back =back =item B contains your grammar rules: A rule is made of a left-hand-side symbol, followed by a C<':'> and one or more right-hand-sides separated by C<'|'> and terminated by a C<';'>: exp: exp '+' exp | exp '-' exp ; A right hand side may be empty: input: #empty | input line ; (if you have more than one empty rhs, Parse::Yapp will issue a warning, as this is usually a mistake, and you will certainly have a reduce/reduce conflict) A rhs may be followed by an optional C<%prec> directive, followed by a token, giving the rule an explicit precedence (see yacc manuals for its precise meaning) and optional semantic action code block (see below). exp: '-' exp %prec NEG { -$_[1] } | exp '+' exp { $_[1] + $_[3] } | NUM ; Note that in Parse::Yapp, a lhs I appear more than once as a rule name (This differs from yacc). =item C may contain any valid Perl code and will be appended at the very end of your parser module. Here you can write your lexer, error report subs and anything relevant to you parser. =item C Semantic actions are run every time a I occurs in the parsing flow and they must return a semantic value. They are (usually, but see below C) written at the very end of the rhs, enclosed with C<{ }>, and are copied verbatim to your parser file, inside of the rules table. Be aware that matching braces in Perl is much more difficult than in C: inside strings they don't need to match. While in C it is very easy to detect the beginning of a string construct, or a single character, it is much more difficult in Perl, as there are so many ways of writing such literals. So there is no check for that today. If you need a brace in a double-quoted string, just quote it (C<\{> or C<\}>). For single-quoted strings, you will need to make a comment matching it I. Sorry for the inconvenience. { "{ My string block }". "\{ My other string block \}". qq/ My unmatched brace \} /. # Force the match: { q/ for my closing brace } / q/ My opening brace { / # must be closed: } } All of these constructs should work. In Parse::Yapp, semantic actions are called like normal Perl sub calls, with their arguments passed in C<@_>, and their semantic value are their return values. $_[1] to $_[n] are the parameters just as $1 to $n in yacc, while $_[0] is the parser object itself. Having $_[0] being the parser object itself allows you to call parser methods. That's how the yacc macros are implemented: yyerrok is done by calling $_[0]->YYErrok YYERROR is done by calling $_[0]->YYError YYACCEPT is done by calling $_[0]->YYAccept YYABORT is done by calling $_[0]->YYAbort All those methods explicitly return I, for convenience. YYRECOVERING is done by calling $_[0]->YYRecovering Four useful methods in error recovery sub $_[0]->YYCurtok $_[0]->YYCurval $_[0]->YYExpect $_[0]->YYLexer return respectivly the current input token that made the parse fail, its semantic value (both can be used to modify their values too, but I ! See I section for an example), a list which contains the tokens the parser expected when the failure occurred and a reference to the lexer routine. Note that if C<$_[0]-EYYCurtok> is declared as a C<%nonassoc> token, it can be included in C<$_[0]-EYYExpect> list whenever the input try to use it in an associative way. This is not a bug: the token IS expected to report an error if encountered. To detect such a thing in your error reporting sub, the following example should do the trick: grep { $_[0]->YYCurtok eq $_ } $_[0]->YYExpect and do { #Non-associative token used in an associative expression }; Accessing semantics values on the left of your reducing rule is done through the method $_[0]->YYSemval( index ) where index is an integer. Its value being I<1 .. n> returns the same values than I<$_[1] .. $_[n]>, but I<-n .. 0> returns values on the left of the rule being reduced (It is related to I<$-n .. $0 .. $n> in yacc, but you cannot use I<$_[0]> or I<$_[-n]> constructs in Parse::Yapp for obvious reasons) There is also a provision for a user data area in the parser object, accessed by the method: $_[0]->YYData which returns a reference to an anonymous hash, which let you have all of your parsing data held inside the object (see the Calc.yp or ParseYapp.yp files in the distribution for some examples). That's how you can make you parser module reentrant: all of your module states and variables are held inside the parser object. Note: unfortunately, method calls in Perl have a lot of overhead, and when YYData is used, it may be called a huge number of times. If your are not a *real* purist and efficiency is your concern, you may access directly the user-space in the object: $parser->{USER} wich is a reference to an anonymous hash array, and then benchmark. If no action is specified for a rule, the equivalant of a default action is run, which returns the first parameter: { $_[1] } =item C It is also possible to embed semantic actions inside of a rule: typedef: TYPE { $type = $_[1] } identlist { ... } ; When the Parse::Yapp's parser encounter such an embedded action, it modifies the grammar as if you wrote (although @x-1 is not a legal lhs value): @x-1: /* empty */ { $type = $_[1] }; typedef: TYPE @x-1 identlist { ... } ; where I is a sequential number incremented for each "in rule" action, and I<-1> represents the "dot position" in the rule where the action arises. In such actions, you can use I<$_[1]..$_[n]> variables, which are the semantic values on the left of your action. Be aware that the way Parse::Yapp modifies your grammar because of I can produce, in some cases, spurious conflicts that wouldn't happen otherwise. =item C Now that you grammar file is written, you can use yapp on it to generate your parser module: yapp -v Calc.yp will create two files F, your parser module, and F a verbose output of your parser rules, conflicts, warnings, states and summary. What your are missing now is a lexer routine. =item C is called each time the parser need to read the next token. It is called with only one argument that is the parser object itself, so you can access its methods, specially the $_[0]->YYData data area. It is its duty to return the next token and value to the parser. They C be returned as a list of two variables, the first one is the token known by the parser (symbolic or literal), the second one being anything you want (usually the content of the token, or the literal value) from a simple scalar value to any complex reference, as the parsing driver never use it but to call semantic actions: ( 'NUMBER', $num ) or ( '>=', '>=' ) or ( 'ARRAY', [ @values ] ) When the lexer reach the end of input, it must return the C<''> empty token with an undef value: ( '', undef ) Note that your lexer should I return C<'error'> as token value: for the driver, this is the error token used for error recovery and would lead to odd reactions. Now that you have your lexer written, maybe you will need to output meaningful error messages, instead of the default which is to print 'Parse error.' on STDERR. So you will need an Error reporting sub. =item C If you want one, write it knowing that it is passed as parameter the parser object. So you can share information with the lexer routine quite easily. You can also use the C<$_[0]-EYYErrok> method in it, which will resume parsing as if no error occurred. Of course, since the invalid token is still invalid, you're supposed to fix the problem by yourself. The method C<$_[0]-EYYLexer> may help you, as it returns a reference to the lexer routine, and can be called as ($tok,$val)=&{$_[0]->Lexer} to get the next token and semantic value from the input stream. To make them current for the parser, use: ($_[0]->YYCurtok, $_[0]->YYCurval) = ($tok, $val) and know what you're doing... =item C Now you've got everything to do the parsing. First, use the parser module: use Calc; Then create the parser object: $parser=new Calc; Now, call the YYParse method, telling it where to find the lexer and error report subs: $result=$parser->YYParse(yylex => \&Lexer, yyerror => \&ErrorReport); (assuming Lexer and ErrorReport subs have been written in your current package) The order in which parameters appear is unimportant. Et voila. The YYParse method will do the parse, then return the last semantic value returned, or undef if error recovery cannot recover. If you need to be sure the parse has been successful (in case your last returned semantic value I undef) make a call to: $parser->YYNberr() which returns the total number of time the error reporting sub has been called. =item C in Parse::Yapp is implemented the same way it is in yacc. =item C To debug your parser, you can call the YYParse method with a debug parameter: $parser->YYParse( ... , yydebug => value, ... ) where value is a bitfield, each bit representing a specific debug output: Bit Value Outputs 0x01 Token reading (useful for Lexer debugging) 0x02 States information 0x04 Driver actions (shifts, reduces, accept...) 0x08 Parse Stack dump 0x10 Error Recovery tracing To have a full debugging output, use debug => 0x1F Debugging output is sent to STDERR, and be aware that it can produce C outputs. =item C By default, the parser modules generated will need the Parse::Yapp module installed on the system to run. They use the Parse::Yapp::Driver which can be safely shared between parsers in the same script. In the case you'd prefer to have a standalone module generated, use the C<-s> switch with yapp: this will automagically copy the driver code into your module so you can use/distribute it without the need of the Parse::Yapp module, making it really a C. If you do so, please remember to include Parse::Yapp's copyright notice in your main module copyright, so others can know about Parse::Yapp module. =item C by default will be included in the generated parser module, which will help to find the guilty line in your source file in case of a syntax error. You can disable this feature by compiling your grammar with yapp using the C<-n> switch. =back =head1 BUGS AND SUGGESTIONS If you find bugs, think of anything that could improve Parse::Yapp or have any questions related to it, feel free to contact the author. =head1 AUTHOR William N. Braswell, Jr. (Remove "NOSPAM".) =head1 SEE ALSO yapp(1) perl(1) yacc(1) bison(1). =head1 COPYRIGHT The Parse::Yapp module and its related modules and shell scripts are copyright: Copyright © 1998, 1999, 2000, 2001, Francois Desarmenien. Copyright © 2017 William N. Braswell, Jr. You may use and distribute them under the terms of either the GNU General Public License or the Artistic License, as specified in the Perl README file. If you use the "standalone parser" option so people don't need to install Parse::Yapp on their systems in order to run you software, this copyright noticed should be included in your software copyright too, and the copyright notice in the embedded driver should be left untouched. =cut