From 2431a80d8a420d235fafc4f524bd519694c1e0f6 Mon Sep 17 00:00:00 2001 From: Joel de Guzman Date: Sun, 13 Apr 2008 03:02:30 +0000 Subject: [PATCH] spirit2 ! :) [SVN r44360] --- classic/test/bug_000008.cpp | 2 +- doc/Jamfile | 27 + doc/_concepts_template_.qbk | 46 + doc/_reference_template_.qbk | 56 + doc/acknowledgments.qbk | 147 + doc/faq.qbk | 10 + doc/html/images/FlowOfControl.png | Bin 0 -> 80302 bytes doc/html/images/Thumbs.db | Bin 0 -> 7168 bytes doc/html/images/TokenStructure.png | Bin 0 -> 59634 bytes doc/introduction.qbk | 10 + doc/lex.qbk | 50 + doc/lex/introduction.qbk | 137 + doc/lex/lexer_attributes.qbk | 12 + doc/lex/lexer_primitives.qbk | 15 + doc/lex/lexer_quickstart1.qbk | 97 + doc/lex/lexer_quickstart2.qbk | 133 + doc/lex/lexer_quickstart3.qbk | 151 + doc/lex/lexer_semantic_actions.qbk | 10 + doc/lex/lexer_states.qbk | 21 + doc/lex/lexer_static_model.qbk | 119 + doc/lex/lexer_tutorials.qbk | 59 + doc/lex/parsing_using_a_lexer.qbk | 15 + doc/lex/token_definition.qbk | 11 + doc/lex/tokenizing.qbk | 15 + doc/lex/tokens_values.qbk | 207 ++ doc/notes.qbk | 24 + doc/notes/style_guide.qbk | 87 + doc/outline.txt | 97 + doc/preface.qbk | 217 ++ doc/qi_and_karma.qbk | 52 + doc/qi_and_karma/attributes.qbk | 10 + doc/qi_and_karma/debugging.qbk | 10 + doc/qi_and_karma/directives.qbk | 10 + doc/qi_and_karma/error_handling.qbk | 10 + doc/qi_and_karma/generating.qbk | 24 + doc/qi_and_karma/grammars.qbk | 10 + doc/qi_and_karma/operators.qbk | 10 + doc/qi_and_karma/parse_trees_and_asts.qbk | 10 + doc/qi_and_karma/parsing.qbk | 44 + doc/qi_and_karma/peg.qbk | 10 + doc/qi_and_karma/primitives.qbk | 10 + doc/qi_and_karma/quick_reference.qbk | 43 + doc/qi_and_karma/rules.qbk | 10 + doc/qi_and_karma/semantic_actions.qbk | 10 + doc/qi_and_karma/tutorials.qbk | 10 + doc/rationale.qbk | 10 + doc/reference/lex/lexer.qbk | 10 + doc/reference/lex/lexer_class.qbk | 19 + doc/reference/lex/token.qbk | 10 + doc/reference/lex/token_class.qbk | 10 + doc/reference/lex/tokendef.qbk | 10 + doc/reference/lex/tokendef_class.qbk | 10 + doc/reference/lex/tokenset.qbk | 10 + doc/reference/lex/tokenset_class.qbk | 10 + doc/reference/qi_and_karma/action.qbk | 10 + doc/reference/qi_and_karma/auxiliary.qbk | 10 + doc/reference/qi_and_karma/binary.qbk | 10 + doc/reference/qi_and_karma/char.qbk | 10 + doc/reference/qi_and_karma/debug.qbk | 10 + doc/reference/qi_and_karma/directive.qbk | 10 + doc/reference/qi_and_karma/generator.qbk | 10 + doc/reference/qi_and_karma/nonterminal.qbk | 10 + doc/reference/qi_and_karma/numeric.qbk | 10 + doc/reference/qi_and_karma/operator.qbk | 10 + doc/reference/qi_and_karma/parser.qbk | 43 + doc/reference/qi_and_karma/stream.qbk | 10 + doc/reference/qi_and_karma/string.qbk | 10 + doc/references.qbk | 91 + doc/spirit2.qbk | 143 + doc/what_s_new.qbk | 10 + example/karma/Jamfile | 12 + example/karma/basic_facilities.cpp | 178 ++ example/karma/functor_facilities.cpp | 202 ++ example/karma/quick_start1.cpp | 119 + example/lex/Jamfile | 22 + example/lex/example.hpp | 26 + example/lex/example1.cpp | 136 + example/lex/example2.cpp | 169 + example/lex/example3.cpp | 161 + example/lex/example4.cpp | 239 ++ example/lex/example5.cpp | 283 ++ example/lex/example6.cpp | 263 ++ example/lex/print_numbers.cpp | 118 + example/lex/static_lexer/Jamfile | 13 + .../lex/static_lexer/word_count_generate.cpp | 42 + .../lex/static_lexer/word_count_static.cpp | 118 + .../lex/static_lexer/word_count_static.hpp | 111 + .../lex/static_lexer/word_count_tokens.hpp | 40 + example/lex/strip_comments.cpp | 164 + example/lex/strip_comments_lexer.cpp | 121 + example/lex/word_count.cpp | 172 + example/lex/word_count_functor.cpp | 184 ++ example/lex/word_count_functor_flex.cpp | 1571 ++++++++++ example/lex/word_count_lexer.cpp | 138 + example/qi/Jamfile | 46 + example/qi/calc1.cpp | 104 + example/qi/calc2.cpp | 123 + example/qi/calc3.cpp | 110 + example/qi/calc3_lexer.cpp | 201 ++ example/qi/calc4.cpp | 126 + example/qi/calc5.cpp | 227 ++ example/qi/calc6/calc6.cpp | 102 + example/qi/calc6/calc6.hpp | 183 ++ example/qi/calc6/calc6a.cpp | 58 + example/qi/calc6/calc6b.cpp | 18 + example/qi/calc6/calc6b.hpp | 70 + example/qi/calc6/calc6c.cpp | 16 + example/qi/calc6/calc6c.hpp | 65 + example/qi/calc7/calc7.cpp | 99 + example/qi/calc7/calc7.hpp | 208 ++ example/qi/calc7/calc7a.cpp | 125 + example/qi/calc7/calc7b.cpp | 17 + example/qi/calc7/calc7b.hpp | 101 + example/qi/calc7/calc7c.cpp | 16 + example/qi/calc7/calc7c.hpp | 131 + example/qi/complex_number.cpp | 94 + example/qi/employee.cpp | 132 + example/qi/mini_c/mini_c.cpp | 123 + example/qi/mini_c/mini_c.hpp | 354 +++ example/qi/mini_c/mini_ca.cpp | 155 + example/qi/mini_c/mini_cb.cpp | 19 + example/qi/mini_c/mini_cb.hpp | 115 + example/qi/mini_c/mini_cc.cpp | 17 + example/qi/mini_c/mini_cc.hpp | 144 + example/qi/mini_c/mini_cd.cpp | 16 + example/qi/mini_c/mini_cd.hpp | 71 + example/qi/mini_xml1.cpp | 224 ++ example/qi/mini_xml2.cpp | 225 ++ example/qi/mini_xml_karma.cpp | 262 ++ example/qi/mini_xml_samples/1.xml | 1 + example/qi/mini_xml_samples/2.xml | 1 + example/qi/mini_xml_samples/3.xml | 5 + example/qi/num_list.cpp | 98 + example/qi/num_list2.cpp | 97 + example/qi/num_list3.cpp | 97 + example/qi/roman.cpp | 168 + example/qi/sum.cpp | 92 + index.html | 15 + phoenix/doc/Jamfile.v2 | 12 + phoenix/doc/html/boostbook.css | 511 +++ phoenix/doc/html/images/add2.png | Bin 0 -> 1250 bytes phoenix/doc/html/images/add2_call.png | Bin 0 -> 998 bytes phoenix/doc/html/images/adder.png | Bin 0 -> 1325 bytes phoenix/doc/html/images/alert.png | Bin 0 -> 603 bytes phoenix/doc/html/images/banner.png | Bin 0 -> 18002 bytes phoenix/doc/html/images/fbox.png | Bin 0 -> 1231 bytes phoenix/doc/html/images/funnel_in.png | Bin 0 -> 3571 bytes phoenix/doc/html/images/funnel_out.png | Bin 0 -> 5085 bytes phoenix/doc/html/images/home.png | Bin 0 -> 358 bytes phoenix/doc/html/images/lambda_cpp.png | Bin 0 -> 2118 bytes phoenix/doc/html/images/next.png | Bin 0 -> 336 bytes phoenix/doc/html/images/note.png | Bin 0 -> 658 bytes phoenix/doc/html/images/organization.png | Bin 0 -> 3337 bytes phoenix/doc/html/images/prev.png | Bin 0 -> 334 bytes phoenix/doc/html/images/smiley.png | Bin 0 -> 867 bytes phoenix/doc/html/images/tip.png | Bin 0 -> 640 bytes phoenix/doc/html/images/up.png | Bin 0 -> 370 bytes phoenix/doc/html/index.html | 224 ++ phoenix/doc/html/phoenix/acknowledgement.html | 87 + phoenix/doc/html/phoenix/actors.html | 82 + phoenix/doc/html/phoenix/algorithm.html | 408 +++ phoenix/doc/html/phoenix/basics.html | 243 ++ phoenix/doc/html/phoenix/composite.html | 1534 +++++++++ phoenix/doc/html/phoenix/inside_phoenix.html | 750 +++++ phoenix/doc/html/phoenix/intrinsic.html | 320 ++ phoenix/doc/html/phoenix/introduction.html | 66 + phoenix/doc/html/phoenix/organization.html | 208 ++ phoenix/doc/html/phoenix/primitives.html | 329 ++ phoenix/doc/html/phoenix/references.html | 86 + phoenix/doc/html/phoenix/starter_kit.html | 503 +++ phoenix/doc/html/phoenix/wrap_up.html | 72 + phoenix/doc/users_manual.qbk | 2769 +++++++++++++++++ phoenix/example/Jamfile.v2 | 24 + phoenix/example/users_manual/algorithm.cpp | 21 + phoenix/example/users_manual/all_odds.cpp | 35 + phoenix/example/users_manual/arguments.cpp | 22 + phoenix/example/users_manual/callback.cpp | 26 + phoenix/example/users_manual/factorial.cpp | 40 + phoenix/example/users_manual/find_if.cpp | 30 + phoenix/example/users_manual/function.cpp | 47 + phoenix/example/users_manual/if.cpp | 35 + phoenix/example/users_manual/lambda.cpp | 79 + phoenix/example/users_manual/references.cpp | 22 + phoenix/example/users_manual/values.cpp | 20 + phoenix/index.html | 10 + phoenix/test/Jamfile.v2 | 90 + phoenix/test/algorithm/iteration.cpp | 57 + phoenix/test/algorithm/querying.cpp | 270 ++ phoenix/test/algorithm/querying2.cpp | 83 + phoenix/test/algorithm/transformation1.cpp | 390 +++ phoenix/test/algorithm/transformation2.cpp | 188 ++ phoenix/test/algorithm/transformation3.cpp | 176 ++ phoenix/test/algorithm/transformation4.cpp | 153 + .../test/bind/bind_function_object_tests.cpp | 106 + phoenix/test/bind/bind_function_tests.cpp | 57 + .../test/bind/bind_member_function_tests.cpp | 76 + .../test/bind/bind_member_variable_tests.cpp | 37 + phoenix/test/container/container_tests.hpp | 814 +++++ phoenix/test/container/container_tests1a.cpp | 46 + phoenix/test/container/container_tests1b.cpp | 48 + phoenix/test/container/container_tests2a.cpp | 46 + phoenix/test/container/container_tests2b.cpp | 47 + phoenix/test/container/container_tests3a.cpp | 60 + phoenix/test/container/container_tests3b.cpp | 61 + phoenix/test/container/container_tests4a.cpp | 47 + phoenix/test/container/container_tests4b.cpp | 46 + phoenix/test/container/container_tests5a.cpp | 53 + phoenix/test/container/container_tests5b.cpp | 52 + phoenix/test/container/container_tests6a.cpp | 69 + phoenix/test/container/container_tests6b.cpp | 70 + phoenix/test/core/compose_tests.cpp | 84 + phoenix/test/core/primitives_tests.cpp | 69 + phoenix/test/detail/type_deduction_tests.cpp | 374 +++ phoenix/test/function/function_tests.cpp | 116 + phoenix/test/object/cast_tests.cpp | 63 + phoenix/test/object/new_delete_tests.cpp | 52 + phoenix/test/operator/arithmetic_tests.cpp | 54 + phoenix/test/operator/bitwise_tests.cpp | 73 + phoenix/test/operator/comparison_tests.cpp | 28 + phoenix/test/operator/if_else_tests.cpp | 34 + phoenix/test/operator/io_tests.cpp | 51 + phoenix/test/operator/logical_tests.cpp | 36 + phoenix/test/operator/member.cpp | 83 + phoenix/test/operator/misc_binary_tests.cpp | 97 + phoenix/test/operator/self_tests.cpp | 55 + phoenix/test/operator/unary_tests.cpp | 64 + phoenix/test/scope/bug_000008.cpp | 96 + phoenix/test/scope/dynamic_tests.cpp | 79 + phoenix/test/scope/lambda_tests.cpp | 179 ++ phoenix/test/scope/let_tests.cpp | 146 + phoenix/test/statement/exceptions.cpp | 100 + phoenix/test/statement/if_tests.cpp | 70 + phoenix/test/statement/loops_tests.cpp | 79 + phoenix/test/statement/switch_tests.cpp | 69 + test/Jamfile | 98 + test/karma/alternative.cpp | 88 + test/karma/binary.cpp | 101 + test/karma/case_handling.cpp | 176 ++ test/karma/center_alignment.cpp | 79 + test/karma/char.cpp | 151 + test/karma/delimiter.cpp | 55 + test/karma/eps.cpp | 31 + test/karma/format_manip.cpp | 200 ++ test/karma/functor.cpp | 58 + test/karma/grammar.cpp | 63 + test/karma/grammar_fail.cpp | 46 + test/karma/int_numerics.cpp | 306 ++ test/karma/kleene.cpp | 113 + test/karma/lazy.cpp | 51 + test/karma/left_alignment.cpp | 70 + test/karma/list.cpp | 66 + test/karma/lit.cpp | 137 + test/karma/none.cpp | 32 + test/karma/optional.cpp | 68 + test/karma/pattern.cpp | 188 ++ test/karma/real_numerics.cpp | 448 +++ test/karma/right_alignment.cpp | 70 + test/karma/rule_fail.cpp | 36 + test/karma/sequence.cpp | 130 + test/karma/test.hpp | 290 ++ test/lex/lexertl1.cpp | 72 + test/lex/lexertl2.cpp | 96 + test/lex/lexertl3.cpp | 78 + test/lex/lexertl4.cpp | 96 + test/lex/lexertl5.cpp | 115 + test/lex/state_switcher_test.cpp | 92 + test/lex/test.hpp | 89 + test/lex/test_parser.hpp | 67 + test/qi/alternative.cpp | 87 + test/qi/and_predicate.cpp | 27 + test/qi/binary.cpp | 110 + test/qi/char.cpp | 102 + test/qi/char_class.cpp | 170 + test/qi/debug.cpp | 241 ++ test/qi/difference.cpp | 78 + test/qi/eps.cpp | 33 + test/qi/expect.cpp | 89 + test/qi/functor.cpp | 68 + test/qi/grammar.cpp | 109 + test/qi/grammar_fail.cpp | 42 + test/qi/int.cpp | 164 + test/qi/kleene.cpp | 113 + test/qi/lazy.cpp | 54 + test/qi/lexeme.cpp | 38 + test/qi/list.cpp | 64 + test/qi/lit.cpp | 79 + test/qi/match_manip.cpp | 293 ++ test/qi/no_case.cpp | 95 + test/qi/none.cpp | 25 + test/qi/not_predicate.cpp | 27 + test/qi/optional.cpp | 66 + test/qi/permutation.cpp | 121 + test/qi/plus.cpp | 98 + test/qi/range_run.cpp | 167 + test/qi/raw.cpp | 39 + test/qi/real.cpp | 374 +++ test/qi/rule.cpp | 227 ++ test/qi/rule_fail.cpp | 31 + test/qi/sequence.cpp | 204 ++ test/qi/sequential_or.cpp | 77 + test/qi/symbols.cpp | 189 ++ test/qi/test.hpp | 75 + test/qi/tst.cpp | 354 +++ test/qi/uint.cpp | 175 ++ test/support/detail/sstream.hpp | 46 + test/support/hold_any.cpp | 201 ++ test/support/multi_pass.cpp | 770 +++++ test/support/multi_pass_compile.cpp | 63 + 308 files changed, 34985 insertions(+), 1 deletion(-) create mode 100644 doc/Jamfile create mode 100644 doc/_concepts_template_.qbk create mode 100644 doc/_reference_template_.qbk create mode 100644 doc/acknowledgments.qbk create mode 100644 doc/faq.qbk create mode 100644 doc/html/images/FlowOfControl.png create mode 100644 doc/html/images/Thumbs.db create mode 100644 doc/html/images/TokenStructure.png create mode 100644 doc/introduction.qbk create mode 100644 doc/lex.qbk create mode 100644 doc/lex/introduction.qbk create mode 100644 doc/lex/lexer_attributes.qbk create mode 100644 doc/lex/lexer_primitives.qbk create mode 100644 doc/lex/lexer_quickstart1.qbk create mode 100644 doc/lex/lexer_quickstart2.qbk create mode 100644 doc/lex/lexer_quickstart3.qbk create mode 100644 doc/lex/lexer_semantic_actions.qbk create mode 100644 doc/lex/lexer_states.qbk create mode 100644 doc/lex/lexer_static_model.qbk create mode 100644 doc/lex/lexer_tutorials.qbk create mode 100644 doc/lex/parsing_using_a_lexer.qbk create mode 100644 doc/lex/token_definition.qbk create mode 100644 doc/lex/tokenizing.qbk create mode 100644 doc/lex/tokens_values.qbk create mode 100644 doc/notes.qbk create mode 100644 doc/notes/style_guide.qbk create mode 100644 doc/outline.txt create mode 100644 doc/preface.qbk create mode 100644 doc/qi_and_karma.qbk create mode 100644 doc/qi_and_karma/attributes.qbk create mode 100644 doc/qi_and_karma/debugging.qbk create mode 100644 doc/qi_and_karma/directives.qbk create mode 100644 doc/qi_and_karma/error_handling.qbk create mode 100644 doc/qi_and_karma/generating.qbk create mode 100644 doc/qi_and_karma/grammars.qbk create mode 100644 doc/qi_and_karma/operators.qbk create mode 100644 doc/qi_and_karma/parse_trees_and_asts.qbk create mode 100644 doc/qi_and_karma/parsing.qbk create mode 100644 doc/qi_and_karma/peg.qbk create mode 100644 doc/qi_and_karma/primitives.qbk create mode 100644 doc/qi_and_karma/quick_reference.qbk create mode 100644 doc/qi_and_karma/rules.qbk create mode 100644 doc/qi_and_karma/semantic_actions.qbk create mode 100644 doc/qi_and_karma/tutorials.qbk create mode 100644 doc/rationale.qbk create mode 100644 doc/reference/lex/lexer.qbk create mode 100644 doc/reference/lex/lexer_class.qbk create mode 100644 doc/reference/lex/token.qbk create mode 100644 doc/reference/lex/token_class.qbk create mode 100644 doc/reference/lex/tokendef.qbk create mode 100644 doc/reference/lex/tokendef_class.qbk create mode 100644 doc/reference/lex/tokenset.qbk create mode 100644 doc/reference/lex/tokenset_class.qbk create mode 100644 doc/reference/qi_and_karma/action.qbk create mode 100644 doc/reference/qi_and_karma/auxiliary.qbk create mode 100644 doc/reference/qi_and_karma/binary.qbk create mode 100644 doc/reference/qi_and_karma/char.qbk create mode 100644 doc/reference/qi_and_karma/debug.qbk create mode 100644 doc/reference/qi_and_karma/directive.qbk create mode 100644 doc/reference/qi_and_karma/generator.qbk create mode 100644 doc/reference/qi_and_karma/nonterminal.qbk create mode 100644 doc/reference/qi_and_karma/numeric.qbk create mode 100644 doc/reference/qi_and_karma/operator.qbk create mode 100644 doc/reference/qi_and_karma/parser.qbk create mode 100644 doc/reference/qi_and_karma/stream.qbk create mode 100644 doc/reference/qi_and_karma/string.qbk create mode 100644 doc/references.qbk create mode 100644 doc/spirit2.qbk create mode 100644 doc/what_s_new.qbk create mode 100644 example/karma/Jamfile create mode 100644 example/karma/basic_facilities.cpp create mode 100644 example/karma/functor_facilities.cpp create mode 100644 example/karma/quick_start1.cpp create mode 100644 example/lex/Jamfile create mode 100644 example/lex/example.hpp create mode 100644 example/lex/example1.cpp create mode 100644 example/lex/example2.cpp create mode 100644 example/lex/example3.cpp create mode 100644 example/lex/example4.cpp create mode 100644 example/lex/example5.cpp create mode 100644 example/lex/example6.cpp create mode 100644 example/lex/print_numbers.cpp create mode 100644 example/lex/static_lexer/Jamfile create mode 100644 example/lex/static_lexer/word_count_generate.cpp create mode 100644 example/lex/static_lexer/word_count_static.cpp create mode 100644 example/lex/static_lexer/word_count_static.hpp create mode 100644 example/lex/static_lexer/word_count_tokens.hpp create mode 100644 example/lex/strip_comments.cpp create mode 100644 example/lex/strip_comments_lexer.cpp create mode 100644 example/lex/word_count.cpp create mode 100644 example/lex/word_count_functor.cpp create mode 100644 example/lex/word_count_functor_flex.cpp create mode 100644 example/lex/word_count_lexer.cpp create mode 100644 example/qi/Jamfile create mode 100644 example/qi/calc1.cpp create mode 100644 example/qi/calc2.cpp create mode 100644 example/qi/calc3.cpp create mode 100644 example/qi/calc3_lexer.cpp create mode 100644 example/qi/calc4.cpp create mode 100644 example/qi/calc5.cpp create mode 100644 example/qi/calc6/calc6.cpp create mode 100644 example/qi/calc6/calc6.hpp create mode 100644 example/qi/calc6/calc6a.cpp create mode 100644 example/qi/calc6/calc6b.cpp create mode 100644 example/qi/calc6/calc6b.hpp create mode 100644 example/qi/calc6/calc6c.cpp create mode 100644 example/qi/calc6/calc6c.hpp create mode 100644 example/qi/calc7/calc7.cpp create mode 100644 example/qi/calc7/calc7.hpp create mode 100644 example/qi/calc7/calc7a.cpp create mode 100644 example/qi/calc7/calc7b.cpp create mode 100644 example/qi/calc7/calc7b.hpp create mode 100644 example/qi/calc7/calc7c.cpp create mode 100644 example/qi/calc7/calc7c.hpp create mode 100644 example/qi/complex_number.cpp create mode 100644 example/qi/employee.cpp create mode 100644 example/qi/mini_c/mini_c.cpp create mode 100644 example/qi/mini_c/mini_c.hpp create mode 100644 example/qi/mini_c/mini_ca.cpp create mode 100644 example/qi/mini_c/mini_cb.cpp create mode 100644 example/qi/mini_c/mini_cb.hpp create mode 100644 example/qi/mini_c/mini_cc.cpp create mode 100644 example/qi/mini_c/mini_cc.hpp create mode 100644 example/qi/mini_c/mini_cd.cpp create mode 100644 example/qi/mini_c/mini_cd.hpp create mode 100644 example/qi/mini_xml1.cpp create mode 100644 example/qi/mini_xml2.cpp create mode 100644 example/qi/mini_xml_karma.cpp create mode 100644 example/qi/mini_xml_samples/1.xml create mode 100644 example/qi/mini_xml_samples/2.xml create mode 100644 example/qi/mini_xml_samples/3.xml create mode 100644 example/qi/num_list.cpp create mode 100644 example/qi/num_list2.cpp create mode 100644 example/qi/num_list3.cpp create mode 100644 example/qi/roman.cpp create mode 100644 example/qi/sum.cpp create mode 100644 index.html create mode 100644 phoenix/doc/Jamfile.v2 create mode 100644 phoenix/doc/html/boostbook.css create mode 100644 phoenix/doc/html/images/add2.png create mode 100644 phoenix/doc/html/images/add2_call.png create mode 100644 phoenix/doc/html/images/adder.png create mode 100644 phoenix/doc/html/images/alert.png create mode 100644 phoenix/doc/html/images/banner.png create mode 100644 phoenix/doc/html/images/fbox.png create mode 100644 phoenix/doc/html/images/funnel_in.png create mode 100644 phoenix/doc/html/images/funnel_out.png create mode 100644 phoenix/doc/html/images/home.png create mode 100644 phoenix/doc/html/images/lambda_cpp.png create mode 100644 phoenix/doc/html/images/next.png create mode 100644 phoenix/doc/html/images/note.png create mode 100644 phoenix/doc/html/images/organization.png create mode 100644 phoenix/doc/html/images/prev.png create mode 100644 phoenix/doc/html/images/smiley.png create mode 100644 phoenix/doc/html/images/tip.png create mode 100644 phoenix/doc/html/images/up.png create mode 100644 phoenix/doc/html/index.html create mode 100644 phoenix/doc/html/phoenix/acknowledgement.html create mode 100644 phoenix/doc/html/phoenix/actors.html create mode 100644 phoenix/doc/html/phoenix/algorithm.html create mode 100644 phoenix/doc/html/phoenix/basics.html create mode 100644 phoenix/doc/html/phoenix/composite.html create mode 100644 phoenix/doc/html/phoenix/inside_phoenix.html create mode 100644 phoenix/doc/html/phoenix/intrinsic.html create mode 100644 phoenix/doc/html/phoenix/introduction.html create mode 100644 phoenix/doc/html/phoenix/organization.html create mode 100644 phoenix/doc/html/phoenix/primitives.html create mode 100644 phoenix/doc/html/phoenix/references.html create mode 100644 phoenix/doc/html/phoenix/starter_kit.html create mode 100644 phoenix/doc/html/phoenix/wrap_up.html create mode 100644 phoenix/doc/users_manual.qbk create mode 100644 phoenix/example/Jamfile.v2 create mode 100644 phoenix/example/users_manual/algorithm.cpp create mode 100644 phoenix/example/users_manual/all_odds.cpp create mode 100644 phoenix/example/users_manual/arguments.cpp create mode 100644 phoenix/example/users_manual/callback.cpp create mode 100644 phoenix/example/users_manual/factorial.cpp create mode 100644 phoenix/example/users_manual/find_if.cpp create mode 100644 phoenix/example/users_manual/function.cpp create mode 100644 phoenix/example/users_manual/if.cpp create mode 100644 phoenix/example/users_manual/lambda.cpp create mode 100644 phoenix/example/users_manual/references.cpp create mode 100644 phoenix/example/users_manual/values.cpp create mode 100644 phoenix/index.html create mode 100644 phoenix/test/Jamfile.v2 create mode 100644 phoenix/test/algorithm/iteration.cpp create mode 100644 phoenix/test/algorithm/querying.cpp create mode 100644 phoenix/test/algorithm/querying2.cpp create mode 100644 phoenix/test/algorithm/transformation1.cpp create mode 100644 phoenix/test/algorithm/transformation2.cpp create mode 100644 phoenix/test/algorithm/transformation3.cpp create mode 100644 phoenix/test/algorithm/transformation4.cpp create mode 100644 phoenix/test/bind/bind_function_object_tests.cpp create mode 100644 phoenix/test/bind/bind_function_tests.cpp create mode 100644 phoenix/test/bind/bind_member_function_tests.cpp create mode 100644 phoenix/test/bind/bind_member_variable_tests.cpp create mode 100644 phoenix/test/container/container_tests.hpp create mode 100644 phoenix/test/container/container_tests1a.cpp create mode 100644 phoenix/test/container/container_tests1b.cpp create mode 100644 phoenix/test/container/container_tests2a.cpp create mode 100644 phoenix/test/container/container_tests2b.cpp create mode 100644 phoenix/test/container/container_tests3a.cpp create mode 100644 phoenix/test/container/container_tests3b.cpp create mode 100644 phoenix/test/container/container_tests4a.cpp create mode 100644 phoenix/test/container/container_tests4b.cpp create mode 100644 phoenix/test/container/container_tests5a.cpp create mode 100644 phoenix/test/container/container_tests5b.cpp create mode 100644 phoenix/test/container/container_tests6a.cpp create mode 100644 phoenix/test/container/container_tests6b.cpp create mode 100644 phoenix/test/core/compose_tests.cpp create mode 100644 phoenix/test/core/primitives_tests.cpp create mode 100644 phoenix/test/detail/type_deduction_tests.cpp create mode 100644 phoenix/test/function/function_tests.cpp create mode 100644 phoenix/test/object/cast_tests.cpp create mode 100644 phoenix/test/object/new_delete_tests.cpp create mode 100644 phoenix/test/operator/arithmetic_tests.cpp create mode 100644 phoenix/test/operator/bitwise_tests.cpp create mode 100644 phoenix/test/operator/comparison_tests.cpp create mode 100644 phoenix/test/operator/if_else_tests.cpp create mode 100644 phoenix/test/operator/io_tests.cpp create mode 100644 phoenix/test/operator/logical_tests.cpp create mode 100644 phoenix/test/operator/member.cpp create mode 100644 phoenix/test/operator/misc_binary_tests.cpp create mode 100644 phoenix/test/operator/self_tests.cpp create mode 100644 phoenix/test/operator/unary_tests.cpp create mode 100644 phoenix/test/scope/bug_000008.cpp create mode 100644 phoenix/test/scope/dynamic_tests.cpp create mode 100644 phoenix/test/scope/lambda_tests.cpp create mode 100644 phoenix/test/scope/let_tests.cpp create mode 100644 phoenix/test/statement/exceptions.cpp create mode 100644 phoenix/test/statement/if_tests.cpp create mode 100644 phoenix/test/statement/loops_tests.cpp create mode 100644 phoenix/test/statement/switch_tests.cpp create mode 100644 test/Jamfile create mode 100644 test/karma/alternative.cpp create mode 100644 test/karma/binary.cpp create mode 100644 test/karma/case_handling.cpp create mode 100644 test/karma/center_alignment.cpp create mode 100644 test/karma/char.cpp create mode 100644 test/karma/delimiter.cpp create mode 100644 test/karma/eps.cpp create mode 100644 test/karma/format_manip.cpp create mode 100644 test/karma/functor.cpp create mode 100644 test/karma/grammar.cpp create mode 100644 test/karma/grammar_fail.cpp create mode 100644 test/karma/int_numerics.cpp create mode 100644 test/karma/kleene.cpp create mode 100644 test/karma/lazy.cpp create mode 100644 test/karma/left_alignment.cpp create mode 100644 test/karma/list.cpp create mode 100644 test/karma/lit.cpp create mode 100644 test/karma/none.cpp create mode 100644 test/karma/optional.cpp create mode 100644 test/karma/pattern.cpp create mode 100644 test/karma/real_numerics.cpp create mode 100644 test/karma/right_alignment.cpp create mode 100644 test/karma/rule_fail.cpp create mode 100644 test/karma/sequence.cpp create mode 100644 test/karma/test.hpp create mode 100644 test/lex/lexertl1.cpp create mode 100644 test/lex/lexertl2.cpp create mode 100644 test/lex/lexertl3.cpp create mode 100644 test/lex/lexertl4.cpp create mode 100644 test/lex/lexertl5.cpp create mode 100644 test/lex/state_switcher_test.cpp create mode 100644 test/lex/test.hpp create mode 100644 test/lex/test_parser.hpp create mode 100644 test/qi/alternative.cpp create mode 100644 test/qi/and_predicate.cpp create mode 100644 test/qi/binary.cpp create mode 100644 test/qi/char.cpp create mode 100644 test/qi/char_class.cpp create mode 100644 test/qi/debug.cpp create mode 100644 test/qi/difference.cpp create mode 100644 test/qi/eps.cpp create mode 100644 test/qi/expect.cpp create mode 100644 test/qi/functor.cpp create mode 100644 test/qi/grammar.cpp create mode 100644 test/qi/grammar_fail.cpp create mode 100644 test/qi/int.cpp create mode 100644 test/qi/kleene.cpp create mode 100644 test/qi/lazy.cpp create mode 100644 test/qi/lexeme.cpp create mode 100644 test/qi/list.cpp create mode 100644 test/qi/lit.cpp create mode 100644 test/qi/match_manip.cpp create mode 100644 test/qi/no_case.cpp create mode 100644 test/qi/none.cpp create mode 100644 test/qi/not_predicate.cpp create mode 100644 test/qi/optional.cpp create mode 100644 test/qi/permutation.cpp create mode 100644 test/qi/plus.cpp create mode 100644 test/qi/range_run.cpp create mode 100644 test/qi/raw.cpp create mode 100644 test/qi/real.cpp create mode 100644 test/qi/rule.cpp create mode 100644 test/qi/rule_fail.cpp create mode 100644 test/qi/sequence.cpp create mode 100644 test/qi/sequential_or.cpp create mode 100644 test/qi/symbols.cpp create mode 100644 test/qi/test.hpp create mode 100644 test/qi/tst.cpp create mode 100644 test/qi/uint.cpp create mode 100644 test/support/detail/sstream.hpp create mode 100644 test/support/hold_any.cpp create mode 100644 test/support/multi_pass.cpp create mode 100644 test/support/multi_pass_compile.cpp diff --git a/classic/test/bug_000008.cpp b/classic/test/bug_000008.cpp index fbe6116a5..e814928f5 100644 --- a/classic/test/bug_000008.cpp +++ b/classic/test/bug_000008.cpp @@ -11,7 +11,7 @@ // or https://sf.net/mailarchive/forum.php?thread_id=2692308&forum_id=1595 // for a description of the bug being tested for by this program // - // the problem should be solved with version 1.3 of phoenix/closures.hpp + // the problem should be solved with version 1.3 of phoenix/closures.hpp> #if defined(BOOST_SPIRIT_DEBUG) && defined(__GNUC__) && defined(__WIN32__) // It seems that MinGW has some problems with threads and iostream ? diff --git a/doc/Jamfile b/doc/Jamfile new file mode 100644 index 000000000..3ac28ebec --- /dev/null +++ b/doc/Jamfile @@ -0,0 +1,27 @@ +#============================================================================== +# Copyright (c) 2001-2007 Joel de Guzman +# Copyright (c) 2001-2007 Hartmut Kaiser +# +# Use, modification and distribution is subject to the Boost Software +# License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +#============================================================================== + +project spirit/doc ; + +import boostbook : boostbook ; +using quickbook : quickbook ; + +boostbook spirit2 + : + spirit2.qbk + : + boost.root=../../../.. + boost.libraries=../../../libraries.htm + html.stylesheet=../../../../doc/html/boostbook.css + chunk.section.depth=5 + chunk.first.sections=1 + toc.section.depth=4 + toc.max.depth=4 + generate.section.toc.level=4 + ; diff --git a/doc/_concepts_template_.qbk b/doc/_concepts_template_.qbk new file mode 100644 index 000000000..e8009553c --- /dev/null +++ b/doc/_concepts_template_.qbk @@ -0,0 +1,46 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section XXX] + +[heading Description] + +Description of XXX concept + +[variablelist Notation + [[`xxx`] [An XXX]] +] + +[heading Valid Expressions] + +(For any Forward Sequence the following expressions must be valid:) + +In addition to the requirements defined in _XXX-Basic_concept_, for any +XXX the following must be met: + +[table + [[Expression] [Semantics] [Return type] [Complexity]] + [[`xxx`] [Semantics of `xxx`] [XXX] [Constant]] +] + +[heading Type Requirements] + +[table + [[Expression] [Requirements]] + [[`xxx`] [Requirements for `xxx`]] +] + +[heading Invariants] + +For any XXX xxx the following invariants always hold: + +[heading Models] + +Links to models of XXX concept + +[endsect] diff --git a/doc/_reference_template_.qbk b/doc/_reference_template_.qbk new file mode 100644 index 000000000..99665cef0 --- /dev/null +++ b/doc/_reference_template_.qbk @@ -0,0 +1,56 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section XXX] + +[heading Description] + +Description of XXX + +[heading Header] + + #include + +[heading Synopsis] + + template + struct XXX; + +[heading Template parameters] + +[table + [[Parameter] [Description] [Default]] + [[`T`] [What is T] []] +] + +[heading Model of] + +Link to concept + +[heading Objects] + +Objects provided by the library + +[variablelist Notation + [[`xxx`] [An XXX]] +] + +Semantics of an expression is defined only where it differs from, or is not +defined in _concept-of_XXX_. + +[table + [[Expression] [Semantics] [Return type] [Complexity]] + [[`xxx`] [Semantics of `xxx`] [XXX] [Constant]] +] + +[heading Example] + +Real example code. Use Quickbook import mechanism to link to actual +working code snippets here. + +[endsect] diff --git a/doc/acknowledgments.qbk b/doc/acknowledgments.qbk new file mode 100644 index 000000000..87f41dc8b --- /dev/null +++ b/doc/acknowledgments.qbk @@ -0,0 +1,147 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Acknowledgments] + +This version of Spirit is a complete rewrite of the /classic/ Spirit many +people have been contributing to (see below). But there are a couple of people +who already managed to help significantly during this rewrite. We would like to +express our special acknowledgement to: + +[*Eric Niebler] for writing Boost.Proto, without which this rewrite wouldn't +have been possible, and helping with examples, advices, and suggestions on +how to use Boost.Proto in the best possible way. + +[*Ben Hanson] for providing us with an early version of his Lexertl library, +which is proposed to be included into Boost (as Boost.Lexer), but at the time +of this writing the Boost review for this library is still pending. + +__fixme__: Add more people + + +[heading Acknowledgements from the Spirit V1 /classic/ Documentation] + +Special thanks for working on Spirit /classic/ to: + +[*Dan Nuffer] for his work on lexers, parse trees, ASTs, XML parsers, the +multi-pass iterator as well as administering Spirit's site, editing, +maintaining the CVS and doing the releases plus a zillion of other chores that +were almost taken for granted. + +[*Hartmut Kaiser] for his work on the C parser, the work on the C/C++ +preprocessor, utility parsers, the original port to Intel 5.0, various work on +Phoenix, porting to v1.5, the meta-parsers, the grouping-parsers, extensive +testing and painstaking attention to details. + +[*Martin Wille] who improved grammar multi thread safety, contributed the eol_p +parser, the dynamic parsers, documentation and for taking an active role in +almost every aspect from brainstorming and design to coding. And, as always, +helps keep the regression tests for g++ on Linux as green as ever :-). + +[*Martijn W. Van Der Lee] our Web site administrator and for contributing the +RFC821 parser. + +[*Giovanni Bajo] for last minute tweaks of Spirit 1.8.0 for CodeWarrior 8.3. +Actually, I'm ashamed Giovanni was not in this list already. He's done a lot +since Spirit 1.5, the first Boost.Spirit release. He's instrumental in the +porting of the Spirit iterators stuff to the new Boost Iterators Library +(version 2). He also did various bug fixes and wrote some tests here and there. + +[*Juan Carlos Arevalo-Baeza (JCAB)*] for his work on the C++ parser, the position +iterator, ports to v1.5 and keeping the mailing list discussions alive and +kicking. + +[*Vaclav Vesely], lots of stuff, the no\_actions directive, various patches +fixes, the distinct parsers, the lazy parser, some phoenix tweaks and add-ons +(e.g. new\_). Also, *Stefan Slapeta] and wife for editing Vaclav's distinct +parser doc. + +[*Raghavendra Satish] for doing the original v1.3 port to VC++ and his work on +Phoenix. + +[*Noah Stein] for following up and helping Ragav on the VC++ ports. + +[*Hakki Dogusan], for his original v1.0 Pascal parser. + +[*John (EBo) David] for his work on the VM and watching over my shoulder as I +code giving the impression of distance eXtreme programming. + +[*Chris Uzdavinis] for feeding in comments and valuable suggestions as well as +editing the documentation. + +[*Carsten Stoll], for his work on dynamic parsers. + +[*Andy Elvey] and his conifer parser. + +[*Bruce Florman], who did the original v1.0 port to VC++. + +[*Jeff Westfahl] for porting the loop parsers to v1.5 and contributing the file +iterator. + +[*Peter Simons] for the RFC date parser example and tutorial plus helping out +with some nitty gritty details. + +[*Markus Sch'''ö'''pflin] for suggesting the end_p parser and lots of other +nifty things and his active presence in the mailing list. + +[*Doug Gregor] for mentoring and his ability to see things that others don't. + +[*David Abrahams] for giving Joel a job that allows him to still work on Spirit, +plus countless advice and help on C++ and specifically template +metaprogramming. + +[*Aleksey Gurtovoy] for his MPL library from which we stole many metaprogramming +tricks especially for less conforming compilers such as Borland and VC6/7. + +[*Gustavo Guerra] for his last minute review of Spirit and constant feedback, +plus patches here and there (e.g. proposing the new dot behavior of the real +numerics parsers). + +[*Nicola Musatti], [*Paul Snively], [*Alisdair Meredith] and [*Hugo Duncan] for +testing and sending in various patches. + +[*Steve Rowe] for his splendid work on the TSTs that will soon be taken into +Spirit. + +[*Jonathan de Halleux] for his work on actors. + +[*Angus Leeming] for last minute editing work on the 1.8.0 release +documentation, his work on Phoenix and his active presence in the Spirit +mailing list. + +[*Joao Abecasis] for his active presence in the Spirit mailing list, providing +user support, participating in the discussions and so on. + +[*Guillaume Melquiond] for a last minute patch to multi_pass for 1.8.1. + +[*Peder Holt] for his porting work on Phoenix, Fusion and Spirit to VC6. + +To Joels wife Mariel who did the graphics in this document. + +My, there's a lot in this list! And it's a continuing list. We add people to +this list everytime. We hope we did not forget anyone. If we missed +someone you know who has helped in any way, please inform us. + +Special thanks also to people who gave feedback and valuable comments, +particularly members of Boost and Spirit mailing lists. This includes all those +who participated in the review: + +[*John Maddock], our review manager, [*Aleksey Gurtovoy], [*Andre Hentz], +[*Beman Dawes], [*Carl Daniel], [*Christopher Currie], [*Dan Gohman], +[*Dan Nuffer], [*Daryle Walker], [*David Abrahams], [*David B. Held], +[*Dirk Gerrits], [*Douglas Gregor], [*Hartmut Kaiser], [*Iain K.Hanson], +[*Juan Carlos Arevalo-Baeza], [*Larry Evans], [*Martin Wille], +[*Mattias Flodin], [*Noah Stein], [*Nuno Lucas], [*Peter Dimov], +[*Peter Simons], [*Petr Kocmid], [*Ross Smith], [*Scott Kirkwood], +[*Steve Cleary], [*Thorsten Ottosen], [*Tom Wenisch], [*Vladimir Prus] + +Finally thanks to SourceForge for hosting the Spirit project and Boost: a C++ +community comprised of extremely talented library authors who participate in +the discussion and peer review of well crafted C++ libraries. + +[endsect] diff --git a/doc/faq.qbk b/doc/faq.qbk new file mode 100644 index 000000000..aff508db6 --- /dev/null +++ b/doc/faq.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section FAQ] +[endsect] diff --git a/doc/html/images/FlowOfControl.png b/doc/html/images/FlowOfControl.png new file mode 100644 index 0000000000000000000000000000000000000000..2e131c0843af033ae9dc7c4e6e5c78e11afe0c5b GIT binary patch literal 80302 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 Mr>mdKI;Vst0Jhl3SO5S3 literal 0 HcmV?d00001 diff --git a/doc/introduction.qbk b/doc/introduction.qbk new file mode 100644 index 000000000..204861eee --- /dev/null +++ b/doc/introduction.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Introduction] +[endsect] diff --git a/doc/lex.qbk b/doc/lex.qbk new file mode 100644 index 000000000..73a69e286 --- /dev/null +++ b/doc/lex.qbk @@ -0,0 +1,50 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section __lex__] + +[include lex/introduction.qbk] + +[section __lex__ Tutorials] +[include lex/lexer_tutorials.qbk] +[include lex/lexer_quickstart1.qbk] +[include lex/lexer_quickstart2.qbk] +[include lex/lexer_quickstart3.qbk] +[endsect] + +[section Abstracts] +[section Lexer Primitives] +[include lex/lexer_primitives.qbk] +[include lex/tokens_values.qbk] +[include lex/token_definition.qbk] +[endsect] +[include lex/tokenizing.qbk] +[include lex/lexer_semantic_actions.qbk] +[include lex/lexer_static_model.qbk] +[include lex/parsing_using_a_lexer.qbk] +[include lex/lexer_attributes.qbk] +[include lex/lexer_states.qbk] +[endsect] + +[section Quick Reference] +[endsect] + +[section Reference] +[section Concepts] +[include reference/lex/lexer.qbk] +[include reference/lex/token.qbk] +[include reference/lex/tokendef.qbk] +[include reference/lex/tokenset.qbk] +[endsect] +[include reference/lex/lexer_class.qbk] +[include reference/lex/token_class.qbk] +[include reference/lex/tokendef_class.qbk] +[include reference/lex/tokenset_class.qbk] +[endsect] + +[endsect] diff --git a/doc/lex/introduction.qbk b/doc/lex/introduction.qbk new file mode 100644 index 000000000..c333552d9 --- /dev/null +++ b/doc/lex/introduction.qbk @@ -0,0 +1,137 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Introduction to __lex__] + +Lexical scanning is the process of analyzing the stream of input characters and +separating it into strings called tokens, separated by whitespace. +Most compiler texts start here, and devote several chapters to discussing +various ways to build scanners. __lex__ is a library built to take care of the +complexities of creating a lexer for your grammar (in this documentation we +will use the terms 'lexical analyzer', 'lexer' and 'scanner' interchangably). +All it needs to create a lexer is to know the set of patterns describing the +different tokens you want to recognize in the input. To make this a bit more +formal, here are some definitions: + +* A token is a sequence of consecutive characters having a collective meaning. + Tokens may have attributes specific to the token type, carrying additional + information about the matched character sequence. +* A pattern is a rule expressed as a regular expression and describing how a + particular token can be formed. For example, [^\[A-Za-z\]\[A-Za-z_0-9\]*] is + a pattern for a rule matching C++ identifiers. +* Characters between tokens are called whitespace; these include spaces, tabs, + newlines, and formfeeds. Many people also count comments as whitespace, + though since some tools such as lint look at comments, this conflation is not + perfect. + +[heading Why Using a Separate Lexer] + +Typically, lexical scanning is done in a separate module from the parser, +feeding the parser with a stream of input tokens only. Now, theoretically it is +not necessary to do this separation. In the end there is only one set of +syntactical rules defining the language, so in theory we could write the whole +parser in one module. In fact, __qi__ allows to write parsers without using a +lexer, parsing the input character stream directly, and for the most part this +is the way __spirit__ has been used since its invention. + +However, the separation has both practical and theoretical bases and proves to +be very useful in practical applications. In 1956, Noam Chomsky defined the +"Chomsky Hierarchy" of grammars: + +* Type 0: Unrestricted grammars (e.g., natural languages) +* Type 1: Context-Sensitive grammars +* Type 2: Context-Free grammars +* Type 3: Regular grammars + +The complexity of these grammars increases from regular grammars being the +simplest to unrestricted grammars being the most complex. Similarily, the +complexity of the recognizers for these grammars increases. Although, a few +features of some programming languages (such as C++) are Type 1, fortunately +for the most part programming languages can be described using only the Types 3 +and 2. The neat part about these two types is that they are well known and the +ways to parse them are well understood. It has been shown that any regular +grammar can be parsed using a state machine (finite automaton). Similarly, +context-free grammars can always be parsed using a push-down automaton +(essentially a state machine augmented by a stack). + +In real programming languages and practical grammars the parts that can be +handled as regular expressions tend to be the lower-level parts, such as the +definition of an identifier or of an integer value: + + letter := [a-zA-Z] + digit := [0-9] + + identifier := letter [ letter | digit ]* + integer := digit* + +Higher level parts of practical grammars tend to be more complex and can't be +implemented using plain regular expressions anymore. We need to store +information on the built-in hardware stack while recursing the grammar +hierarchy, and that in fact this is the preferred approach used for top-down +parsing. Since it takes a different kind of abstract machine to parse the two +types of grammars, it proved to be efficient to separate the lexical scanner +into a separate module which is built around the idea of a state machine. The +goal here is to use the simplest parsing technique needed for the job. + +Another, more practical reason for separating the scanner from the parser is +the need for backtracking during parsing. The input data is a stream of +characters, which is often thought to be processed left to right without any +backtracking. Unfortunately, in practice most of the time that isn't possible. +Almost every language has certain keywords such as IF, FOR, and WHILE. The +decision if a certain character sequence actually comprises a keyword or just +an identifier often can be made only after seeing the first delimiter /after/ +it. This already is a limited form of backtracking, since we need to store the +string long enough to be able to make the decision. The same is true for more +coarse grained language features such as nested IF/ELSE statements, where the +decision about to which IF belongs the last ELSE statement can be made only +after seeing the whole construct. + +So the structure of a conventional compiler often involves splitting up the +functions of the lower-level and higher-level parsing. The lexical scanner +deals with things at the character level, collecting characters into strings, +converting character sequence into different representations as integers, etc., +and passing them along to the parser proper as indivisible tokens. It's also +considered normal to let the scanner do additional jobs, such as identifying +keywords, storing identifiers in tables, etc. + +Now, __spirit__ follows this structure, where __lex__ can be used to implement +state machine based recognizers, while __qi__ can be used to build recognizers +for context free grammars. Since both modules are seemlessly integrated with +each other and with the C++ target language it is even possible to use the +provided functionality to build more complex grammar recognizers. + +[heading Advantages of using __lex__] + +The advantage of using __lex__ to create the lexical analyzer over using more +traditional tools such as __flex__ is its carefully crafted integration with +the __spirit__ library and the C++ host language. You don't need any external +tools to generate the code, your lexer will be perfectly integrated with the +rest of your program, making it possible to freely access any context +information and data structure. Since the C++ compiler sees all the code it +will generate optimal code nomatter what configuration options have been chosen +by the user. __lex__ gives you all the features you could get from a similar +__flex__ program without the need to leave C++ as a host language: + +* the definition of tokens is done using regular expressions (patterns) +* the token definitions can refer to special substitution string (pattern + macros) simplifying pattern definitions +* the generated lexical scanner may have multiple start states +* it is possible to attach code to any of the token definitions; this code gets + executed whenever the corresponding token pattern has been matched + +Even if it is possible to use __lex__ to generate C++ code representing +the lexical analyzer (we will refer to that as the /static/ model, described in +more detail in the section __sec_lex_static_model__) - a model +very similar to the way __flex__ operates - we will mainly focus on the +opposite, the /dynamic/ model. You can directly integrate the token definitions +into your C++ program, building the lexical analyzer dynamicly at runtime. The +dynamic model is something not supported by __flex__ or other lexical scanner +generators (such as __re2c__, __ragel__, etc.). But it is very flexible and +allows to speed up the development of your application. + +[endsect] diff --git a/doc/lex/lexer_attributes.qbk b/doc/lex/lexer_attributes.qbk new file mode 100644 index 000000000..517f28108 --- /dev/null +++ b/doc/lex/lexer_attributes.qbk @@ -0,0 +1,12 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Lexer Attributes] + + +[endsect] diff --git a/doc/lex/lexer_primitives.qbk b/doc/lex/lexer_primitives.qbk new file mode 100644 index 000000000..0cf96ad3e --- /dev/null +++ b/doc/lex/lexer_primitives.qbk @@ -0,0 +1,15 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Lexer Primitives] + +[/ Describe the primitive lexer constructs, such as token_def, token_set? ] +[/ Describe the primitive lexer constructs usable in parsers, such as + in_state[], set_state(), token(), etc. ] + +[endsect] diff --git a/doc/lex/lexer_quickstart1.qbk b/doc/lex/lexer_quickstart1.qbk new file mode 100644 index 000000000..03235ca4a --- /dev/null +++ b/doc/lex/lexer_quickstart1.qbk @@ -0,0 +1,97 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Quickstart 1 - A word counter using __lex__] + +__lex__ is very modular, which follows the general building principle of the +__spirit__ libraries. You never pay for features you don't use. It is nicely +integrated with the other parts of __spirit__ but nevertheless can be used +separately to build standalone lexical analyzers. +The first quick start example describes a standalone application: +counting characters, words and lines in a file, very similar to what the well +known Unix command `wc` is doing (for the full example code see here: +[@../../example/lex/word_count_functor.cpp word_count_functor.cpp]). + +[import ../example/lex/word_count_functor.cpp] + + +[heading Prerequisites] + +The only required `#include` specific to /Spirit.Lex/ follows. It is a wrapper +for all necessary definitions to use /Spirit.Lex/ in a standalone fashion, and +on top of the __lexertl__ library. Additionally we `#include` two of the Boost +headers to define `boost::bind()` and `boost::ref()`. + +[wcf_includes] + +To make all the code below more readable we introduce the following namespaces. + +[wcf_namespaces] + + +[heading Defining Tokens] + +The most important step while creating a lexer using __lex__ is to define the +tokens to be recognized in the input sequence. This is normally done by +defining the regular expressions describing the matching character sequences, +and optionally their corresponding token ids. Additionally the defined tokens +need to be associated with an instance of a lexer object as provided by the +library. The following code snippet shows how this can be done using __lex__. + +[wcf_token_definition] + + +[heading Doing the Useful Work] + +We will use a setup, where we want the __lex__ library to invoke a given +function after any of of the generated tokens is recognized. For this reason +we need to implement a functor taking at least the generated token as an +argument and returning a boolean value allowing to stop the tokenization +process. The default token type used in this example carries a token value of +the type `iterator_range` pointing to the matched range in the +underlying input sequence. + +[wcf_functor] + +All what's left is to write some boilerplate code helping to tie together the +pieces described so far. To simplify this example we call the `lex::tokenize()` +function implemented in __lex__ (for a more detailed description of this +function see here: __fixme__), even if we could have written a loop to iterate +over the lexer iterators [`first`, `last`) as well. + + +[heading Pulling Everything Together] + +[wcf_main] + + +[heading Comparing __lex__ with __flex__] + +This example was deliberately chosen to be similar as much as possible to the +equivalent __flex__ program (see below), which isn't too different from what +has to be written when using __lex__. + +[note Interestingly enough, performance comparisons of lexical analyzers + written using __lex__ with equivalent programs generated by + __flex__ show that both have comparable execution speeds! + Generally, thanks to the highly optimized __lexertl__ library and + due its carefully designed integration with __spirit__ the + abstraction penalty to be paid for using __lex__ is neglectible. +] + +The remaining examples in this tutorial will use more sophisticated features +of __lex__, mainly to allow further simplification of the code to be written, +while maintaining the similarity with corresponding features of __flex__. +__lex__ has been designed to be as much as possible similar to __flex__, that +is why this documentation will provide the corresponding __flex__ code for the +shown __lex__ examples almost everywhere. So consequently, here is the __flex__ +code corresponding to the example as shown above. + +[wcf_flex_version] + +[endsect] diff --git a/doc/lex/lexer_quickstart2.qbk b/doc/lex/lexer_quickstart2.qbk new file mode 100644 index 000000000..1e32ab71a --- /dev/null +++ b/doc/lex/lexer_quickstart2.qbk @@ -0,0 +1,133 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Quickstart 2 - A better word counter using __lex__] + +People knowing __flex__ will probably complain about the example from the +section __sec_lex_quickstart_1__ as being overly complex and not being +written to leverage the possibilities provided by this tool. In particular the +previous example did not directly use the lexer actions to count the lines, +words and characters. So the example provided in this step of the tutorial will +show how to use semantic actions in __lex__. Even if it still +will allow to count text elements only it introduces other new concepts and +configuration options along the lines (for the full example code +see here: [@../../example/lex/word_count_lexer.cpp word_count_lexer.cpp]). + +[import ../example/lex/word_count_lexer.cpp] + + +[heading Prerequisites] + +In addition to the only required `#include` specific to /Spirit.Lex/ this +example needs to include a couple of header files from the __phoenix2__ +library. This example shows how to attach functors to token definitions, which +could be done using any type of C++ technique resulting in a callable object. +Using __phoenix2__ for this task simplifies things and avoids adding +dependencies to other libraries (__phoenix2__ is already in use for +__spirit__ anyway). + +[wcl_includes] + +To make all the code below more readable we introduce the following namespaces. + +[wcl_namespaces] + +To give a preview at what to expect from this example, here is the flex program +which has been used as the starting point. The useful code is directly included +inside the actions associated with each of the token definitions. + +[wcl_flex_version] + + +[heading Semantic Actions in __lex__] + +__lex__ uses a very similar way of associating actions with the token +definitions (which should look familiar to anybody knowlegdeable with +__spirit__ as well): specifying the operations to execute inside of a pair of +`[]` brackets. In order to be able to attach semantic actions to token +definitions for each of them there is defined an instance of a `token_def<>`. + +[wcl_token_definition] + +The semantics of the shown code is as follows. The code inside the `[]` +brackets will be executed whenever the corresponding token has been matched by +the lexical analyzer. This is very similar to __flex__, where the action code +associated with a token definition gets executed after the recognition of a +matching input sequence. The code above uses functors constructed using +__phoenix2__, but it is possible to insert any C++ functor as long as it +exposes the interface: + + void f (Range r, std::size_t id, Context& ctx, bool& matched); + +[variablelist where: + [[`Range r`] [This is a `boost::iterator_range` holding two + iterators pointing to the matched range in the + underlying input sequence. The type of the + held iterators is the same as specified while + defining the type of the `lexertl_lexer<...>` + (its first template parameter).]] + [[`std::size_t id`] [This is the token id for the matched token.]] + [[`Context& ctx`] [This is a reference to a lexer specific, + unspecified type, providing the context for the + current lexer state. It can be used to access + different internal data items and is needed for + lexer state control from inside a semantic + action.]] + [[`bool& matched`] [This boolean value is pre/initialized to `true`. + If the functor sets it to `false` the lexer + stops calling any semantic actions attached to + this token and behaves as if the token have not + been matched in the first place.]] +] + +Even if it is possible to write your own functor implementations, the preferred +way of defining lexer semantic actions is to use __phoenix2__. In this case you +can access the three parameters described in the table above by using the +predefined __phoenix2__ placeholders: `_1` for the iterator range, `_2` for the +token id, `_3` for the reference to the lexer state, and `_4` for the reference +to the boolean value signaling the outcome of the semantic action. + +[important All placeholders (`_1`, `_2`, etc.) used in /lexer/ semantic + actions in conjunction with functors created based on __phoenix2__ + need to be imported from the `namespace boost::phoenix::arg_names` + (and *not* `namespace boost::spirit::arg_names`, which is + different from using placeholders in __qi__ or __karma__). + Using the wrong placeholders leads to subtle compilation errors + which are difficult to backtrack to their cause. +] + + +[heading Associating Token Definitions with the Lexer] + +If you compare the with the code from __sec_lex_quickstart_1__ with regard to +the way how token definitions are associated with the lexer, you will notice +a different syntax being used here. If in the previous example we have been +using the `self.add()` style of the API, then here we directly assign the token +definitions to `self`, combining the different token definitions using the `|` +operator. Here is the code snippet again: + + self = word [++ref(w), ref(c) += distance(_1)] + | eol [++ref(c), ++ref(l)] + | any [++ref(c)] + ; + +This way we have a very powerful and natural way of building the lexical +analyzer. If translated into English this may be read as: The lexical analyer +will recognize ('`=`') tokens as defined by any of ('`|`') the token +definitions `word`, `eol`, and `any`. + +A second difference to the previous example is that we do not explicitly +specify any token ids to use for the separate tokens. Using semantic actions to +trigger some useful work free'd us from the need to define these. To ensure +every token gets assigned a id the __lex__ library internally assigns unique +numbers to the token definitions, starting with the constant defined by +`boost::spirit::lex::min_token_id`. + + + +[endsect] diff --git a/doc/lex/lexer_quickstart3.qbk b/doc/lex/lexer_quickstart3.qbk new file mode 100644 index 000000000..5a9cc5cad --- /dev/null +++ b/doc/lex/lexer_quickstart3.qbk @@ -0,0 +1,151 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Quickstart 3 - Counting Words Using a Parser] + +The whole purpose of integrating __lex__ as part of the __spirit__ library was +to add a library allowing to merge lexical analysis with the parsing +process as defined by a __spirit__ grammar. __spirit__ parsers read their input +from an input sequence accessed by iterators. So naturally, we chose iterators +to be used as the interface beween the lexer and the parser. A second goal of +the lexer/parser integration was to enable the usage of possibly different +lexical analyzer libraries. The utilization of iterators seemed to be the +right choice from this standpoint as well, mainly because these can be used as +an abstraction layer hiding implementation specifics of the used lexer +library. The [link spirit.lex.flowcontrol picture] below shows the common +flow control implemented while parsing combined with lexical analysis. + +[fig ./images/FlowOfControl.png..The common flow control implemented while parsing combined with lexical analysis..spirit.lex.flowcontrol] + +Another problem related to the integration of the lexical analyzer with the +parser was to find a way how the defined tokens syntactically could be blended +with the grammar definition syntax of __spirit__. For tokens defined as +instances of the `token_def<>` class the most natural way of integration was +to allow to directly use these as parser components. Semantically these parser +components succeed matching their input whenever the corresponding token type +has been matched by the lexer. This quick start example will demonstrate this +(and more) by counting words again, simply by adding up the numbers inside +of semantic actions of a parser (for the full example code see here: +[@../../example/lex/word_count.cpp word_count.cpp]). + + +[import ../example/lex/word_count.cpp] + + +[heading Prerequisites] + +This example uses two of the __spirit__ library components: __lex__ and __qi__, +consequently we have to `#include` the corresponding header files. Again, we +need to include a couple of header files from the __phoenix2__ library. This +example shows how to attach functors to parser components, which +could be done using any type of C++ technique resulting in a callable object. +Using __phoenix2__ for this task simplifies things and avoids adding +dependencies to other libraries (__phoenix2__ is already in use for +__spirit__ anyway). + +[wcp_includes] + +To make all the code below more readable we introduce the following namespaces. + +[wcp_namespaces] + + +[heading Defining Tokens] + +If compared to the two previous quick start examples (__sec_lex_quickstart_1__ +and __sec_lex_quickstart_2__) the token definition class for this example does +not reveal any surprises. However, it uses lexer token definition macros to +simplify the composition of the regular expressions, which will be described in +more detail in the section __fixme__. Generally, any token definition is usable +without modification either for a standalone lexical analyzer or in conjunction +with a parser. + +[wcp_token_definition] + + +[heading Using Token Definition Instances as Parsers] + +While the integration of lexer and parser in the control flow is achieved by +using special iterators wrapping the lexical analyzer, we still nead a means of +expressing in the grammar what tokens to match and where. The token definition +class above uses three different ways of defining a token: + +* Using an instance of a `token_def<>`, which is handy whenever you need to + specify a token attribute (for more information about lexer related + attributes please look here: __sec_lex_attributes__). +* Using a single character as the token, in this case the character represents + itself as a token, where the token id is the ASCII character value. +* Using a regular expression represented as a string, where the token id needs + to be specified explicitly to make the token accessible from the grammar + level. + +All three token definition methods require a different method of grammar +integration. But as you can see from the following code snippet, each of this +methods is straightforward and blends the corresponding token instance +naturally with the surrounding __qi__ grammar syntax. + +[table + [[Token definition] [Parser integration]] + [[`token_def<>`] [The `token_def<>` instance is directly usable as a + parser component. Parsing of this component will + succeed if the regular expression used to define + this has been matched successfully.]] + [[single character] [The single character is directly usable in the + grammar, under certain circumstances it needs to be + wrapped by a `char_()` parser component, though. + Parsing of this component will succeed if the + single character has been matched.]] + [[explicit token id] [To use an explicit token id in a __qi__ grammar you + are required to wrap it with the special `token()` + parser component. Parsing of this component will + succeed if the current token has the same token + id as specified in the expression `token()`.]] +] + +The grammar definition below uses each of the three types demonstrating their +usage. + +[wcp_grammar_definition] + +As already described (see: __sec_qi_karma_attributes__), the __qi__ parser +library builds upon a set of of fully attributed parser components. +Consequently, all the token definitions do support the this attribute model as +well. The most natural way of implementing this was to use the token values as +the attributes exposed by the parser component corresponding to the token +definition (you can read more about this topic here: __sec_lex_tokenvalues__). +The example above takes advantage of the full integration of the token values +as the `token_def<>`'s parser attributes: the `word` token definition is +declared as a `token_def`, making every instance of a `word` token +carry the string representation of the matched input sequence as its value. +The semantic action attached to `tok.word` receives this string (represented by +the `_1` placeholder) and uses it to calculate the number of matched +characters: `ref(c) += size(_1)`. + +[important All placeholders (`_1`, `_2`, etc.) used in /parser/ semantic + actions in conjunction with functors created based on __phoenix2__ + need to be imported from the `namespace boost::spirit::arg_names` + (and *not* `namespace boost::phoenix::arg_names`, which is + different from using placeholders in __lex__). + Using the wrong placeholders leads to subtle compilation errors + which are difficult to backtrack to their cause. +] + + +[heading Pulling Everything Together] + +The main function needs to implement a bit more logic now as we have to +initialize and start not only the lexical analysis but the parsing process as +well. The three type definitions (`typedef` statements) simplify the creation +of the lexical analyzer and the grammar. After reading the contents of the +given file into memory it calls the function __api_tokenize_and_parse__ to +initialize the lexical analysis and parsing processes. + +[wcp_main] + + +[endsect] diff --git a/doc/lex/lexer_semantic_actions.qbk b/doc/lex/lexer_semantic_actions.qbk new file mode 100644 index 000000000..48d95726e --- /dev/null +++ b/doc/lex/lexer_semantic_actions.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Lexer Semantic Actions] +[endsect] diff --git a/doc/lex/lexer_states.qbk b/doc/lex/lexer_states.qbk new file mode 100644 index 000000000..628e4b4c4 --- /dev/null +++ b/doc/lex/lexer_states.qbk @@ -0,0 +1,21 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Lexer States] + + +[heading Controlling the Lexer State from Lexer Semantic Actions] + + +[heading Controlling the Lexer State from Parser Semantic Actions] + + +[heading Using a Lexer State for the Skip Parser] + + +[endsect] diff --git a/doc/lex/lexer_static_model.qbk b/doc/lex/lexer_static_model.qbk new file mode 100644 index 000000000..5f828706f --- /dev/null +++ b/doc/lex/lexer_static_model.qbk @@ -0,0 +1,119 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section The /Static/ Lexer Model] + +The documentation of __lex__ so far mostly was about describing the features of +the /dynamic/ model, where the tables needed for lexical analysis are generated +from the regular expressions at runtime. The big advantage of the dynamic model +is its flexibility, and its integration with the __spirit__ library and the C++ +host language. Its big disadvantage is the need to spend additional runtime to +generate the tables, which especially might be a limitation for larger lexical +analyers. The /static/ model strives to build upon the smooth integration with +__spirit__ and C++, and reuses large parts of the __lex__ library as described +so far, while overcoming the additional runtime requirements by using +pre-generated tables and tokenizer routines. To make the code generation as +simple as possible, it is possible reuse the token definition types developed +using the /dynamic/ model without any changes. As will be shown in this +section, building a code generator based on an existing token definition type +is a matter of writing 3 lines of code. + +Assuming you already built a dynamic lexer for your problem, there are two more +steps needed to create a static lexical analyzer using __lex__: + +# generating the C++ code for the static analyzer (including the tokenization + function and corresponding tables), and +# modifying the dynamic lexical anlyzer to use the generated code. + +Both steps are described in more detail in the two sections below (for the full +source code used in this example see the code here: +[@../../example/lex/static_lexer/word_count_tokens.hpp the common token definition], +[@../../example/lex/static_lexer/word_count_generate.cpp the code generator], +[@../../example/lex/static_lexer/word_count_static.hpp the generated code], and +[@../../example/lex/static_lexer/word_count_static.cpp the static lexical analyzer]). + +[import ../example/lex/static_lexer/word_count_tokens.hpp] +[import ../example/lex/static_lexer/word_count_static.cpp] +[import ../example/lex/static_lexer/word_count_generate.cpp] + +But first we provide the code snippets needed to understand the further +descriptions. Both, the definition of the used token identifier and the of the +token definition class in this example are put into a separate header file to +make these available to the code generator and the static lexical analyzer. + +[wc_static_tokenids] + +The important point here is, that the token definition class is not different +from a similar class to be used for a dynamic lexical analyzer. The library +has been designed in a way, that all components (dynamic lexical analyzer, code +generator, and static lexical analyzer) can reuse the very same token definition +syntax. + +[wc_static_tokendef] + +The only thing changing between the three different use cases is the template +parameter used to instantiate a concrete token definition. Fot the dynamic +model and the code generator you probably will use the __class_lexertl_lexer__ +template, where for the static model you will use the +__class_lexertl_static_lexer__ type as the template parameter. + +This example not only shows how to build a static lexer, but it additionally +demonstrates, how such a lexer can be used for parsing in conjunction with a +__qi__ grammar. For completeness we provide the simple grammar used in this +example. As you can see, this grammar does not have any dependencies on the +static lexical analyzer, and for this reason it is not different from a grammar +used either without a lexer or using a dynamic lexical analyzer as described +before. + +[wc_static_grammar] + + +[heading Generating the Static Analyzer] + +The first additional step to perform in order to create a static lexical +analyzer is to create a small standalone program for creating the lexer tables +and the corresponding tokenization function. For this purpose the __lex__ +library exposes a special API - the function __api_generate_static__. It +implements the whole code generator, no further code is needed. All what it +takes to invoke this function is to supply a token definition instance, an +output stream to use to generate the code to, and an optional string to be used +as a prefix for the name of the generated function. All in all just a couple +lines of code. + +[wc_static_generate_main] + +The shown code generator will generate output, which should be stored in a file +for later inclusion into the static lexical analzyer as shown in the next +topic (the full generated code can be viewed +[@../../example/lex/static_lexer/word_count_static.hpp here]). + + +[heading Modifying the Dynamic Analyzer] + +The second required step to convert an existing dynamic lexer into a static one +is to change your main program at two places. First, you need to change the +type of the used lexer (that is the template parameter used while instantiating +your token definition class). While in the dynamic model we have been using the +__class_lexertl_lexer__ template, we now need to change that to the +__class_lexertl_static_lexer__ type. The second change is tightly related to +the first one and involves correcting the corresponding `#include` statement to: + +[wc_static_include] + +Otherwise the main program is not different from an equivalent program using +the dynamic model. This feature makes it really easy for instance to develop +the lexer in dynamic mode and to switch to the static mode after the code has +been stabilized. The simple generator application showed above enables the +integration of the code generator into any existing build process. The +following code snippet provides the overall main function, highlighting +the code to be changed. + +[wc_static_main] + + +[endsect] diff --git a/doc/lex/lexer_tutorials.qbk b/doc/lex/lexer_tutorials.qbk new file mode 100644 index 000000000..8438bcf36 --- /dev/null +++ b/doc/lex/lexer_tutorials.qbk @@ -0,0 +1,59 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section __lex__ Tutorials Overview] + +The __lex__ library implements several components on top of possibly different +lexer generator libraries. It exposes a pair of iterators, which, when +dereferenced, return a stream of tokens generated from the underlying character +stream. The generated tokens are based on the token definitions supplied by the +user. + +Currently, __lex__ is built on top of Ben Hansons excellent __lexertl__ +library (which is a proposed Boost library). __lexertl__ provides the necessary +functionality to build state +machines based on a set of supplied regular expressions. But __lex__ is not +restricted to be used with __lexertl__. We expect it to be usable in +conjunction with any other lexical scanner generator library, all what needs +to be implemented is a set of wrapper objects exposing a well defined +interface as described in this documentation. + +[note For the sake of clarity all examples in this documentation assume + __lex__ to be used on top of __lexertl__.] + +Building a lexer using __lex__ is highly configurable, where most of this +configuration has to be done at compile time. Almost all of the configurable +parameters have generally useful default values, though, which means that +starting a project is easy and straightforward. Here is a (non-complete) list +of features you can tweak to adjust the generated lexer instance to the actual +needs: + +* Select and customize the token type to be generated by the lexer instance. +* Select and customize the token value types the generated token instances will + be able to hold. +* Select the iterator type of the underlying input stream, which will be used + as the source for the character stream to tokenize. +* Customize the iterator type returned by the lexer to enable debug support, + special handling of certain input sequences, etc. +* Select the /dynamic/ or the /static/ runtime model for the lexical + analyzer. + +Special care has been taken during the development of the library that +optimal code will be generated regardless of the configuration options +selected. + +The series of tutorial examples of this section will guide you through some +common use cases helping to understand the big picture. The first two quick +start examples (__sec_lex_quickstart_1__ and __sec_lex_quickstart_2__) +introduce the __lex__ library while building two standalone applications, not +being connected to or depending on any other part of __spirit__. The section +__sec_lex_quickstart_3__ demonstrates how to use a lexer in conjunction with a +parser (where certainly the parser is built using __qi__). + +[endsect] + diff --git a/doc/lex/parsing_using_a_lexer.qbk b/doc/lex/parsing_using_a_lexer.qbk new file mode 100644 index 000000000..a94a3267e --- /dev/null +++ b/doc/lex/parsing_using_a_lexer.qbk @@ -0,0 +1,15 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Parsing using a Lexer] + +[/ write about integration of lexer component with __qi__] + +[/ write about iterator interface exposed by a __lex__ lexer] + +[endsect] diff --git a/doc/lex/token_definition.qbk b/doc/lex/token_definition.qbk new file mode 100644 index 000000000..5e64c3c5c --- /dev/null +++ b/doc/lex/token_definition.qbk @@ -0,0 +1,11 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Ways to define Tokens] + +[endsect] diff --git a/doc/lex/tokenizing.qbk b/doc/lex/tokenizing.qbk new file mode 100644 index 000000000..08fde93c9 --- /dev/null +++ b/doc/lex/tokenizing.qbk @@ -0,0 +1,15 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Tokenizing Input Data] + +[heading The tokenize() function] + +[heading The generate_static() function] + +[endsect] diff --git a/doc/lex/tokens_values.qbk b/doc/lex/tokens_values.qbk new file mode 100644 index 000000000..5c26d870f --- /dev/null +++ b/doc/lex/tokens_values.qbk @@ -0,0 +1,207 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section About Tokens and Token Values] + +As already discussed, lexical scanning is the process of analyzing the stream +of input characters and separating it into strings called tokens, most of the +time separated by whitespace. The different token types recognized by a lexical +analyzer often get assigned unique integer token identifiers (token ids). These +token ids arenormally used by the parser to identifiy the current token without +having to look at the matched string again. The __lex__ library is not +different with respect to this, as it uses the token ids as the main means of +identification of the different token types defined for a particular lexical +analyzer. However, it is different from commonly used lexical analyzers in the +sense that it returns (references to) instances of a (user defined) token class +to the user. The only real limitation posed on this token class is consequently, +that it has to carry at least the token id of the token it represents. For more +information about the interface a user defined token type has to expose please +look at the __sec_ref_lex_token__ reference. The library provides a default +token type based on the __lexertl__ library which should be sufficient in most +use cases: the __class_lexertl_token__ type. This section focusses on the +description of general features a token class may implement and how this +integrates with the other parts of the __lex__ library. + +[heading The Anatomy of a Token] + +It is very important to understand the difference between a token definition +(represented by the __class_token_def__ template) and a token itself (for +instance represented by the __class_lexertl_token__ template). + +The token definition is used to describe the main features of a particular +token type, especially: + +* to simplify the definition of a token type using a regular expression pattern + applied while matching this token type, +* to associate a token type with a particular lexer state, +* to optionally assign a token id to a token type, +* to optionally associate some code to execute whenever an instance of this + token type has been matched, +* and to optionally specify the attribute type of the token value. + +The token itself is a data structure returned by the lexer iterators. +Dereferencing a lexer iterator returns a reference to the last matched token +instance. It encapsulates the part of the underlying input sequence matched by +the regular expression used during the definiton of this token type. +Incrementing the lexer iterator invokes the lexical analyzer to +match the next token by advancing the underlying input stream. The token data +structure contains at least the token id of the matched token type, +allowing to identify the matched character sequence. Optionally, the token +instance may contain a token value and/or the lexer state this token instance +was matched in. The following [link spirit.lex.tokenstructure figure] shows the +schematic structure of a token. + +[fig ./images/TokenStructure.png..The structure of a token..spirit.lex.tokenstructure] + +The token value and the token state may be omitted for optimization reasons, +avoiding the token to carry more data than actually required. This +configuration can be achieved by supplying appropriate template parameters +for the __class_lexertl_token__ template while defining the token type. + +The lexer iterator returns the same token type for each of the different +matched token definitions. To accomodate for the possibly different token +/value/ types exposed by the various token types (token definitions), the +general type of the token value is a __boost_variant__. As a minimum (for the +default configuration) this token value variant will be configured to always +hold a __boost_iterator_range__ containing the pair of iterators pointing to +the matched input sequence for this token instance. + +[note If the lexical analyzer is used in conjunction with a __qi__ parser, the + stored __boost_iterator_range__ token value will be converted to the + requested token type (parser attribute) exactly once. This happens at the + time of the first access to the token value requiring the + corresponding type conversion. The converted token value will be stored + in the __boost_variant__ replacing the initially stored iterator range. + This avoids to convert the input sequence to the token value more than + once, thus optimizing the integration of the lexer with __qi__, even + during parser backtracking. +] + +Here is the template prototype of the __class_lexertl_token__ template: + + template < + typename Iterator = char const*, + typename AttributeTypes = mpl::vector0<>, + typename HasState = mpl::true_ + > + struct lexertl_token; + +[variablelist where: + [[Iterator] [This is the type of the iterator used to access the + underlying input stream. It defaults to a plain + `char const*`.]] + [[AttributeTypes] [This is either a mpl sequence containing all + attribute types used for the token definitions or the + type `omitted`. If the mpl sequence is empty (which is + the default), all token instances will store a + `boost::iterator_range` pointing to the start + and the end of the matched section in the input stream. + If the type is `omitted`, the generated tokens will + contain no token value (attribute) at all.]] + [[HasState] [This is either `mpl::true_` or `mpl::false_`, allowing + to control whether the generated token instances will + contain the lexer state they were generated in. The + default is mpl::true_, so all token instances will + contain the lexer state.]] +] + +Normally, during its construction, a token instance always holds the +__boost_iterator_range__ as its token value (except, if it has been defined +using the `omitted` token value type). This iterator range then is +converted in place to the requested token value type (attribute) when it is +requested for the first time. + + +[heading The Physiognomy of a Token Definition] + +The token definitions (represented by the __class_token_def__ template) are +normally used as part of the definition of the lexical analyzer. At the same +time a token definition instance may be used as a parser component in __qi__. + +The template prototype of this class is shown here: + + template< + typename Attribute = unused_type, + typename Char = char + > + class token_def; + +[variablelist where: + [[Attribute] [This is the type of the token value (attribute) + supported by token instances representing this token + type. This attribute type is exposed to the __qi__ + library, whenever this token definition is used as a + parser component. The default attribute type is + `unused_type`, which means the token instance holds a + __boost_iterator_range__ pointing to the start + and the end of the matched section in the input stream. + If the attribute is `omitted` the token instance will + expose no token type at all. Any other type will be + used directly as the token value type.]] + [[Char] [This is the value type of the iterator for the + underlying input sequence. It defaults to `char`.]] +] + +The semantics of the template parameters for the token type and the token +definition type are very similar and interdependent. As a rule of thumb you can +think of the token definition type as the means of specifying everything +related to a single specific token type (such as `identifier` or `integer`). +On the other hand the token type is used to define the general proerties of all +token instances generated by the __lex__ library. + +[important If you don't list any token value types in the token type definition + declaration (resulting in the usage of the default __boost_iterator_range__ + token type) everything will compile and work just fine, just a bit + less efficient. This is because the token value will be converted + from the matched input sequence every time it is requested. + + But as soon as you specify at least one token value type while + defining the token type you'll have to list all value types used for + __class_token_def__ declarations in the token definition class, + otherwise compilation errors will occur. +] + + +[heading Examples of using __class_lexertl_token__] + +Let's start with some examples. We refer to one of the __lex__ examples (for +the full source code of this example please see +[@../../example/lex/example4.cpp example4.cpp]). + +[import ../example/lex/example4.cpp] + +The first code snippet shows an excerpt of the token definition class, the +definition of a couple of token types. Some of the token types do not expose a +special token value (`if_`, `else_`, and `while_`). Their token value will +always hold the iterator range of the matched input sequence only. The token +definitions for the `identifier` and the integer `constant` are specialized +to expose an explicit token type each: `std::string` and `unsigned int`. + +[example4_token_def] + +As the parsers generated by __qi__ are fully attributed, any __qi__ parser +component needs to expose a certain type as its parser attribute. Naturally, +the __class_token_def__ exposes the token value type as its parser attribute, +enabling a smooth integration with __qi__. + +The next code snippet demonstrates how the required token value types are +specified while defining the token type to use. All of the token value types +used for at least one of the token definitions have to be re-iterated for the +token definition as well. + +[example4_token] + +To avoid the token to have a token value at all, the special tag `omitted` can +be used: `token_def` and `lexertl_token`. + + + + + + +[endsect] diff --git a/doc/notes.qbk b/doc/notes.qbk new file mode 100644 index 000000000..e581cba82 --- /dev/null +++ b/doc/notes.qbk @@ -0,0 +1,24 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Notes] + +[section Portability] +[endsect] + +[section Porting from Spirit 1.8.x] +[endsect] + +[section Style Guide] +[include notes/style_guide.qbk] +[endsect] + +[section Techniques] +[endsect] + +[endsect] diff --git a/doc/notes/style_guide.qbk b/doc/notes/style_guide.qbk new file mode 100644 index 000000000..bfd75ee9a --- /dev/null +++ b/doc/notes/style_guide.qbk @@ -0,0 +1,87 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +At some point, especially when there are lots of semantic actions attached to +various points, the grammar tends to be quite difficult to follow. In order to +keep an easy-to-read, consistent en aesthetically pleasing look to the Spirit +code, the following coding styleguide is advised. + +This coding style is adapted and extended from the ANTLR/PCCTS style (Terrence +Parr) and [@http://groups.yahoo.com/group/boost/files/coding_guidelines.html +Boost coding guidelines] (David Abrahams and Nathan Myers) and is the +combined work of Joel de Guzman, Chris Uzdavinis and Hartmut Kaiser. + +* Rule names use std C++ (Boost) convention. The rule name may be very long. +* The '=' is neatly indented 4 spaces below. Like in Boost, use spaces instead + of tabs. +* Breaking the operands into separate lines puts the semantic actions neatly + to the right. +* Semicolon at the last line terminates the rule. +* The adjacent parts of a sequence should be indented accordingly to have all, + what belongs to one level, at one indentation level. + + program + = program_heading [heading_action] + >> block [block_action] + >> '.' + | another_sequence + >> etc + ; + +* Prefer literals in the grammar instead of identifiers. e.g. `"program"` instead + of `PROGRAM`, `'>='` instead of `GTE` and `'.'` instead of `DOT`. This makes it much + easier to read. If this isn't possible (for instance where the used tokens + must be identified through integers) capitalized identifiers should be used + instead. +* Breaking the operands may not be needed for short expressions. + e.g. `*(',' >> file_identifier)` as long as the line does not + exceed 80 characters. +* If a sequence fits on one line, put spaces inside the parentheses + to clearly separate them from the rules. + + program_heading + = no_case["program"] + >> identifier + >> '(' + >> file_identifier + >> *( ',' >> file_identifier ) + >> ')' + >> ';' + ; + +* Nesting directives: If a rule does not fit on one line (80 characters) + it should be continued on the next line intended by one level. The brackets + of directives, semantic expressions (using Phoenix or LL lambda expressions) + or parsers should be placed as follows. + + identifier + = no_case + [ + lexeme + [ + alpha >> *(alnum | '_') [id_action] + ] + ] + ; + +* Nesting unary operators (e.g.Kleene star): Unary rule operators + (Kleene star, `'!'`, `'+'` etc.) should be moved out one space before + the corresponding indentation level, if this rule has a body or a + sequence after it, which does not fit on on line. This makes the + formatting more consistent and moves the rule 'body' at the same + indentation level as the rule itself, highlighting the unary operator. + + block + = *( label_declaration_part + | constant_definition_part + | type_definition_part + | variable_declaration_part + | procedure_and_function_declaration_part + ) + >> statement_part + ; diff --git a/doc/outline.txt b/doc/outline.txt new file mode 100644 index 000000000..7dad9ae6a --- /dev/null +++ b/doc/outline.txt @@ -0,0 +1,97 @@ +# Copyright (C) 2001-2008 Joel de Guzman +# Copyright (C) 2001-2008 Hartmut Kaiser +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + +Preface +What's New +Introduction +Qi and Karma + Tutorials + Abstracts + Parsing Expression Grammar + Parsing and Generating + Primitives + Operators + Attributes + Semantic Actions + Directives + Rules + Grammars + Debugging + Error Handling + Parse Trees and ASTs + Quick Reference + Reference + Concepts + Parser + Generator + Parser Director + Generator Director + Char + String + Numeric + Binary + Directive + Action + Nonterminal + Operator + Stream + Auxiliary + Debug +Lex + Introduction + Tutorials + Abstracts + Parsing using a Lexer + Lexer Primitives + Lexer States + Lexer Attributes + Lexer Semantic Actions + Quick Reference + Reference + Concepts + Lexer + Token + TokenDef + TokenSet + Lexer Class + Token Class + TokenDef Class + TokenSet Class +FAQ +Notes + Portability + Porting from Spirit 1.8.x + Style Guide + Techniques +Rationale +Acknowledgments +References + +----------------------------------------------------------------- + +Concepts Outline: + Description + Notation + Valid Expressions + Expression | Semantics | Return type | Complexity + Type Requirements + Expression | Requirements + Invariants + Models + +Reference Page Outline: + Description + Header + Synopsis + Template parameters + Model of + Objects + Expression Semantics + Expression | Semantics | Return type | Complexity + Example + + diff --git a/doc/preface.qbk b/doc/preface.qbk new file mode 100644 index 000000000..d1415b1c0 --- /dev/null +++ b/doc/preface.qbk @@ -0,0 +1,217 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Preface] + +[:['["Examples of designs that meet most of the criteria for +"goodness" (easy to understand, flexible, efficient) are a recursive- +descent parser, which is traditional procedural code. Another example +is the STL, which is a generic library of containers and algorithms +depending crucially on both traditional procedural code and on +parametric polymorphism.]] [*--Bjarne Stroustrup]] + +[heading History] + +[heading /80s/] + +In the Mid 80s, Joel wrote his first calculator in Pascal. It has been +an unforgettable coding experience. He was amazed how a mutually +recursive set of functions can model a grammar specification. In time, +the skills he acquired from that academic experience became very +practical. Periodically Joel was tasked to do some parsing. For +instance, whenever he needs to perform any form of I/O, even in +binary, he tries to approach the task somewhat formally by writing a +grammar using Pascal- like syntax diagrams and then write a +corresponding recursive-descent parser. This worked very well. + +[heading /90s/] + +The arrival of the Internet and the World Wide Web magnified this +thousand-fold. At one point Joel had to write an HTML parser for a Web +browser project. He got a recursive-descent HTML parser working based +on the W3C formal specifications easily. He was certainly glad that +HTML had a formal grammar specification. Because of the influence of +the Internet, Joel then had to do more parsing. RFC specifications +were everywhere. SGML, HTML, XML, even email addresses and those +seemingly trivial URLs were all formally specified using small EBNF- +style grammar specifications. This made him wish for a tool similar to +big- time parser generators such as YACC and ANTLR, where a parser is +built automatically from a grammar specification. Yet, he wants it to +be extremely small; small enough to fit in my pocket, yet scalable. + +It must be able to practically parse simple grammars such as email +addresses to moderately complex grammars such as XML and perhaps some +small to medium-sized scripting languages. Scalability is a prime +goal. You should be able to use it for small tasks such as parsing +command lines without incurring a heavy payload, as you do when you +are using YACC or PCCTS. Even now that it has evolved and matured to +become a multi-module library, true to its original intent, Spirit can +still be used for extreme micro-parsing tasks. You only pay for +features that you need. The power of Spirit comes from its modularity +and extensibility. Instead of giving you a sledgehammer, it gives you +the right ingredients to create a sledgehammer easily. + +The result was Spirit. Spirit was a personal project that was +conceived when Joel was doing R&D in Japan. Inspired by the GoF's +composite and interpreter patterns, he realized that he can model a +recursive-descent parser with hierarchical-object composition of +primitives (terminals) and composites (productions). The original +version was implemented with run-time polymorphic classes. A parser is +generated at run time by feeding in production rule strings such as: + + "prod ::= {'A' | 'B'} 'C';" + +A compile function compiled the parser, dynamically creating a +hierarchy of objects and linking semantic actions on the fly. A very +early text can be found here: __early_spirit__. + +[heading /2001 to 2006/] + +Version 1.0 to 1.8 was a complete rewrite of the original Spirit +parser using expression templates and static polymorphism, inspired by +the works of Todd Veldhuizen (__todd__exprtemplates__, C++ Report, +June 1995). Initially, the static-Spirit version was meant only to +replace the core of the original dynamic-Spirit. Dynamic-spirit +needed a parser to implement itself anyway. The original employed a +hand-coded recursive-descent parser to parse the input grammar +specification strings. Incidentially it was the time, when Hartmut +joined the Spirit development. + +After its initial "open-source" debut in May 2001, static-Spirit +became a success. At around November 2001, the Spirit website had an +activity percentile of 98%, making it the number one parser tool at +Source Forge at the time. Not bad for such a niche project such as a +parser library. The "static" portion of Spirit was forgotten and +static-Spirit simply became Spirit. The library soon evolved to +acquire more dynamic features. + +Spirit was formally accepted into __boost__ in October 2002. Boost is +a peer-reviewed, open collaborative development effort that is a +collection of free Open Source C++ libraries covering a wide range of +domains. The Boost Libraries have become widely known as an industry +standard for design and implementation quality, robustness, and +reusability. + +[heading /2007/] + +Over the years, especially after Spirit was accepted into Boost, +Spirit has served its purpose quite admirably. The focus of what we'll +now call [*/Classic-Spirit/] (versions prior to 2.0) was on +transduction parsing where the input string is merely translated to an +output string. A lot of parsers are of the transduction type. When the +time came to add attributes to the parser library, it was done rather +in an ad-hoc manner, with the goal being 100% backward compatible with +classic Spirit. Some parsers have attributes, some don't. + +Spirit V2 is another major rewrite. Spirit V2 grammars are fully +attributed (see __attr_grammar__). All parser components have +attributes. To do this efficiently and ellegantly, we had to use a +couple of infrastructure libraries. Some of which haven't been written +yet at the time, some were quite new when Spirit debuted, and some +needed work. __mpl__ is an important infrastructure library, yet is +not sufficient to implement Spirit V2. Another library had to be +written: __fusion__. Fusion sits between MPL and STL --between compile +time and runtime -- mapping types to values. Fusion is a direct +descendant of both MPL and __boost_tuples__ (Fusion is now a full +fledged __boost__ library). __phoenix__ also had to be beefed up to +support Spirit V2. The result is __phoenix2__. Last but not least, +Spirit V2 uses an __todd__exprtemplates__ library called +__boost_proto__. + +[heading New Ideas: Spirit V2] + +Just before the development of Spirit V2 began, Hartmut came across +the __string_template__ library which is a part of the ANTLR parser +framework. It is a Java template engine (with ports for C# and Python) +for generating source code, web pages, emails, or any other formatted +text output. With it, he got the the idea of using a formal notation +(a grammar) to describe the expected structure of an input character +sequence. The same grammar may be used to formalize the structure of a +corresponding output character sequence. This is possible because +parsing, most of the time, is implemented by comparing the input with +the patterns defined by the grammar. If we use the same patterns to +format a matching output, the generated sequence will follow the rules +of the grammar as well. + +This insight lead to the implementation of a grammar driven output generation +library compatibile with the Spirit parser library. As it turned out, parsing +and generation are tightly connected and have very similar concepts. The +duality of these two sides of the same medal is ubiquitous, which +allowed us to build the parser library __qi__ and the generator library +__karma__ using the same component infastructure. + +The idea of creating a lexer library well integrated with the Spirit parsers is +not new. This has been discussed almost for the whole time of the existence of +Classic-Spirit (pre V2) now. Several attempts to integrate existing lexer +libraries and frameworks with Spirit have been made and served as a proof of +concept and usability (for example see __wave__: The Boost C/C++ Preprocessor +Library, and __slex__: a fully dynamic C++ lexer implemented with Spirit). +Based on these experiences we added __lex__: a fully integrated lexer library +to the mix, allowing to take advantage of the power of regular expressions for +token matching, removing pressure from the parser components, simplifying +parser grammars. Again, Spirit's modular structure allowed us to reuse the same +underlying component library as for the parser and generator libraries. + + +[heading How to use this manual] + +Each major section (there are two: __sec_qi_and_karma__, and __sec_lex__) is +roughly divided into 3 parts: + +# Tutorials: A step by step guide with heavily annotated code. These + are meant to get the user acquainted with the library as quickly as + possible. The objective is to build the confidence of the user in + using the library using abundant examples and detailed instructions. + Examples speak volumes. + +# Abstracts: A high level summary of key topics. The objective is to + give the user a high level view of the library, the key concepts, + background and theories. + +# Reference: Detailed formal technical reference. We start with a quick + reference -- an easy to use table that maps into the reference proper. + The reference proper starts with C++ __cpp_concepts__ followed by + models of the concepts. + +Some icons are used to mark certain topics indicative of their relevance. +These icons precede some text to indicate: + +[table Icons + + [[Icon] [Name] [Meaning]] + + [[__note__] [Note] [Generally useful information (an aside that + doesn't fit in the flow of the text)]] + + [[__tip__] [Tip] [Suggestion on how to do something + (especially something that not be obvious)]] + + [[__important__] [Important] [Important note on something to take + particular notice of]] + + [[__caution__] [Caution] [Take special care with this - it may + not be what you expect and may cause bad + results]] + + [[__danger__] [Danger] [This is likely to cause serious + trouble if ignored]] +] + +This documentation is automatically generated by Boost QuickBook documentation +tool. QuickBook can be found in the __boost_tools__. + +[heading Support] + +Please direct all questions to Spirit's mailing list. You can subscribe to the +__spirit_list__. The mailing list has a searchable archive. A search link to +this archive is provided in __spirit__'s home page. You may also read and post +messages to the mailing list through __spirit_general__ (thanks to __gmane__). +The news group mirrors the mailing list. Here is a link to the archives: +__mlist_archive__. + +[endsect] [/ Preface] diff --git a/doc/qi_and_karma.qbk b/doc/qi_and_karma.qbk new file mode 100644 index 000000000..cfd7f5a09 --- /dev/null +++ b/doc/qi_and_karma.qbk @@ -0,0 +1,52 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Qi and Karma] + +[include qi_and_karma/tutorials.qbk] + +[section Abstracts] +[include qi_and_karma/peg.qbk] +[include qi_and_karma/parsing.qbk] +[include qi_and_karma/generating.qbk] +[include qi_and_karma/primitives.qbk] +[include qi_and_karma/operators.qbk] +[include qi_and_karma/attributes.qbk] +[include qi_and_karma/semantic_actions.qbk] +[include qi_and_karma/directives.qbk] +[include qi_and_karma/rules.qbk] +[include qi_and_karma/grammars.qbk] +[include qi_and_karma/debugging.qbk] +[include qi_and_karma/error_handling.qbk] +[include qi_and_karma/parse_trees_and_asts.qbk] +[endsect] + +[/section Quick Reference] +[include qi_and_karma/quick_reference.qbk] +[/endsect] + +[section Reference] +[section Concepts] +[include reference/qi_and_karma/parser.qbk] +[include reference/qi_and_karma/generator.qbk] +[endsect] +[include reference/qi_and_karma/char.qbk] +[include reference/qi_and_karma/string.qbk] +[include reference/qi_and_karma/numeric.qbk] +[include reference/qi_and_karma/binary.qbk] +[include reference/qi_and_karma/directive.qbk] +[include reference/qi_and_karma/action.qbk] +[include reference/qi_and_karma/nonterminal.qbk] +[include reference/qi_and_karma/operator.qbk] +[include reference/qi_and_karma/stream.qbk] +[include reference/qi_and_karma/auxiliary.qbk] +[include reference/qi_and_karma/debug.qbk] +[endsect] + +[endsect] + diff --git a/doc/qi_and_karma/attributes.qbk b/doc/qi_and_karma/attributes.qbk new file mode 100644 index 000000000..f4eab850e --- /dev/null +++ b/doc/qi_and_karma/attributes.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Attributes] +[endsect] diff --git a/doc/qi_and_karma/debugging.qbk b/doc/qi_and_karma/debugging.qbk new file mode 100644 index 000000000..adbbb84c1 --- /dev/null +++ b/doc/qi_and_karma/debugging.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Debugging] +[endsect] diff --git a/doc/qi_and_karma/directives.qbk b/doc/qi_and_karma/directives.qbk new file mode 100644 index 000000000..14ba5d9fc --- /dev/null +++ b/doc/qi_and_karma/directives.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Directives] +[endsect] diff --git a/doc/qi_and_karma/error_handling.qbk b/doc/qi_and_karma/error_handling.qbk new file mode 100644 index 000000000..39cfd5bae --- /dev/null +++ b/doc/qi_and_karma/error_handling.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Error Handling] +[endsect] diff --git a/doc/qi_and_karma/generating.qbk b/doc/qi_and_karma/generating.qbk new file mode 100644 index 000000000..1f9a01ab6 --- /dev/null +++ b/doc/qi_and_karma/generating.qbk @@ -0,0 +1,24 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Parsing and Generating] + +[heading The API functions exposed by __qi__ ] + +[heading The parse() function] + +[heading The phrase_parse() function] + +[heading The tokenize_and_parse() function] + +[heading The tokenize_and_phrase_parse() function] + +[heading The make_parser() function] + +[endsect] + diff --git a/doc/qi_and_karma/grammars.qbk b/doc/qi_and_karma/grammars.qbk new file mode 100644 index 000000000..1ff8814ec --- /dev/null +++ b/doc/qi_and_karma/grammars.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Grammars] +[endsect] diff --git a/doc/qi_and_karma/operators.qbk b/doc/qi_and_karma/operators.qbk new file mode 100644 index 000000000..fbd635ce5 --- /dev/null +++ b/doc/qi_and_karma/operators.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Operators] +[endsect] diff --git a/doc/qi_and_karma/parse_trees_and_asts.qbk b/doc/qi_and_karma/parse_trees_and_asts.qbk new file mode 100644 index 000000000..b0028bc05 --- /dev/null +++ b/doc/qi_and_karma/parse_trees_and_asts.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Parse Trees and ASTs] +[endsect] diff --git a/doc/qi_and_karma/parsing.qbk b/doc/qi_and_karma/parsing.qbk new file mode 100644 index 000000000..3fd1e7af4 --- /dev/null +++ b/doc/qi_and_karma/parsing.qbk @@ -0,0 +1,44 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Parsing] + +Central to the library is the parser. The parser does the actual +work of recognizing a linear input stream of data read sequentially +from start to end by the supplied iterators. The parser attempts to +match the input following a well-defined set of specifications known +as grammar rules. The parser returns a `bool` to report the success or +failure. When successful, the parser calls a client-supplied semantic +action, if there is one. The semantic action extracts structural +information depending on the data passed by the parser and the +hierarchical context of the parser it is attached to. + +Parsers come in different flavors. The Spirit library comes bundled with an extensive set of pre-defined parsers that perform various parsing tasks from the trivial to the complex. The parser, as a concept, has a public conceptual interface contract. Following the contract, anyone can write a conforming parser that will play along well with the library's predefined components. We shall provide a blueprint detailing the conceptual interface of the parser later. + +Clients of the library generally do not need to write their own hand-coded parsers at all. Spirit has an immense repertoire of pre-defined parsers covering all aspects of syntax and semantic analysis. We shall examine this repertoire of parsers in the following sections. In the rare case where a specific functionality is not available, it is extremely easy to write a user-defined parser. The ease in writing a parser entity is the main reason for Spirit's extensibility. + + + + + + + +[heading The API functions exposed by __qi__ ] + +[heading The parse() function] + +[heading The phrase_parse() function] + +[heading The tokenize_and_parse() function] + +[heading The tokenize_and_phrase_parse() function] + +[heading The make_parser() function] + +[endsect] + diff --git a/doc/qi_and_karma/peg.qbk b/doc/qi_and_karma/peg.qbk new file mode 100644 index 000000000..2b4002119 --- /dev/null +++ b/doc/qi_and_karma/peg.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Parsing Expression Grammar] +[endsect] diff --git a/doc/qi_and_karma/primitives.qbk b/doc/qi_and_karma/primitives.qbk new file mode 100644 index 000000000..78e07b53c --- /dev/null +++ b/doc/qi_and_karma/primitives.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Primitives] +[endsect] diff --git a/doc/qi_and_karma/quick_reference.qbk b/doc/qi_and_karma/quick_reference.qbk new file mode 100644 index 000000000..47aa8204b --- /dev/null +++ b/doc/qi_and_karma/quick_reference.qbk @@ -0,0 +1,43 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Quick Reference] + +The following tables use some conventions to encode the attribute type exposed +by a component + +[variablelist + [[`attribute_of(P)`] [The component exposes the same attribute as the + component 'P' used as part of the overall + construct]] + [[`value_type(I)`] [The component exposes the value_type of the + underlying iterator 'I' as its attribute type]] +] + +[table Character Parsers + [[Component] [Description] [Attribute]] + [[`char_`] [] [`char`]] + [[`wchar`] [] [`wchar_t`]] + [[`lit`] [] [`unused`]] + [[`wlit`] [] [`unused`]] + [[`'x'`] [] [`unused`]] + [[`L'x'`] [] [`unused`]] + [[`alnum`] [] [`Char`]] + [[`alpha`] [] [`Char`]] + [[`blank`] [] [`Char`]] + [[`cntrl`] [] [`Char`]] + [[`digit`] [] [`Char`]] + [[`graph`] [] [`Char`]] + [[`print`] [] [`Char`]] + [[`punct`] [] [`Char`]] + [[`space`] [] [`Char`]] + [[`xdigit`] [] [`Char`]] + [[`~P`] [] [`attribute_of(P)`]] +] + +[endsect] diff --git a/doc/qi_and_karma/rules.qbk b/doc/qi_and_karma/rules.qbk new file mode 100644 index 000000000..cc4bf59c9 --- /dev/null +++ b/doc/qi_and_karma/rules.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Rules] +[endsect] diff --git a/doc/qi_and_karma/semantic_actions.qbk b/doc/qi_and_karma/semantic_actions.qbk new file mode 100644 index 000000000..b104760c1 --- /dev/null +++ b/doc/qi_and_karma/semantic_actions.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Semantic Actions] +[endsect] diff --git a/doc/qi_and_karma/tutorials.qbk b/doc/qi_and_karma/tutorials.qbk new file mode 100644 index 000000000..dfe8ffd2b --- /dev/null +++ b/doc/qi_and_karma/tutorials.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Tutorials] +[endsect] diff --git a/doc/rationale.qbk b/doc/rationale.qbk new file mode 100644 index 000000000..8bc4bba5b --- /dev/null +++ b/doc/rationale.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Rationale] +[endsect] diff --git a/doc/reference/lex/lexer.qbk b/doc/reference/lex/lexer.qbk new file mode 100644 index 000000000..25cee90d4 --- /dev/null +++ b/doc/reference/lex/lexer.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Lexer] +[endsect] diff --git a/doc/reference/lex/lexer_class.qbk b/doc/reference/lex/lexer_class.qbk new file mode 100644 index 000000000..74c14db2a --- /dev/null +++ b/doc/reference/lex/lexer_class.qbk @@ -0,0 +1,19 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Lexer Class] + +[heading The lexertl_lexer Class Implementing the Dynamic Model] + +[heading The lexertl_actor_lexer Class Implementing the Dynamic Model] + +[heading The lexertl_static_lexer Class Implementing the Static Model] + +[heading The lexertl_static_actor_lexer Class Implementing the Static Model] + +[endsect] diff --git a/doc/reference/lex/token.qbk b/doc/reference/lex/token.qbk new file mode 100644 index 000000000..b6d20a676 --- /dev/null +++ b/doc/reference/lex/token.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Token] +[endsect] diff --git a/doc/reference/lex/token_class.qbk b/doc/reference/lex/token_class.qbk new file mode 100644 index 000000000..93d530b62 --- /dev/null +++ b/doc/reference/lex/token_class.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Token Class] +[endsect] diff --git a/doc/reference/lex/tokendef.qbk b/doc/reference/lex/tokendef.qbk new file mode 100644 index 000000000..5137889bb --- /dev/null +++ b/doc/reference/lex/tokendef.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section TokenDef] +[endsect] diff --git a/doc/reference/lex/tokendef_class.qbk b/doc/reference/lex/tokendef_class.qbk new file mode 100644 index 000000000..c9f4056cd --- /dev/null +++ b/doc/reference/lex/tokendef_class.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section TokenDef Class] +[endsect] diff --git a/doc/reference/lex/tokenset.qbk b/doc/reference/lex/tokenset.qbk new file mode 100644 index 000000000..d0737ec0a --- /dev/null +++ b/doc/reference/lex/tokenset.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section TokenSet] +[endsect] diff --git a/doc/reference/lex/tokenset_class.qbk b/doc/reference/lex/tokenset_class.qbk new file mode 100644 index 000000000..18ac25767 --- /dev/null +++ b/doc/reference/lex/tokenset_class.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section TokenSet Class] +[endsect] diff --git a/doc/reference/qi_and_karma/action.qbk b/doc/reference/qi_and_karma/action.qbk new file mode 100644 index 000000000..d61b3789f --- /dev/null +++ b/doc/reference/qi_and_karma/action.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Action] +[endsect] diff --git a/doc/reference/qi_and_karma/auxiliary.qbk b/doc/reference/qi_and_karma/auxiliary.qbk new file mode 100644 index 000000000..fc2465d66 --- /dev/null +++ b/doc/reference/qi_and_karma/auxiliary.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Auxiliary] +[endsect] diff --git a/doc/reference/qi_and_karma/binary.qbk b/doc/reference/qi_and_karma/binary.qbk new file mode 100644 index 000000000..171cf74f0 --- /dev/null +++ b/doc/reference/qi_and_karma/binary.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Binary] +[endsect] diff --git a/doc/reference/qi_and_karma/char.qbk b/doc/reference/qi_and_karma/char.qbk new file mode 100644 index 000000000..9af0c3882 --- /dev/null +++ b/doc/reference/qi_and_karma/char.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Char] +[endsect] diff --git a/doc/reference/qi_and_karma/debug.qbk b/doc/reference/qi_and_karma/debug.qbk new file mode 100644 index 000000000..3383f16d1 --- /dev/null +++ b/doc/reference/qi_and_karma/debug.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Debug] +[endsect] diff --git a/doc/reference/qi_and_karma/directive.qbk b/doc/reference/qi_and_karma/directive.qbk new file mode 100644 index 000000000..e17ee97ca --- /dev/null +++ b/doc/reference/qi_and_karma/directive.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Directive] +[endsect] diff --git a/doc/reference/qi_and_karma/generator.qbk b/doc/reference/qi_and_karma/generator.qbk new file mode 100644 index 000000000..9748c0456 --- /dev/null +++ b/doc/reference/qi_and_karma/generator.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Generator] +[endsect] diff --git a/doc/reference/qi_and_karma/nonterminal.qbk b/doc/reference/qi_and_karma/nonterminal.qbk new file mode 100644 index 000000000..db23dcff4 --- /dev/null +++ b/doc/reference/qi_and_karma/nonterminal.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Nonterminal] +[endsect] diff --git a/doc/reference/qi_and_karma/numeric.qbk b/doc/reference/qi_and_karma/numeric.qbk new file mode 100644 index 000000000..cb41e094e --- /dev/null +++ b/doc/reference/qi_and_karma/numeric.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Numeric] +[endsect] diff --git a/doc/reference/qi_and_karma/operator.qbk b/doc/reference/qi_and_karma/operator.qbk new file mode 100644 index 000000000..fbd635ce5 --- /dev/null +++ b/doc/reference/qi_and_karma/operator.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Operators] +[endsect] diff --git a/doc/reference/qi_and_karma/parser.qbk b/doc/reference/qi_and_karma/parser.qbk new file mode 100644 index 000000000..b8cb5d297 --- /dev/null +++ b/doc/reference/qi_and_karma/parser.qbk @@ -0,0 +1,43 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Parser] + +[heading Description] + +Description of Parser concept + +[variablelist Notation + [[`p`] [A Parser]] +] + +[heading Valid Expressions] + +For any Parser the following expressions must be valid: + +[table + [[Expression] [Semantics] [Return type] [Complexity]] + [[`xxx`] [Semantics of `xxx`] [Parser] [Constant]] +] + +[heading Type Requirements] + +[table + [[Expression] [Requirements]] + [[`xxx`] [Requirements for `xxx`]] +] + +[heading Invariants] + +For any Parser xxx the following invariants always hold: + +[heading Models] + +Links to models of Parser concept + +[endsect] diff --git a/doc/reference/qi_and_karma/stream.qbk b/doc/reference/qi_and_karma/stream.qbk new file mode 100644 index 000000000..0437a451a --- /dev/null +++ b/doc/reference/qi_and_karma/stream.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section Stream] +[endsect] diff --git a/doc/reference/qi_and_karma/string.qbk b/doc/reference/qi_and_karma/string.qbk new file mode 100644 index 000000000..13339d86e --- /dev/null +++ b/doc/reference/qi_and_karma/string.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section String] +[endsect] diff --git a/doc/references.qbk b/doc/references.qbk new file mode 100644 index 000000000..d64325f56 --- /dev/null +++ b/doc/references.qbk @@ -0,0 +1,91 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section References] + +[table +[[ ] [Authors] [Title, Publisher/link, Date Published]] +[[1.] [Todd Veldhuizen] [[@http://www.extreme.indiana.edu/%7Etveldhui/papers/Expression-Templates/exprtmpl.html + "Expression Templates"]. C++ Report, June 1995.]] +[[2.] [Peter Naur (ed.)] [[@http://www.masswerk.at/algol60/report.htm + "Report on the Algorithmic Language ALGOL 60"]. CACM, May 1960.]] +[[3.] [ISO/IEC] [[@http://www.cl.cam.ac.uk/%7Emgk25/iso-14977.pdf "ISO-EBNF"], + ISO/IEC 14977: 1996(E).]] +[[4.] [Richard J.Botting, Ph.D.] [[@http://www.csci.csusb.edu/dick/maths/intro_ebnf.html + "XBNF"] (citing Leu-Weiner, 1973). + California State University, San Bernardino, 1998.]] +[[5.] [James Coplien.] ["Curiously Recurring Template Pattern". + C++ Report, Feb. 1995.]] +[[6.] [Thierry Geraud and + Alexandre Duret-Lutz] [[@http://www.coldewey.com/europlop2000/papers/geraud%2Bduret.zip + Generic Programming Redesign of Patterns] + Proceedings of the 5th European Conference on Pattern Languages + of Programs(EuroPLoP'2000) Irsee, Germany, July 2000.]] +[[7.] [Geoffrey Furnish] [[@http://www.adtmag.com/joop/carticle.aspx?ID=627 + "Disambiguated Glommable Expression Templates Reintroduced"] + C++ Report, May 2000]] +[[8.] [Erich Gamma, + Richard Helm, + Ralph Jhonson, + and John Vlissides] [Design Patterns, Elements of Reusable Object-Oriented Software. + Addison-Wesley, 1995.]] +[[9.] [Alfred V. Aho, + Revi Sethi, + Feffrey D. Ulman] [Compilers, Principles, Techniques and Tools + Addison-Wesley, June 1987.]] +[[10.] [Dick Grune and + Ceriel Jacobs] [[@http://www.cs.vu.nl/%7Edick/PTAPG.html + Parsing Techniques: A Practical Guide.] + Ellis Horwood Ltd.: West Sussex, England, 1990. + (electronic copy, 1998).]] +[[11.] [T. J. Parr, + H. G. Dietz, and + W. E. Cohen] [[@http://citeseer.ist.psu.edu/6885.html + PCCTS Reference Manual (Version 1.00)]. + School of Electrical Engineering, Purdue University, + West Lafayette, August 1991.]] +[[12.] [Adrian Johnstone and + Elizabeth Scott.] [[@ftp://ftp.cs.rhul.ac.uk/pub/rdp + RDP, A Recursive Descent Compiler Compiler]. + Technical Report CSD TR 97 25, Dept. of Computer Science, + Egham, Surrey, England, Dec. 20, 1997.]] +[[13.] [Adrian Johnstone] [[@http://www.cs.rhul.ac.uk/research/languages/projects/lookahead_backtrack.shtml + Languages and Architectures, + Parser generators with backtrack or extended lookahead capability] + Department of Computer Science, Royal Holloway, University of London, + Egham, Surrey, England]] +[[14.] [Damian Conway] [[@http://www.csse.monash.edu.au/%7Edamian/papers/#Embedded_Input_Parsing_for_C + Parsing with C++ Classes]. + ACM SIGPLAN Notices, 29:1, 1994.]] +[[15.] [Joel de Guzman] [[@http://spirit.sourceforge.net/distrib/spirit_1_8_5/libs/spirit/index.html + "Spirit Version 1.8"], 1998-2003.]] +[[16.] [S. Doaitse Swierstra and + Luc Duponcheel] [[@http://citeseer.ist.psu.edu/448665.html + Deterministic, Error-Correcting Combinator Parsers] + Dept. of Computer Science, Utrecht University P.O.Box 80.089, + 3508 TB Utrecht, The Netherland]] +[[17.] [Bjarne Stroustrup] [[@http://www.research.att.com/%7Ebs/whitespace98.pdf + Generalizing Overloading for C++2000] + Overload, Issue 25. April 1, 1998.]] +[[18.] [Dr. John Maddock] [[@http://www.boost.org/libs/regex/index.html + Regex++ Documentation] + http://www.boost.org/libs/regex/index.htm]] +[[19.] [Anonymous + Edited by Graham Hutton] [[@http://www.cs.nott.ac.uk/~gmh//faq.html + Frequently Asked Questions for comp.lang.functional]. + Edited by Graham Hutton, University of Nottingham.]] +[[20.] [Hewlett-Packard] [[@http://www.sgi.com/tech/stl/ + Standard Template Library Programmer's Guide.], Hewlett-Packard Company, 1994]] +[[21.] [Boost Libraries] [[@http://boost.org/libs/libraries.htm + Boost Libraries Documentation].]] +[[22.] [Brian McNamara and + Yannis Smaragdakis] [[@http://www.cc.gatech.edu/~yannis/fc++/ FC++:Functional Programming in C++].]] +[[23.] [Todd Veldhuizen] [[@ftp://ftp.cs.indiana.edu/pub/techreports/TR542.pdf Techniques for Scientic C++.]]] +] + +[endsect] diff --git a/doc/spirit2.qbk b/doc/spirit2.qbk new file mode 100644 index 000000000..0bf61af81 --- /dev/null +++ b/doc/spirit2.qbk @@ -0,0 +1,143 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[article Spirit + [quickbook 1.4] + [version 2.0] + [authors [de Guzman, Joel], [Kaiser, Hartmut]] + [copyright 2001 2002 2003 2004 2005 2006 2007 2008 Joel de Guzman, Hartmut Kaiser] + [purpose Parser and Generator Library] + [license + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + [@http://www.boost.org/LICENSE_1_0.txt]) + ] +] + +[/ November 14, 2007 ] + +[/ Some links ] + +[def __spirit__ [@http://spirit.sourceforge.net Spirit]] +[def __phoenix__ [@http://boost.org/libs/spirit/phoenix/index.html Phoenix]] +[def __phoenix2__ [@http://spirit.sourceforge.net/dl_more/phoenix_v2/libs/spirit/phoenix/doc/html/index.html Phoenix2]] +[def __fusion__ [@http://spirit.sourceforge.net/dl_more/fusion_v2/libs/fusion/doc/html/index.html Fusion]] +[def __mpl__ [@http://www.boost.org/libs/mpl/index.html MPL]] +[def __boost_tuples__ [@http://www.boost.org/libs/tuple/index.html Boost.Tuples]] +[def __boost_proto__ -Boost.Proto-] +[def __boost__ [@http://www.boost.org/ Boost]] +[def __boost_tools__ [@http://www.boost.org/tools/index.html Boost Tools]] +[def __spirit_list__ [@https://lists.sourceforge.net/lists/listinfo/spirit-general Spirit Mailing List]] +[def __spirit_general__ [@news://news.gmane.org/gmane.comp.spirit.general Spirit General NNTP news portal]] +[def __gmane__ [@http://www.gmane.org Gmane]] +[def __mlist_archive__ [@http://news.gmane.org/gmane.comp.parsers.spirit.general]] + +[def __early_spirit__ [@http://spirit.sourceforge.net/dl_docs/pre-spirit.htm pre-Spirit]] +[def __todd__exprtemplates__ [@http://ubiety.uwaterloo.ca/~tveldhui/papers/Expression-Templates/exprtmpl.html Expression Templates]] +[def __cpp_concepts__ [@http://en.wikipedia.org/wiki/C%2B%2B0x#Concept Concepts]] +[def __attr_grammar__ [@http://en.wikipedia.org/wiki/Attribute_grammar Attribute Grammar]] +[def __string_template__ [@http://www.stringtemplate.org/ StringTemplate]] +[def __lexertl__ [@http://www.benhanson.net/lexertl.html Lexertl]] +[def __wave__ [@http://www.boost.org/libs/wave/index.html Wave]] +[def __slex__ [@http://spirit.sourceforge.net/repository/applications/slex.zip SLex]] +[def __flex__ [@http://flex.sourceforge.net/ Flex]] +[def __re2c__ [@http://re2c.sourceforge.net/ re2c]] +[def __ragel__ [@http://www.cs.queensu.ca/~thurston/ragel/ Ragel]] + +[def __boost_variant__ [@http://www.boost.org/doc/html/variant.html `boost::variant<>`]] +[def __boost_iterator_range__ [@http://www.boost.org/libs/range/doc/utility_class.html#iter_range `boost::iterator_range<>`]] + + +[def __qi__ /Spirit.Qi/] +[def __karma__ /Spirit.Karma/] +[def __lex__ /Spirit.Lex/] + + +[def __fixme__ *FIXME*] + + +[/ Sections ] + +[def __sec_qi_and_karma__ [link spirit.qi_and_karma Qi and Karma]] +[def __sec_qi_karma_attributes__ [link spirit.qi_and_karma.abstracts.attributes Attributes]] + +[def __sec_lex__ [link spirit.__lex__ Lex]] +[def __sec_lex_quickstart_1__ [link spirit.__lex__.__lex___tutorials.quickstart_1___a_word_counter_using___lex__ Lex Quickstart 1 - A word counter using __lex__]] +[def __sec_lex_quickstart_2__ [link spirit.__lex__.__lex___tutorials.quickstart_2___a_better_word_counter_using___lex__ Lex Quickstart 2 - A better word counter using __lex__]] +[def __sec_lex_quickstart_3__ [link spirit.__lex__.__lex___tutorials.quickstart_3___counting_words_using_a_parser Lex Quickstart 3 - Counting Words Using a Parser]] + +[def __sec_lex_static_model__ [link spirit.__lex__.abstracts.the__static__lexer_model The /Static/ Model]] +[def __sec_lex_primitives__ [link spirit.__lex__.abstracts.lexer_primitives Lexer Primitives]] +[def __sec_lex_tokenvalues__ [link spirit.__lex__.abstracts.lexer_primitives.about_tokens_and_token_values About Tokens and Token Values]] +[def __sec_lex_attributes__ [link spirit.__lex__.abstracts.lexer_attributes Lexer Attributes]] + +[def __sec_ref_lex_token__ [link spirit.__lex__.reference.concepts.token Token Reference]] +[def __sec_ref_lex_token_def__ [link spirit.__lex__.reference.concepts.tokendef TokenDef Reference]] + +[/ References to API descriptions ] + +[def __api_tokenize_and_parse__ [link spirit.qi_and_karma.abstracts.parsing_and_generating.the_tokenize_and_phrase_parse___function `tokenize_and_parse()`]] +[def __api_generate_static__ [link spirit.__lex__.abstracts.tokenizing_input_data.the_generate_static___function `generate_static()`]] + + +[/ References to classes ] + +[def __class_token_def__ [link spirit.__lex__.reference.tokendef_class `token_def<>`]] + +[def __class_lexertl_token__ [link spirit.__lex__.reference.token_class `lexertl_token<>`]] +[def __class_lexertl_lexer__ [link spirit.__lex__.reference.lexer_class.the_lexertl_lexer_class_implementing_the_dynamic_model `lexertl_lexer<>`]] +[def __class_lexertl_static_lexer__ [link spirit.__lex__.reference.lexer_class.the_lexertl_static_lexer_class_implementing_the_static_model `lexertl_static_lexer<>`]] + + +[/ Some images ] + +[def __note__ [$../../../../doc/html/images/adm_note.png]] +[def __tip__ [$../../../../doc/html/images/adm_tip.png]] +[def __important__ [$../../../../doc/html/images/adm_important.png]] +[def __caution__ [$../../../../doc/html/images/adm_caution.png]] +[def __danger__ [$../../../../doc/html/images/adm_danger.png]] + + +[/ some templates] + +[/ fig[ref title label] + Image element with a title. + + ref := Reference to the image file. + title := The title to associate with this figure. + label := the id to use to be able to reference this picture +] +[template fig[ref title label]''' +
+ '''[title]''' + + + + + + '''[title]''' + + +
+'''] + + +[/ Here we go ] + +[include preface.qbk] +[include what_s_new.qbk] +[include introduction.qbk] +[include qi_and_karma.qbk] +[include lex.qbk] +[include faq.qbk] +[include notes.qbk] +[include rationale.qbk] +[include acknowledgments.qbk] +[include references.qbk] + + diff --git a/doc/what_s_new.qbk b/doc/what_s_new.qbk new file mode 100644 index 000000000..067dd736b --- /dev/null +++ b/doc/what_s_new.qbk @@ -0,0 +1,10 @@ +[/============================================================================== + Copyright (C) 2001-2008 Joel de Guzman + Copyright (C) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +===============================================================================/] + +[section What's New] +[endsect] diff --git a/example/karma/Jamfile b/example/karma/Jamfile new file mode 100644 index 000000000..6e45845d2 --- /dev/null +++ b/example/karma/Jamfile @@ -0,0 +1,12 @@ +#============================================================================== +# Copyright (c) 2001-2007 Joel de Guzman +# Copyright (c) 2001-2007 Hartmut Kaiser +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#============================================================================== +project spirit-karma-example ; + +exe basic_facilities : basic_facilities.cpp ; +exe functor_facilities : functor_facilities.cpp ; + diff --git a/example/karma/basic_facilities.cpp b/example/karma/basic_facilities.cpp new file mode 100644 index 000000000..98529d06c --- /dev/null +++ b/example/karma/basic_facilities.cpp @@ -0,0 +1,178 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// The main purpose of this example is to show the uniform and easy way of +// output formatting for different container types. +// +// Since the 'stream' primitive used below uses the streaming operator defined +// for the container value_type, you must make sure to have a corresponding +// operator<<() available for this contained data type. OTOH this means, that +// the format descriptions used below will be usable for any contained type as +// long as this type has an associated streaming operator defined. + +// use a larger value for the alignment field width (default is 10) +#define BOOST_KARMA_DEFAULT_FIELD_LENGTH 25 + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::ascii; +namespace karma = boost::spirit::karma; + +/////////////////////////////////////////////////////////////////////////////// +// Output the given containers in list format +// Note: the format description does not depend on the type of the sequence +// nor does it depend on the type of the elements contained in the +// sequence +/////////////////////////////////////////////////////////////////////////////// +template +void output_container(std::ostream& os, Container const& c) +{ + // output the container as a space separated sequence + os << + karma::format_delimited( + *stream, // format description + c, // data + space // delimiter + ) << std::endl << std::endl; + + os << + karma::format_delimited( + '[' << *stream << ']', // format description + c, // data + space // delimiter + ) << std::endl << std::endl; + + // output the container as a comma separated list + os << + karma::format( + stream % ", ", // format description + c // data + ) << std::endl << std::endl; + + os << + karma::format( + '[' << (stream % ", ") << ']', // format description + c // data + ) << std::endl << std::endl; + + // output the container as a comma separated list of items enclosed in '()' + os << + karma::format( + ('(' << stream << ')') % ", ", // format description + c // data + ) << std::endl << std::endl; + + os << + karma::format( + '[' << ( + ('(' << stream << ')') % ", " + ) << ']', // format description + c // data + ) << std::endl << std::endl; + + // output the container as a HTML list + os << + karma::format_delimited( + "
    " << + *verbatim["
  1. " << stream << "
  2. "] + << "
", // format description + c, // data + '\n' // delimiter + ) << std::endl; + + // output the container as right aligned column + os << + karma::format_delimited( + *verbatim[ + "|" << right_align[stream] << "|" + ], // format description + c, // data + '\n' // delimiter + ) << std::endl; + + os << std::endl; +} + +int main() +{ + /////////////////////////////////////////////////////////////////////////// + // vector + std::vector v (8); + std::generate(v.begin(), v.end(), std::rand); // randomly fill the vector + + std::cout << "-------------------------------------------------------------" + << std::endl; + std::cout << "std::vector" << std::endl; + output_container(std::cout, v); + + /////////////////////////////////////////////////////////////////////////// + // list + std::list l; + l.push_back('A'); + l.push_back('B'); + l.push_back('C'); + + std::cout << "-------------------------------------------------------------" + << std::endl; + std::cout << "std::list" << std::endl; + output_container(std::cout, l); + + /////////////////////////////////////////////////////////////////////////// + // C-style array + int i[4] = { 3, 6, 9, 12 }; + + std::cout << "-------------------------------------------------------------" + << std::endl; + std::cout << "int i[]" << std::endl; + output_container(std::cout, boost::make_iterator_range(i, i+4)); + + /////////////////////////////////////////////////////////////////////////// + // strings + std::string str("Hello world!"); + + std::cout << "-------------------------------------------------------------" + << std::endl; + std::cout << "std::string" << std::endl; + output_container(std::cout, str); + + /////////////////////////////////////////////////////////////////////////// + // vector of boost::date objects + // Note: any registered facets get used! + using namespace boost::gregorian; + std::vector dates; + dates.push_back(date(2005, Jun, 25)); + dates.push_back(date(2006, Jan, 13)); + dates.push_back(date(2007, May, 03)); + + date_facet* facet(new date_facet("%A %B %d, %Y")); + std::cout.imbue(std::locale(std::cout.getloc(), facet)); + + std::cout << "-------------------------------------------------------------" + << std::endl; + std::cout << "std::vector" << std::endl; + output_container(std::cout, dates); + + /////////////////////////////////////////////////////////////////////////// + // fusion tuples + // this will work in the future +// boost::fusion::vector fv(42, 'a', 45.8); +// +// std::cout << "boost::fusion::vector" << std::endl; +// output_container(std::cout, fv); + return 0; +} + diff --git a/example/karma/functor_facilities.cpp b/example/karma/functor_facilities.cpp new file mode 100644 index 000000000..523e1e852 --- /dev/null +++ b/example/karma/functor_facilities.cpp @@ -0,0 +1,202 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This examples demonstrate how to write functor based generators for special +// purposes. + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace boost::spirit; + +/////////////////////////////////////////////////////////////////////////////// +// The functor generator 'counter' can be used for output annotation with some +// item counting information. +/////////////////////////////////////////////////////////////////////////////// +struct counter_impl : boost::spirit::karma::functor_base +{ + template + bool operator()(Parameter const&, Context& ctx, OutputIterator& sink) const + { + namespace karma = boost::spirit::karma; + return karma::generate(sink, int_ << ": ", counter++); + } + + counter_impl(int& counter_) + : counter(counter_) {} + + int& counter; +}; + +inline boost::spirit::result_of::as_generator::type +counter(int& counter_) +{ + using namespace boost::spirit::karma; + return as_generator(counter_impl(counter_)); +} + +/////////////////////////////////////////////////////////////////////////////// +// The functor generator 'confix' allows a simple syntax for generating +// output wrapped inside a pair of a prefix and a suffix. +/////////////////////////////////////////////////////////////////////////////// +template +struct confix_impl : public boost::spirit::karma::functor_base +{ + template + struct apply + { + typedef boost::spirit::hold_any type; + }; + + template + bool operator()(Parameter const& v, Context& ctx, OutputIterator& sink) const + { + namespace karma = boost::spirit::karma; + return karma::generate(sink, open << xpr << close, v); + } + + confix_impl(char const* open_, char const* close_, Expr const& xpr_) + : open(open_), close(close_), xpr(xpr_) {} + + std::string open; + std::string close; + Expr xpr; +}; + +template +inline typename boost::spirit::result_of::as_generator >::type +confix(Expr const& xpr_, char const* open_ = "", char const* close_ = "") +{ + using namespace boost::spirit::karma; + return as_generator(confix_impl(open_, close_, xpr_)); +} + +/////////////////////////////////////////////////////////////////////////////// +// The functor generator 'list' allows a simple syntax for generating +// list formatted output. +// +// This example uses phoenix::bind to allow to omit the second argument from +// the operator() and to allow to switch the remaining two arguments. +/////////////////////////////////////////////////////////////////////////////// +template +struct list_impl : boost::spirit::karma::functor_base +{ + // this function will be called to generate the output + template + bool operator()(OutputIterator& sink, Parameter const& v) const + { + namespace karma = boost::spirit::karma; + return karma::generate(sink, xpr % delim, v); + } + + list_impl(Expr const& xpr_, char const* delim_) + : xpr(xpr_), delim(delim_) {} + + Expr xpr; + std::string delim; +}; + +// Supply the expected parameter type explicitly +struct list_impl_mf +{ + // the expected parameter type of a functor has to be defined using a + // embedded apply metafunction + template + struct apply + { + typedef boost::spirit::hold_any type; + }; +}; + +template +inline list_impl +list(Expr const& xpr, char const* delim) +{ + return list_impl(xpr, delim); +} + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + namespace karma = boost::spirit::karma; + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + + /////////////////////////////////////////////////////////////////////////// + // Output the given containers in list format + // We use a special functor generator here to annotate the output with + // a integer counting the entries. + /////////////////////////////////////////////////////////////////////////// + std::vector v (8); + std::generate(v.begin(), v.end(), std::rand); // randomly fill the vector + + int counter1 = 1; + std::cout << + karma::format( + (counter(counter1) << int_) % ", ", // format description + v // data + ) << std::endl; + + // Here we initialize the counter to 100 + int counter2 = 100; + std::cout << + karma::format( + '[' << ( + (counter(counter2) << int_) % ", " + ) << ']', // format description + v // data + ) << std::endl; + + /////////////////////////////////////////////////////////////////////////// + // list + // The output format description used below adds special item formatting + /////////////////////////////////////////////////////////////////////////// + std::list names; + names.push_back("Spirit"); + names.push_back("Qi"); + names.push_back("Karma"); + + // specifying a prefix item suffix scheme directly + std::cout << + karma::format( + ('{' << stream << '}') % ", ", // format description + names // data + ) << std::endl; + + // The confix generator nicely wraps the given expression with prefix and + // suffix strings + std::cout << + karma::format( + confix(stream % ", ", "[", "]"), // format description + names // data + ) << std::endl; + + /////////////////////////////////////////////////////////////////////////// + // Output the given container as a list + // We use a separate metafunction list_impl_mf to specify the expected + // parameter type of this functor generator. + // We use phoenix::bind to allow to omit the 2nd argument from the functor + // function operator and to change the sequence of the remaining two + // arguments. + /////////////////////////////////////////////////////////////////////////// + std::string str("Hello world!"); + std::cout << + karma::format( + karma::as_generator_mf(bind(list(stream, ", "), _3, _1)), + str + ) << std::endl; + + return 0; +} diff --git a/example/karma/quick_start1.cpp b/example/karma/quick_start1.cpp new file mode 100644 index 000000000..342892630 --- /dev/null +++ b/example/karma/quick_start1.cpp @@ -0,0 +1,119 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// The main purpose of this example is to show how a single container type can +// be formatted using different output grammars. + +#include +#include + +#include +#include +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::ascii; +namespace karma = boost::spirit::karma; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + /////////////////////////////////////////////////////////////////////////// + // vector + std::vector v (8); + std::generate(v.begin(), v.end(), std::rand); // randomly fill the vector + + std::cout << "Output 8 integers from a std::vector..." << std::endl; + + // output the container as a sequence without any separation + std::cout << "...without any separation" << std::endl; + std::cout << + karma::format( + *int_, // format description + v // data + ) << std::endl << std::endl; + + // output the container as a space separated sequence + std::cout << "...as space delited list" << std::endl; + std::cout << + karma::format_delimited( + *int_, // format description + v, // data + space // delimiter + ) << std::endl << std::endl; + + std::cout << + karma::format_delimited( + '[' << *int_ << ']', // format description + v, // data + space // delimiter + ) << std::endl << std::endl; + + // output the container as a comma separated list + std::cout << "...as comma separated list" << std::endl; + std::cout << + karma::format( + int_ % ", ", // format description + v // data + ) << std::endl << std::endl; + + std::cout << + karma::format( + '[' << (int_ % ", ") << ']', // format description + v // data + ) << std::endl << std::endl; + + // output the container as a comma separated list of double's + std::cout << "...as comma separated list of doubles" << std::endl; + std::cout << + karma::format( + double_ % ", ", // format description + v // data + ) << std::endl << std::endl; + + // output the container as a comma separated list of items enclosed in '()' + std::cout << "..as list of ints enclosed in '()'" << std::endl; + std::cout << + karma::format( + ('(' << int_ << ')') % ", ", // format description + v // data + ) << std::endl << std::endl; + + std::cout << + karma::format( + '[' << ( + ('(' << int_ << ')') % ", " + ) << ']', // format description + v // data + ) << std::endl << std::endl; + + // output the container as a HTML list + std::cout << "...as HTML bullet list" << std::endl; + std::cout << + karma::format_delimited( + "
    " << + // no delimiting within verbatim + *verbatim["
  1. " << int_ << "
  2. "] + << "
", // format description + v, // data + '\n' // delimiter + ) << std::endl; + + // output the container as right aligned column + std::cout << "...right aligned in a column" << std::endl; + std::cout << + karma::format_delimited( + *verbatim[ + "|" << right_align[int_] << "|" + ], // format description + v, // data + '\n' // delimiter + ) << std::endl; + + std::cout << std::endl; + return 0; +} + diff --git a/example/lex/Jamfile b/example/lex/Jamfile new file mode 100644 index 000000000..b206c3753 --- /dev/null +++ b/example/lex/Jamfile @@ -0,0 +1,22 @@ +#============================================================================== +# Copyright (c) 2001-2007 Joel de Guzman +# Copyright (c) 2001-2008 Hartmut Kaiser +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#============================================================================== + +project spirit-lexer-example ; + +exe example1 : example1.cpp ; +exe example2 : example2.cpp ; +exe example3 : example3.cpp ; +exe example4 : example4.cpp ; +exe example5 : example5.cpp ; +exe example6 : example6.cpp ; +exe print_numbers : print_numbers.cpp ; +exe word_count : word_count.cpp ; +exe word_count_functor : word_count_functor.cpp ; +exe word_count_lexer : word_count_lexer.cpp ; +exe strip_comments : strip_comments.cpp ; + diff --git a/example/lex/example.hpp b/example/lex/example.hpp new file mode 100644 index 000000000..d5a772787 --- /dev/null +++ b/example/lex/example.hpp @@ -0,0 +1,26 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// Copyright (c) 2001-2007 Joel de Guzman +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////////// +// Helper function reading a file into a string +/////////////////////////////////////////////////////////////////////////////// +inline std::string +read_from_file(char const* infile) +{ + std::ifstream instream(infile); + if (!instream.is_open()) { + std::cerr << "Couldn't open file: " << infile << std::endl; + exit(-1); + } + instream.unsetf(std::ios::skipws); // No white space skipping! + return std::string(std::istreambuf_iterator(instream.rdbuf()), + std::istreambuf_iterator()); +} + diff --git a/example/lex/example1.cpp b/example/lex/example1.cpp new file mode 100644 index 000000000..1eda85b04 --- /dev/null +++ b/example/lex/example1.cpp @@ -0,0 +1,136 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// Copyright (c) 2001-2007 Joel de Guzman +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// Simple lexer/parser to test the Spirit installation. +// +// This example shows, how to create a simple lexer recognizing 4 different +// tokens, and how to use a single token definition as the skip parser during +// the parsing. Additionally it demonstrates how to use one of the defined +// tokens as a parser component in the grammar. +// +// The grammar recognizes a simple input structure, for instance: +// +// { +// hello world, hello it is me +// } +// +// Any number of simple sentences (optionally comma separated) inside a pair +// of curly braces will be matched. + +#include +#include + +#include +#include +#include + +#include "example.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::lex; + +/////////////////////////////////////////////////////////////////////////////// +// Token definition +/////////////////////////////////////////////////////////////////////////////// +template +struct example1_tokens : lexer_def +{ + template + void def (Self& self) + { + // define tokens and associate them with the lexer + identifier = "[a-zA-Z_][a-zA-Z0-9_]*"; + self = token_def<>(',') | '{' | '}' | identifier; + + // any token definition to be used as the skip parser during parsing + // has to be associated with a separate lexer state (here 'WS') + white_space = "[ \\t\\n]+"; + self("WS") = white_space; + } + + token_def<> identifier, white_space; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Grammar definition +/////////////////////////////////////////////////////////////////////////////// +template +struct example1_grammar + : grammar_def > > +{ + template + example1_grammar(TokenDef const& tok) + { + start = '{' >> *(tok.identifier >> -char_(',')) >> '}'; + } + + rule > > start; +}; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + // iterator type used to expose the underlying input stream + typedef std::string::iterator base_iterator_type; + + // This is the token type to return from the lexer iterator + typedef lexertl_token token_type; + + // This is the lexer type to use to tokenize the input. + // We use the lexertl based lexer engine. + typedef lexertl_lexer lexer_type; + + // This is the token definition type (derived from the given lexer type). + typedef example1_tokens example1_tokens; + + // This is the iterator type exposed by the lexer + typedef lexer::iterator_type iterator_type; + + // This is the type of the grammar to parse + typedef example1_grammar example1_grammar; + + // now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process + example1_tokens tokens; // Our token definition + example1_grammar def (tokens); // Our grammar definition + + lexer lex(tokens); // Our lexer + grammar calc(def); // Our parser + + std::string str (read_from_file("example1.input")); + + // At this point we generate the iterator pair used to expose the + // tokenized input stream. + std::string::iterator it = str.begin(); + iterator_type iter = lex.begin(it, str.end()); + iterator_type end = lex.end(); + + // Parsing is done based on the the token stream, not the character + // stream read from the input. + // Note, how we use the token_def defined above as the skip parser. It must + // be explicitly wrapped inside a state directive, switching the lexer + // state for the duration of skipping whitespace. + bool r = phrase_parse(iter, end, calc, in_state("WS")[tokens.white_space]); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + } + else + { + std::string rest(iter, end); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \"" << rest << "\"\n"; + std::cout << "-------------------------\n"; + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} diff --git a/example/lex/example2.cpp b/example/lex/example2.cpp new file mode 100644 index 000000000..328fcd323 --- /dev/null +++ b/example/lex/example2.cpp @@ -0,0 +1,169 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// Copyright (c) 2001-2007 Joel de Guzman +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example shows how to create a simple lexer recognizing a couple of +// different tokens and how to use this with a grammar. This example has a +// heavily backtracking grammar which makes it a candidate for lexer based +// parsing (all tokens are scanned and generated only once, even if +// backtracking is required) which speeds up the overall parsing process +// considerably, out-weighting the overhead needed for setting up the lexer. +// Additionally it demonstrates how to use one of the defined tokens as a +// parser component in the grammar. +// +// The grammar recognizes a simple input structure: any number of English +// simple sentences (statements, questions and commands) are recognized and +// are being counted separately. + +// #define BOOST_SPIRIT_DEBUG +// #define BOOST_SPIRIT_LEXERTL_DEBUG + +#include +#include +#include + +#include +#include +#include + +#include "example.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::lex; +using boost::phoenix::ref; + +/////////////////////////////////////////////////////////////////////////////// +// Token definition +/////////////////////////////////////////////////////////////////////////////// +template +struct example2_tokens : lexer_def +{ + template + void def (Self& self) + { + // A 'word' is comprised of one or more letters and an optional + // apostrophe. If it contains an apostrophe, there may only be one and + // the apostrophe must be preceded and succeeded by at least 1 letter. + // For example, "I'm" and "doesn't" meet the definition of 'word' we + // define below. + word = "[a-zA-Z]+('[a-zA-Z]+)?"; + + // associate the tokens and the token set with the lexer + self = token_def<>(',') | '!' | '.' | '?' | ' ' | '\n' | word; + } + + token_def<> word; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Grammar definition +/////////////////////////////////////////////////////////////////////////////// +template +struct example2_grammar : grammar_def +{ + template + example2_grammar(TokenDef const& tok) + : paragraphs(0), commands(0), questions(0), statements(0) + { + story + = +paragraph + ; + + paragraph + = ( +( command [ ++ref(commands) ] + | question [ ++ref(questions) ] + | statement [ ++ref(statements) ] + ) + >> *char_(' ') >> +char_('\n') + ) + [ ++ref(paragraphs) ] + ; + + command + = +(tok.word | ' ' | ',') >> '!' + ; + + question + = +(tok.word | ' ' | ',') >> '?' + ; + + statement + = +(tok.word | ' ' | ',') >> '.' + ; + + BOOST_SPIRIT_DEBUG_NODE(story); + BOOST_SPIRIT_DEBUG_NODE(paragraph); + BOOST_SPIRIT_DEBUG_NODE(command); + BOOST_SPIRIT_DEBUG_NODE(question); + BOOST_SPIRIT_DEBUG_NODE(statement); + } + + rule story, paragraph, command, question, statement; + int paragraphs, commands, questions, statements; +}; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + // iterator type used to expose the underlying input stream + typedef std::string::iterator base_iterator_type; + + // This is the token type to return from the lexer iterator + typedef lexertl_token token_type; + + // This is the lexer type to use to tokenize the input. + // Here we use the lexertl based lexer engine. + typedef lexertl_lexer lexer_type; + + // This is the token definition type (derived from the given lexer type). + typedef example2_tokens example2_tokens; + + // this is the iterator type exposed by the lexer + typedef lexer::iterator_type iterator_type; + + // this is the type of the grammar to parse + typedef example2_grammar example2_grammar; + + // now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process + example2_tokens tokens; // Our token definition + example2_grammar def (tokens); // Our grammar definition + + lexer lex(tokens); // Our lexer + grammar calc(def, def.story); // Our grammar + + std::string str (read_from_file("example2.input")); + + // At this point we generate the iterator pair used to expose the + // tokenized input stream. + std::string::iterator it = str.begin(); + iterator_type iter = lex.begin(it, str.end()); + iterator_type end = lex.end(); + + // Parsing is done based on the the token stream, not the character + // stream read from the input. + bool r = parse(iter, end, calc); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "There were " + << def.commands << " commands, " + << def.questions << " questions, and " + << def.statements << " statements.\n"; + std::cout << "-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} diff --git a/example/lex/example3.cpp b/example/lex/example3.cpp new file mode 100644 index 000000000..e8a72d134 --- /dev/null +++ b/example/lex/example3.cpp @@ -0,0 +1,161 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// Copyright (c) 2001-2007 Joel de Guzman +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example shows how to create a simple lexer recognizing a couple of +// different tokens and how to use this with a grammar. This example has a +// heavily backtracking grammar which makes it a candidate for lexer based +// parsing (all tokens are scanned and generated only once, even if +// backtracking is required) which speeds up the overall parsing process +// considerably, out-weighting the overhead needed for setting up the lexer. +// +// Additionally, this example demonstrates, how to define a token set usable +// as the skip parser during parsing, allowing to define several tokens to be +// ignored. +// +// This example recognizes couplets, which are sequences of numbers enclosed +// in matching pairs of parenthesis. See the comments below to for details +// and examples. + +// #define BOOST_SPIRIT_LEXERTL_DEBUG +// #define BOOST_SPIRIT_DEBUG + +#include +#include + +#include +#include +#include + +#include "example.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::lex; + +/////////////////////////////////////////////////////////////////////////////// +// Token definition +/////////////////////////////////////////////////////////////////////////////// +template +struct example3_tokens : lexer_def +{ + typedef typename Lexer::token_set token_set; + + template + void def (Self& self) + { + // define the tokens to match + ellipses = "\\.\\.\\."; + number = "[0-9]+"; + + // define the whitespace to ignore (spaces, tabs, newlines and C-style + // comments) + white_space + = token_def<>("[ \\t\\n]+") // whitespace + | "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/" // C style comments + ; + + // associate the tokens and the token set with the lexer + self = ellipses | '(' | ')' | number; + self("WS") = white_space; + } + + // these tokens expose the iterator_range of the matched input sequence + token_def<> ellipses, identifier, number; + token_set white_space; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Grammar definition +/////////////////////////////////////////////////////////////////////////////// +template +struct example3_grammar + : grammar_def > +{ + template + example3_grammar(TokenDef const& tok) + { + start + = +(couplet | tok.ellipses) + ; + + // A couplet matches nested left and right parenthesis. + // For example: + // (1) (1 2) (1 2 3) ... + // ((1)) ((1 2)(3 4)) (((1) (2 3) (1 2 (3) 4))) ... + // (((1))) ... + couplet + = tok.number + | '(' >> +couplet >> ')' + ; + + BOOST_SPIRIT_DEBUG_NODE(start); + BOOST_SPIRIT_DEBUG_NODE(couplet); + } + + typedef typename Lexer::token_set token_set; + rule > start, couplet; +}; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + // iterator type used to expose the underlying input stream + typedef std::string::iterator base_iterator_type; + + // This is the token type to return from the lexer iterator + typedef lexertl_token token_type; + + // This is the lexer type to use to tokenize the input. + // Here we use the lexertl based lexer engine. + typedef lexertl_lexer lexer_type; + + // This is the token definition type (derived from the given lexer type). + typedef example3_tokens example3_tokens; + + // this is the iterator type exposed by the lexer + typedef lexer::iterator_type iterator_type; + + // this is the type of the grammar to parse + typedef example3_grammar example3_grammar; + + // now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process + example3_tokens tokens; // Our token definition + example3_grammar def (tokens); // Our grammar definition + + lexer lex(tokens); // Our lexer + grammar calc(def); // Our grammar + + std::string str (read_from_file("example3.input")); + + // At this point we generate the iterator pair used to expose the + // tokenized input stream. + std::string::iterator it = str.begin(); + iterator_type iter = lex.begin(it, str.end()); + iterator_type end = lex.end(); + + // Parsing is done based on the the token stream, not the character + // stream read from the input. + // Note, how we use the token_set defined above as the skip parser. + std::string ws("WS"); + bool r = phrase_parse(iter, end, calc, in_state(ws)[tokens.white_space]); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} diff --git a/example/lex/example4.cpp b/example/lex/example4.cpp new file mode 100644 index 000000000..0f91989a0 --- /dev/null +++ b/example/lex/example4.cpp @@ -0,0 +1,239 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// Copyright (c) 2001-2007 Joel de Guzman +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example shows how to create a simple lexer recognizing a couple of +// different tokens aimed at a simple language and how to use this lexer with +// a grammar. It shows how to associate values to tokens and how to access the +// token values from inside the grammar. +// +// We use explicit token value types, making the corresponding token instances +// carry convert the matched input into an instance of that type. The token +// value is exposed as the parser attribute if this token is used as a +// parser component somewhere in a grammar. +// +// Additionally, this example demonstrates, how to define a token set usable +// as the skip parser during parsing, allowing to define several tokens to be +// ignored. +// +// This example recognizes a very simple programming language having +// assignment statements and if and while control structures. Look at the file +// example4.input for an example. + +#include +#include +#include + +#include +#include +#include + +#include "example.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::lex; +using namespace boost::spirit::arg_names; + +using boost::phoenix::val; + +/////////////////////////////////////////////////////////////////////////////// +// Token definition +/////////////////////////////////////////////////////////////////////////////// +template +struct example4_tokens : lexer_def +{ + typedef typename Lexer::token_set token_set; + + template + void def (Self& self) + { + // define the tokens to match + identifier = "[a-zA-Z_][a-zA-Z0-9_]*"; + constant = "[0-9]+"; + if_ = "if"; + else_ = "else"; + while_ = "while"; + + // define the whitespace to ignore (spaces, tabs, newlines and C-style + // comments) + white_space + = token_def<>("[ \\t\\n]+") + | "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/" + ; + + // associate the tokens and the token set with the lexer + self = token_def<>('(') | ')' | '{' | '}' | '=' | ';' | constant; + self += if_ | else_ | while_ | identifier; + self("WS") = white_space; + } + +//[example4_token_def + // these tokens expose the iterator_range of the matched input sequence + token_def<> if_, else_, while_; + + // The following two tokens have an associated value type, 'identifier' + // carries a string (the identifier name) and 'constant' carries the + // matched integer value. + // + // Note: any token value type specified explicitly during a token_def<> + // declaration needs to be listed during token type definition as + // well (see the typedef for the token_type below). + // + // The conversion of the matched input to an instance of this type occurs + // once (on first access), which makes token values as efficient as + // possible. Moreover, token instances are constructed once by the lexer + // library. From this point on tokens are passed by reference only, + // avoiding tokens being copied around. + token_def identifier; + token_def constant; +//] + + // token set to be used as the skip parser + token_set white_space; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Grammar definition +/////////////////////////////////////////////////////////////////////////////// +template +struct example4_grammar + : grammar_def > +{ + template + example4_grammar(TokenDef const& tok) + { + program + = +block + ; + + block + = '{' >> *statement >> '}' + ; + + statement + = assignment + | if_stmt + | while_stmt + ; + + assignment + = (tok.identifier >> '=' >> expression >> ';') + [ + std::cout << val("assignment statement to: ") << _1 << "\n" + ] + ; + + if_stmt + = ( tok.if_ >> '(' >> expression >> ')' >> block + >> -(tok.else_ >> block) + ) + [ + std::cout << val("if expression: ") << _2 << "\n" + ] + ; + + while_stmt + = (tok.while_ >> '(' >> expression >> ')' >> block) + [ + std::cout << val("while expression: ") << _2 << "\n" + ] + ; + + // since expression has a variant return type accommodating for + // std::string and unsigned integer, both possible values may be + // returned to the calling rule + expression + = tok.identifier [ _val = _1 ] + | tok.constant [ _val = _1 ] + ; + } + + typedef typename Lexer::token_set token_set; + typedef boost::variant expression_type; + + rule > program, block, statement; + rule > assignment, if_stmt; + rule > while_stmt; + + // the expression is the only rule having a return value + rule > expression; +}; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + // iterator type used to expose the underlying input stream + typedef std::string::iterator base_iterator_type; + +//[example4_token + // This is the lexer token type to use. The second template parameter lists + // all attribute types used for token_def's during token definition (see + // calculator_tokens<> above). Here we use the predefined lexertl token + // type, but any compatible token type may be used instead. + // + // If you don't list any token value types in the following declaration + // (or just use the default token type: lexertl_token) + // it will compile and work just fine, just a bit less efficient. This is + // because the token value will be generated from the matched input + // sequence every time it is requested. But as soon as you specify at + // least one token value type you'll have to list all value types used + // for token_def<> declarations in the token definition class above, + // otherwise compilation errors will occur. + typedef lexertl_token< + base_iterator_type, boost::mpl::vector + > token_type; +//] + // Here we use the lexertl based lexer engine. + typedef lexertl_lexer lexer_type; + + // This is the token definition type (derived from the given lexer type). + typedef example4_tokens example4_tokens; + + // this is the iterator type exposed by the lexer + typedef lexer::iterator_type iterator_type; + + // this is the type of the grammar to parse + typedef example4_grammar example4_grammar; + + // now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process + example4_tokens tokens; // Our token definition + example4_grammar def (tokens); // Our grammar definition + + lexer lex(tokens); // Our lexer + grammar calc(def, def.program); // Our grammar + + std::string str (read_from_file("example4.input")); + + // At this point we generate the iterator pair used to expose the + // tokenized input stream. + std::string::iterator it = str.begin(); + iterator_type iter = lex.begin(it, str.end()); + iterator_type end = lex.end(); + + // Parsing is done based on the the token stream, not the character + // stream read from the input. + // Note, how we use the token_set defined above as the skip parser. It must + // be explicitly wrapped inside a state directive, switching the lexer + // state for the duration of skipping whitespace. + bool r = phrase_parse(iter, end, calc, in_state("WS")[tokens.white_space]); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} diff --git a/example/lex/example5.cpp b/example/lex/example5.cpp new file mode 100644 index 000000000..a5390c5bc --- /dev/null +++ b/example/lex/example5.cpp @@ -0,0 +1,283 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// Copyright (c) 2001-2007 Joel de Guzman +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example shows how to create a simple lexer recognizing a couple of +// different tokens aimed at a simple language and how to use this lexer with +// a grammar. It shows how to associate values to tokens and how to access the +// token values from inside the grammar. +// +// Additionally, this example demonstrates, how to define a token set usable +// as the skip parser during parsing, allowing to define several tokens to be +// ignored. +// +// The main purpose of this example is to show, how inheritance can be used to +// overload parts of a base grammar and add token definitions to a base lexer. +// +// Further, it shows how you can use the 'omitted' attribute type specifier +// for token definitions to force the token to have no attribute (expose an +// unused attribute). +// +// This example recognizes a very simple programming language having +// assignment statements and if and while control structures. Look at the file +// example5.input for an example. + +#include +#include +#include + +#include +#include +#include + +#include "example.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::lex; +using namespace boost::spirit::arg_names; + +using boost::phoenix::val; + +/////////////////////////////////////////////////////////////////////////////// +// Token definition base, defines all tokens for the base grammar below +/////////////////////////////////////////////////////////////////////////////// +template +struct example5_base_tokens : lexer_def +{ + typedef typename Lexer::token_set token_set; + + template + void def (Self& self) + { + // define the tokens to match + identifier = "[a-zA-Z_][a-zA-Z0-9_]*"; + constant = "[0-9]+"; + if_ = "if"; + while_ = "while"; + + // define the whitespace to ignore (spaces, tabs, newlines and C-style + // comments) + white_space + = token_def<>("[ \\t\\n]+") + | "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/" + ; + + // associate the tokens and the token set with the lexer + self += token_def<>('(') | ')' | '{' | '}' | '=' | ';' | constant; + self += if_ | while_ | identifier; + self("WS") = white_space; + } + + // these tokens have no value + token_def if_, while_; + + // The following two tokens have an associated value type, identifier + // carries a string (the identifier name) and constant carries the matched + // integer value. + // + // Note: any explicitly token value type specified during a token_def<> + // declaration needs to be listed during token type definition as + // well (see the typedef for the token_type below). + // + // The conversion of the matched input to an instance of this type occurs + // once (on first access), which makes token values as efficient as + // possible. Moreover, token instances are constructed once by the lexer + // library. From this point on tokens are passed by reference only, + // avoiding tokens being copied around. + token_def identifier; + token_def constant; + + // token set to be used as the skip parser + token_set white_space; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Grammar definition base, defines a basic language +/////////////////////////////////////////////////////////////////////////////// +template +struct example5_base_grammar + : grammar_def > +{ + template + example5_base_grammar(TokenDef const& tok) + { + program + = +block + ; + + block + = '{' >> *statement >> '}' + ; + + statement + = assignment + | if_stmt + | while_stmt + ; + + assignment + = (tok.identifier >> '=' >> expression >> ';') + [ + std::cout << val("assignment statement to: ") << _1 << "\n" + ] + ; + + if_stmt + = (tok.if_ >> '(' >> expression >> ')' >> block) + [ + std::cout << val("if expression: ") << _1 << "\n" + ] + ; + + while_stmt + = (tok.while_ >> '(' >> expression >> ')' >> block) + [ + std::cout << val("while expression: ") << _1 << "\n" + ] + ; + + // since expression has a variant return type accommodating for + // std::string and unsigned integer, both possible values may be + // returned to the calling rule + expression + = tok.identifier [ _val = _1 ] + | tok.constant [ _val = _1 ] + ; + } + + typedef + grammar_def > + base_type; + typedef typename base_type::skipper_type skipper_type; + + rule program, block, statement; + rule assignment, if_stmt; + rule while_stmt; + + // the expression is the only rule having a return value + typedef boost::variant expression_type; + rule expression; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Token definition for derived lexer, defines additional tokens +/////////////////////////////////////////////////////////////////////////////// +template +struct example5_tokens : example5_base_tokens +{ + typedef typename Lexer::token_set token_set; + + template + void def (Self& self) + { + // define the additional token to match + else_ = "else"; + + // associate the new token with the lexer, note we add 'else' before + // anything else to add it to the token set before the identifier + // token, otherwise "else" would be matched as an identifier + self = else_; + + // call the base class definition function + example5_base_tokens::def(self); + } + + // this token has no value + token_def else_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Derived grammar definition, defines a language extension +/////////////////////////////////////////////////////////////////////////////// +template +struct example5_grammar : example5_base_grammar +{ + template + example5_grammar(TokenDef const& tok) + : example5_base_grammar(tok) + { + // we alter the if_stmt only + this->if_stmt + = this->if_stmt.copy() >> -(tok.else_ >> this->block) + ; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + // iterator type used to expose the underlying input stream + typedef std::string::iterator base_iterator_type; + + // This is the lexer token type to use. The second template parameter lists + // all attribute types used for token_def's during token definition (see + // calculator_tokens<> above). Here we use the predefined lexertl token + // type, but any compatible token type may be used instead. + // + // If you don't list any token value types in the following declaration + // (or just use the default token type: lexertl_token) + // it will compile and work just fine, just a bit less efficient. This is + // because the token value will be generated from the matched input + // sequence every time it is requested. But as soon as you specify at + // least one token value type you'll have to list all value types used + // for token_def<> declarations in the token definition class above, + // otherwise compilation errors will occur. + typedef lexertl_token< + base_iterator_type, boost::mpl::vector + > token_type; + + // Here we use the lexertl based lexer engine. + typedef lexertl_lexer lexer_type; + + // This is the token definition type (derived from the given lexer type). + typedef example5_tokens example5_tokens; + + // this is the iterator type exposed by the lexer + typedef lexer::iterator_type iterator_type; + + // this is the type of the grammar to parse + typedef example5_grammar example5_grammar; + + // now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process + example5_tokens tokens; // Our token definition + example5_grammar def (tokens); // Our grammar definition + + lexer lex(tokens); // Our lexer + grammar calc(def, def.program); // Our grammar + + std::string str (read_from_file("example5.input")); + + // At this point we generate the iterator pair used to expose the + // tokenized input stream. + std::string::iterator it = str.begin(); + iterator_type iter = lex.begin(it, str.end()); + iterator_type end = lex.end(); + + // Parsing is done based on the the token stream, not the character + // stream read from the input. + // Note, how we use the token_set defined above as the skip parser. It must + // be explicitly wrapped inside a state directive, switching the lexer + // state for the duration of skipping whitespace. + std::string ws("WS"); + bool r = phrase_parse(iter, end, calc, in_state(ws)[tokens.white_space]); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} diff --git a/example/lex/example6.cpp b/example/lex/example6.cpp new file mode 100644 index 000000000..9a88af0fd --- /dev/null +++ b/example/lex/example6.cpp @@ -0,0 +1,263 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// Copyright (c) 2001-2007 Joel de Guzman +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example shows how to create a simple lexer recognizing a couple of +// different tokens aimed at a simple language and how to use this lexer with +// a grammar. It shows how to associate values to tokens and how to access the +// token values from inside the grammar. +// +// Additionally, this example demonstrates, how to define a token set usable +// as the skip parser during parsing, allowing to define several tokens to be +// ignored. +// +// The example demonstrates how to use the add(...)(...) syntax to associate +// token definitions with the lexer and how token ids can be used in the +// parser to refer to a token, without having to directly reference its +// definition. +// +// This example recognizes a very simple programming language having +// assignment statements and if and while control structures. Look at the file +// example6.input for an example. +// +// This example is essentially identical to example4.cpp. The only difference +// is that we use the self.add() syntax to define tokens and to associate them +// with the lexer. + +#include +#include +#include + +#include +#include +#include + +#include "example.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::lex; +using namespace boost::spirit::arg_names; + +using boost::phoenix::val; + +/////////////////////////////////////////////////////////////////////////////// +// Token id definitions +/////////////////////////////////////////////////////////////////////////////// +enum token_ids +{ + ID_CONSTANT = 1000, + ID_IF, + ID_ELSE, + ID_WHILE, + ID_IDENTIFIER +}; + +/////////////////////////////////////////////////////////////////////////////// +// Token definitions +/////////////////////////////////////////////////////////////////////////////// +template +struct example6_tokens : lexer_def +{ + typedef typename Lexer::token_set token_set; + + template + void def (Self& self) + { + // define the tokens to match + identifier = "[a-zA-Z_][a-zA-Z0-9_]*"; + constant = "[0-9]+"; + + // define the whitespace to ignore (spaces, tabs, newlines and C-style + // comments) + white_space + = token_def<>("[ \\t\\n]+") + | "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/" + ; + + // associate the tokens and the token set with the lexer + self = token_def<>('(') | ')' | '{' | '}' | '=' | ';'; + + // Token definitions can be added by using some special syntactic + // construct as shown below. + // Note, that the token definitions added this way expose the iterator + // pair pointing to the matched input stream as their attribute. + self.add + (constant, ID_CONSTANT) + ("if", ID_IF) + ("else", ID_ELSE) + ("while", ID_WHILE) + (identifier, ID_IDENTIFIER) + ; + + // add whitespace tokens to another lexer state (here: "WS") + self("WS") = white_space; + } + + // The following two tokens have an associated value type, identifier + // carries a string (the identifier name) and constant carries the matched + // integer value. + // + // Note: any explicitly token value type specified during a token_def<> + // declaration needs to be listed during token type definition as + // well (see the typedef for the token_type below). + // + // The conversion of the matched input to an instance of this type occurs + // once (on first access), which makes token values as efficient as + // possible. Moreover, token instances are constructed once by the lexer + // library. From this point on tokens are passed by reference only, + // avoiding tokens being copied around. + token_def identifier; + token_def constant; + + // token set to be used as the skip parser + token_set white_space; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Grammar definition +/////////////////////////////////////////////////////////////////////////////// +template +struct example6_grammar + : grammar_def > +{ + template + example6_grammar(TokenDef const& tok) + { + program + = +block + ; + + block + = '{' >> *statement >> '}' + ; + + statement + = assignment + | if_stmt + | while_stmt + ; + + assignment + = (tok.identifier >> '=' >> expression >> ';') + [ + std::cout << val("assignment statement to: ") + << _1 << "\n" + ] + ; + + if_stmt + = ( token(ID_IF) >> '(' >> expression >> ')' >> block + >> -(token(ID_ELSE) >> block) + ) + [ + std::cout << val("if expression: ") + << _2 << "\n" + ] + ; + + while_stmt + = (token(ID_WHILE) >> '(' >> expression >> ')' >> block) + [ + std::cout << val("while expression: ") + << _2 << "\n" + ] + ; + + // since expression has a variant return type accommodating for + // std::string and unsigned integer, both possible values may be + // returned to the calling rule + expression + = tok.identifier [ _val = _1 ] + | tok.constant [ _val = _1 ] + ; + } + + typedef typename Lexer::token_set token_set; + typedef boost::variant expression_type; + + rule > program, block, statement; + rule > assignment, if_stmt; + rule > while_stmt; + + // the expression is the only rule having a return value + rule > expression; +}; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + // iterator type used to expose the underlying input stream + typedef std::string::iterator base_iterator_type; + + // This is the lexer token type to use. The second template parameter lists + // all attribute types used for token_def's during token definition (see + // calculator_tokens<> above). Here we use the predefined lexertl token + // type, but any compatible token type may be used instead. + // + // If you don't list any token value types in the following declaration + // (or just use the default token type: lexertl_token) + // it will compile and work just fine, just a bit less efficient. This is + // because the token value will be generated from the matched input + // sequence every time it is requested. But as soon as you specify at + // least one token value type you'll have to list all value types used + // for token_def<> declarations in the token definition class above, + // otherwise compilation errors will occur. + typedef lexertl_token< + base_iterator_type, boost::mpl::vector + > token_type; + + // Here we use the lexertl based lexer engine. + typedef lexertl_lexer lexer_type; + + // This is the token definition type (derived from the given lexer type). + typedef example6_tokens example6_tokens; + + // this is the iterator type exposed by the lexer + typedef lexer::iterator_type iterator_type; + + // this is the type of the grammar to parse + typedef example6_grammar example6_grammar; + + // now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process + example6_tokens tokens; // Our token definition + example6_grammar def (tokens); // Our grammar definition + + lexer lex(tokens); // Our lexer + grammar calc(def, def.program); // Our grammar + + std::string str (read_from_file("example6.input")); + + // At this point we generate the iterator pair used to expose the + // tokenized input stream. + std::string::iterator it = str.begin(); + iterator_type iter = lex.begin(it, str.end()); + iterator_type end = lex.end(); + + // Parsing is done based on the the token stream, not the character + // stream read from the input. + // Note, how we use the token_def defined above as the skip parser. It must + // be explicitly wrapped inside a state directive, switching the lexer + // state for the duration of skipping whitespace. + std::string ws("WS"); + bool r = phrase_parse(iter, end, calc, in_state(ws)[tokens.white_space]); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} diff --git a/example/lex/print_numbers.cpp b/example/lex/print_numbers.cpp new file mode 100644 index 000000000..8d3a3e48d --- /dev/null +++ b/example/lex/print_numbers.cpp @@ -0,0 +1,118 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example is the equivalent to the following lex program: +// +// %{ +// #include +// %} +// %% +// [0-9]+ { printf("%s\n", yytext); } +// .|\n ; +// %% +// main() +// { +// yylex(); +// } +// +// Its purpose is to print all the (integer) numbers found in a file + +#include +#include +#include + +#include +#include + +#include "example.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::lex; +using namespace boost::spirit::arg_names; + +/////////////////////////////////////////////////////////////////////////////// +// Token definition: We use the lexertl based lexer engine as the underlying +// lexer type. +/////////////////////////////////////////////////////////////////////////////// +template +struct print_numbers_tokens : lexer_def +{ + // define tokens and associate it with the lexer + template + void def (Self& self) + { + self = token_def("[0-9]*") | ".|\n"; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// Grammar definition +/////////////////////////////////////////////////////////////////////////////// +template +struct print_numbers_grammar : grammar_def +{ + print_numbers_grammar() + { + start = *( token(lex::min_token_id) [ std::cout << _1 << "\n" ] + | token(lex::min_token_id+1) + ) + ; + } + + rule start; +}; + +/////////////////////////////////////////////////////////////////////////////// +int main(int argc, char* argv[]) +{ + // iterator type used to expose the underlying input stream + typedef std::string::iterator base_iterator_type; + + // the token type to be used, 'int' is available as the type of the token + // value and no lexer state is supported + typedef lexertl_token< + base_iterator_type, boost::mpl::vector, boost::mpl::false_ + > token_type; + + // lexer type + typedef lexertl_lexer lexer_type; + + // iterator type exposed by the lexer + typedef + lexer_iterator >::type + iterator_type; + + // now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process + print_numbers_tokens print_tokens; // Our token definition + print_numbers_grammar def; // Our grammar definition + + // Parsing is done based on the the token stream, not the character + // stream read from the input. + std::string str (read_from_file(1 == argc ? "print_numbers.input" : argv[1])); + base_iterator_type first = str.begin(); + bool r = tokenize_and_parse(first, str.end(), make_lexer(print_tokens), + make_parser(def)); + + if (r) { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + } + else { + std::string rest(first, str.end()); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \"" << rest << "\"\n"; + std::cout << "-------------------------\n"; + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + + diff --git a/example/lex/static_lexer/Jamfile b/example/lex/static_lexer/Jamfile new file mode 100644 index 000000000..c9a35df49 --- /dev/null +++ b/example/lex/static_lexer/Jamfile @@ -0,0 +1,13 @@ +#============================================================================== +# Copyright (c) 2001-2007 Joel de Guzman +# Copyright (c) 2001-2008 Hartmut Kaiser +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#============================================================================== + +project spirit-static-lexer-example ; + +exe generate_tables : generate_tables.cpp ; +exe word_count_static : word_count_static.cpp ; + diff --git a/example/lex/static_lexer/word_count_generate.cpp b/example/lex/static_lexer/word_count_generate.cpp new file mode 100644 index 000000000..889de06eb --- /dev/null +++ b/example/lex/static_lexer/word_count_generate.cpp @@ -0,0 +1,42 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// The purpose of this example is to show, how it is possible to use a lexer +// token definition for two purposes: +// +// . To generate C++ code implementing a static lexical analyzer allowing +// to recognize all defined tokens (this file) +// . To integrate the generated C++ lexer into the /Spirit/ framework. +// (see the file: word_count_static.cpp) + +// #define BOOST_SPIRIT_LEXERTL_DEBUG + +#include +#include + +#include + +#include "word_count_tokens.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::lex; + +/////////////////////////////////////////////////////////////////////////////// +//[wc_static_generate_main +int main(int argc, char* argv[]) +{ + // create the lexer object instance needed to invoke the generator + word_count_tokens > word_count; // the token definition + + // open the output file, where the generated tokenizer function will be + // written to + std::ofstream out(argc < 2 ? "word_count_static.hpp" : argv[1]); + + // invoke the generator, passing the token definition, the output stream + // and the name prefix of the tokenizing function to be generated + char const* function_name = (argc < 3 ? "" : argv[2]); + return generate_static(make_lexer(word_count), out, function_name) ? 0 : -1; +} +//] diff --git a/example/lex/static_lexer/word_count_static.cpp b/example/lex/static_lexer/word_count_static.cpp new file mode 100644 index 000000000..d8fcc628f --- /dev/null +++ b/example/lex/static_lexer/word_count_static.cpp @@ -0,0 +1,118 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// The purpose of this example is to show, how it is possible to use a lexer +// token definition for two purposes: +// +// . To generate C++ code implementing a static lexical analyzer allowing +// to recognize all defined tokens +// . To integrate the generated C++ lexer into the /Spirit/ framework. +// + +// #define BOOST_SPIRIT_LEXERTL_DEBUG +#define BOOST_VARIANT_MINIMIZE_SIZE + +#include +//[wc_static_include +#include +//] +#include +#include +#include + +#include +#include + +#include "../example.hpp" +#include "word_count_tokens.hpp" // token definition + +#include "word_count_static.hpp" // generated tokenizer + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::lex; + +/////////////////////////////////////////////////////////////////////////////// +// Grammar definition +/////////////////////////////////////////////////////////////////////////////// +//[wc_static_grammar +// This is an ordinary grammar definition following the rules defined by +// Spirit.Qi. There is nothing specific about it, except it gets the token +// definition class instance passed to the constructor to allow accessing the +// embedded token_def<> instances. +template +struct word_count_grammar : grammar_def +{ + template + word_count_grammar(TokenDef const& tok) + : c(0), w(0), l(0) + { + using boost::spirit::arg_names::_1; + using boost::phoenix::ref; + using boost::phoenix::size; + + // associate the defined tokens with the lexer, at the same time + // defining the actions to be executed + start = *( tok.word [++ref(w), ref(c) += size(_1)] + | char_('\n') [++ref(l), ++ref(c)] + | token(IDANY) [++ref(c)] + ) + ; + } + + std::size_t c, w, l; // counter for characters, words, and lines + rule start; +}; +//] + +/////////////////////////////////////////////////////////////////////////////// +//[wc_static_main +int main(int argc, char* argv[]) +{ + // Define the token type to be used: 'std::string' is available as the type + // of the token value. + typedef lexertl_token< + char const*, boost::mpl::vector + > token_type; + + // Define the lexer type to be used as the base class for our token + // definition. + // + // This is the only place where the code is different from an equivalent + // dynamic lexical analyzer. We use the `lexertl_static_lexer<>` instead of + // the `lexertl_lexer<>` as the base class for our token defintion type. + // + typedef lexertl_static_lexer lexer_type; + + // Define the iterator type exposed by the lexer. + typedef lexer_iterator >::type iterator_type; + + // Now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process. + word_count_tokens word_count; // Our token definition + word_count_grammar def (word_count); // Our grammar definition + + // Read in the file into memory. + std::string str (read_from_file(1 == argc ? "word_count.input" : argv[1])); + char const* first = str.c_str(); + char const* last = &first[str.size()]; + + // Parsing is done based on the the token stream, not the character + // stream read from the input. + bool r = tokenize_and_parse(first, last, make_lexer(word_count), + make_parser(def)); + + if (r) { // success + std::cout << "lines: " << def.l << ", words: " << def.w + << ", characters: " << def.c << "\n"; + } + else { + std::string rest(first, last); + std::cerr << "Parsing failed\n" << "stopped at: \"" + << rest << "\"\n"; + } + return 0; +} +//] diff --git a/example/lex/static_lexer/word_count_static.hpp b/example/lex/static_lexer/word_count_static.hpp new file mode 100644 index 000000000..6aa402565 --- /dev/null +++ b/example/lex/static_lexer/word_count_static.hpp @@ -0,0 +1,111 @@ +// Copyright (c) 2008 Ben Hanson +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file licence_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// Auto-generated by boost::lexer +#if !defined(BOOST_SPIRIT_LEXER_NEXT_TOKEN_Feb_13_2008_12_01_20) +#define BOOST_SPIRIT_LEXER_NEXT_TOKEN_Feb_13_2008_12_01_20 + +#include +#include + +// the generated table of state names and the tokenizer have to be +// defined in the boost::spirit::lex::static namespace +namespace boost { namespace spirit { namespace lex { namespace static_ { + +// this table defines the names of the lexer states +char const* const lexer_state_names[1] = +{ + "INITIAL", +}; + +template +std::size_t next_token (std::size_t &start_state_, Iterator const& start_, + Iterator &start_token_, Iterator const& end_) +{ + enum {end_state_index, id_index, state_index, bol_index, eol_index, + dead_state_index, dfa_offset}; + static const std::size_t npos = static_cast(~0); + static const std::size_t lookup_[256] = {8, 8, 8, 8, 8, 8, 8, 8, + 8, 7, 6, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 7, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8}; + static const std::size_t dfa_alphabet_ = 9; + static const std::size_t dfa_[45] = {0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3, + 4, 2, 1, 65536, 0, 0, 0, 0, + 0, 0, 2, 1, 65537, 0, 0, 0, + 0, 0, 0, 0, 1, 65538, 0, 0, + 0, 0, 0, 0, 0}; + + if (start_token_ == end_) return 0; + + const std::size_t *ptr_ = dfa_ + dfa_alphabet_; + Iterator curr_ = start_token_; + bool end_state_ = *ptr_ != 0; + std::size_t id_ = *(ptr_ + id_index); + Iterator end_token_ = start_token_; + + while (curr_ != end_) + { + std::size_t const state_ = + ptr_[lookup_[static_cast + (*curr_++)]]; + + if (state_ == 0) break; + + ptr_ = &dfa_[state_ * dfa_alphabet_]; + + if (*ptr_) + { + end_state_ = true; + id_ = *(ptr_ + id_index); + end_token_ = curr_; + } + } + + if (end_state_) + { + // return longest match + start_token_ = end_token_; + } + else + { + id_ = npos; + } + + return id_; +} + +}}}} // namespace boost::spirit::lex::static_ + +#endif diff --git a/example/lex/static_lexer/word_count_tokens.hpp b/example/lex/static_lexer/word_count_tokens.hpp new file mode 100644 index 000000000..f73e7e236 --- /dev/null +++ b/example/lex/static_lexer/word_count_tokens.hpp @@ -0,0 +1,40 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#if !defined(SPIRIT_LEXER_EXAMPLE_WORD_COUNT_TOKENS_FEB_10_2008_0739PM) +#define SPIRIT_LEXER_EXAMPLE_WORD_COUNT_TOKENS_FEB_10_2008_0739PM + +/////////////////////////////////////////////////////////////////////////////// +// Token definition: We keep the base class for the token definition as a +// template parameter to allow this class to be used for +// both: the code generation and the lexical analysis +/////////////////////////////////////////////////////////////////////////////// +//[wc_static_tokenids +enum tokenids +{ + IDANY = boost::spirit::lex::min_token_id + 1, +}; +//] + +//[wc_static_tokendef +// This token definition class can be used without any change for all three +// possible use cases: a dynamic lexical analyzer, a code generator, and a +// static lexical analyzer. +template +struct word_count_tokens : boost::spirit::lex::lexer_def +{ + template + void def (Self& self) + { + // define tokens and associate them with the lexer + word = "[^ \t\n]+"; + self = word | '\n' | token_def<>(".", IDANY); + } + + boost::spirit::lex::token_def word; +}; +//] + +#endif diff --git a/example/lex/strip_comments.cpp b/example/lex/strip_comments.cpp new file mode 100644 index 000000000..a9e04124d --- /dev/null +++ b/example/lex/strip_comments.cpp @@ -0,0 +1,164 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example is the equivalent to the following lex program: +// +// %{ +// /* INITIAL is the default start state. COMMENT is our new */ +// /* state where we remove comments. */ +// %} +// +// %s COMMENT +// %% +// "//".* ; +// "/*" BEGIN COMMENT; +// . ECHO; +// [\n] ECHO; +// "*/" BEGIN INITIAL; +// . ; +// [\n] ; +// %% +// +// main() +// { +// yylex(); +// } +// +// Its purpose is to strip comments out of C code. +// +// Additionally this example demonstrates the use of lexer states to structure +// the lexer definition. + +// #define BOOST_SPIRIT_LEXERTL_DEBUG + +#include +#include +#include +#include + +#include +#include + +#include "example.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::lex; +using namespace boost::spirit::arg_names; + +/////////////////////////////////////////////////////////////////////////////// +// Token definition: We use the lexertl based lexer engine as the underlying +// lexer type. +/////////////////////////////////////////////////////////////////////////////// +enum tokenids +{ + IDANY = lex::min_token_id + 10 +}; + +template +struct strip_comments_tokens : lexer_def +{ + template + void def (Self& self) + { + // define tokens and associate them with the lexer + cppcomment = "//.*\n"; + ccomment = "/\\*"; + endcomment = "\\*/"; + + // The following tokens are associated with the default lexer state + // (the "INITIAL" state). Specifying 'INITIAL' as a lexer state is + // strictly optional. + self.add + (cppcomment) // no explicit token id is associated + (ccomment) + (".", IDANY) // IDANY is the token id associated with this token + // definition + ; + + // The following tokens are associated with the lexer state "COMMENT". + // We switch lexer states from inside the parsing process using the + // in_state("COMMENT")[] parser component as shown below. + self("COMMENT").add + (endcomment) + (".", IDANY) + ; + } + + token_def<> cppcomment, ccomment, endcomment; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Grammar definition +/////////////////////////////////////////////////////////////////////////////// +template +struct strip_comments_grammar : grammar_def +{ + template + strip_comments_grammar(TokenDef const& tok) + { + // The in_state("COMMENT")[...] parser component switches the lexer + // state to be 'COMMENT' during the matching of the embedded parser. + start = *( tok.ccomment + >> in_state("COMMENT") + [ + // the lexer is in the 'COMMENT' state during + // matching of the following parser components + *token(IDANY) >> tok.endcomment + ] + | tok.cppcomment + | token(IDANY) + ) + ; + } + + rule start; +}; + +/////////////////////////////////////////////////////////////////////////////// +int main(int argc, char* argv[]) +{ + // iterator type used to expose the underlying input stream + typedef std::string::iterator base_iterator_type; + + // lexer type + typedef lexertl_lexer > lexer_type; + + // iterator type exposed by the lexer + typedef + lexer_iterator >::type + iterator_type; + + // now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process + strip_comments_tokens strip_comments; // Our token definition + strip_comments_grammar def (strip_comments); // Our grammar definition + + // Parsing is done based on the the token stream, not the character + // stream read from the input. + std::string str (read_from_file(1 == argc ? "strip_comments.input" : argv[1])); + base_iterator_type first = str.begin(); + bool r = tokenize_and_parse(first, str.end(), make_lexer(strip_comments), + make_parser(def)); + + if (r) { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + } + else { + std::string rest(first, str.end()); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \"" << rest << "\"\n"; + std::cout << "-------------------------\n"; + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + + diff --git a/example/lex/strip_comments_lexer.cpp b/example/lex/strip_comments_lexer.cpp new file mode 100644 index 000000000..2eec0376d --- /dev/null +++ b/example/lex/strip_comments_lexer.cpp @@ -0,0 +1,121 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example is the equivalent to the following lex program: +// +// %{ +// /* INITIAL is the default start state. COMMENT is our new */ +// /* state where we remove comments. */ +// %} +// +// %s COMMENT +// %% +// "//".* ; +// "/*" BEGIN COMMENT; +// . ECHO; +// [\n] ECHO; +// "*/" BEGIN INITIAL; +// . ; +// [\n] ; +// %% +// +// main() +// { +// yylex(); +// } +// +// Its purpose is to strip comments out of C code. +// +// Additionally this example demonstrates the use of lexer states to structure +// the lexer definition. + +// #define BOOST_SPIRIT_LEXERTL_DEBUG + +#include +#include +#include +#include +#include + +#include +#include + +#include "example.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::lex; + +/////////////////////////////////////////////////////////////////////////////// +// Token definition: We use the lexertl based lexer engine as the underlying +// lexer type. +/////////////////////////////////////////////////////////////////////////////// +enum tokenids +{ + IDANY = lex::min_token_id + 10, + IDEOL = lex::min_token_id + 11 +}; + +template +struct strip_comments_tokens : lexer_def +{ + template + void def (Self& self) + { + // define tokens and associate them with the lexer + cppcomment = "//[^\n]*"; + ccomment = "/\\*"; + endcomment = "\\*/"; + any = "."; + eol = "\n"; + + // The following tokens are associated with the default lexer state + // (the "INITIAL" state). Specifying 'INITIAL' as a lexer state is + // strictly optional. + self = cppcomment + | ccomment [ set_state("COMMENT") ] + | eol [ echo_input(std::cout) ] + | any [ echo_input(std::cout) ] + ; + + // The following tokens are associated with the lexer state 'COMMENT'. + self("COMMENT") + = endcomment [ set_state("INITIAL") ] + | eol + | any + ; + } + + token_def<> cppcomment, ccomment, endcomment, any, eol; +}; + + /////////////////////////////////////////////////////////////////////////////// +int main(int argc, char* argv[]) +{ + // iterator type used to expose the underlying input stream + typedef std::string::iterator base_iterator_type; + + // lexer type + typedef lexertl_actor_lexer > lexer_type; + + // now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process + strip_comments_tokens strip_comments; // Our token definition + + // Parsing is done based on the the token stream, not the character + // stream read from the input. + std::string str (read_from_file(1 == argc ? "strip_comments.input" : argv[1])); + base_iterator_type first = str.begin(); + bool r = tokenize(first, str.end(), make_lexer(strip_comments)); + + if (!r) { + std::string rest(first, str.end()); + std::cerr << "Lexical analysis failed\n" << "stopped at: \"" + << rest << "\"\n"; + } + return 0; +} + + + diff --git a/example/lex/word_count.cpp b/example/lex/word_count.cpp new file mode 100644 index 000000000..d5750f63f --- /dev/null +++ b/example/lex/word_count.cpp @@ -0,0 +1,172 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example is the equivalent to the following lex program: +/* +//[wcp_flex_version + %{ + int c = 0, w = 0, l = 0; + %} + word [^ \t\n]+ + eol \n + %% + {word} { ++w; c += yyleng; } + {eol} { ++c; ++l; } + . { ++c; } + %% + main() + { + yylex(); + printf("%d %d %d\n", l, w, c); + } +//] +*/ +// Its purpose is to do the word count function of the wc command in UNIX. It +// prints the number of lines, words and characters in a file. +// +// The example additionally demonstrates how to use the add_pattern(...)(...) +// syntax to define lexer patterns. These patterns are essentially parameter- +// less 'macros' for regular expressions, allowing to simplify their +// definition. + +// #define BOOST_SPIRIT_LEXERTL_DEBUG +#define BOOST_VARIANT_MINIMIZE_SIZE + +//[wcp_includes +#include +#include +#include +#include +#include +//] + +#include +#include + +#include "example.hpp" + +//[wcp_namespaces +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::lex; +//] + +/////////////////////////////////////////////////////////////////////////////// +// Token definition: We use the lexertl based lexer engine as the underlying +// lexer type. +/////////////////////////////////////////////////////////////////////////////// +//[wcp_token_ids +enum tokenids +{ + IDANY = lex::min_token_id + 10 +}; +//] + +//[wcp_token_definition +template +struct word_count_tokens : lexer_def +{ + template + void def (Self& self) + { + // define patterns (lexer macros) to be used during token definition + // below + self.add_pattern + ("WORD", "[^ \t\n]+") + ; + + // define tokens and associate them with the lexer + word = "{WORD}"; // reference the pattern 'WORD' as defined above + + // this lexer will recognize 3 token types: words, newlines, and + // everything else + self.add + (word) // no token id is needed here + ('\n') // characters are usable as tokens as well + (".", IDANY) + ; + } + + token_def word; +}; +//] + +/////////////////////////////////////////////////////////////////////////////// +// Grammar definition +/////////////////////////////////////////////////////////////////////////////// +//[wcp_grammar_definition +template +struct word_count_grammar : grammar_def +{ + template + word_count_grammar(TokenDef const& tok) + : c(0), w(0), l(0) + { + using boost::phoenix::ref; + using boost::phoenix::size; + + // As documented in the Spirit.Qi documentation, any placeholders + // (_1 et.al.) used in semantic actions inside a grammar need to be + // imported from the namespace boost::spirit::arg_names, and not from + // the corresponding namespace in Phoenix. + using boost::spirit::arg_names::_1; + + start = *( tok.word [++ref(w), ref(c) += size(_1)] + | char_('\n') [++ref(c), ++ref(l)] + | token(IDANY) [++ref(c)] + ) + ; + } + + std::size_t c, w, l; + rule start; +}; +//] + +/////////////////////////////////////////////////////////////////////////////// +//[wcp_main +int main(int argc, char* argv[]) +{ +/*< define the token type to be used: `std::string` is available as the + type of the token value +>*/ typedef lexertl_token< + char const*, boost::mpl::vector + > token_type; + +/*< define the lexer type to use implementing the state machine +>*/ typedef lexertl_lexer lexer_type; + +/*< define the iterator type exposed by the lexer type +>*/ typedef lexer_iterator >::type iterator_type; + + // now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process + word_count_tokens word_count; // Our token definition + word_count_grammar def (word_count); // Our grammar definition + + // read in the file int memory + std::string str (read_from_file(1 == argc ? "word_count.input" : argv[1])); + char const* first = str.c_str(); + char const* last = &first[str.size()]; + + // Parsing is done based on the the token stream, not the character + // stream read from the input. The function `tokenize_and_parse()` wraps + // the passed iterator range `[first, last)` by the lexical analyzer and + // uses its exposed iterators to parse the toke stream. + bool r = tokenize_and_parse(first, last, make_lexer(word_count), + make_parser(def)); + + if (r) { + std::cout << "lines: " << def.l << ", words: " << def.w + << ", characters: " << def.c << "\n"; + } + else { + std::string rest(first, last); + std::cerr << "Parsing failed\n" << "stopped at: \"" + << rest << "\"\n"; + } + return 0; +} +//] diff --git a/example/lex/word_count_functor.cpp b/example/lex/word_count_functor.cpp new file mode 100644 index 000000000..8169ca354 --- /dev/null +++ b/example/lex/word_count_functor.cpp @@ -0,0 +1,184 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example is the equivalent to the following flex program: +/* +//[wcf_flex_version + %{ + #define ID_WORD 1000 + #define ID_EOL 1001 + #define ID_CHAR 1002 + int c = 0, w = 0, l = 0; + %} + %% + [^ \t\n]+ { return ID_WORD; } + \n { return ID_EOL; } + . { return ID_CHAR; } + %% + bool count(int tok) + { + switch (tok) { + case ID_WORD: ++w; c += yyleng; break; + case ID_EOL: ++l; ++c; break; + case ID_CHAR: ++c; break; + default: + return false; + } + return true; + } + void main() + { + int tok = EOF; + do { + tok = yylex(); + if (!count(tok)) + break; + } while (EOF != tok); + printf("%d %d %d\n", l, w, c); + } +//] +*/ +// Its purpose is to do the word count function of the wc command in UNIX. It +// prints the number of lines, words and characters in a file. +// +// This examples shows how to use the tokenize() function together with a +// simple functor, which gets executed whenever a token got matched in the +// input sequence. + +// #define BOOST_SPIRIT_LEXERTL_DEBUG + +//[wcf_includes +#include +#include +#include +//] + +#include +#include + +#include "example.hpp" + +//[wcf_namespaces +using namespace boost::spirit; +using namespace boost::spirit::lex; +//] + +/////////////////////////////////////////////////////////////////////////////// +// Token id definitions +/////////////////////////////////////////////////////////////////////////////// +//[wcf_token_ids +enum token_ids +{ + ID_WORD = 1000, + ID_EOL, + ID_CHAR +}; +//] + +//[wcf_token_definition +/*` The template `word_count_tokens` defines three different tokens: + `ID_WORD`, `ID_EOL`, and `ID_CHAR`, representing a word (anything except + a whitespace or a newline), a newline character, and any other character + (`ID_WORD`, `ID_EOL`, and `ID_CHAR` are enum values representing the token + ids, but could be anything else convertible to an integer as well). + The direct base class of any token definition class needs to be the + template `lexer_def<>`, where the corresponding template parameter (here: + `lexertl_lexer`) defines which underlying lexer engine has + to be used to provide the required state machine functionality. In this + example we use the Lexertl based lexer engine as the underlying lexer type. +*/ +template +struct word_count_tokens : lexer_def +{ + template + void def (Self& self) + { + // define tokens (the regular expression to match and the corresponding + // token id) and add them to the lexer + self.add + ("[^ \t\n]+", ID_WORD) // words (anything except ' ', '\t' or '\n') + ("\n", ID_EOL) // newline characters + (".", ID_CHAR) // anything else is a plain character + ; + } +}; +//] + +//[wcf_functor +/*` In this example the struct 'counter' is used as a functor counting the + characters, words and lines in the analyzed input sequence by identifying + the matched tokens as passed from the /Spirit.Lex/ library. +*/ +struct counter +{ +//<- this is an implementation detail and doesn't show up in the documentation + typedef bool result_type; + +//-> + // the function operator gets called for each of the matched tokens + // c, l, w are references to the counters used to keep track of the numbers + template + bool operator()(Token const& t, std::size_t& c, std::size_t& w, std::size_t& l) const + { + switch (t.id()) { + case ID_WORD: // matched a word + // since we're using a default token type in this example, every + // token instance contains a `iterator_range` as its + // token value pointing to the matched character sequence in the input + ++w; c += t.value().size(); + break; + case ID_EOL: // matched a newline character + ++l; ++c; + break; + case ID_CHAR: // matched something else + ++c; + break; + } + return true; // always continue to tokenize + } +}; +//] + +/////////////////////////////////////////////////////////////////////////////// +//[wcf_main +/*` The main function simply loads the given file into memory (as a + `std::string`), instantiates an instance of the token definition template + using the correct iterator type (`word_count_tokens`), + and finally calls `lex::tokenize`, passing an instance of the counter functor + defined above. The return value of `lex::tokenize` will be `true` if the + whole input sequence has been successfully tokenized, and `false` otherwise. +*/ +int main(int argc, char* argv[]) +{ + // these variables are used to count characters, words and lines + std::size_t c = 0, w = 0, l = 0; + + // read input from the given file + std::string str (read_from_file(1 == argc ? "word_count.input" : argv[1])); + + // create the token definition instance needed to invoke the lexical analyzer + word_count_tokens > word_count_functor; + + // tokenize the given string, the bound functor gets invoked for each of + // the matched tokens + char const* first = str.c_str(); + char const* last = &first[str.size()]; + bool r = lex::tokenize(first, last, make_lexer(word_count_functor), + boost::bind(counter(), _1, boost::ref(c), boost::ref(w), boost::ref(l))); + + // print results + if (r) { + std::cout << "lines: " << l << ", words: " << w + << ", characters: " << c << "\n"; + } + else { + std::string rest(first, last); + std::cout << "Lexical analysis failed\n" << "stopped at: \"" + << rest << "\"\n"; + } + return 0; +} +//] + diff --git a/example/lex/word_count_functor_flex.cpp b/example/lex/word_count_functor_flex.cpp new file mode 100644 index 000000000..d81b8ea86 --- /dev/null +++ b/example/lex/word_count_functor_flex.cpp @@ -0,0 +1,1571 @@ +#line 2 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor_flex.cpp" +/* A lexical scanner generated by flex */ + +/* Scanner skeleton version: + * $Header: /home/daffy/u0/vern/flex/RCS/flex.skl,v 2.91 96/09/10 16:58:48 vern Exp $ + */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 + +#include +#include + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include +#ifndef _WIN32 +#include +#endif + +/* Use prototypes in function declarations. */ +#define YY_USE_PROTOS + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define YY_USE_PROTOS +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef __TURBOC__ + #pragma warn -rch + #pragma warn -use +#include +#include +#define YY_USE_CONST +#define YY_USE_PROTOS +#endif + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + + +#ifdef YY_USE_PROTOS +#define YY_PROTO(proto) proto +#else +#define YY_PROTO(proto) () +#endif + + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yy_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yy_start - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE yyrestart( yyin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#define YY_BUF_SIZE 16384 + +typedef struct yy_buffer_state *YY_BUFFER_STATE; + +extern int yyleng; +extern FILE *yyin, *yyout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* The funky do-while in the following #define is used to turn the definition + * int a single C statement (which needs a semi-colon terminator). This + * avoids problems with code like: + * + * if ( condition_holds ) + * yyless( 5 ); + * else + * do_something_else(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the yyless() call. + */ + +/* Return all but the first 'n' matched characters back to the input stream. */ + +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + *yy_cp = yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yytext_ptr ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ +typedef unsigned int yy_size_t; + + +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via yyrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + }; + +static YY_BUFFER_STATE yy_current_buffer = 0; + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + */ +#define YY_CURRENT_BUFFER yy_current_buffer + + +/* yy_hold_char holds the character lost when yytext is formed. */ +static char yy_hold_char; + +static int yy_n_chars; /* number of characters read into yy_ch_buf */ + + +int yyleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void yyrestart YY_PROTO(( FILE *input_file )); + +void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer )); +void yy_load_buffer_state YY_PROTO(( void )); +YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size )); +void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b )); +void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file )); +void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b )); +#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer ) + +YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size )); +YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str )); +YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len )); + +static void *yy_flex_alloc YY_PROTO(( yy_size_t )); +static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t )); +static void yy_flex_free YY_PROTO(( void * )); + +#define yy_new_buffer yy_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! yy_current_buffer ) \ + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \ + yy_current_buffer->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (yy_current_buffer->yy_at_bol) + +typedef unsigned char YY_CHAR; +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; +typedef int yy_state_type; +extern char *yytext; +#define yytext_ptr yytext + +static yy_state_type yy_get_previous_state YY_PROTO(( void )); +static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state )); +static int yy_get_next_buffer YY_PROTO(( void )); +static void yy_fatal_error YY_PROTO(( yyconst char msg[] )); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yytext_ptr = yy_bp; \ + yyleng = (int) (yy_cp - yy_bp); \ + yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yy_c_buf_p = yy_cp; + +#define YY_NUM_RULES 4 +#define YY_END_OF_BUFFER 5 +static yyconst short int yy_accept[9] = + { 0, + 0, 0, 5, 1, 3, 2, 1, 0 + } ; + +static yyconst int yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst int yy_meta[4] = + { 0, + 1, 2, 2 + } ; + +static yyconst short int yy_base[10] = + { 0, + 0, 0, 5, 0, 6, 6, 0, 6, 3 + } ; + +static yyconst short int yy_def[10] = + { 0, + 8, 1, 8, 9, 8, 8, 9, 0, 8 + } ; + +static yyconst short int yy_nxt[10] = + { 0, + 4, 5, 6, 7, 8, 3, 8, 8, 8 + } ; + +static yyconst short int yy_chk[10] = + { 0, + 1, 1, 1, 9, 3, 8, 8, 8, 8 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *yytext; +#line 1 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor.flex" +#define INITIAL 0 +#line 2 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor.flex" +#include +#if defined(_WIN32) + #include +#endif + #define ID_WORD 1000 + #define ID_EOL 1001 + #define ID_CHAR 1002 +#line 375 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor_flex.cpp" + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int yywrap YY_PROTO(( void )); +#else +extern int yywrap YY_PROTO(( void )); +#endif +#endif + +#ifndef YY_NO_UNPUT +static void yyunput YY_PROTO(( int c, char *buf_ptr )); +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int )); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen YY_PROTO(( yyconst char * )); +#endif + +#ifndef YY_NO_INPUT +#ifdef __cplusplus +static int yyinput YY_PROTO(( void )); +#else +static int input YY_PROTO(( void )); +#endif +#endif + +#if YY_STACK_USED +static int yy_start_stack_ptr = 0; +static int yy_start_stack_depth = 0; +static int *yy_start_stack = 0; +#ifndef YY_NO_PUSH_STATE +static void yy_push_state YY_PROTO(( int new_state )); +#endif +#ifndef YY_NO_POP_STATE +static void yy_pop_state YY_PROTO(( void )); +#endif +#ifndef YY_NO_TOP_STATE +static int yy_top_state YY_PROTO(( void )); +#endif + +#else +#define YY_NO_PUSH_STATE 1 +#define YY_NO_POP_STATE 1 +#define YY_NO_TOP_STATE 1 +#endif + +#ifdef YY_MALLOC_DECL +YY_MALLOC_DECL +#else +#if __STDC__ +#ifndef __cplusplus +#include +#endif +#else +/* Just try to get by without declaring the routines. This will fail + * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int) + * or sizeof(void*) != sizeof(int). + */ +#endif +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ + +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( yy_current_buffer->yy_is_interactive ) \ + { \ + int c = '*', n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + } +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL int yylex YY_PROTO(( void )) +#endif + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +YY_DECL + { + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + +#line 11 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor.flex" + +#line 539 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor_flex.cpp" + + if ( yy_init ) + { + yy_init = 0; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yy_start ) + yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! yy_current_buffer ) + yy_current_buffer = + yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_load_buffer_state(); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yy_start; +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 9 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 6 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + + +do_action: /* This label is used only to access EOF actions. */ + + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yy_hold_char; + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 12 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor.flex" +{ return ID_WORD; } + YY_BREAK +case 2: +YY_RULE_SETUP +#line 13 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor.flex" +{ return ID_EOL; } + YY_BREAK +case 3: +YY_RULE_SETUP +#line 14 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor.flex" +{ return ID_CHAR; } + YY_BREAK +case 4: +YY_RULE_SETUP +#line 15 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor.flex" +ECHO; + YY_BREAK +#line 642 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor_flex.cpp" +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * yylex(). If so, then we have to assure + * consistency between yy_current_buffer and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yy_n_chars = yy_current_buffer->yy_n_chars; + yy_current_buffer->yy_input_file = yyin; + yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + yy_did_buffer_switch_on_eof = 0; + + if ( yywrap() ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yy_c_buf_p = yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = + yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yy_c_buf_p = + &yy_current_buffer->yy_ch_buf[yy_n_chars]; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ + } /* end of yylex */ + + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int yy_get_next_buffer() + { + register char *dest = yy_current_buffer->yy_ch_buf; + register char *source = yytext_ptr; + register int number_to_move, i; + int ret_val; + + if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( yy_current_buffer->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + yy_current_buffer->yy_n_chars = yy_n_chars = 0; + + else + { + int num_to_read = + yy_current_buffer->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ +#ifdef YY_USES_REJECT + YY_FATAL_ERROR( +"input buffer overflow, can't enlarge buffer because scanner uses REJECT" ); +#else + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = yy_current_buffer; + + int yy_c_buf_p_offset = + (int) (yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + yy_flex_realloc( (void *) b->yy_ch_buf, + b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = yy_current_buffer->yy_buf_size - + number_to_move - 1; +#endif + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), + yy_n_chars, num_to_read ); + + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + if ( yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + yyrestart( yyin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + yy_current_buffer->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + yy_n_chars += number_to_move; + yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; + yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yytext_ptr = &yy_current_buffer->yy_ch_buf[0]; + + return ret_val; + } + + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +static yy_state_type yy_get_previous_state() + { + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = yy_start; + + for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 9 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; + } + + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + +#ifdef YY_USE_PROTOS +static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state ) +#else +static yy_state_type yy_try_NUL_trans( yy_current_state ) +yy_state_type yy_current_state; +#endif + { + register int yy_is_jam; + register char *yy_cp = yy_c_buf_p; + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 9 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 8); + + return yy_is_jam ? 0 : yy_current_state; + } + + +#ifndef YY_NO_UNPUT +#ifdef YY_USE_PROTOS +static void yyunput( int c, register char *yy_bp ) +#else +static void yyunput( c, yy_bp ) +int c; +register char *yy_bp; +#endif + { + register char *yy_cp = yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yy_hold_char; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = yy_n_chars + 2; + register char *dest = &yy_current_buffer->yy_ch_buf[ + yy_current_buffer->yy_buf_size + 2]; + register char *source = + &yy_current_buffer->yy_ch_buf[number_to_move]; + + while ( source > yy_current_buffer->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + yy_current_buffer->yy_n_chars = + yy_n_chars = yy_current_buffer->yy_buf_size; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + + yytext_ptr = yy_bp; + yy_hold_char = *yy_cp; + yy_c_buf_p = yy_cp; + } +#endif /* ifndef YY_NO_UNPUT */ + + +#ifdef __cplusplus +static int yyinput() +#else +static int input() +#endif + { + int c; + + *yy_c_buf_p = yy_hold_char; + + if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* This was really a NUL. */ + *yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = yy_c_buf_p - yytext_ptr; + ++yy_c_buf_p; + + switch ( yy_get_next_buffer() ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + yyrestart( yyin ); + + /* fall through */ + + case EOB_ACT_END_OF_FILE: + { + if ( yywrap() ) + return EOF; + + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */ + *yy_c_buf_p = '\0'; /* preserve yytext */ + yy_hold_char = *++yy_c_buf_p; + + + return c; + } + + +#ifdef YY_USE_PROTOS +void yyrestart( FILE *input_file ) +#else +void yyrestart( input_file ) +FILE *input_file; +#endif + { + if ( ! yy_current_buffer ) + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_init_buffer( yy_current_buffer, input_file ); + yy_load_buffer_state(); + } + + +#ifdef YY_USE_PROTOS +void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) +#else +void yy_switch_to_buffer( new_buffer ) +YY_BUFFER_STATE new_buffer; +#endif + { + if ( yy_current_buffer == new_buffer ) + return; + + if ( yy_current_buffer ) + { + /* Flush out information for old buffer. */ + *yy_c_buf_p = yy_hold_char; + yy_current_buffer->yy_buf_pos = yy_c_buf_p; + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + yy_current_buffer = new_buffer; + yy_load_buffer_state(); + + /* We don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yy_did_buffer_switch_on_eof = 1; + } + + +#ifdef YY_USE_PROTOS +void yy_load_buffer_state( void ) +#else +void yy_load_buffer_state() +#endif + { + yy_n_chars = yy_current_buffer->yy_n_chars; + yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos; + yyin = yy_current_buffer->yy_input_file; + yy_hold_char = *yy_c_buf_p; + } + + +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) +#else +YY_BUFFER_STATE yy_create_buffer( file, size ) +FILE *file; +int size; +#endif + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + yy_init_buffer( b, file ); + + return b; + } + + +#ifdef YY_USE_PROTOS +void yy_delete_buffer( YY_BUFFER_STATE b ) +#else +void yy_delete_buffer( b ) +YY_BUFFER_STATE b; +#endif + { + if ( ! b ) + return; + + if ( b == yy_current_buffer ) + yy_current_buffer = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + yy_flex_free( (void *) b->yy_ch_buf ); + + yy_flex_free( (void *) b ); + } + + +#ifndef _WIN32 +#include +#else +#ifndef YY_ALWAYS_INTERACTIVE +#ifndef YY_NEVER_INTERACTIVE +extern int isatty YY_PROTO(( int )); +#endif +#endif +#endif + +#ifdef YY_USE_PROTOS +void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) +#else +void yy_init_buffer( b, file ) +YY_BUFFER_STATE b; +FILE *file; +#endif + + + { + yy_flush_buffer( b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + +#if YY_ALWAYS_INTERACTIVE + b->yy_is_interactive = 1; +#else +#if YY_NEVER_INTERACTIVE + b->yy_is_interactive = 0; +#else + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; +#endif +#endif + } + + +#ifdef YY_USE_PROTOS +void yy_flush_buffer( YY_BUFFER_STATE b ) +#else +void yy_flush_buffer( b ) +YY_BUFFER_STATE b; +#endif + + { + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == yy_current_buffer ) + yy_load_buffer_state(); + } + + +#ifndef YY_NO_SCAN_BUFFER +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size ) +#else +YY_BUFFER_STATE yy_scan_buffer( base, size ) +char *base; +yy_size_t size; +#endif + { + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + yy_switch_to_buffer( b ); + + return b; + } +#endif + + +#ifndef YY_NO_SCAN_STRING +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_string( yyconst char *yy_str ) +#else +YY_BUFFER_STATE yy_scan_string( yy_str ) +yyconst char *yy_str; +#endif + { + int len; + for ( len = 0; yy_str[len]; ++len ) + ; + + return yy_scan_bytes( yy_str, len ); + } +#endif + + +#ifndef YY_NO_SCAN_BYTES +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len ) +#else +YY_BUFFER_STATE yy_scan_bytes( bytes, len ) +yyconst char *bytes; +int len; +#endif + { + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = len + 2; + buf = (char *) yy_flex_alloc( n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); + + for ( i = 0; i < len; ++i ) + buf[i] = bytes[i]; + + buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR; + + b = yy_scan_buffer( buf, n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; + } +#endif + + +#ifndef YY_NO_PUSH_STATE +#ifdef YY_USE_PROTOS +static void yy_push_state( int new_state ) +#else +static void yy_push_state( new_state ) +int new_state; +#endif + { + if ( yy_start_stack_ptr >= yy_start_stack_depth ) + { + yy_size_t new_size; + + yy_start_stack_depth += YY_START_STACK_INCR; + new_size = yy_start_stack_depth * sizeof( int ); + + if ( ! yy_start_stack ) + yy_start_stack = (int *) yy_flex_alloc( new_size ); + + else + yy_start_stack = (int *) yy_flex_realloc( + (void *) yy_start_stack, new_size ); + + if ( ! yy_start_stack ) + YY_FATAL_ERROR( + "out of memory expanding start-condition stack" ); + } + + yy_start_stack[yy_start_stack_ptr++] = YY_START; + + BEGIN(new_state); + } +#endif + + +#ifndef YY_NO_POP_STATE +static void yy_pop_state() + { + if ( --yy_start_stack_ptr < 0 ) + YY_FATAL_ERROR( "start-condition stack underflow" ); + + BEGIN(yy_start_stack[yy_start_stack_ptr]); + } +#endif + + +#ifndef YY_NO_TOP_STATE +static int yy_top_state() + { + return yy_start_stack[yy_start_stack_ptr - 1]; + } +#endif + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +#ifdef YY_USE_PROTOS +static void yy_fatal_error( yyconst char msg[] ) +#else +static void yy_fatal_error( msg ) +char msg[]; +#endif + { + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); + } + + + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + yytext[yyleng] = yy_hold_char; \ + yy_c_buf_p = yytext + n; \ + yy_hold_char = *yy_c_buf_p; \ + *yy_c_buf_p = '\0'; \ + yyleng = n; \ + } \ + while ( 0 ) + + +/* Internal utility routines. */ + +#ifndef yytext_ptr +#ifdef YY_USE_PROTOS +static void yy_flex_strncpy( char *s1, yyconst char *s2, int n ) +#else +static void yy_flex_strncpy( s1, s2, n ) +char *s1; +yyconst char *s2; +int n; +#endif + { + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; + } +#endif + +#ifdef YY_NEED_STRLEN +#ifdef YY_USE_PROTOS +static int yy_flex_strlen( yyconst char *s ) +#else +static int yy_flex_strlen( s ) +yyconst char *s; +#endif + { + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; + } +#endif + + +#ifdef YY_USE_PROTOS +static void *yy_flex_alloc( yy_size_t size ) +#else +static void *yy_flex_alloc( size ) +yy_size_t size; +#endif + { + return (void *) malloc( size ); + } + +#ifdef YY_USE_PROTOS +static void *yy_flex_realloc( void *ptr, yy_size_t size ) +#else +static void *yy_flex_realloc( ptr, size ) +void *ptr; +yy_size_t size; +#endif + { + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); + } + +#ifdef YY_USE_PROTOS +static void yy_flex_free( void *ptr ) +#else +static void yy_flex_free( ptr ) +void *ptr; +#endif + { + free( ptr ); + } + +#if YY_MAIN +int main() + { + yylex(); + return 0; + } +#endif +#line 15 "c:\\CVS\\spirit\\libs\\spirit\\example\\lex\\word_count_functor.flex" + + +bool count(int tok, int* c, int* w, int* l) +{ + switch (tok) { + case ID_WORD: ++*w; *c += yyleng; break; + case ID_EOL: ++*l; ++*c; break; + case ID_CHAR: ++*c; break; + default: + return false; + } + return true; +} + +void main(int argc, char* argv[]) +{ + int tok = EOF; + int c = 0, w = 0, l = 0; + yyin = fopen(1 == argc ? "word_count.input" : argv[1], "r"); + if (NULL == yyin) { + fprintf(stderr, "Couldn't open input file!\n"); + exit(-1); + } + + boost::timer tim; + do { + tok = yylex(); + if (!count(tok, &c, &w, &l)) + break; + } while (EOF != tok); + printf("lines: %d, words: %d, characters: %d\n", l, w, c); + printf("Time elapsed: %f", tim.elapsed()); + fclose(yyin); +} + +extern "C" int yywrap() +{ + return 1; +} + diff --git a/example/lex/word_count_lexer.cpp b/example/lex/word_count_lexer.cpp new file mode 100644 index 000000000..101b9261e --- /dev/null +++ b/example/lex/word_count_lexer.cpp @@ -0,0 +1,138 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This example is the equivalent to the following lex program: +/* +//[wcl_flex_version + %{ + int c = 0, w = 0, l = 0; + %} + %% + [^ \t\n]+ { ++w; c += yyleng; } + \n { ++c; ++l; } + . { ++c; } + %% + main() + { + yylex(); + printf("%d %d %d\n", l, w, c); + } +//] +*/ +// Its purpose is to do the word count function of the wc command in UNIX. It +// prints the number of lines, words and characters in a file. +// +// This examples shows how to use semantic actions associated with token +// definitions to directly attach actions to tokens. These get executed +// whenever the corresponding token got matched in the input sequence. Note, +// how this example implements all functionality directly in the lexer +// definition without any need for a parser. + +// #define BOOST_SPIRIT_LEXERTL_DEBUG + +//[wcl_includes +#include +#include +#include +#include +#include +//] + +#include +#include + +#include "example.hpp" + +//[wcl_namespaces +using namespace boost::spirit; +using namespace boost::spirit::lex; +//] + +/////////////////////////////////////////////////////////////////////////////// +// Token definition: We use the lexertl based lexer engine as the underlying +// lexer type. +// +// Note, the token definition type is derived from the 'lexertl_actor_lexer' +// template, which is a necessary to being able to use lexer semantic actions. +/////////////////////////////////////////////////////////////////////////////// +//[wcl_token_definition +template +struct word_count_tokens : lexer_def +{ + word_count_tokens() + : c(0), w(0), l(0), + word("[^ \t\n]+"), eol("\n"), any(".") // define tokens + {} + + template + void def (Self& self) + { + using boost::phoenix::ref; + using boost::phoenix::distance; + + // Note that all placeholders used in lexer semantic actions in + // conjunction with functors created based on Phoenix2 need to be from + // the namespace boost::phoenix::arg_names (not spirit::arg_names). + // Using the wrong placeholders leads to subtle compilation errors + // which are difficult to backtrack to their cause. + using boost::phoenix::arg_names::_1; + + // associate tokens with the lexer + self = word [++ref(w), ref(c) += distance(_1)] + | eol [++ref(c), ++ref(l)] + | any [++ref(c)] + ; + } + + std::size_t c, w, l; + token_def<> word, eol, any; +}; +//] + +/////////////////////////////////////////////////////////////////////////////// +//[wcl_main +int main(int argc, char* argv[]) +{ + // read input from the given file + std::string str (read_from_file(1 == argc ? "word_count.input" : argv[1])); + + // Specifying 'omitted' as the token value type generates a token class not + // holding any token value at all (not even the iterator_range of the + // matched input sequence), therefor optimizing the token, the lexer, and + // possibly the parser implementation as much as possible. + // + // Specifying mpl::false_ as the 3rd template parameter generates a token + // type and an iterator, both holding no lexer state, allowing for even more + // aggressive optimizations. + // + // As a result the token instances contain the token ids as the only data + // member. + typedef lexertl_token token_type; + + // lexer type + typedef lexertl_actor_lexer lexer_type; + + // create the lexer object instance needed to invoke the lexical analysis + word_count_tokens word_count_lexer; + + // tokenize the given string, all generated tokens are discarded + char const* first = str.c_str(); + char const* last = &first[str.size()]; + bool r = tokenize(first, last, make_lexer(word_count_lexer)); + + if (r) { + std::cout << "lines: " << word_count_lexer.l + << ", words: " << word_count_lexer.w + << ", characters: " << word_count_lexer.c + << "\n"; + } + else { + std::string rest(first, last); + std::cout << "Lexical analysis failed\n" << "stopped at: \"" + << rest << "\"\n"; + } + return 0; +} +//] diff --git a/example/qi/Jamfile b/example/qi/Jamfile new file mode 100644 index 000000000..9c8cc35b2 --- /dev/null +++ b/example/qi/Jamfile @@ -0,0 +1,46 @@ +#============================================================================== +# Copyright (c) 2001-2007 Joel de Guzman +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +#============================================================================== +project spirit-qi-example ; + +exe sum : sum.cpp ; +exe complex_number : complex_number.cpp ; +exe employee : employee.cpp ; +exe roman : roman.cpp ; +exe mini_xml1 : mini_xml1.cpp ; +exe mini_xml2 : mini_xml2.cpp ; +exe num_list : num_list.cpp ; +exe num_list2 : num_list2.cpp ; +exe num_list3 : num_list3.cpp ; + +exe calc1 : calc1.cpp ; +exe calc2 : calc2.cpp ; +exe calc3 : calc3.cpp ; +exe calc4 : calc4.cpp ; +exe calc5 : calc5.cpp ; + +exe calc6 : + calc6/calc6.cpp + calc6/calc6a.cpp + calc6/calc6b.cpp + calc6/calc6c.cpp + ; + +exe calc7 : + calc7/calc7.cpp + calc7/calc7a.cpp + calc7/calc7b.cpp + calc7/calc7c.cpp + ; + +exe mini_c : + mini_c/mini_c.cpp + mini_c/mini_ca.cpp + mini_c/mini_cb.cpp + mini_c/mini_cc.cpp + mini_c/mini_cd.cpp + ; + diff --git a/example/qi/calc1.cpp b/example/qi/calc1.cpp new file mode 100644 index 000000000..f1c7913ea --- /dev/null +++ b/example/qi/calc1.cpp @@ -0,0 +1,104 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// Plain calculator example demonstrating the grammar. The parser is a +// syntax checker only and does not do any semantic evaluation. +// +// [ JDG May 10, 2002 ] spirit1 +// [ JDG March 4, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; + +/////////////////////////////////////////////////////////////////////////////// +// Our calculator grammar +/////////////////////////////////////////////////////////////////////////////// +template +struct calculator : grammar_def +{ + calculator() + { + expression = + term + >> *( ('+' >> term) + | ('-' >> term) + ) + ; + + term = + factor + >> *( ('*' >> factor) + | ('/' >> factor) + ) + ; + + factor = + uint_ + | '(' >> expression >> ')' + | ('-' >> factor) + | ('+' >> factor) + ; + } + + rule expression, term, factor; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Expression parser...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Type an expression...or [q or Q] to quit\n\n"; + + typedef std::string::const_iterator iterator_type; + typedef calculator calculator; + + calculator def; // Our grammar definition + grammar calc(def, def.expression); // Our grammar + + std::string str; + while (std::getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + std::string::const_iterator iter = str.begin(); + std::string::const_iterator end = str.end(); + bool r = phrase_parse(iter, end, calc, space); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + } + else + { + std::string rest(iter, end); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \": " << rest << "\"\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/calc2.cpp b/example/qi/calc2.cpp new file mode 100644 index 000000000..c2110b41d --- /dev/null +++ b/example/qi/calc2.cpp @@ -0,0 +1,123 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A Calculator example demonstrating the grammar and semantic actions +// using phoenix to "bind" plain functions. The parser prints code suitable +// for a stack based virtual machine. +// +// [ JDG May 10, 2002 ] spirit1 +// [ JDG March 4, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include + +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +using boost::phoenix::bind; + +/////////////////////////////////////////////////////////////////////////////// +// Semantic actions +/////////////////////////////////////////////////////////////////////////////// +namespace +{ + void do_int(int n) { std::cout << "push " << n << std::endl; } + void do_add() { std::cout << "add\n"; } + void do_subt() { std::cout << "subtract\n"; } + void do_mult() { std::cout << "mult\n"; } + void do_div() { std::cout << "divide\n"; } + void do_neg() { std::cout << "negate\n"; } +} + +/////////////////////////////////////////////////////////////////////////////// +// Our calculator grammar +/////////////////////////////////////////////////////////////////////////////// +template +struct calculator : grammar_def +{ + calculator() + { + expression = + term + >> *( ('+' >> term [bind(&do_add)]) + | ('-' >> term [bind(&do_subt)]) + ) + ; + + term = + factor + >> *( ('*' >> factor [bind(&do_mult)]) + | ('/' >> factor [bind(&do_div)]) + ) + ; + + factor = + uint_ [bind(&do_int, _1)] + | '(' >> expression >> ')' + | ('-' >> factor [bind(&do_neg)]) + | ('+' >> factor) + ; + } + + rule expression, term, factor; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Expression parser...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Type an expression...or [q or Q] to quit\n\n"; + + typedef std::string::const_iterator iterator_type; + typedef calculator calculator; + + calculator def; // Our grammar definition + grammar calc(def, def.expression); // Our grammar + + std::string str; + while (std::getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + std::string::const_iterator iter = str.begin(); + std::string::const_iterator end = str.end(); + bool r = phrase_parse(iter, end, calc, space); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + } + else + { + std::string rest(iter, end); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \": " << rest << "\"\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/calc3.cpp b/example/qi/calc3.cpp new file mode 100644 index 000000000..5fe6c8deb --- /dev/null +++ b/example/qi/calc3.cpp @@ -0,0 +1,110 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A calculator example demonstrating the grammar and semantic actions +// using phoenix to do the actual expression evaluation. The parser is +// essentially an "interpreter" that evaluates expressions on the fly. +// +// [ JDG June 29, 2002 ] spirit1 +// [ JDG March 5, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include + +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +/////////////////////////////////////////////////////////////////////////////// +// Our calculator grammar +/////////////////////////////////////////////////////////////////////////////// +template +struct calculator : grammar_def +{ + calculator() + { + expression = + term [_val = _1] + >> *( ('+' >> term [_val += _1]) + | ('-' >> term [_val -= _1]) + ) + ; + + term = + factor [_val = _1] + >> *( ('*' >> factor [_val *= _1]) + | ('/' >> factor [_val /= _1]) + ) + ; + + factor = + uint_ [_val = _1] + | '(' >> expression [_val = _1] >> ')' + | ('-' >> factor [_val = -_1]) + | ('+' >> factor [_val = _1]) + ; + } + + rule expression, term, factor; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Expression parser...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Type an expression...or [q or Q] to quit\n\n"; + + typedef std::string::const_iterator iterator_type; + typedef calculator calculator; + + calculator def; // Our grammar definition + grammar calc(def, def.expression); // Our grammar + + std::string str; + int result; + while (std::getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + std::string::const_iterator iter = str.begin(); + std::string::const_iterator end = str.end(); + bool r = phrase_parse(iter, end, calc, result, space); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "result = " << result << std::endl; + std::cout << "-------------------------\n"; + } + else + { + std::string rest(iter, end); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \": " << rest << "\"\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/calc3_lexer.cpp b/example/qi/calc3_lexer.cpp new file mode 100644 index 000000000..c8c68f63f --- /dev/null +++ b/example/qi/calc3_lexer.cpp @@ -0,0 +1,201 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A calculator example demonstrating the grammar and semantic actions +// using phoenix to do the actual expression evaluation. The parser is +// essentially an "interpreter" that evaluates expressions on the fly. +// +// Additionally this examples shows how to build and use a lexer based on +// Ben Hansons Lexertl (http://www.benhanson.net/lexertl.html). This way the +// parser matches the grammar against the tokens generated by the lexer +// component and not against the input character stream. +// +// Even if the benefits of using a lexer for this small calculator grammar may +// not outweight the corresponding overhead, we provide this example because +// it allows to concentrate on the essentials without having to understand +// the semantics first. +// +// [ JDG June 29, 2002 ] spirit1 +// [ JDG March 5, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include + +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::lex; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +/////////////////////////////////////////////////////////////////////////////// +// Our token definition +// This class is used to define all the tokens to be recognized by the lexer. +/////////////////////////////////////////////////////////////////////////////// +template +struct calculator_tokens : lexer_def +{ + template + void def (Self& self) + { + // unsigned integer token definition + ui = "[1-9][0-9]*"; + + // whitespace token definitions + ws = "[ \\t\\f\\v]+"; + c_comment = "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/"; + + // build token set + skipper = ws | c_comment; // += is allowed as well + + // associate the tokens and the token set with the lexer + // default lexer state + self = token_def<>('+') | '-' | '*' | '/' | '(' | ')'; + self += ui; // still default state + + // The token_set 'skipper' get's assigned to a separate lexer state + // which allows to use it separately from the main tokenization + // (it is used as the skipper parser below) + self("SKIPPER") = skipper; // lexer state "SKIPPER" + } + + // This are the tokens to be recognized by the lexer. + token_def ui; // matched tokens will have a unsigned int + token_def<> ws, c_comment; // attribute will not be used + + // This is the only token set explicitly defined by this lexer because it + // needs to be accessible from the outside (used as skip parser below). + typename Lexer::token_set skipper; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Our calculator grammar +// +// The difference to the original example (calc3.cpp) is that we are +// specifying a second template parameter referring to the lexer. Further, we +// use a defined tokenset from above as the skip parser. +/////////////////////////////////////////////////////////////////////////////// +template +struct calculator : grammar_def +{ + template + calculator(TokenDef const& tok) + { + // grammar + expression = + term [_val = _1] + >> *( ('+' >> term [_val += _1]) + | ('-' >> term [_val -= _1]) + ) + ; + + term = + factor [_val = _1] + >> *( ('*' >> factor [_val *= _1]) + | ('/' >> factor [_val /= _1]) + ) + ; + + factor = + tok.ui [_val = _1] + | '(' >> expression [_val = _1] >> ')' + | ('-' >> factor [_val = -_1]) + | ('+' >> factor [_val = _1]) + ; + } + + rule expression, term, factor; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Expression parser...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Type an expression...or [q or Q] to quit\n\n"; + + // iterator type used to expose the underlying input stream + typedef std::string::const_iterator base_iterator_type; + + // This is the lexer token type to use. The second template parameter lists + // all attribute types used for token_def's during token definition (see + // calculator_tokens<> above). Here we use the predefined lexertl token + // type, but any compatible token type may be used. + typedef lexertl_token< + base_iterator_type, boost::mpl::vector + > token_type; + + // This is the lexer type to use to tokenize the input. + // Here we use the lexertl based lexer engine. + typedef lexertl_lexer lexer_type; + + // This is the token definition type (derived from the given lexer type). + typedef calculator_tokens calculator_tokens; + + // this is the iterator type exposed by the lexer + typedef lexer::iterator_type iterator_type; + + // this is the type of the grammar to parse + typedef calculator calculator; + + // now we use the types defined above to create the lexer and grammar + // object instances needed to invoke the parsing process + calculator_tokens tokens; // Our token definition + calculator def (tokens); // Our grammar definition + + lexer lex(tokens); // Our lexer + grammar calc(def, def.expression); // Our grammar + + // get input line by line and feed the parser to evaluate the expressions + // read in from the input + std::string str; + int result; + while (std::getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + // At this point we generate the iterator pair used to expose the + // tokenized input stream. + iterator_type iter = lex.begin(str.begin(), str.end()); + iterator_type end = lex.end(); + + // Parsing is done based on the the token stream, not the character + // stream read from the input. + // Note, how we use the token_set defined above as the skip parser. + bool r = phrase_parse(iter, end, calc, result, tokens.skipper); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "result = " << result << std::endl; + std::cout << "-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/calc4.cpp b/example/qi/calc4.cpp new file mode 100644 index 000000000..5daa6d93c --- /dev/null +++ b/example/qi/calc4.cpp @@ -0,0 +1,126 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// This time, we'll incorporate error handling and reporting. +// +// [ JDG June 29, 2002 ] spirit1 +// [ JDG March 5, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include + +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +using boost::phoenix::val; +using boost::phoenix::construct; + +/////////////////////////////////////////////////////////////////////////////// +// Our calculator grammar +/////////////////////////////////////////////////////////////////////////////// +template +struct calculator : grammar_def +{ + calculator() + { + expression = + term [_val = _1] + >> *( ('+' > term [_val += _1]) + | ('-' > term [_val -= _1]) + ) + ; + + term = + factor [_val = _1] + >> *( ('*' > factor [_val *= _1]) + | ('/' > factor [_val /= _1]) + ) + ; + + factor = + uint_ [_val = _1] + | '(' > expression [_val = _1] > ')' + | ('-' > factor [_val = -_1]) + | ('+' > factor [_val = _1]) + ; + + expression.name("expression"); + term.name("term"); + factor.name("factor"); + + on_error + ( + expression + , std::cout + << val("Error! Expecting ") + << _4 // what failed? + << val(" here: \"") + << construct(_3, _2) // iterators to error-pos, end + << val("\"") + << std::endl + ); + } + + rule expression, term, factor; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Expression parser...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Type an expression...or [q or Q] to quit\n\n"; + + typedef std::string::const_iterator iterator_type; + typedef calculator calculator; + + calculator def; // Our grammar definition + grammar calc(def, def.expression); // Our grammar + + std::string str; + int result; + while (std::getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + std::string::const_iterator iter = str.begin(); + std::string::const_iterator end = str.end(); + bool r = phrase_parse(iter, end, calc, result, space); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "result = " << result << std::endl; + std::cout << "-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/calc5.cpp b/example/qi/calc5.cpp new file mode 100644 index 000000000..074753362 --- /dev/null +++ b/example/qi/calc5.cpp @@ -0,0 +1,227 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// Yet another calculator example! This time, we will compile to a simple +// virtual machine. This is actually one of the very first Spirit example +// circa 2000. Now, it's ported to Spirit2. +// +// [ JDG Sometime 2000 ] pre-boost +// [ JDG September 18, 2002 ] spirit1 +// [ JDG April 8, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +using boost::phoenix::ref; +using boost::phoenix::push_back; +using boost::phoenix::val; +using boost::phoenix::construct; + +/////////////////////////////////////////////////////////////////////////////// +// The Virtual Machine +/////////////////////////////////////////////////////////////////////////////// +enum byte_code +{ + op_neg, // negate the top stack entry + op_add, // add top two stack entries + op_sub, // subtract top two stack entries + op_mul, // multiply top two stack entries + op_div, // divide top two stack entries + op_int, // push constant integer into the stack +}; + +class vmachine +{ +public: + + vmachine(unsigned stackSize = 4096) + : stack(stackSize) + , stack_ptr(stack.begin()) + { + } + + int top() const { return stack_ptr[-1]; }; + void execute(std::vector& code); + +private: + + std::vector stack; + std::vector::iterator stack_ptr; +}; + +void vmachine::execute(std::vector& code) +{ + std::vector::iterator pc = code.begin(); + stack_ptr = stack.begin(); + + while (pc != code.end()) + { + switch (*pc++) + { + case op_neg: + stack_ptr[-1] = -stack_ptr[-1]; + break; + + case op_add: + --stack_ptr; + stack_ptr[-1] += stack_ptr[0]; + break; + + case op_sub: + --stack_ptr; + stack_ptr[-1] -= stack_ptr[0]; + break; + + case op_mul: + --stack_ptr; + stack_ptr[-1] *= stack_ptr[0]; + break; + + case op_div: + --stack_ptr; + stack_ptr[-1] /= stack_ptr[0]; + break; + + case op_int: + *stack_ptr++ = *pc++; + break; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Our calculator grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +struct calculator : grammar_def +{ + calculator(std::vector& code) + : code(code) + { + expression = + term + >> *( ('+' > term [push_back(ref(code), op_add)]) + | ('-' > term [push_back(ref(code), op_sub)]) + ) + ; + + term = + factor + >> *( ('*' > factor [push_back(ref(code), op_mul)]) + | ('/' > factor [push_back(ref(code), op_div)]) + ) + ; + + factor = + uint_ [ + push_back(ref(code), op_int), + push_back(ref(code), _1) + ] + | '(' > expression > ')' + | ('-' > factor [push_back(ref(code), op_neg)]) + | ('+' > factor) + ; + + expression.name("expression"); + term.name("term"); + factor.name("factor"); + + on_error + ( + expression + , std::cout + << val("Error! Expecting ") + << _4 // what failed? + << val(" here: \"") + << construct(_3, _2) // iterators to error-pos, end + << val("\"") + << std::endl + ); + } + + rule expression, term, factor; + std::vector& code; +}; + +template +bool compile(Grammar const& calc, std::string const& expr) +{ + std::string::const_iterator iter = expr.begin(); + std::string::const_iterator end = expr.end(); + bool r = phrase_parse(iter, end, calc, space); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + return true; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Expression parser...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Type an expression...or [q or Q] to quit\n\n"; + + typedef std::string::const_iterator iterator_type; + typedef calculator calculator; + + vmachine mach; // Our virtual machine + std::vector code; // Our VM code + calculator def(code); // Our grammar definition + grammar + calc(def, def.expression); // Our grammar + + std::string str; + while (std::getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + code.clear(); + if (::compile(calc, str)) + { + mach.execute(code); + std::cout << "\n\nresult = " << mach.top() << std::endl; + std::cout << "-------------------------\n\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/calc6/calc6.cpp b/example/qi/calc6/calc6.cpp new file mode 100644 index 000000000..2c8062818 --- /dev/null +++ b/example/qi/calc6/calc6.cpp @@ -0,0 +1,102 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// Now we'll introduce variables and assignment. This time, we'll also +// be renaming some of the rules -- a strategy for a grander scheme +// to come ;-) +// +// This version also shows off grammar modularization. Here you will +// see how expressions and statements are built as modular grammars. +// +// [ JDG April 9, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include "calc6.hpp" + +template +bool compile(Grammar const& calc, std::string const& expr) +{ + std::string::const_iterator iter = expr.begin(); + std::string::const_iterator end = expr.end(); + bool r = phrase_parse(iter, end, calc, space); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + return true; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +struct var_printer +{ + var_printer(std::vector const& stack) + : stack(stack) + { + } + + template + void operator()(String const& s, Data const& data) + { + std::cout << " " << s << ": " << stack[data] << std::endl; + } + + std::vector const& stack; +}; + +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Expression parser...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Type some statements... "; + std::cout << "Then type period ('.') to compile, run and print results\n\n"; + + typedef std::string::const_iterator iterator_type; + typedef statement statement; + + vmachine mach; // Our virtual machine + std::vector code; // Our VM code + statement def(code); // Our grammar definition + grammar calc(def); // Our grammar + + std::string str; + std::string program; + while (std::getline(std::cin, str)) + { + if (str.empty() || str[0] == '.') + break; + program += str; + } + + if (::compile(calc, program)) + { + mach.execute(code, def.nvars); + + std::cout << "Results------------------\n\n"; + def.vars.for_each(var_printer(mach.get_stack())); + std::cout << "-------------------------\n\n"; + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/calc6/calc6.hpp b/example/qi/calc6/calc6.hpp new file mode 100644 index 000000000..ead83c285 --- /dev/null +++ b/example/qi/calc6/calc6.hpp @@ -0,0 +1,183 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CALC6) +#define BOOST_SPIRIT_CALC6 + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +using boost::phoenix::function; +using boost::phoenix::ref; + +/////////////////////////////////////////////////////////////////////////////// +// The Virtual Machine +/////////////////////////////////////////////////////////////////////////////// +enum byte_code +{ + op_neg, // negate the top stack entry + op_add, // add top two stack entries + op_sub, // subtract top two stack entries + op_mul, // multiply top two stack entries + op_div, // divide top two stack entries + + op_load, // load a variable + op_store, // store a variable + op_int, // push constant integer into the stack +}; + +class vmachine +{ +public: + + vmachine(unsigned stackSize = 4096) + : stack(stackSize) + , stack_ptr(stack.begin()) + { + } + + std::vector const& get_stack() const { return stack; }; + void execute(std::vector& code, int nvars); + +private: + + std::vector stack; + std::vector::iterator stack_ptr; +}; + +/////////////////////////////////////////////////////////////////////////////// +// A generic compiler that compiles 1 to 3 codes +/////////////////////////////////////////////////////////////////////////////// +struct compile_op +{ + template + struct result { typedef void type; }; + + compile_op(std::vector& code) + : code(code) + { + } + + void operator()(int a) const + { + code.push_back(a); + } + + void operator()(int a, int b) const + { + code.push_back(a); + code.push_back(b); + } + + void operator()(int a, int b, int c) const + { + code.push_back(a); + code.push_back(b); + code.push_back(c); + } + + std::vector& code; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Our error handler +/////////////////////////////////////////////////////////////////////////////// +struct error_handler_ +{ + template + struct result { typedef void type; }; + + template + void operator()( + std::string const& what + , Iterator err_pos, Iterator last) const + { + std::cout + << "Error! Expecting " + << what // what failed? + << " here: \"" + << std::string(err_pos, last) // iterators to error-pos, end + << "\"" + << std::endl + ; + } +}; + +function const error_handler = error_handler_(); + +/////////////////////////////////////////////////////////////////////////////// +// Our expression grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +struct expression : grammar_def +{ + expression(std::vector& code, symbols& vars); + + rule + expr, additive_expr, multiplicative_expr + , unary_expr, primary_expr, variable; + + std::vector& code; + symbols& vars; + function op; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Our statement grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +struct var_adder +{ + template + struct result { typedef void type; }; + + var_adder(symbols& vars) + : vars(vars) + { + } + + void operator()(std::string const& var, int& nvars) const + { + vars.add(var.begin(), var.end(), nvars++); + }; + + symbols& vars; +}; + +template +struct statement : grammar_def +{ + statement(std::vector& code); + + std::vector& code; + symbols vars; + int nvars; + + expression expr_def; + grammar > expr; + rule start, var_decl; + rule identifier; + rule var_ref; + rule, space_type> assignment; + rule assignment_rhs; + + function add_var; + function op; +}; + +#endif + diff --git a/example/qi/calc6/calc6a.cpp b/example/qi/calc6/calc6a.cpp new file mode 100644 index 000000000..1f4be6ce0 --- /dev/null +++ b/example/qi/calc6/calc6a.cpp @@ -0,0 +1,58 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "calc6.hpp" + +void vmachine::execute(std::vector& code, int nvars) +{ + std::vector::iterator pc = code.begin(); + std::vector::iterator locals = stack.begin(); + stack_ptr = stack.begin() + nvars; + + while (pc != code.end()) + { + switch (*pc++) + { + case op_neg: + stack_ptr[-1] = -stack_ptr[-1]; + break; + + case op_add: + --stack_ptr; + stack_ptr[-1] += stack_ptr[0]; + break; + + case op_sub: + --stack_ptr; + stack_ptr[-1] -= stack_ptr[0]; + break; + + case op_mul: + --stack_ptr; + stack_ptr[-1] *= stack_ptr[0]; + break; + + case op_div: + --stack_ptr; + stack_ptr[-1] /= stack_ptr[0]; + break; + + case op_load: + *stack_ptr++ = locals[*pc++]; + break; + + case op_store: + --stack_ptr; + locals[*pc++] = stack_ptr[0]; + break; + + case op_int: + *stack_ptr++ = *pc++; + break; + } + } +} + diff --git a/example/qi/calc6/calc6b.cpp b/example/qi/calc6/calc6b.cpp new file mode 100644 index 000000000..44041886e --- /dev/null +++ b/example/qi/calc6/calc6b.cpp @@ -0,0 +1,18 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "calc6b.hpp" + +// This is not really called. Its only purpose is to +// instantiate the constructor of the grammar. +void instantiate_expression() +{ + typedef std::string::const_iterator iterator_type; + symbols vars; + std::vector code; + expression g(code, vars); +} + diff --git a/example/qi/calc6/calc6b.hpp b/example/qi/calc6/calc6b.hpp new file mode 100644 index 000000000..ed03253ac --- /dev/null +++ b/example/qi/calc6/calc6b.hpp @@ -0,0 +1,70 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CALC6B) +#define BOOST_SPIRIT_CALC6B + +#include "calc6.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// Our expression grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +expression::expression(std::vector& code, symbols& vars) + : code(code) + , vars(vars) + , op(code) +{ + expr = + additive_expr.alias() + ; + + additive_expr = + multiplicative_expr + >> *( ('+' > multiplicative_expr [op(op_add)]) + | ('-' > multiplicative_expr [op(op_sub)]) + ) + ; + + multiplicative_expr = + unary_expr + >> *( ('*' > unary_expr [op(op_mul)]) + | ('/' > unary_expr [op(op_div)]) + ) + ; + + unary_expr = + primary_expr + | ('-' > primary_expr [op(op_neg)]) + | ('+' > primary_expr) + ; + + primary_expr = + uint_ [op(op_int, _1)] + | variable + | '(' > expr > ')' + ; + + variable = + ( + lexeme[ + vars + >> !(alnum | '_') // make sure we have whole words + ] + ) [op(op_load, _1)] + ; + + expr.name("expression"); + additive_expr.name("additive-expression"); + multiplicative_expr.name("multiplicative-expression"); + unary_expr.name("unary-expression"); + primary_expr.name("primary-expression"); + variable.name("variable"); + + on_error(expr, error_handler(_4, _3, _2)); +} + +#endif diff --git a/example/qi/calc6/calc6c.cpp b/example/qi/calc6/calc6c.cpp new file mode 100644 index 000000000..e6b83eb9f --- /dev/null +++ b/example/qi/calc6/calc6c.cpp @@ -0,0 +1,16 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "calc6c.hpp" + +// This is not really called. Its only purpose is to +// instantiate the constructor of the grammar. +void instantiate_statement() +{ + typedef std::string::const_iterator iterator_type; + std::vector code; + statement g(code); +} diff --git a/example/qi/calc6/calc6c.hpp b/example/qi/calc6/calc6c.hpp new file mode 100644 index 000000000..4687e32aa --- /dev/null +++ b/example/qi/calc6/calc6c.hpp @@ -0,0 +1,65 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CALC6C) +#define BOOST_SPIRIT_CALC6C + +#include "calc6.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// Our statement grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +statement::statement(std::vector& code) + : code(code) + , nvars(0) + , expr_def(code, vars) + , expr(expr_def, expr_def.expr) + , add_var(vars) + , op(code) +{ + identifier %= + raw[lexeme[alpha >> *(alnum | '_')]] + ; + + var_ref = + lexeme + [ + vars [_val = _1] + >> !(alnum | '_') // make sure we have whole words + ] + ; + + var_decl = + "var" + > !var_ref // make sure the variable isn't redeclared + > identifier [add_var(_1, ref(nvars))] + > (';' | '=' > assignment_rhs(ref(nvars)-1)) + ; + + assignment = + var_ref [_a = _1] + >> '=' + > assignment_rhs(_a) + ; + + assignment_rhs = + expr + > char_(';') [op(op_store, _r1)] + ; + + start = +(var_decl | assignment); + + identifier.name("identifier"); + var_ref.name("variable-reference"); + var_decl.name("variable-declaration"); + assignment.name("assignment"); + assignment_rhs.name("assignment-rhs"); + + on_error(start, error_handler(_4, _3, _2)); +} + +#endif \ No newline at end of file diff --git a/example/qi/calc7/calc7.cpp b/example/qi/calc7/calc7.cpp new file mode 100644 index 000000000..e34437f1a --- /dev/null +++ b/example/qi/calc7/calc7.cpp @@ -0,0 +1,99 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// Now we'll introduce boolean expressions and control structures. +// Is it obvious now what we are up to? ;-) +// +// [ JDG April 9, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include "calc7.hpp" + +template +bool compile(Grammar const& calc, std::string const& expr) +{ + std::string::const_iterator iter = expr.begin(); + std::string::const_iterator end = expr.end(); + bool r = phrase_parse(iter, end, calc, space); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + return true; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +struct var_printer +{ + var_printer(std::vector const& stack) + : stack(stack) + { + } + + template + void operator()(String const& s, Data const& data) + { + std::cout << " " << s << ": " << stack[data] << std::endl; + } + + std::vector const& stack; +}; + +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Expression parser...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Type some statements... "; + std::cout << "Then type period ('.') to compile, run and print results\n\n"; + + typedef std::string::const_iterator iterator_type; + typedef statement statement; + + vmachine mach; // Our virtual machine + std::vector code; // Our VM code + statement def(code); // Our grammar definition + grammar + calc(def, def.statement_list); // Our grammar + + std::string str; + std::string program; + while (std::getline(std::cin, str)) + { + if (str.empty() || str[0] == '.') + break; + program += str; + } + + if (::compile(calc, program)) + { + mach.execute(code, def.nvars); + + std::cout << "Results------------------\n\n"; + def.vars.for_each(var_printer(mach.get_stack())); + std::cout << "-------------------------\n\n"; + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/calc7/calc7.hpp b/example/qi/calc7/calc7.hpp new file mode 100644 index 000000000..b6088d277 --- /dev/null +++ b/example/qi/calc7/calc7.hpp @@ -0,0 +1,208 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CALC7) +#define BOOST_SPIRIT_CALC7 + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +using boost::phoenix::function; +using boost::phoenix::ref; +using boost::phoenix::size; + +/////////////////////////////////////////////////////////////////////////////// +// The Virtual Machine +/////////////////////////////////////////////////////////////////////////////// +enum byte_code +{ + op_neg, // negate the top stack entry + op_add, // add top two stack entries + op_sub, // subtract top two stack entries + op_mul, // multiply top two stack entries + op_div, // divide top two stack entries + + op_not, // boolean negate the top stack entry + op_eq, // compare the top two stack entries for == + op_neq, // compare the top two stack entries for != + op_lt, // compare the top two stack entries for < + op_lte, // compare the top two stack entries for <= + op_gt, // compare the top two stack entries for > + op_gte, // compare the top two stack entries for >= + + op_and, // logical and top two stack entries + op_or, // logical or top two stack entries + + op_load, // load a variable + op_store, // store a variable + + op_int, // push constant integer into the stack + op_true, // push constant 0 into the stack + op_false, // push constant 1 into the stack + + op_jump_if, // jump to an absolute position in the code if top stack + // evaluates to false + op_jump // jump to an absolute position in the code +}; + +class vmachine +{ +public: + + vmachine(unsigned stackSize = 4096) + : stack(stackSize) + , stack_ptr(stack.begin()) + { + } + + std::vector const& get_stack() const { return stack; }; + void execute(std::vector& code, int nvars); + +private: + + std::vector stack; + std::vector::iterator stack_ptr; +}; + +/////////////////////////////////////////////////////////////////////////////// +// A generic compiler that compiles 1 to 3 codes +/////////////////////////////////////////////////////////////////////////////// +struct compile_op +{ + template + struct result { typedef void type; }; + + compile_op(std::vector& code) + : code(code) + { + } + + void operator()(int a) const + { + code.push_back(a); + } + + void operator()(int a, int b) const + { + code.push_back(a); + code.push_back(b); + } + + void operator()(int a, int b, int c) const + { + code.push_back(a); + code.push_back(b); + code.push_back(c); + } + + std::vector& code; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Our error handler +/////////////////////////////////////////////////////////////////////////////// +struct error_handler_ +{ + template + struct result { typedef void type; }; + + template + void operator()( + std::string const& what + , Iterator err_pos, Iterator last) const + { + std::cout + << "Error! Expecting " + << what // what failed? + << " here: \"" + << std::string(err_pos, last) // iterators to error-pos, end + << "\"" + << std::endl + ; + } +}; + +function const error_handler = error_handler_(); + +/////////////////////////////////////////////////////////////////////////////// +// Our expression grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +struct expression : grammar_def +{ + expression(std::vector& code, symbols& vars); + + rule + expr, equality_expr, relational_expr + , logical_expr, additive_expr, multiplicative_expr + , unary_expr, primary_expr, variable + ; + + std::vector& code; + symbols& vars; + function op; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Our statement grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +struct var_adder +{ + template + struct result { typedef void type; }; + + var_adder(symbols& vars) + : vars(vars) + { + } + + void operator()(std::string const& var, int& nvars) const + { + vars.add(var.begin(), var.end(), nvars++); + }; + + symbols& vars; +}; + +template +struct statement : grammar_def +{ + statement(std::vector& code); + + std::vector& code; + symbols vars; + int nvars; + + expression expr_def; + grammar > expr; + rule + statement_, statement_list, var_decl, compound_statement + ; + + rule, space_type> if_statement; + rule, space_type> while_statement; + rule identifier; + rule var_ref; + rule, space_type> assignment; + rule assignment_rhs; + + function add_var; + function op; +}; + +#endif diff --git a/example/qi/calc7/calc7a.cpp b/example/qi/calc7/calc7a.cpp new file mode 100644 index 000000000..1be7d2851 --- /dev/null +++ b/example/qi/calc7/calc7a.cpp @@ -0,0 +1,125 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "calc7.hpp" + +# pragma warning(disable: 4800) // forcing value to bool 'true' or 'false' + // (performance warning) + +void vmachine::execute(std::vector& code, int nvars) +{ + std::vector::iterator pc = code.begin(); + std::vector::iterator locals = stack.begin(); + stack_ptr = stack.begin() + nvars; + + while (pc != code.end()) + { + switch (*pc++) + { + case op_neg: + stack_ptr[-1] = -stack_ptr[-1]; + break; + + case op_not: + stack_ptr[-1] = !bool(stack_ptr[-1]); + break; + + case op_add: + --stack_ptr; + stack_ptr[-1] += stack_ptr[0]; + break; + + case op_sub: + --stack_ptr; + stack_ptr[-1] -= stack_ptr[0]; + break; + + case op_mul: + --stack_ptr; + stack_ptr[-1] *= stack_ptr[0]; + break; + + case op_div: + --stack_ptr; + stack_ptr[-1] /= stack_ptr[0]; + break; + + case op_eq: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] == stack_ptr[0]); + break; + + case op_neq: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] != stack_ptr[0]); + break; + + case op_lt: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] < stack_ptr[0]); + break; + + case op_lte: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] <= stack_ptr[0]); + break; + + case op_gt: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] > stack_ptr[0]); + break; + + case op_gte: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] >= stack_ptr[0]); + break; + + case op_and: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1]) && bool(stack_ptr[0]); + break; + + case op_or: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1]) || bool(stack_ptr[0]); + break; + + case op_load: + *stack_ptr++ = locals[*pc++]; + break; + + case op_store: + --stack_ptr; + locals[*pc++] = stack_ptr[0]; + break; + + case op_int: + *stack_ptr++ = *pc++; + break; + + case op_true: + *stack_ptr++ = true; + break; + + case op_false: + *stack_ptr++ = false; + break; + + case op_jump: + pc = code.begin() + *pc; + break; + + case op_jump_if: + if (!bool(stack_ptr[-1])) + pc = code.begin() + *pc; + else + ++pc; + --stack_ptr; + break; + } + } +} + diff --git a/example/qi/calc7/calc7b.cpp b/example/qi/calc7/calc7b.cpp new file mode 100644 index 000000000..c0f2c6de4 --- /dev/null +++ b/example/qi/calc7/calc7b.cpp @@ -0,0 +1,17 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "calc7b.hpp" + +// This is not really called. Its only purpose is to +// instantiate the constructor of the grammar. +void instantiate_expression() +{ + typedef std::string::const_iterator iterator_type; + symbols vars; + std::vector code; + expression g(code, vars); +} diff --git a/example/qi/calc7/calc7b.hpp b/example/qi/calc7/calc7b.hpp new file mode 100644 index 000000000..98c1930df --- /dev/null +++ b/example/qi/calc7/calc7b.hpp @@ -0,0 +1,101 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CALC7B) +#define BOOST_SPIRIT_CALC7B + +#include "calc7.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// Our expression grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +expression::expression(std::vector& code, symbols& vars) + : code(code) + , vars(vars) + , op(code) +{ + expr = + equality_expr.alias() + ; + + equality_expr = + relational_expr + >> *( ("==" > relational_expr [op(op_eq)]) + | ("!=" > relational_expr [op(op_neq)]) + ) + ; + + relational_expr = + logical_expr + >> *( ("<=" > logical_expr [op(op_lte)]) + | ('<' > logical_expr [op(op_lt)]) + | (">=" > logical_expr [op(op_gte)]) + | ('>' > logical_expr [op(op_gt)]) + ) + ; + + logical_expr = + additive_expr + >> *( ("&&" > additive_expr [op(op_and)]) + | ("||" > additive_expr [op(op_or)]) + ) + ; + + additive_expr = + multiplicative_expr + >> *( ('+' > multiplicative_expr [op(op_add)]) + | ('-' > multiplicative_expr [op(op_sub)]) + ) + ; + + multiplicative_expr = + unary_expr + >> *( ('*' > unary_expr [op(op_mul)]) + | ('/' > unary_expr [op(op_div)]) + ) + ; + + unary_expr = + primary_expr + | ('!' > primary_expr [op(op_not)]) + | ('-' > primary_expr [op(op_neg)]) + | ('+' > primary_expr) + ; + + primary_expr = + uint_ [op(op_int, _1)] + | variable + | lit("true") [op(op_true)] + | lit("false") [op(op_false)] + | '(' > expr > ')' + ; + + variable = + ( + lexeme[ + vars + >> !(alnum | '_') // make sure we have whole words + ] + ) [op(op_load, _1)] + ; + + expr.name("expression"); + equality_expr.name("equality-expression"); + relational_expr.name("relational-expression"); + logical_expr.name("logical-expression"); + additive_expr.name("additive-expression"); + multiplicative_expr.name("multiplicative-expression"); + unary_expr.name("unary-expression"); + primary_expr.name("primary-expression"); + variable.name("variable"); + + on_error(expr, error_handler(_4, _3, _2)); +} + +#endif + + diff --git a/example/qi/calc7/calc7c.cpp b/example/qi/calc7/calc7c.cpp new file mode 100644 index 000000000..a29595f5a --- /dev/null +++ b/example/qi/calc7/calc7c.cpp @@ -0,0 +1,16 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "calc7c.hpp" + +// This is not really called. Its only purpose is to +// instantiate the constructor of the grammar. +void instantiate_statement() +{ + typedef std::string::const_iterator iterator_type; + std::vector code; + statement g(code); +} diff --git a/example/qi/calc7/calc7c.hpp b/example/qi/calc7/calc7c.hpp new file mode 100644 index 000000000..44d7c2290 --- /dev/null +++ b/example/qi/calc7/calc7c.hpp @@ -0,0 +1,131 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_CALC7C) +#define BOOST_SPIRIT_CALC7C + +#include "calc7.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// Our statement grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +statement::statement(std::vector& code) + : code(code) + , nvars(0) + , expr_def(code, vars) + , expr(expr_def, expr_def.expr) + , add_var(vars) + , op(code) +{ + identifier %= + raw[lexeme[alpha >> *(alnum | '_')]] + ; + + var_ref = + lexeme + [ + vars [_val = _1] + >> !(alnum | '_') // make sure we have whole words + ] + ; + + var_decl = + "var" + > !var_ref // make sure the variable isn't redeclared + > identifier [add_var(_1, ref(nvars))] + > (';' | '=' > assignment_rhs(ref(nvars)-1)) + ; + + assignment = + var_ref [_a = _1] + >> '=' + > assignment_rhs(_a) + ; + + assignment_rhs = + expr + > char_(';') [op(op_store, _r1)] + ; + + if_statement = + lit("if") + >> '(' + > expr [ + op(op_jump_if, 0), // we shall fill this (0) in later + _a = size(ref(code))-1 // mark its position + ] + > ')' + > statement_ [ + // now we know where to jump to (after the if branch) + ref(code)[_a] = size(ref(code)) + ] + >> + -( + lexeme[ + "else" + >> !(alnum | '_') // make sure we have whole words + ] + [ + ref(code)[_a] += 2, // adjust for the "else" jump + op(op_jump, 0), // we shall fill this (0) in later + _a = size(ref(code))-1 // mark its position + ] + > statement_ [ + // now we know where to jump to (after the else branch) + ref(code)[_a] = size(ref(code)) + ] + ) + ; + + while_statement = + lit("while") [ + _a = size(ref(code)) // mark our position + ] + >> '(' + > expr [ + op(op_jump_if, 0), // we shall fill this (0) in later + _b = size(ref(code))-1 // mark its position + ] + > ')' + > statement_ [ + op(op_jump, _a), // loop back + // now we know where to jump to (to exit the loop) + ref(code)[_b] = size(ref(code)) + ] + ; + + compound_statement = + '{' >> -statement_list >> '}' + ; + + statement_ = + var_decl + | assignment + | compound_statement + | if_statement + | while_statement + ; + + statement_list = +statement_; + + identifier.name("identifier"); + var_ref.name("variable-reference"); + var_decl.name("variable-declaration"); + assignment.name("assignment"); + assignment_rhs.name("assignment-rhs"); + if_statement.name("if-statement"); + while_statement.name("while-statement"); + compound_statement.name("compound-statement"); + statement_.name("statement"); + statement_list.name("statement-list"); + + on_error(statement_list, error_handler(_4, _3, _2)); +} + +#endif + + diff --git a/example/qi/complex_number.cpp b/example/qi/complex_number.cpp new file mode 100644 index 000000000..84c971a67 --- /dev/null +++ b/example/qi/complex_number.cpp @@ -0,0 +1,94 @@ +/*============================================================================= + Copyright (c) 2002-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A complex number micro parser. +// +// [ JDG May 10, 2002 ] spirit1 +// [ JDG May 9, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include + +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +/////////////////////////////////////////////////////////////////////////////// +// Our complex number parser/compiler +/////////////////////////////////////////////////////////////////////////////// +template +bool parse_complex(Iterator first, Iterator last, std::complex& c) +{ + double rN = 0.0; + double iN = 0.0; + bool r = phrase_parse(first, last, + + // Begin grammar + ( + '(' >> double_[ref(rN) = _1] + >> -(',' >> double_[ref(iN) = _1]) >> ')' + | double_[ref(rN) = _1] + ), + // End grammar + + space); + + if (!r || first != last) // fail if we did not get a full match + return false; + c = std::complex(rN, iN); + return r; +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA complex number micro parser for Spirit...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout << "Give me a complex number of the form r or (r) or (r,i) \n"; + std::cout << "Type [q or Q] to quit\n\n"; + + std::string str; + while (getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + std::complex c; + if (parse_complex(str.begin(), str.end(), c)) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "got: " << c << std::endl; + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/employee.cpp b/example/qi/employee.cpp new file mode 100644 index 000000000..e5c8e3bc0 --- /dev/null +++ b/example/qi/employee.cpp @@ -0,0 +1,132 @@ +/*============================================================================= + Copyright (c) 2002-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A parser for arbitrary tuples. This example presents a parser +// for an employee structure. +// +// [ JDG May 9, 2007 ] +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +/////////////////////////////////////////////////////////////////////////////// +// Our employee struct +/////////////////////////////////////////////////////////////////////////////// +struct employee +{ + int age; + std::string surname; + std::string forename; + double salary; +}; + +// We need to tell fusion about our employee struct +// to make it a first-class fusion citizen +BOOST_FUSION_ADAPT_STRUCT( + employee, + (int, age) + (std::string, surname) + (std::string, forename) + (double, salary) +) + +/////////////////////////////////////////////////////////////////////////////// +// Our employee parser +/////////////////////////////////////////////////////////////////////////////// +template +struct employee_parser : grammar_def +{ + employee_parser() + { + quoted_string %= lexeme['"' >> +(char_ - '"') >> '"']; + + start %= + lit("employee") + >> '{' + >> int_ >> ',' + >> quoted_string >> ',' + >> quoted_string >> ',' + >> double_ + >> '}' + ; + } + + rule quoted_string; + rule start; +}; + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tAn employee parser for Spirit...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout + << "Give me an employee of the form :" + << "employee{age, \"surname\", \"forename\", salary } \n"; + std::cout << "Type [q or Q] to quit\n\n"; + + typedef std::string::const_iterator iterator_type; + typedef employee_parser employee_parser; + + employee_parser def; // Our grammar definition + grammar g(def); // Our grammar + std::string str; + while (getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + employee emp; + std::string::const_iterator iter = str.begin(); + std::string::const_iterator end = str.end(); + bool r = phrase_parse(iter, end, g, emp, space); + + if (r && iter == end) + { + std::cout << boost::fusion::tuple_open('['); + std::cout << boost::fusion::tuple_close(']'); + std::cout << boost::fusion::tuple_delimiter(", "); + + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "got: " << boost::fusion::as_vector(emp) << std::endl; + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/mini_c/mini_c.cpp b/example/qi/mini_c/mini_c.cpp new file mode 100644 index 000000000..af8329754 --- /dev/null +++ b/example/qi/mini_c/mini_c.cpp @@ -0,0 +1,123 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// Not a calculator anymore, right? :-) +// +// [ JDG April 10, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include "mini_c.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// Our main compiler +/////////////////////////////////////////////////////////////////////////////// +template +bool compile(Grammar const& prog, std::string const& expr) +{ + typedef white_space_def white_space_def; + white_space_def white_; // Our skipper definition + grammar white_space(white_); // Our skipper + + std::string::const_iterator iter = expr.begin(); + std::string::const_iterator end = expr.end(); + bool r = phrase_parse(iter, end, prog, white_space); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + return true; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int main(int argc, char **argv) +{ + char const* filename; + if (argc > 1) + { + filename = argv[1]; + } + else + { + std::cerr << "Error: No input file provided." << std::endl; + return 1; + } + + std::ifstream in(filename, std::ios_base::in); + + if (!in) + { + std::cerr << "Error: Could not open input file: " + << filename << std::endl; + return 1; + } + + std::string source_code; // We will read the contents here. + in.unsetf(std::ios::skipws); // No white space skipping! + std::copy( + std::istream_iterator(in), + std::istream_iterator(), + std::back_inserter(source_code)); + + typedef std::string::const_iterator iterator_type; + typedef program program; + + vmachine mach; // Our virtual machine + std::vector code; // Our VM code + program def(code); // Our grammar definition + grammar prog(def); // Our grammar + + if (::compile(prog, source_code)) + { + std::string fmain("main"); + std::string::iterator fbegin = fmain.begin(); + function_info* f = def.functions.lookup()->find(fbegin, fmain.end()); + if (f == 0) + { + std::cerr << "Error: main function not defined" << std::endl; + return 1; + } + + int nargs = argc-2; + if (f->arity != nargs) + { + std::cerr << "Error: main function requires " << f->arity << " arguments." << std::endl; + std::cerr << nargs << "supplied." << std::endl; + return 1; + } + + for (int i = 0; i < nargs; ++i) + mach.stack[i] = boost::lexical_cast(argv[i+2]); + + int r = mach.execute( + code // code + , code.begin() + f->address // pc + , mach.stack.begin() // frame_ptr + ); + + std::cout << "-------------------------\n"; + std::cout << "Result: " << r << std::endl; + std::cout << "-------------------------\n\n"; + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/mini_c/mini_c.hpp b/example/qi/mini_c/mini_c.hpp new file mode 100644 index 000000000..070e92f8c --- /dev/null +++ b/example/qi/mini_c/mini_c.hpp @@ -0,0 +1,354 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_MINI_C) +#define BOOST_SPIRIT_MINI_C + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +using boost::phoenix::function; +using boost::phoenix::ref; +using boost::phoenix::size; + +/////////////////////////////////////////////////////////////////////////////// +// The Virtual Machine +/////////////////////////////////////////////////////////////////////////////// +enum byte_code +{ + op_neg, // negate the top stack entry + op_add, // add top two stack entries + op_sub, // subtract top two stack entries + op_mul, // multiply top two stack entries + op_div, // divide top two stack entries + + op_not, // boolean negate the top stack entry + op_eq, // compare the top two stack entries for == + op_neq, // compare the top two stack entries for != + op_lt, // compare the top two stack entries for < + op_lte, // compare the top two stack entries for <= + op_gt, // compare the top two stack entries for > + op_gte, // compare the top two stack entries for >= + + op_and, // logical and top two stack entries + op_or, // logical or top two stack entries + + op_load, // load a variable + op_store, // store a variable + + op_int, // push constant integer into the stack + op_true, // push constant 0 into the stack + op_false, // push constant 1 into the stack + + op_jump_if, // jump to an absolute position in the code if top stack + // evaluates to false + op_jump, // jump to an absolute position in the code + + op_stk_adj, // adjust the stack (for args and locals) + op_call, // function call + op_return // return from function +}; + +class vmachine +{ +public: + + vmachine(unsigned stackSize = 4096) + : stack(stackSize) + { + } + + int execute( + std::vector const& code // the program code + , std::vector::const_iterator pc // program counter + , std::vector::iterator frame_ptr // start of arguments and locals + ); + + std::vector stack; +}; + +/////////////////////////////////////////////////////////////////////////////// +// A generic compiler that compiles 1 to 3 codes +/////////////////////////////////////////////////////////////////////////////// +struct function_info +{ + int arity; + int address; +}; + +struct compile_op +{ + template + struct result { typedef void type; }; + + compile_op(std::vector& code) + : code(code) + { + } + + void operator()(int a) const + { + code.push_back(a); + } + + void operator()(int a, int b) const + { + code.push_back(a); + code.push_back(b); + } + + void operator()(int a, int b, int c) const + { + code.push_back(a); + code.push_back(b); + code.push_back(c); + } + + // special overload for function calls + void operator()(function_info const& info, int got_nargs, bool& parse_result) const + { + if (got_nargs == info.arity) + { + code.push_back(op_call); + code.push_back(info.arity); + code.push_back(info.address); + } + else + { + parse_result = false; // fail the parse + std::cerr << "wrong number of args" << std::endl; + } + } + + std::vector& code; +}; + + +/////////////////////////////////////////////////////////////////////////////// +// Our error handler +/////////////////////////////////////////////////////////////////////////////// +struct error_handler_ +{ + template + struct result { typedef void type; }; + + template + void operator()( + std::string const& what + , Iterator err_pos, Iterator last) const + { + std::cout + << "Error! Expecting " + << what // what failed? + << " here: \"" + << std::string(err_pos, last) // iterators to error-pos, end + << "\"" + << std::endl + ; + } +}; + +function const error_handler = error_handler_(); + +/////////////////////////////////////////////////////////////////////////////// +// A functor that adds variables to our (variables) symbol-table +/////////////////////////////////////////////////////////////////////////////// +struct var_adder +{ + template + struct result { typedef void type; }; + + var_adder(symbols& vars, int& nvars) + : vars(vars), nvars(nvars) + { + } + + void operator()(std::string const& var) const + { + vars.add(var.begin(), var.end(), nvars++); + }; + + symbols& vars; + int& nvars; +}; + +/////////////////////////////////////////////////////////////////////////////// +// A functor that adds functions to our (function) symbol-table +/////////////////////////////////////////////////////////////////////////////// +struct function_adder +{ + template + struct result { typedef void type; }; + + function_adder(symbols& functions) + : functions(functions) + { + } + + void operator()(std::string const& function_id, int arity, int address) const + { + function_info info = {arity, address}; + functions.add(function_id.begin(), function_id.end(), info); + }; + + symbols& functions; +}; + +/////////////////////////////////////////////////////////////////////////////// +// A functor that resets the function-related state variables +/////////////////////////////////////////////////////////////////////////////// +struct function_state_reset +{ + template + struct result { typedef void type; }; + + function_state_reset( + std::vector& code + , symbols& vars + , int& nvars) + : code(code) + , vars(vars) + , nvars(nvars) + { + } + + void operator()(int address) const + { + code[address+1] = nvars; + nvars = 0; // reset + vars.clear(); // reset + }; + + std::vector& code; + symbols& vars; + int& nvars; +}; + +/////////////////////////////////////////////////////////////////////////////// +// White-space and comments grammar definition +/////////////////////////////////////////////////////////////////////////////// +template +struct white_space_def : grammar_def +{ + white_space_def() + { + start = + space // tab/space/cr/lf + | "/*" >> *(char_ - "*/") >> "*/" // C-style comments + ; + } + + rule start; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Our expression grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +struct expression : grammar_def > > +{ + expression( + std::vector& code + , symbols& vars + , symbols& functions); + + typedef grammar > white_space; + + rule + expr, equality_expr, relational_expr + , logical_expr, additive_expr, multiplicative_expr + , unary_expr, primary_expr, variable + ; + + rule, white_space> function_call; + + std::vector& code; + symbols& vars; + symbols& functions; + function op; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Our statement grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +struct statement : grammar_def > > +{ + statement(std::vector& code, symbols& functions); + + typedef grammar > white_space; + + std::vector& code; + symbols vars; + symbols& functions; + int nvars; + bool has_return; + + expression expr_def; + grammar > expr; + rule + statement_, statement_list, var_decl, compound_statement + , return_statement; + + rule, white_space> if_statement; + rule, white_space> while_statement; + rule identifier; + rule var_ref; + rule, white_space> assignment; + rule assignment_rhs; + + function add_var; + function op; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Our program grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +struct program : grammar_def > > +{ + program(std::vector& code); + + typedef grammar > white_space; + + std::vector& code; + rule identifier; + rule start; + + typedef + locals< + std::string // function name + , int // address + > + function_locals; + + symbols functions; + statement statement_def; + grammar > statement; + + rule function; + boost::phoenix::function add_function; + boost::phoenix::function state_reset; + boost::phoenix::function op; +}; + +#endif + + diff --git a/example/qi/mini_c/mini_ca.cpp b/example/qi/mini_c/mini_ca.cpp new file mode 100644 index 000000000..cc9329ec8 --- /dev/null +++ b/example/qi/mini_c/mini_ca.cpp @@ -0,0 +1,155 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "mini_c.hpp" + +# pragma warning(disable: 4800) // forcing value to bool 'true' or 'false' + // (performance warning) + +int vmachine::execute( + std::vector const& code + , std::vector::const_iterator pc + , std::vector::iterator frame_ptr +) +{ + std::vector::iterator stack_ptr = frame_ptr; + + while (true) + { + BOOST_ASSERT(pc != code.end()); + + switch (*pc++) + { + case op_neg: + stack_ptr[-1] = -stack_ptr[-1]; + break; + + case op_not: + stack_ptr[-1] = !bool(stack_ptr[-1]); + break; + + case op_add: + --stack_ptr; + stack_ptr[-1] += stack_ptr[0]; + break; + + case op_sub: + --stack_ptr; + stack_ptr[-1] -= stack_ptr[0]; + break; + + case op_mul: + --stack_ptr; + stack_ptr[-1] *= stack_ptr[0]; + break; + + case op_div: + --stack_ptr; + stack_ptr[-1] /= stack_ptr[0]; + break; + + case op_eq: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] == stack_ptr[0]); + break; + + case op_neq: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] != stack_ptr[0]); + break; + + case op_lt: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] < stack_ptr[0]); + break; + + case op_lte: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] <= stack_ptr[0]); + break; + + case op_gt: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] > stack_ptr[0]); + break; + + case op_gte: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1] >= stack_ptr[0]); + break; + + case op_and: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1]) && bool(stack_ptr[0]); + break; + + case op_or: + --stack_ptr; + stack_ptr[-1] = bool(stack_ptr[-1]) || bool(stack_ptr[0]); + break; + + case op_load: + *stack_ptr++ = frame_ptr[*pc++]; + break; + + case op_store: + --stack_ptr; + frame_ptr[*pc++] = stack_ptr[0]; + break; + + case op_int: + *stack_ptr++ = *pc++; + break; + + case op_true: + *stack_ptr++ = true; + break; + + case op_false: + *stack_ptr++ = false; + break; + + case op_jump: + pc = code.begin() + *pc; + break; + + case op_jump_if: + if (!bool(stack_ptr[-1])) + pc = code.begin() + *pc; + else + ++pc; + --stack_ptr; + break; + + case op_stk_adj: + stack_ptr += *pc++; + break; + + case op_call: + { + int nargs = *pc++; + int jump = *pc++; + + // a function call is a recursive call to execute + int r = execute( + code + , code.begin() + jump + , stack_ptr - nargs + ); + + // cleanup after return from function + stack_ptr[-nargs] = r; // get return value + stack_ptr -= (nargs - 1); // the stack will now contain + // the return value + } + break; + + case op_return: + return stack_ptr[-1]; + } + } +} + diff --git a/example/qi/mini_c/mini_cb.cpp b/example/qi/mini_c/mini_cb.cpp new file mode 100644 index 000000000..64f507144 --- /dev/null +++ b/example/qi/mini_c/mini_cb.cpp @@ -0,0 +1,19 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "mini_cb.hpp" + +// This is not really called. Its only purpose is to +// instantiate the constructor of the grammar. +void instantiate_expression() +{ + typedef std::string::const_iterator iterator_type; + symbols functions; + symbols vars; + std::vector code; + expression g(code, vars, functions); +} + diff --git a/example/qi/mini_c/mini_cb.hpp b/example/qi/mini_c/mini_cb.hpp new file mode 100644 index 000000000..5eab8a769 --- /dev/null +++ b/example/qi/mini_c/mini_cb.hpp @@ -0,0 +1,115 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_MINI_CB) +#define BOOST_SPIRIT_MINI_CB + +#include "mini_c.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// Our expression grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +expression::expression( + std::vector& code + , symbols& vars + , symbols& functions) + : code(code) + , vars(vars) + , functions(functions) + , op(code) +{ + expr = + equality_expr.alias() + ; + + equality_expr = + relational_expr + >> *( ("==" > relational_expr [op(op_eq)]) + | ("!=" > relational_expr [op(op_neq)]) + ) + ; + + relational_expr = + logical_expr + >> *( ("<=" > logical_expr [op(op_lte)]) + | ('<' > logical_expr [op(op_lt)]) + | (">=" > logical_expr [op(op_gte)]) + | ('>' > logical_expr [op(op_gt)]) + ) + ; + + logical_expr = + additive_expr + >> *( ("&&" > additive_expr [op(op_and)]) + | ("||" > additive_expr [op(op_or)]) + ) + ; + + additive_expr = + multiplicative_expr + >> *( ('+' > multiplicative_expr [op(op_add)]) + | ('-' > multiplicative_expr [op(op_sub)]) + ) + ; + + multiplicative_expr = + unary_expr + >> *( ('*' > unary_expr [op(op_mul)]) + | ('/' > unary_expr [op(op_div)]) + ) + ; + + unary_expr = + primary_expr + | ('!' > primary_expr [op(op_not)]) + | ('-' > primary_expr [op(op_neg)]) + | ('+' > primary_expr) + ; + + primary_expr = + uint_ [op(op_int, _1)] + | variable + | function_call + | lit("true") [op(op_true)] + | lit("false") [op(op_false)] + | '(' > expr > ')' + ; + + variable = + ( + lexeme[ + vars + >> !(alnum | '_') // make sure we have whole words + ] + ) [op(op_load, _1)] + ; + + function_call = + functions [_a = _1] + >> '(' + >> -( + expr [++_b] + >> *(',' > expr [++_b]) + ) + > char_(')') [op(_a, _b, pass)] + ; + + expr.name("expression"); + equality_expr.name("equality-expression"); + relational_expr.name("relational-expression"); + logical_expr.name("logical-expression"); + additive_expr.name("additive-expression"); + multiplicative_expr.name("multiplicative-expression"); + unary_expr.name("unary-expression"); + primary_expr.name("primary-expression"); + variable.name("variable"); + function_call.name("function-call"); + + on_error(expr, error_handler(_4, _3, _2)); +} + +#endif diff --git a/example/qi/mini_c/mini_cc.cpp b/example/qi/mini_c/mini_cc.cpp new file mode 100644 index 000000000..db9b509f2 --- /dev/null +++ b/example/qi/mini_c/mini_cc.cpp @@ -0,0 +1,17 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "mini_cc.hpp" + +// This is not really called. Its only purpose is to +// instantiate the constructor of the grammar. +void instantiate_statement() +{ + typedef std::string::const_iterator iterator_type; + symbols functions; + std::vector code; + statement g(code, functions); +} diff --git a/example/qi/mini_c/mini_cc.hpp b/example/qi/mini_c/mini_cc.hpp new file mode 100644 index 000000000..dc866f2f8 --- /dev/null +++ b/example/qi/mini_c/mini_cc.hpp @@ -0,0 +1,144 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_MINI_CC) +#define BOOST_SPIRIT_MINI_CC + +#include "mini_c.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// Our statement grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +statement::statement( + std::vector& code, symbols& functions) + : code(code) + , functions(functions) + , nvars(0) + , expr_def(code, vars, functions) + , expr(expr_def, expr_def.expr) + , add_var(var_adder(vars, nvars)) + , op(code) +{ + identifier %= + raw[lexeme[alpha >> *(alnum | '_')]] + ; + + var_ref = + lexeme + [ + vars [_val = _1] + >> !(alnum | '_') // make sure we have whole words + ] + ; + + var_decl = + "int" + > !var_ref // make sure the variable isn't redeclared + > identifier [add_var(_1)] + > (';' | '=' > assignment_rhs(ref(nvars)-1)) + ; + + assignment = + var_ref [_a = _1] + >> '=' + > assignment_rhs(_a) + ; + + assignment_rhs = + expr + > char_(';') [op(op_store, _r1)] + ; + + if_statement = + lit("if") + >> '(' + > expr [ + op(op_jump_if, 0), // we shall fill this (0) in later + _a = size(ref(code))-1 // mark its position + ] + > ')' + > statement_ [ + // now we know where to jump to (after the if branch) + ref(code)[_a] = size(ref(code)) + ] + >> + -( + lexeme[ + "else" + >> !(alnum | '_') // make sure we have whole words + ] [ + ref(code)[_a] += 2, // adjust for the "else" jump + op(op_jump, 0), // we shall fill this (0) in later + _a = size(ref(code))-1 // mark its position + ] + > statement_ [ + // now we know where to jump to (after the else branch) + ref(code)[_a] = size(ref(code)) + ] + ) + ; + + while_statement = + lit("while") [ + _a = size(ref(code)) // mark our position + ] + >> '(' + > expr [ + op(op_jump_if, 0), // we shall fill this (0) in later + _b = size(ref(code))-1 // mark its position + ] + > ')' + > statement_ [ + op(op_jump, _a), // loop back + // now we know where to jump to (to exit the loop) + ref(code)[_b] = size(ref(code)) + ] + ; + + compound_statement = + '{' >> -statement_list >> '}' + ; + + return_statement = + lexeme[ + "return" + >> !(alnum | '_') // make sure we have whole words + ] + >> -( + eps(ref(has_return)) > expr [op(op_return)] + ) + > ';' + ; + + statement_ = + var_decl + | assignment + | compound_statement + | if_statement + | while_statement + | return_statement + ; + + statement_list = +statement_; + + identifier.name("identifier"); + var_ref.name("variable-reference"); + var_decl.name("variable-declaration"); + assignment.name("assignment"); + assignment_rhs.name("assignment-rhs"); + if_statement.name("if-statement"); + while_statement.name("while-statement"); + compound_statement.name("compound-statement"); + return_statement.name("return-statement"); + statement_.name("statement"); + statement_list.name("statement-list"); + + on_error(statement_list, error_handler(_4, _3, _2)); +} + +#endif + diff --git a/example/qi/mini_c/mini_cd.cpp b/example/qi/mini_c/mini_cd.cpp new file mode 100644 index 000000000..cc1ae1431 --- /dev/null +++ b/example/qi/mini_c/mini_cd.cpp @@ -0,0 +1,16 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include "mini_cd.hpp" + +// This is not really called. Its only purpose is to +// instantiate the constructor of the grammar. +void instantiate_program() +{ + typedef std::string::const_iterator iterator_type; + std::vector code; + program g(code); +} diff --git a/example/qi/mini_c/mini_cd.hpp b/example/qi/mini_c/mini_cd.hpp new file mode 100644 index 000000000..2d0cee886 --- /dev/null +++ b/example/qi/mini_c/mini_cd.hpp @@ -0,0 +1,71 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_MINI_CD) +#define BOOST_SPIRIT_MINI_CD + +#include "mini_c.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// Our program grammar and compiler +/////////////////////////////////////////////////////////////////////////////// +template +program::program(std::vector& code) + : code(code) + , statement_def(code, functions) + , statement(statement_def, statement_def.statement_list) + , add_function(function_adder(functions)) + , state_reset(function_state_reset(code, statement_def.vars, statement_def.nvars)) + , op(code) +{ + bool& has_return = statement_def.has_return; + int& nvars = statement_def.nvars; + boost::phoenix::function& add_var = statement_def.add_var; + + identifier %= + raw[lexeme[alpha >> *(alnum | '_')]] + ; + + function = + ( + lit("void") [ref(has_return) = false] + | lit("int") [ref(has_return) = true] + ) + >> !functions // no duplicate functions! + >> identifier [_a = _1] + >> '(' + > -( + identifier [add_var(_1)] + >> *(',' > identifier [add_var(_1)]) + ) + > ')' + > char_('{') [ + _b = size(ref(code)), + add_function( + _a // function name + , ref(nvars) // arity + , size(ref(code)) // address + ), + op(op_stk_adj, 0) // adjust this later + ] + > statement + > char_('}') [state_reset(_b)] + ; + + start = + +function + ; + + identifier.name("identifier"); + function.name("function"); + start.name("program"); + + on_error(start, error_handler(_4, _3, _2)); +} + +#endif + + diff --git a/example/qi/mini_xml1.cpp b/example/qi/mini_xml1.cpp new file mode 100644 index 000000000..fd4b1e8a6 --- /dev/null +++ b/example/qi/mini_xml1.cpp @@ -0,0 +1,224 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A mini XML-like parser +// +// [ JDG March 25, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +namespace fusion = boost::fusion; +namespace phoenix = boost::phoenix; + +using phoenix::at_c; +using phoenix::push_back; + +/////////////////////////////////////////////////////////////////////////////// +// Our mini XML tree representation +/////////////////////////////////////////////////////////////////////////////// +struct mini_xml; + +typedef + boost::variant< + boost::recursive_wrapper + , std::string + > +mini_xml_node; + +struct mini_xml +{ + std::string name; // tag name + std::vector children; // children +}; + +// We need to tell fusion about our mini_xml struct +// to make it a first-class fusion citizen +BOOST_FUSION_ADAPT_STRUCT( + mini_xml, + (std::string, name) + (std::vector, children) +) + +/////////////////////////////////////////////////////////////////////////////// +// Print out the mini xml tree +/////////////////////////////////////////////////////////////////////////////// +int const tabsize = 4; + +void tab(int indent) +{ + for (int i = 0; i < indent; ++i) + std::cout << ' '; +} + +struct mini_xml_printer +{ + mini_xml_printer(int indent = 0) + : indent(indent) + { + } + + void operator()(mini_xml const& xml) const; + + int indent; +}; + +struct mini_xml_node_printer : boost::static_visitor<> +{ + mini_xml_node_printer(int indent = 0) + : indent(indent) + { + } + + void operator()(mini_xml const& xml) const + { + mini_xml_printer(indent+tabsize)(xml); + } + + void operator()(std::string const& text) const + { + tab(indent+tabsize); + std::cout << "text: \"" << text << '"' << std::endl; + } + + int indent; +}; + +void mini_xml_printer::operator()(mini_xml const& xml) const +{ + tab(indent); + std::cout << "tag: " << xml.name << std::endl; + tab(indent); + std::cout << '{' << std::endl; + + BOOST_FOREACH(mini_xml_node const& node, xml.children) + { + boost::apply_visitor(mini_xml_node_printer(indent), node); + } + + tab(indent); + std::cout << '}' << std::endl; +} + +/////////////////////////////////////////////////////////////////////////////// +// Our mini XML grammar definition +/////////////////////////////////////////////////////////////////////////////// +template +struct mini_xml_def : grammar_def +{ + mini_xml_def() + { + text = lexeme[+(char_ - '<') [_val += _1]]; + node = (xml | text) [_val = _1]; + + start_tag = + '<' + >> lexeme[+(char_ - '>') [_val += _1]] + >> '>' + ; + + end_tag = + "> lit(_r1) + >> '>' + ; + + xml = + start_tag [at_c<0>(_val) = _1] + >> *node [push_back(at_c<1>(_val), _1)] + >> end_tag(at_c<0>(_val)) + ; + } + + rule xml; + rule node; + rule text; + rule start_tag; + rule end_tag; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int main(int argc, char **argv) +{ + char const* filename; + if (argc > 1) + { + filename = argv[1]; + } + else + { + std::cerr << "Error: No input file provided." << std::endl; + return 1; + } + + std::ifstream in(filename, std::ios_base::in); + + if (!in) + { + std::cerr << "Error: Could not open input file: " + << filename << std::endl; + return 1; + } + + std::string storage; // We will read the contents here. + in.unsetf(std::ios::skipws); // No white space skipping! + std::copy( + std::istream_iterator(in), + std::istream_iterator(), + std::back_inserter(storage)); + + typedef mini_xml_def mini_xml_def; + mini_xml_def def; // Our grammar definition + grammar xml(def, def.xml); // Our grammar + mini_xml ast; // our tree + + std::string::const_iterator iter = storage.begin(); + std::string::const_iterator end = storage.end(); + bool r = phrase_parse(iter, end, xml, ast, space); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + mini_xml_printer printer; + printer(ast); + return 0; + } + else + { + std::string::const_iterator some = iter+30; + std::string context(iter, (some>end)?end:some); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \": " << context << "...\"\n"; + std::cout << "-------------------------\n"; + return 1; + } +} + + diff --git a/example/qi/mini_xml2.cpp b/example/qi/mini_xml2.cpp new file mode 100644 index 000000000..e306a2a94 --- /dev/null +++ b/example/qi/mini_xml2.cpp @@ -0,0 +1,225 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A mini XML-like parser +// +// [ JDG March 25, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +namespace fusion = boost::fusion; +namespace phoenix = boost::phoenix; + +using phoenix::at_c; +using phoenix::push_back; + +/////////////////////////////////////////////////////////////////////////////// +// Our mini XML tree representation +/////////////////////////////////////////////////////////////////////////////// +struct mini_xml; + +typedef + boost::variant< + boost::recursive_wrapper + , std::string + > +mini_xml_node; + +struct mini_xml +{ + std::string name; // tag name + std::vector children; // children +}; + +// We need to tell fusion about our mini_xml struct +// to make it a first-class fusion citizen +BOOST_FUSION_ADAPT_STRUCT( + mini_xml, + (std::string, name) + (std::vector, children) +) + +/////////////////////////////////////////////////////////////////////////////// +// Print out the mini xml tree +/////////////////////////////////////////////////////////////////////////////// +int const tabsize = 4; + +void tab(int indent) +{ + for (int i = 0; i < indent; ++i) + std::cout << ' '; +} + +struct mini_xml_printer +{ + mini_xml_printer(int indent = 0) + : indent(indent) + { + } + + void operator()(mini_xml const& xml) const; + + int indent; +}; + +struct mini_xml_node_printer : boost::static_visitor<> +{ + mini_xml_node_printer(int indent = 0) + : indent(indent) + { + } + + void operator()(mini_xml const& xml) const + { + mini_xml_printer(indent+tabsize)(xml); + } + + void operator()(std::string const& text) const + { + tab(indent+tabsize); + std::cout << "text: \"" << text << '"' << std::endl; + } + + int indent; +}; + +void mini_xml_printer::operator()(mini_xml const& xml) const +{ + tab(indent); + std::cout << "tag: " << xml.name << std::endl; + tab(indent); + std::cout << '{' << std::endl; + + BOOST_FOREACH(mini_xml_node const& node, xml.children) + { + boost::apply_visitor(mini_xml_node_printer(indent), node); + } + + tab(indent); + std::cout << '}' << std::endl; +} + +/////////////////////////////////////////////////////////////////////////////// +// Our mini XML grammar definition +/////////////////////////////////////////////////////////////////////////////// +template +struct mini_xml_def + : grammar_def, space_type> +{ + mini_xml_def() + { + text %= lexeme[+(char_ - '<')]; + node %= xml | text; + + start_tag %= + '<' + >> lexeme[+(char_ - '>')] + >> '>' + ; + + end_tag = + "> lit(_r1) + >> '>' + ; + + xml %= + start_tag[_a = _1] + >> *node + >> end_tag(_a) + ; + } + + rule, space_type> xml; + rule node; + rule text; + rule start_tag; + rule end_tag; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int main(int argc, char **argv) +{ + char const* filename; + if (argc > 1) + { + filename = argv[1]; + } + else + { + std::cerr << "Error: No input file provided." << std::endl; + return 1; + } + + std::ifstream in(filename, std::ios_base::in); + + if (!in) + { + std::cerr << "Error: Could not open input file: " + << filename << std::endl; + return 1; + } + + std::string storage; // We will read the contents here. + in.unsetf(std::ios::skipws); // No white space skipping! + std::copy( + std::istream_iterator(in), + std::istream_iterator(), + std::back_inserter(storage)); + + typedef mini_xml_def mini_xml_def; + mini_xml_def def; // Our grammar definition + grammar xml(def, def.xml); // Our grammar + mini_xml ast; // our tree + + std::string::const_iterator iter = storage.begin(); + std::string::const_iterator end = storage.end(); + bool r = phrase_parse(iter, end, xml, ast, space); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + mini_xml_printer printer; + printer(ast); + return 0; + } + else + { + std::string::const_iterator some = iter+30; + std::string context(iter, (some>end)?end:some); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \": " << context << "...\"\n"; + std::cout << "-------------------------\n"; + return 1; + } +} + + diff --git a/example/qi/mini_xml_karma.cpp b/example/qi/mini_xml_karma.cpp new file mode 100644 index 000000000..e2bea4693 --- /dev/null +++ b/example/qi/mini_xml_karma.cpp @@ -0,0 +1,262 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + Copyright (c) 2001-2007 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A mini XML-like parser, Karma is used to print out the generated AST +// +// [ JDG March 25, 2007 ] spirit2 +// [ HK April 02, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::karma; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +namespace fusion = boost::fusion; +namespace phoenix = boost::phoenix; + +using phoenix::at_c; +using phoenix::push_back; + +/////////////////////////////////////////////////////////////////////////////// +// Our mini XML tree representation +/////////////////////////////////////////////////////////////////////////////// +struct mini_xml; + +typedef + boost::variant< + boost::recursive_wrapper + , std::string + > +mini_xml_node; + +struct mini_xml +{ + std::string name; // tag name + std::vector children; // children +}; + +// We need to tell fusion about our mini_xml struct +// to make it a first-class fusion citizen +BOOST_FUSION_ADAPT_STRUCT( + mini_xml, + (std::string, name) + (std::vector, children) +) + +/////////////////////////////////////////////////////////////////////////////// +// Our mini XML grammar definition +/////////////////////////////////////////////////////////////////////////////// +template +struct mini_xml_parser : + qi::grammar_def +{ + mini_xml_parser() + { + text = lexeme[+(char_ - '<') [text.val += _1]]; + node = (xml | text) [node.val = _1]; + + start_tag = + '<' + >> lexeme[+(char_ - '>') [start_tag.val += _1]] + >> '>' + ; + + end_tag = + "> lit(end_tag._1) + >> '>' + ; + + xml = + start_tag [at_c<0>(xml.val) = _1] + >> *node [push_back(at_c<1>(xml.val), _1)] + >> end_tag(at_c<0>(xml.val)) + ; + } + + qi::rule xml; + qi::rule node; + qi::rule text; + qi::rule start_tag; + qi::rule end_tag; +}; + +/////////////////////////////////////////////////////////////////////////////// +// +/////////////////////////////////////////////////////////////////////////////// +template +struct string_appender +{ + string_appender(String& s) + : str(s) + {} + + template + void operator()(T const &x) const + { + str += x; + } + + String& str; +}; + +template +inline string_appender +make_string_appender(String& str) +{ + return string_appender(str); +} + +template +struct output_iterator +{ + typedef std::basic_string string_type; + typedef string_appender appender_type; + typedef boost::function_output_iterator type; + + static type + call(std::basic_string& str) + { + return boost::make_function_output_iterator( + make_string_appender(str)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// +/////////////////////////////////////////////////////////////////////////////// +template +struct mini_xml_generator + : boost::spirit::karma::grammar_def +{ +// typedef karma::grammar_def base_type; +// boost::mpl::print x; + + mini_xml_generator() + { +// text = verbatim[lit(text._1)]; +// node = (xml | text) [_1 = node._1]; +// +// start_tag = +// '<' +// << verbatim[lit(start_tag._1)] +// << '>' +// ; +// +// end_tag = +// "' +// ; +// +// xml = +// start_tag(at_c<0>(xml._1)) +// << (*node) [ref(at_c<1>(xml._1))] +// << end_tag(at_c<0>(xml._1)) + ; + } + + karma::rule xml; +// karma::rule node; +// karma::rule text; +// karma::rule start_tag; +// karma::rule end_tag; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int main(int argc, char **argv) +{ + char const* filename; + if (argc > 1) + { + filename = argv[1]; + } + else + { + std::cerr << "Error: No input file provided." << std::endl; + return 1; + } + + std::ifstream in(filename, std::ios_base::in); + + if (!in) + { + std::cerr << "Error: Could not open input file: " + << filename << std::endl; + return 1; + } + + std::string storage; // We will read the contents here. + in.unsetf(std::ios::skipws); // No white space skipping! + std::copy( + std::istream_iterator(in), + std::istream_iterator(), + std::back_inserter(storage)); + + typedef mini_xml_parser mini_xml_parser; + mini_xml_parser def; // Our grammar definition + qi::grammar xmlin(def, def.xml); // Our grammar + mini_xml ast; // our tree + + std::string::const_iterator iter = storage.begin(); + std::string::const_iterator end = storage.end(); + bool r = phrase_parse(iter, end, xmlin, ast, space); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "-------------------------\n"; + + typedef output_iterator::type outiter_type; + typedef mini_xml_generator mini_xml_generator; + + mini_xml_generator gen; // Our grammar definition + karma::grammar xmlout(gen, gen.xml); // Our grammar + + std::string generated; + bool r = generate_delimited(output_iterator::call(generated), + xmlout(ast), space); + + if (r) + std::cout << generated << std::endl; + return 0; + } + else + { + std::string::const_iterator some = iter+30; + std::string context(iter, (some>end)?end:some); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \": " << context << "...\"\n"; + std::cout << "-------------------------\n"; + return 1; + } +} + + diff --git a/example/qi/mini_xml_samples/1.xml b/example/qi/mini_xml_samples/1.xml new file mode 100644 index 000000000..54181ed43 --- /dev/null +++ b/example/qi/mini_xml_samples/1.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/example/qi/mini_xml_samples/2.xml b/example/qi/mini_xml_samples/2.xml new file mode 100644 index 000000000..266e4b1ff --- /dev/null +++ b/example/qi/mini_xml_samples/2.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/example/qi/mini_xml_samples/3.xml b/example/qi/mini_xml_samples/3.xml new file mode 100644 index 000000000..9d89cf7b6 --- /dev/null +++ b/example/qi/mini_xml_samples/3.xml @@ -0,0 +1,5 @@ + + bar 1 + bar 2 + bar 3 + \ No newline at end of file diff --git a/example/qi/num_list.cpp b/example/qi/num_list.cpp new file mode 100644 index 000000000..156d1cefd --- /dev/null +++ b/example/qi/num_list.cpp @@ -0,0 +1,98 @@ +/*============================================================================= + Copyright (c) 2002-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// This sample demontrates a parser for a comma separated list of numbers. +// The numbers are inserted in a vector using phoenix. +// +// [ JDG May 10, 2002 ] spirit1 +// [ JDG March 24, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include + +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +/////////////////////////////////////////////////////////////////////////////// +// Our number list compiler +/////////////////////////////////////////////////////////////////////////////// +template +bool parse_numbers(Iterator first, Iterator last, std::vector& v) +{ + bool r = phrase_parse(first, last, + + // Begin grammar + ( + double_[push_back(ref(v), _1)] + >> *(',' >> double_[push_back(ref(v), _1)]) + ) + , + // End grammar + + space); + + if (first != last) // fail if we did not get a full match + return false; + return r; +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA comma separated list parser for Spirit...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout << "Give me a comma separated list of numbers.\n"; + std::cout << "The numbers will be inserted in a vector of numbers\n"; + std::cout << "Type [q or Q] to quit\n\n"; + + std::string str; + while (getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + std::vector v; + if (parse_numbers(str.begin(), str.end(), v)) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << str << " Parses OK: " << std::endl; + + for (std::vector::size_type i = 0; i < v.size(); ++i) + std::cout << i << ": " << v[i] << std::endl; + + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/num_list2.cpp b/example/qi/num_list2.cpp new file mode 100644 index 000000000..95ee677a2 --- /dev/null +++ b/example/qi/num_list2.cpp @@ -0,0 +1,97 @@ +/*============================================================================= + Copyright (c) 2002-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// This sample demontrates a parser for a comma separated list of numbers. +// The numbers are inserted in a vector using phoenix. +// +// [ JDG May 10, 2002 ] spirit1 +// [ JDG March 24, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include + +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +/////////////////////////////////////////////////////////////////////////////// +// Our number list compiler +/////////////////////////////////////////////////////////////////////////////// +template +bool parse_numbers(Iterator first, Iterator last, std::vector& v) +{ + bool r = phrase_parse(first, last, + + // Begin grammar + ( + double_[push_back(ref(v), _1)] % ',' + ) + , + // End grammar + + space); + + if (first != last) // fail if we did not get a full match + return false; + return r; +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA comma separated list parser for Spirit...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout << "Give me a comma separated list of numbers.\n"; + std::cout << "The numbers will be inserted in a vector of numbers\n"; + std::cout << "Type [q or Q] to quit\n\n"; + + std::string str; + while (getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + std::vector v; + if (parse_numbers(str.begin(), str.end(), v)) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << str << " Parses OK: " << std::endl; + + for (std::vector::size_type i = 0; i < v.size(); ++i) + std::cout << i << ": " << v[i] << std::endl; + + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/num_list3.cpp b/example/qi/num_list3.cpp new file mode 100644 index 000000000..fbcd293f6 --- /dev/null +++ b/example/qi/num_list3.cpp @@ -0,0 +1,97 @@ +/*============================================================================= + Copyright (c) 2002-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// This sample demontrates a parser for a comma separated list of numbers. +// The numbers are inserted in a vector using phoenix. +// +// [ JDG May 10, 2002 ] spirit1 +// [ JDG March 24, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include + +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +/////////////////////////////////////////////////////////////////////////////// +// Our number list compiler +/////////////////////////////////////////////////////////////////////////////// +template +bool parse_numbers(Iterator first, Iterator last, std::vector& v) +{ + bool r = phrase_parse(first, last, + + // Begin grammar + ( + double_ % ',' + ) + , + // End grammar + + v, space); + + if (first != last) // fail if we did not get a full match + return false; + return r; +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA comma separated list parser for Spirit...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout << "Give me a comma separated list of numbers.\n"; + std::cout << "The numbers will be inserted in a vector of numbers\n"; + std::cout << "Type [q or Q] to quit\n\n"; + + std::string str; + while (getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + std::vector v; + if (parse_numbers(str.begin(), str.end(), v)) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << str << " Parses OK: " << std::endl; + + for (std::vector::size_type i = 0; i < v.size(); ++i) + std::cout << i << ": " << v[i] << std::endl; + + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/roman.cpp b/example/qi/roman.cpp new file mode 100644 index 000000000..a993558d3 --- /dev/null +++ b/example/qi/roman.cpp @@ -0,0 +1,168 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A Roman Numerals Parser (demonstrating the symbol table). This is +// discussed in the "Symbols" chapter in the Spirit User's Guide. +// +// [ JDG August 22, 2002 ] spirit1 +// [ JDG March 13, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include + +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; +using boost::phoenix::ref; + +/////////////////////////////////////////////////////////////////////////////// +// Parse roman hundreds (100..900) numerals using the symbol table. +// Notice that the data associated with each slot is passed +// to attached semantic actions. +/////////////////////////////////////////////////////////////////////////////// +struct hundreds_ : symbols +{ + hundreds_() + { + add + ("C" , 100) + ("CC" , 200) + ("CCC" , 300) + ("CD" , 400) + ("D" , 500) + ("DC" , 600) + ("DCC" , 700) + ("DCCC" , 800) + ("CM" , 900) + ; + } + +} hundreds; + +/////////////////////////////////////////////////////////////////////////////// +// Parse roman tens (10..90) numerals using the symbol table. +/////////////////////////////////////////////////////////////////////////////// +struct tens_ : symbols +{ + tens_() + { + add + ("X" , 10) + ("XX" , 20) + ("XXX" , 30) + ("XL" , 40) + ("L" , 50) + ("LX" , 60) + ("LXX" , 70) + ("LXXX" , 80) + ("XC" , 90) + ; + } + +} tens; + +/////////////////////////////////////////////////////////////////////////////// +// Parse roman ones (1..9) numerals using the symbol table. +/////////////////////////////////////////////////////////////////////////////// +struct ones_ : symbols +{ + ones_() + { + add + ("I" , 1) + ("II" , 2) + ("III" , 3) + ("IV" , 4) + ("V" , 5) + ("VI" , 6) + ("VII" , 7) + ("VIII" , 8) + ("IX" , 9) + ; + } + +} ones; + +/////////////////////////////////////////////////////////////////////////////// +// roman (numerals) grammar +/////////////////////////////////////////////////////////////////////////////// +template +struct roman : grammar_def +{ + roman() + { + start + = +char_('M') [_val += 1000] + || hundreds [_val += _1] + || tens [_val += _1] + || ones [_val += _1]; + + // Note the use of the || operator. The expression + // a || b reads match a or b and in sequence. Try + // defining the roman numerals grammar in YACC or + // PCCTS. Spirit rules! :-) + } + + rule start; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Main program +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tRoman Numerals Parser\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "Type a Roman Numeral ...or [q or Q] to quit\n\n"; + + typedef std::string::const_iterator iterator_type; + typedef roman roman; + + roman def; // Our grammar definition + grammar roman_parser(def); // Our grammar + + std::string str; + unsigned result; + while (std::getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + std::string::const_iterator iter = str.begin(); + std::string::const_iterator end = str.end(); + bool r = parse(iter, end, roman_parser[ref(result) = _1]); + + if (r && iter == end) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << "result = " << result << std::endl; + std::cout << "-------------------------\n"; + } + else + { + std::string rest(iter, end); + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "stopped at: \": " << rest << "\"\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/example/qi/sum.cpp b/example/qi/sum.cpp new file mode 100644 index 000000000..c6a66d4f9 --- /dev/null +++ b/example/qi/sum.cpp @@ -0,0 +1,92 @@ +/*============================================================================= + Copyright (c) 2002-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +/////////////////////////////////////////////////////////////////////////////// +// +// A parser for summing a comma-separated list of numbers using phoenix. +// +// [ JDG June 28, 2002 ] spirit1 +// [ JDG March 24, 2007 ] spirit2 +// +/////////////////////////////////////////////////////////////////////////////// +#include +#include +#include + +#include +#include + +using namespace boost::phoenix; +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +/////////////////////////////////////////////////////////////////////////////// +// Our adder +/////////////////////////////////////////////////////////////////////////////// +template +bool adder(Iterator first, Iterator last, double& n) +{ + bool r = phrase_parse(first, last, + + // Begin grammar + ( + double_[ref(n) = _1] >> *(',' >> double_[ref(n) += _1]) + ) + , + // End grammar + + space); + + if (first != last) // fail if we did not get a full match + return false; + return r; +} + +//////////////////////////////////////////////////////////////////////////// +// Main program +//////////////////////////////////////////////////////////////////////////// +int +main() +{ + std::cout << "/////////////////////////////////////////////////////////\n\n"; + std::cout << "\t\tA parser for summing a list of numbers...\n\n"; + std::cout << "/////////////////////////////////////////////////////////\n\n"; + + std::cout << "Give me a comma separated list of numbers.\n"; + std::cout << "The numbers are added using Phoenix.\n"; + std::cout << "Type [q or Q] to quit\n\n"; + + std::string str; + while (getline(std::cin, str)) + { + if (str.empty() || str[0] == 'q' || str[0] == 'Q') + break; + + double n; + if (adder(str.begin(), str.end(), n)) + { + std::cout << "-------------------------\n"; + std::cout << "Parsing succeeded\n"; + std::cout << str << " Parses OK: " << std::endl; + + std::cout << "sum = " << n; + std::cout << "\n-------------------------\n"; + } + else + { + std::cout << "-------------------------\n"; + std::cout << "Parsing failed\n"; + std::cout << "-------------------------\n"; + } + } + + std::cout << "Bye... :-) \n\n"; + return 0; +} + + diff --git a/index.html b/index.html new file mode 100644 index 000000000..1eb81150a --- /dev/null +++ b/index.html @@ -0,0 +1,15 @@ + + + + + + + Automatic redirection failed, click this +
link  
+

© Copyright Beman Dawes, 2001

+

Distributed under the Boost Software License, Version 1.0. (See + accompanying file + LICENSE_1_0.txt or copy at + www.boost.org/LICENSE_1_0.txt)

+ + \ No newline at end of file diff --git a/phoenix/doc/Jamfile.v2 b/phoenix/doc/Jamfile.v2 new file mode 100644 index 000000000..1e569300e --- /dev/null +++ b/phoenix/doc/Jamfile.v2 @@ -0,0 +1,12 @@ +project boost/libs/spirit/doc/phoenix/doc ; +import boostbook : boostbook ; +using quickbook ; + +boostbook quickbook + : + users_manual.qbk + : + boost.root=../../../../.. + boost.libraries=../../../../libraries.htm + ; + diff --git a/phoenix/doc/html/boostbook.css b/phoenix/doc/html/boostbook.css new file mode 100644 index 000000000..d84d53843 --- /dev/null +++ b/phoenix/doc/html/boostbook.css @@ -0,0 +1,511 @@ +/*============================================================================= + Copyright (c) 2004 Joel de Guzman + http://spirit.sourceforge.net/ + + Use, modification and distribution is subject to the Boost Software + License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +/*============================================================================= + Body defaults +=============================================================================*/ + + body + { + margin: 1em; + font-family: sans-serif; + } + +/*============================================================================= + Paragraphs +=============================================================================*/ + + p + { + text-align: left; + font-size: 10pt; + line-height: 1.15; + } + +/*============================================================================= + Program listings +=============================================================================*/ + + /* Code on paragraphs */ + p tt.computeroutput + { + font-size: 9pt; + } + + pre.synopsis + { + font-size: 90%; + margin: 1pc 4% 0pc 4%; + padding: 0.5pc 0.5pc 0.5pc 0.5pc; + } + + .programlisting, + .screen + { + font-size: 9pt; + display: block; + margin: 1pc 4% 0pc 4%; + padding: 0.5pc 0.5pc 0.5pc 0.5pc; + } + + /* Program listings in tables don't get borders */ + td .programlisting, + td .screen + { + margin: 0pc 0pc 0pc 0pc; + padding: 0pc 0pc 0pc 0pc; + } + +/*============================================================================= + Headings +=============================================================================*/ + + h1, h2, h3, h4, h5, h6 + { + text-align: left; + margin: 1em 0em 0.5em 0em; + font-weight: bold; + } + + h1 { font: 140% } + h2 { font: bold 140% } + h3 { font: bold 130% } + h4 { font: bold 120% } + h5 { font: italic 110% } + h6 { font: italic 100% } + + /* Top page titles */ + title, + h1.title, + h2.title + h3.title, + h4.title, + h5.title, + h6.title, + .refentrytitle + { + font-weight: bold; + margin-bottom: 1pc; + } + + h1.title { font-size: 140% } + h2.title { font-size: 140% } + h3.title { font-size: 130% } + h4.title { font-size: 120% } + h5.title { font-size: 110% } + h6.title { font-size: 100% } + + .section h1 + { + margin: 0em 0em 0.5em 0em; + font-size: 140%; + } + + .section h2 { font-size: 140% } + .section h3 { font-size: 130% } + .section h4 { font-size: 120% } + .section h5 { font-size: 110% } + .section h6 { font-size: 100% } + + /* Code on titles */ + h1 tt.computeroutput { font-size: 140% } + h2 tt.computeroutput { font-size: 140% } + h3 tt.computeroutput { font-size: 130% } + h4 tt.computeroutput { font-size: 120% } + h5 tt.computeroutput { font-size: 110% } + h6 tt.computeroutput { font-size: 100% } + +/*============================================================================= + Author +=============================================================================*/ + + h3.author + { + font-size: 100% + } + +/*============================================================================= + Lists +=============================================================================*/ + + li + { + font-size: 10pt; + line-height: 1.3; + } + + /* Unordered lists */ + ul + { + text-align: left; + } + + /* Ordered lists */ + ol + { + text-align: left; + } + +/*============================================================================= + Links +=============================================================================*/ + + a + { + text-decoration: none; /* no underline */ + } + + a:hover + { + text-decoration: underline; + } + +/*============================================================================= + Spirit style navigation +=============================================================================*/ + + .spirit-nav + { + text-align: right; + } + + .spirit-nav a + { + color: white; + padding-left: 0.5em; + } + + .spirit-nav img + { + border-width: 0px; + } + +/*============================================================================= + Table of contents +=============================================================================*/ + + .toc + { + margin: 1pc 4% 0pc 4%; + padding: 0.1pc 1pc 0.1pc 1pc; + font-size: 80%; + line-height: 1.15; + } + + .boost-toc + { + float: right; + padding: 0.5pc; + } + +/*============================================================================= + Tables +=============================================================================*/ + + .table-title, + div.table p.title + { + margin-left: 4%; + padding-right: 0.5em; + padding-left: 0.5em; + } + + .informaltable table, + .table table + { + width: 92%; + margin-left: 4%; + margin-right: 4%; + } + + div.informaltable table, + div.table table + { + padding: 4px; + } + + /* Table Cells */ + div.informaltable table tr td, + div.table table tr td + { + padding: 0.5em; + text-align: left; + font-size: 9pt; + } + + div.informaltable table tr th, + div.table table tr th + { + padding: 0.5em 0.5em 0.5em 0.5em; + border: 1pt solid white; + font-size: 80%; + } + +/*============================================================================= + Blurbs +=============================================================================*/ + + div.note, + div.tip, + div.important, + div.caution, + div.warning, + p.blurb + { + font-size: 9pt; /* A little bit smaller than the main text */ + line-height: 1.2; + display: block; + margin: 1pc 4% 0pc 4%; + padding: 0.5pc 0.5pc 0.5pc 0.5pc; + } + + p.blurb img + { + padding: 1pt; + } + +/*============================================================================= + Variable Lists +=============================================================================*/ + + /* Make the terms in definition lists bold */ + div.variablelist dl dt, + span.term + { + font-weight: bold; + font-size: 10pt; + } + + div.variablelist table tbody tr td + { + text-align: left; + vertical-align: top; + padding: 0em 2em 0em 0em; + font-size: 10pt; + margin: 0em 0em 0.5em 0em; + line-height: 1; + } + + div.variablelist dl dt + { + margin-bottom: 0.2em; + } + + div.variablelist dl dd + { + margin: 0em 0em 0.5em 2em; + font-size: 10pt; + } + + div.variablelist table tbody tr td p, + div.variablelist dl dd p + { + margin: 0em 0em 0.5em 0em; + line-height: 1; + } + +/*============================================================================= + Misc +=============================================================================*/ + + /* Title of books and articles in bibliographies */ + span.title + { + font-style: italic; + } + + span.underline + { + text-decoration: underline; + } + + span.strikethrough + { + text-decoration: line-through; + } + + /* Copyright, Legal Notice */ + div div.legalnotice p + { + text-align: left + } + +/*============================================================================= + Colors +=============================================================================*/ + + @media screen + { + /* Links */ + a + { + color: #005a9c; + } + + a:visited + { + color: #9c5a9c; + } + + h1 a, h2 a, h3 a, h4 a, h5 a, h6 a, + h1 a:hover, h2 a:hover, h3 a:hover, h4 a:hover, h5 a:hover, h6 a:hover, + h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited + { + text-decoration: none; /* no underline */ + color: #000000; + } + + /* Syntax Highlighting */ + .keyword { color: #0000AA; } + .identifier { color: #000000; } + .special { color: #707070; } + .preprocessor { color: #402080; } + .char { color: teal; } + .comment { color: #800000; } + .string { color: teal; } + .number { color: teal; } + .white_bkd { background-color: #FFFFFF; } + .dk_grey_bkd { background-color: #999999; } + + /* Copyright, Legal Notice */ + .copyright + { + color: #666666; + font-size: small; + } + + div div.legalnotice p + { + color: #666666; + } + + /* Program listing */ + pre.synopsis + { + border: 1px solid #DCDCDC; + } + + .programlisting, + .screen + { + border: 1px solid #DCDCDC; + } + + td .programlisting, + td .screen + { + border: 0px solid #DCDCDC; + } + + /* Blurbs */ + div.note, + div.tip, + div.important, + div.caution, + div.warning, + p.blurb + { + border: 1px solid #DCDCDC; + } + + /* Table of contents */ + .toc + { + border: 1px solid #DCDCDC; + } + + /* Tables */ + div.informaltable table tr td, + div.table table tr td + { + border: 1px solid #DCDCDC; + } + + div.informaltable table tr th, + div.table table tr th + { + background-color: #F0F0F0; + border: 1px solid #DCDCDC; + } + + /* Misc */ + span.highlight + { + color: #00A000; + } + } + + @media print + { + /* Links */ + a + { + color: black; + } + + a:visited + { + color: black; + } + + .spirit-nav + { + display: none; + } + + /* Program listing */ + pre.synopsis + { + border: 1px solid gray; + } + + .programlisting, + .screen + { + border: 1px solid gray; + } + + td .programlisting, + td .screen + { + border: 0px solid #DCDCDC; + } + + /* Table of contents */ + .toc + { + border: 1px solid gray; + } + + .informaltable table, + .table table + { + border: 1px solid gray; + border-collapse: collapse; + } + + /* Tables */ + div.informaltable table tr td, + div.table table tr td + { + border: 1px solid gray; + } + + div.informaltable table tr th, + div.table table tr th + { + border: 1px solid gray; + } + + /* Misc */ + span.highlight + { + font-weight: bold; + } + } diff --git a/phoenix/doc/html/images/add2.png b/phoenix/doc/html/images/add2.png new file mode 100644 index 0000000000000000000000000000000000000000..11b850a0054254e3a65f136ba9c1ce003c15d2a8 GIT binary patch literal 1250 zcmeAS@N?(olHy`uVBq!ia0y~yVEDnnz+l3`%)r1fL6!9z0|Ntdv6E*A2M5RPhyD*3 z7#OMpd_r6q7#Irj3)EEA?%lh$al=Lq4vwIppqVpg{{R2q%gbx;o;?Br0^h%XZ)s_< zwzfWV=1f{znxv%U(xpq^zklD;)AQ`vGet?oYuBztMMW7K8?RlvwxYb^=+UE2j*c^D z&fL3oZ(3TKqNHSym)F*2eZj4OfclL#{2C{V~Pt!%D8OOUvIxtu2?jy}#mhhu@R9 z)9-|B3MV{QR5vU+usKXnMEKzKO}`oV%LIg&9-hm&u%CU-rp=R8_>$8$es_JK5&piH z$M&aO!FH~X@ht17OqkDIdbak@-X~$j^7ZTH_&)xp^ zI`v#{`uwRf4FYEC6Ludwez2BjqtNHr__}L_3d~L^{>cjk61!_^Klt76YbcKIs5&ya z=#I2;fqq5q3WcNc&5va}PO5ykK7H}6*^RNE|8w;P&0;VUo+GrkYBBTfXLjrf$$YEd z`+s2-HdMdi?O!D>0^@vR5CximTiCbMpcBH)iqW zN5o3ze9XMZ{cp=9j;{xrk1RYW&Awr=w_{OgnCHS#(J8?|x9~tg`>Zp|E}DndCR!R-V+ieEZ4=uO_bgZOzwW z)-P%8aVThVlXQ2*SCMH2Hc93N@_+W;m7DwHBFCLCF_Z4+u6noKdf~jO)fIN3>m1!D ztr9q|z1`R{Y-f*M|M!OG*0SBVg3D_rc^5n`KK&{6*?~32y#_ZeL--A+B+s7rG=KW5 zO;h$32FWdrEzpk6+P*kjpvS+6b>BIq36`D({b$_oUV8c_?#i!IH-9D7DP)*k%Q7u^ zeqs=ikM>7m@nLSA$%YDUD3~$M!&pGbxs_ z6K(Dyx`j8bdH?p?B5YX)o)&3oUb-A`*z9_A5Sl%Q(|12MO|Nozl d^56VpW~}(b#G+zZ$-uzC;OXk;vd$@?2>>~#R%QSI literal 0 HcmV?d00001 diff --git a/phoenix/doc/html/images/add2_call.png b/phoenix/doc/html/images/add2_call.png new file mode 100644 index 0000000000000000000000000000000000000000..782edf87687e305e308c7cae6dcbb6b92ea4104d GIT binary patch literal 998 zcmeAS@N?(olHy`uVBq!ia0y~yVA#UIz+lb6%)r3#^lJbw0|Ntdv6E*A2M5RPhyD*3 z7#NBId_r6q7#Pyh(lj(Q_U_%gb?a6R4vwIppu2bPdU<)xoH4hEsM(q3iR)}6W_H52(#uVv!YlWt) zY1k%wRd#Nh?m}O`nYsQ`oLBmB)h6UklI`|gc-Kg#*Xm?VaK`)`-`QMM@kJT`7V=A< z-ZEQp+vR|tKD@Tq1zv2PS! z%2jUh8!wk`)C=MC&r-Q{ROzndznIv1%i3Mq619f8?GElezo)+AsbBX_ymGPZj3b`C zri)&b20s2(a{k3ssZX_YUZ0v*kZY*+=S_`N)bzA+$!qSr>l5Ey=ut}k=@;-==wmJ&DaWHCZ{sd&}x8*0a7I zwC9iiB(~K`>hDd_g}lOsb%Jpr>%Sk&S$y&II*Imwc^v{(ep}w_Pvt$IE%WPpn?!5c z_O1;}>&w~dI=k(%C7LUz1liXaO;?%~ux+Q$Vg6acF2%YWfsMO6ST1^b>%J7((cmi( z>ou2uCtu6~wT^Y(|6^POz8PHQT(th8S-*Zlu*>=TO%+^HwQc;z`KMcV{fzcex;|g- zhjEQXU54VT#hbULhS{!dgZU|?Wi@O1TaS?83{1OW7A%&Y(a literal 0 HcmV?d00001 diff --git a/phoenix/doc/html/images/adder.png b/phoenix/doc/html/images/adder.png new file mode 100644 index 0000000000000000000000000000000000000000..1578757b2b0a5880927451f90c64bfb847bd25bd GIT binary patch literal 1325 zcmeAS@N?(olHy`uVBq!ia0y~yVA#UIz+lb6%)r3#^lJbw0|Ntdv6E*A2M5RPhyD*3 z7#JD@d_r6q7#LbwT8xd2uU)$q6cn^~&mLYL-i;eJ{{R2q%gc*{gCi|1?fv`rOO`B= zm64e_bLN>dXVTKrtgWq|J$oi8Df#{T_wMd)HC46h>guSdsAETuu3fwK-o1NHj*f~7 zia}mpGiS~$$S>HrW2d5|VvtwR)~#CwI5-3Z1WHOuw6wIGtgL>2nG?>yz@S|cqMjYf2WWCOb;l3s(0)bKe_ora&64( z@HLaKd;AkIS*+nHkUIBvD%bT@hA)m(cRqL~nX&Qsjqir{Znx={&-gK;Ctx?@Twy24 z#d9LwdoPig`i5KGnZJ-Fs=Cp`dA0aPw-4ge?w@lyF13c6H&LSdkMw z`4()HwRD9@ zrqARY@{JZ~G3Hf%v5|A^PMtYdO3C8Hsil{G3z(RE4nBRyc;Qs3+;@y$no_mT?Qd9T zHs#{Pa^GDFAwjA z2o-snG0C32P;Iu|yf!Y@+k3kH-gzzm>ZVFxPTKIYi2eGtUt8GDTOPRiy@=!O&&^-7 zinCu_?@{{k^Qq>G;#mfo@8|qD7IZHs@ZL|Y3{S?HTd&?;Xq!Ks$>fmm(Yj6RcQQhsg~;WJPOnUL~i za543TVGG~c85+5_r*b*&TYu}7gP3~1jz-VYS4veWLJDSk7am{p_2r#eyge#j!q4q5 zu6w+%`r7PT-?#M1QTXgY#mb+~${^m{- z%e-+f{GC+Eq0Gy7qP2^7tkfr+E->GA_my6Wf~VBdl$_mHV_&QZ%+?ZFJX`v2pQCi* zzY}Gv4`28-yM4Q(|GdNZ#cp!DEcvE0`-AN}!%Dt-|6Qpn>UA@xYAi7?wf<%~;YRxs zwPfqyZ5C{@#RB)Tn+yyL44$rjF6*2U FngGE~f)xM& literal 0 HcmV?d00001 diff --git a/phoenix/doc/html/images/alert.png b/phoenix/doc/html/images/alert.png new file mode 100644 index 0000000000000000000000000000000000000000..b4645bc7e7cd81f2818bf22aa898e95f89f7b154 GIT binary patch literal 603 zcmeAS@N?(olHy`uVBq!ia0y~yV31^BU=ZVAW?*1oN<7}ez`(#Bl2vQw%VQUVy#($c(b7<_!Z&&*`7 zwYIFTuIA!kP?6(!`t<3^g$y0J3{Q73WQH(YSjJ#(AQj-}VXe>LZ_gkv$q?hiVP&Xj ztS4or%^)MjpsB?1|Nnp9pi5p13=F0vL4Lvi$p8#BTQ7jZgtNdSvY3H^>jMZgI;}C8 z!N9FSxfgB=H7N+NC77H_+N#nawR{=RqTMBX)(LXL|IjinhDSdCyWWZ3S$0kBOZbahGm>{cU3L4X z*xaKNrl+L*2&>`tT{GF}sK6TCoSy58@94C>csZ@3se0z)OD!J`ePg?KNk!eRu!nuZ zwIuy5g5`UyU*2!`JMiPV^UIVvmW1t{f1*^~1#9h_jDIZO*R6JmbN8&QBXd?)zY=(& z`HG+Mis#k2-hNM1nwI_8efYU@yAuCZhY42|Be;Juo!svFUA16}A_D^hgQu&X%Q~lo FCIA+53ZVc1 literal 0 HcmV?d00001 diff --git a/phoenix/doc/html/images/banner.png b/phoenix/doc/html/images/banner.png new file mode 100644 index 0000000000000000000000000000000000000000..e663460be9d00ee69c69eb0da0176ba84e727c4b GIT binary patch literal 18002 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 O1B0ilpUXO@geCwNOia)K literal 0 HcmV?d00001 diff --git a/phoenix/doc/html/images/funnel_in.png b/phoenix/doc/html/images/funnel_in.png new file mode 100644 index 0000000000000000000000000000000000000000..13ae55547064e6f34fd81bb961b17cedfef382ce GIT binary patch literal 3571 zcmeAS@N?(olHy`uVBq!ia0y~yU<_tpV3^3k%)r3ld1$pS0|Ntdv6E*A2M5RPhyD*3 z7#KAJd_r6q7#Oy0-I|t`X6fO(?d9Js&weVXYxQn9>>Lu^ynL&Gu;|{sd!IdfwtV^W zs@W?RoqrIVS?J~ErE6t>^~#mBv@{M5j;yvxGiT0}lvngk%sO-CjFyR&pI^Y%d*7$* zJr@-fWoYNJ>gF3`W8)pK|NHy-x3AfqRNY$8H@l^!#o8;-!quCHPe4*qa_!Q!dX~1c zk6o{>u2xi3eE#7~qQ`joZ=cD=VWE2C@E>;rn;3T~N2spa`a*~JRJ5eh~I1}5!gYtAz;a3Az^aSW-L z^L8$8@N5r>k7Zx7_p+T+j(%Aav~o`C!f8InqRCu~Cw)$u>^ghR-r~>S zo*mPh`7ChC%G$t{F*okq-nZ-!L$2A?d!bWT$9?r$Rw
aW?4^4HP*v**+qBsR#& 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 R!JPrrn{)MZS?83{1OR1XD98W+ literal 0 HcmV?d00001 diff --git a/phoenix/doc/html/images/funnel_out.png b/phoenix/doc/html/images/funnel_out.png new file mode 100644 index 0000000000000000000000000000000000000000..20c6f8aa689c25c2110e2f4ec89da4f01895f7a5 GIT binary patch literal 5085 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 DMxcR+ literal 0 HcmV?d00001 diff --git a/phoenix/doc/html/images/home.png b/phoenix/doc/html/images/home.png new file mode 100644 index 0000000000000000000000000000000000000000..5584aacb097a80e66a5320312b6e4eb017af1a06 GIT binary patch literal 358 zcmeAS@N?(olHy`uVBq!ia0y~yU=Rjj7G?$phK4%r{|pQa%*9TgAsieWw;%dHU|?WS z3GfMV6&Dfg?(A@OuseF>a6(*+nzF*onKLh6zO-%YmOy{sw6wJU|Nk%gvq74Hfq|za z$S?Rm0x$^OKX;CSfq}EYBeIx*fm;ZK886+f`@_J%pjzS@Q4*Y=R#Ki=l*-_nm|T>f zo0^iDsNj}alvU8YOY t9}F9JU6`43jMG5vNQA&YscZamBUbAM6MgCj?0f8UC{w&*fsej@W zyW$1s?mYAM^G{1l+k5_=m#1e$%ZixJbz4r}eE#k;2M33G^3=+?d;kCc-#T|=S#z(o zwe^`ZXJln$^V(-0y!`O(+qZ$?G26Coiz{xqb?4sX>reQHMwN9=KYI01Mp=__+RTjJRSF6U zmoHz=nY#VatB;3{91RQ%+Xn-BLf4I_OdnS85r0!JY5_^D(1YM^*;Qy ziNx{OVJ~+1>^ht8_;=h^&Js}y?vV)2yE@ys2m8%t7%K`hyvtUfgYlNr}ME8Twm^-liNujG%0 zNOEs!Ti9$;He0QsV^vb)wHuxycC#L@>N4F`JmXMyGH00J>YpzPc1h1{T-8%5`zfL? zTkGxWMAP;ipO1Ymob-)fp7q+Uw-(0Vb4pCDs*fbRYkR}=G4*f6fmX(>iGA5|ixn+H z_>Ni3s*-QIwQ-tl`n5$L8P`fm^;~Lq-0+ESVc6E>-eWbB^kZ-I$7Wt)w{GWM;wdS< zvSlmZ?d*5{CJe`d`Z;QRu6{WECCPP7j-KOgj`Jz}&6)?Zro24b6>-z9dG0ESZo!vF z+QhH-n7*o;-WX9PyIkFO-3{+Uq8q0#sS7;2{nKil8{MCtB<;2Ey~oP5GJZzv+`R3g zC#Sx3eIYI!KPxkBfyJ#7215X_8WAw{tcZNQIzVhd9@xOGeKV4&Ifs^c{W8rtUzY>`)_$z`v zc;V$QuIDcu`~K{0sSK}2OW|Xa_Zy@y9B+9R$G7@D=YoI(E-`Dw>;FGC?Ka4h+1+lh zQ0XZj{CyGAYp;w`L3(Mi-0yOie9TZUWxjUv)Yi>Rt3R*q;W7(9vwm_ui=Bhi{#{S1 zdcG|xVplo-Dn03`?}MJ{|0?%s)x3D|T$b^g>%*Nv)(V{>eO`uXi_ zO$QfoJmv4ooqmh) zwq~6U_ouprtw)=-RePs62jV(y7vxppS`?eqhuSYEGqvS^1?*vwT0Y7r~rYeJQ$X+59(Hb*bw$KHFd zZ~qKidf8~}>C?L&y=u>B-L_eL`i@<5EM=?Pe$U>zDdK06j+wd_+ZEwhNB5rzvJcmm zY!s^7v{rO4uQo^L`Q>%d9AaWxVbA=8ZXNnHNiJ+wOj0+)mNnl^xx=2R2+Gb)Jr_GI zKg9IvrieSUA}z!sw?3Y05bl=iS6I-P$kZLp&3))-P=28npO|HIy14QO&J~SQo*!Sd;NF34+p3G3 zLVH-`7CwB;@sIhz=cJ=M*RY9&PthunHF)w^GWOz=sXuP+sIGnX=(gS`Wi4Huyw?v> zzt3d<{zbk!TqR;rGxs)*$tm}_H~vs^S$1w(kY>}-vr{*{sa6cLlDODsule-Q+*&Eu z&-#UvaSnHY3%c6 zdHUY_oGS0?I|pSulovS^mZb;%e}4M#GyKK&7k-&k{mG{qwrMahFfe$!`njxgN@xNA Ddo@b0 literal 0 HcmV?d00001 diff --git a/phoenix/doc/html/images/next.png b/phoenix/doc/html/images/next.png new file mode 100644 index 0000000000000000000000000000000000000000..59800b4e87f60c0e3383ede2b384b9be0f5ffe8d GIT binary patch literal 336 zcmeAS@N?(olHy`uVBq!ia0y~yU=Rjj7G?$phK4%r{|pQa%*9TgAsieWw;%dHU|?WS z3GfMV6&Dfg?(8r&Hr}>%OQ656nzF)*4nJa0`Jj)#l9-t%+}PK^d+g590~2^trx_V+aGYt)W#Kgko@Q{~>i6>w}LxPb)_bi1gN;4a>^d{wcURt*495hZOYc8|NsA=8=F$dz`$Tq666>BpLD?B9j=thz`(#+;1OBOz`*qZgc+UI zn9N{cU{Eb_jVKAuPb(=;EJ|hYO-wGz&rMCqOjK~oEJ`iUFUl@f@QqL~GB7Y{FI#h- zfq_xR)5S5QVovU)+hxrP02oy7clOl(|F;(fCx<^{O+3$d{!GJf zAT>%@)+Kac%o^ zm8~at%dZNSg`XDA>HpujeRb(tA@xWH1+`xhu}_a!eJ{ORYJcMi$Ma>cBHv0)HBxa4 zSGC~^nY{G5@1(nj{7!xJ-s1V$LbWCK_xDFLe3_MRf4msHv}xJrfBaF7=BYjUcQ!FF PFfe$!`njxgN@xNAS|c%7 literal 0 HcmV?d00001 diff --git a/phoenix/doc/html/images/organization.png b/phoenix/doc/html/images/organization.png new file mode 100644 index 0000000000000000000000000000000000000000..8559621ee3ea05134ce03b0020cfeb2e98c3e04e GIT binary patch literal 3337 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 O7(8A5T-G@yGywo`Ac=zj literal 0 HcmV?d00001 diff --git a/phoenix/doc/html/images/prev.png b/phoenix/doc/html/images/prev.png new file mode 100644 index 0000000000000000000000000000000000000000..d88a40f923e3c554125f01cd366707c60cfcad04 GIT binary patch literal 334 zcmeAS@N?(olHy`uVBq!ia0y~yU=Rjj7G?$phK4%r{|pQa%*9TgAsieWw;%dHU|?WS z3GfMV6&DdqOG`60Hr}>%%ZlYo1O0u~loc*tzSP~>;p||S5Et|R|Noh1c$yg)73T~N2spa`a*~JRJ5eh~I1}5!gYtAz;Fo=OPI2WZRmSpDVDTHL^rZN~B=o=X8 z8<-ql-^0nkz!2u?;uumfC;0|1OPoRyGxLNShYX~Tl_Wf9G1_imu)%RA9}mw<0X2^e zQioc&m}WXSvRw^OFi2qFa&lm1W^U?K=~^Ook|m{hVvche^Q6-g?(V)Vn8U=toEqFE UkjD9gfq{X+)78&qol`;+00?PtqyPW_ literal 0 HcmV?d00001 diff --git a/phoenix/doc/html/images/smiley.png b/phoenix/doc/html/images/smiley.png new file mode 100644 index 0000000000000000000000000000000000000000..30a77f71ce16872d046a1a5d6fd698a2f65a3f62 GIT binary patch literal 867 zcmeAS@N?(olHy`uVBq!ia0y~yU=U|uV36QoW?*1YTQtp`fq{X!*vT`5gM;JtL;nX1 z42*&SJ|V8+B7&<|tz5IBw4@*~EXexT*H!m!O?Pp!sH>^CckiyDuKJ|Dl+(w%CQay1 zOYu8%=FGd73zHKgmoF}|u{3I~kKDVXVbQ_`9c`_Fz7{iQrl~4QMTGi1fByW=jcKLD z*?C#s*_rWAAIx@f)c16=+qAB-t1T`u&hzVsWjnTSn>lml<@5cSX&!B@jUfSci{|F_ z_w^KKXB!)+#6)_3`t<4gwQEt~&MzL%J+!ao-_JD<@64PzGws6Z-hv$8-Me=7btN{` zl~t96rKP3$c$&I9TcsogXQl?mL%y{W;-5&-92C)*?h!W?b)Wnj^{5*w_%-mE4Lj!$7BYguC zr{Y6*7#J8-K`Mgt(@M${i&7bU6O)Vbb5m0?6BXPti&D$;i?WLqd?OT$3=B-#%hsG{ zU|`huba4!+n3FrHHoVC|#v<-Y&ynX`2+ z)ci{*-@n^>-frGI_MA-9eHCQCFMs-NiTvCzJ8a&6h<9#D<(d3(^{E}JLTitR9GSK2 z$*O4*8tU!OHS*&Yg~mSMD)7~hd93j+u`cf5MM<4*zJ;yfFKhYEn9bv3Xeg3g;K-G= z;q?Q<5Qk-d*rznCncd{p+umG~t-YZ3L%_f9;YyrSd?k7nE}0j~xg-T!p1r_pXw_8J z^q9XtRP=q)pSk7_!?YePxacL!`8E4~v$oZii_iB4y^t?YSBana!LlH(Q{_whcc+EB z6^^opPM-68`QEg&=hc<^;brIeKBf1+k=uTZ@Aa)4^R8_EExPXM@|~g)-OB%bBP#i` ie0$=QHXfdLO8@!p%oni+1)dBH3=E#GelF{r5}E*N2(Kal literal 0 HcmV?d00001 diff --git a/phoenix/doc/html/images/tip.png b/phoenix/doc/html/images/tip.png new file mode 100644 index 0000000000000000000000000000000000000000..9f596b0b88eb42562b2d0b0e30d054509be42af2 GIT binary patch literal 640 zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|4rT@hhU+WOo?>8NU@ms@4B_D5xc$)o0Rsa= zZ-7sTtD>UflSfBq&1}s`kJ`U?skgWH)2B~oOmF`E^TxIg4+D?5M~H-?Y?- zI&~E<1_lQGk|4j}{|LZEaktF(-D? z%Sp`&0xgDm?d}|!0v)M>{a+K-KKU!3@9L8LOa5Z1>0OX+j1SY)GfWc?{l8U z$2VD9PtNu%kvS~%Xw8E;=95FN9-q3V{gPE~*|fjR%QkDRKeb=t2Ll5GgQu&X%Q~lo FCIFO8ExiB$ literal 0 HcmV?d00001 diff --git a/phoenix/doc/html/images/up.png b/phoenix/doc/html/images/up.png new file mode 100644 index 0000000000000000000000000000000000000000..17d9c3ec491ae1ba22188ce85985623c92ffa9be GIT binary patch literal 370 zcmeAS@N?(olHy`uVBq!ia0y~yU=Rjj7G?$phK4%r{|pQa%*9TgAsieWw;%dHU|?X- z3h)VW6&DdqOG|Thu-mqE%ZlYoyE{8BU%nLR@2jS)FmvY2qel)W#Kk;%^zi@x|I?Un z*fKCM@RbDl1^-6|46X<6oM2#J;4JWnEM{Qf76M_$OLy!3FfcHvmbgZg1m~xflqVLY zGWaGY7v<-srer26xMdclmgg5`7c2NiC>R+Sn6#IzInThrAO_OlT$Gwvl9`{U5R#dj z%3x@qZ(yu%U~+tY4<`cyLy@P8V@SoEspmFwHW&!FJyeg_(XezvV9WvAI|r@_>dZZG zPW6aiOT!J--9O?NG0%AP;}ge|4lDQN4=-}8`?JGwx}?mMnO)OdyQdu$nQCjPRV}jm z$u!Qa8E-cQ-r3Nz>Y(YPTd#BPEH+&8GWqfD!}4*53%dA!%#3$cIv;a~fq{X+)78&q Iol`;+0POUaApigX literal 0 HcmV?d00001 diff --git a/phoenix/doc/html/index.html b/phoenix/doc/html/index.html new file mode 100644 index 000000000..eee3a8289 --- /dev/null +++ b/phoenix/doc/html/index.html @@ -0,0 +1,224 @@ + + + +Chapter 1. Phoenix 2.0 + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
Next
+
+
+

+Chapter 1. Phoenix 2.0

+

+Joel de Guzman +

+

+Dan Marsden +

+
+
+

+ Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +

+
+
+ +
+

+Preface

+

+ Functional programming is so called because a program consists + entirely of functions. The main program itself is written as a function which + receives the program's input as its argument and delivers the program's output + as its result. Typically the main function is defined in terms of other functions, + which in turn are defined in terms of still more functions until at the bottom + level the functions are language primitives. +

+

+ John Hughes-- Why Functional Programming + Matters +

+

+ lambda_cpp +

+

+ + Description +

+

+ Phoenix enables Functional Programming (FP) in C++. The design and implementation + of Phoenix is highly influenced by FC++ + by Yannis Smaragdakis and Brian McNamara and the BLL + (Boost Lambda Library) by Jaakko Jaarvi and Gary Powell. Phoenix is a blend + of FC++ and BLL using the implementation techniques used in the Spirit + inline parser. Phoenix version 2, this version, will probably be the last release + of the library. Phoenix v2 will be the basis of the Phoenix and BLL + merger. +

+

+ Phoenix is a header only library. It is extremely modular by design. One can + extract and use only a small subset of the full library, literally tearing + the library into small pieces, without fear that the pieces won't work anymore. + The library is organized in highly independent modules and layers. +

+

+ + How to use this manual +

+

+ The Phoenix library is organized in logical modules. This documentation provides + a user's guide and reference for each module in the library. A simple and clear + code example is worth a hundred lines of documentation; therefore, the user's + guide is presented with abundant examples annotated and explained in step-wise + manner. The user's guide is based on examples: lots of them. +

+

+ As much as possible, forward information (i.e. citing a specific piece of information + that has not yet been discussed) is avoided in the user's manual portion of + each module. In many cases, though, it is unavoidable that advanced but related + topics not be interspersed with the normal flow of discussion. To alleviate + this problem, topics categorized as "advanced" may be skipped at + first reading. +

+

+ Some icons are used to mark certain topics indicative of their relevance. These + icons precede some text to indicate: +

+
+

+ + Icons +

+ +++++ + + + + + + + + + + + + + + + + + + + + + + +
IconNameMeaning
noteNoteInformation provided + is auxiliary but will give the reader a deeper insight into a specific + topic. May be skipped.
alertAlertInformation provided + is of utmost importance.
tipTipA potentially useful + and helpful piece of information.
+
+

+ This documentation is automatically generated by Spirit QuickBook documentation + tool. QuickBook can be found in the Spirit + Repository. +

+

+ + Support +

+

+ Please direct all questions to Spirit's mailing list. You can subscribe to + the Spirit + Mailing List. The mailing list has a searchable archive. A search link + to this archive is provided in Spirit's + home page. You may also read and post messages to the mailing list through + Spirit General + NNTP news portal (thanks to Gmane). + The news group mirrors the mailing list. Here is a link to the archives: http://news.gmane.org/gmane.comp.parsers.spirit.general. +

+

+ + ...To my dear daughter, Phoenix +

+
+
+ + + +

Last revised: June 09, 2006 at 13:58:55 GMT

+
+
Next
+ + diff --git a/phoenix/doc/html/phoenix/acknowledgement.html b/phoenix/doc/html/phoenix/acknowledgement.html new file mode 100644 index 000000000..6786bba26 --- /dev/null +++ b/phoenix/doc/html/phoenix/acknowledgement.html @@ -0,0 +1,87 @@ + + + +Acknowledgement + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Acknowledgement

+
    +
  1. + Hartmut Kaiser implemented the original lazy casts and constructors based + on his original work on Spirit SE "semantic expressions" (the precursor + to Phoenix). +
  2. +
  3. + Angus Leeming implemented the container functions on Phoenix-1 which I then + ported to Phoenix-2. +
  4. +
  5. + Daniel Wallin helped with the scope module, local variables, let and lambda + and the algorithms. I frequently discuss design issues with Daniel on Yahoo + Messenger. +
  6. +
  7. + Jaakko Jarvi. DA Lambda MAN! +
  8. +
  9. + Dave Abrahams, for his constant presence, wherever, whenever. +
  10. +
  11. + Aleksey Gurtovoy, DA MPL MAN! +
  12. +
  13. + Doug Gregor, always a source of inpiration. +
  14. +
  15. + Dan Marsden, did almost all the work in bringing Phoenix-2 out the door. +
  16. +
  17. + Eric Niebler did a 2.0 pre-release review and wrote some range related code + that Phoenix stole and used in the algorithms. +
  18. +
  19. + Thorsten Ottosen; Eric's range_ex code began life as "container_algo" + in the old boost sandbox, by Thorsten in 2002-2003. +
  20. +
  21. + Jeremy Siek, even prior to Thorsten, in 2001, started the "container_algo". +
  22. +
  23. + Vladimir Prus wrote the mutating algorithms code from the Boost Wiki. +
  24. +
  25. + Daryle Walker did a 2.0 pre-release review. +
  26. +
+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/html/phoenix/actors.html b/phoenix/doc/html/phoenix/actors.html new file mode 100644 index 000000000..158be9cf3 --- /dev/null +++ b/phoenix/doc/html/phoenix/actors.html @@ -0,0 +1,82 @@ + + + +Actors + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Actors

+

+ The Actor is the main concept + behind the library. Actors are function objects. An actor can accept 0 to + PHOENIX_LIMIT arguments. +

+
+ + +
note You can set PHOENIX_LIMIT, + the predefined maximum arity an actor can take. By default, PHOENIX_LIMIT is set to 10.
+

+ Phoenix supplies an actor class + template whose specializations model the Actor + concept. actor has one template + parameter, Eval, that supplies + the smarts to evaluate the resulting function. +

+
+template <typename Eval>
+struct actor : Eval
+{
+    return_type
+    operator()() const;
+
+    template <typename T0>
+    return_type
+    operator()(T0& _0) const;
+
+    template <typename T0, typename T1>
+    return_type
+    operator()(T0& _0, T1& _1) const;
+
+    //...
+};
+
+

+ The actor class accepts the arguments through a set of function call operators + for 0 to PHOENIX_LIMIT arities + (Don't worry about the details, for now. Note, for example, that we skimp over + the details regarding return_type). + The arguments are then forwarded to the actor's Eval + for evaluation. +

+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/html/phoenix/algorithm.html b/phoenix/doc/html/phoenix/algorithm.html new file mode 100644 index 000000000..3bd04d99c --- /dev/null +++ b/phoenix/doc/html/phoenix/algorithm.html @@ -0,0 +1,408 @@ + + + +Algorithm + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Algorithm

+
+#include <boost/spirit/phoenix/algorithm.hpp>
+
+

+ The algorithm module provides wrappers for the standard algorithms in the + <algorithm> and <numeric> + headers. +

+

+ The algorithms are divided into the categories iteration, transformation and + querying, modelling the Boost.MPL + library. The different algorithm classes can be included using the headers: +

+
+#include <boost/spirit/phoenix/stl/algorithm/iteration.hpp>
+#include <boost/spirit/phoenix/stl/algorithm/transformation.hpp>
+#include <boost/spirit/phoenix/stl/algorithm/querying.hpp>
+
+

+ The functions of the algorithm module take ranges as arguments where appropriate. + This is different to the standard library, but easy enough to pick up. Ranges + are described in detail in the Boost.Range + library. +

+

+ For example, using the standard copy algorithm to copy between 2 arrays: +

+
+int array[] = {1, 2, 3};
+int output[3];
+std::copy(array, array + 3, output); // We have to provide iterators 
+                                     // to both the start and end of array
+
+

+ The analogous code using the phoenix algorithm module is: +

+
+int array[] = {1, 2, 3};
+int output[3];
+copy(arg1, arg2)(array, output); // Notice only 2 arguments, the end of 
+                                 // array is established automatically
+
+

+ The Boost.Range + library provides support for standard containers, strings and arrays, and can + be extended to support additional types. +

+

+ The following tables describe the different categories of algorithms, and their + semantics. +

+
+ + +
tip Arguments in brackets denote optional parameters.
+
+

+ + Iteration Algorithms +

+ ++++ + + + + + + + + + + + + + + +
Functionstl Semantics
for_each(r, c)for_each(begin(r), end(r), f)
accumulate(r, o[, f])accumulate(begin(r), end(r), o[, f])
+
+
+

+ + Querying Algorithms +

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Functionstl Semantics
find(r, a)find(begin(r), end(r), a)
find_if(r, f)find_if(begin(r), end(r), f)
find_end(r1, r2[, f])find_end(begin(r1), end(r1), begin(r2), end(r2)[, f])
find_first_of(r1, r2[, f])find_first_of(begin(r1), end(r1), begin(r2), end(r2)[, f])
adjacent_find(r[, f])adjacent_find(begin(r), end(r)[, f])
count(r, a)count(begin(r), end(r), a)
count_if(r, f)count_if(begin(r), end(r), f)
mismatch(r, i[, f])mismatch(begin(r), end(r), i[, f])
equal(r, i[, f])equal(begin(r), end(r), i[, f])
search(r1, r2[, f])search(begin(r1), end(r1), begin(r2), end(r2)[, f])
lower_bound(r, a[, f])lower_bound(begin(r), end(r), a[, f])
upper_bound(r, a[, f])upper_bound(begin(r), end(r), a[, f])
equal_range(r, a[, f])equal_range(begin(r), end(r), a[, f])
binary_search(r, a[, f])binary_search(begin(r), end(r), a[, f])
includes(r1, r2[, f])includes(begin(r1), end(r1), begin(r2), end(r2)[, f])
min_element(r[, f])min_element(begin(r), end(r)[, f])
max_element(r[, f])max_element(begin(r), end(r)[, f])
lexicographical_compare(r1, r2[, f])lexicographical_compare(begin(r1), end(r1), begin(r2), end(r2)[, f])
+
+
+

+ + Transformation Algorithms +

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Functionstl Semantics
copy(r, o)copy(begin(r), end(r), o)
copy_backward(r, o)copy_backward(begin(r), end(r), o)
transform(r, o, f)transform(begin(r), end(r), o, f)
transform(r, i, o, f)transform(begin(r), end(r), i, o, f)
replace(r, a, b)replace(begin(r), end(r), a, b)
replace_if(r, f, a)replace(begin(r), end(r), f, a)
replace_copy(r, o, a, b)replace_copy(begin(r), end(r), o, a, b)
replace_copy_if(r, o, f, a)replace_copy_if(begin(r), end(r), o, f, a)
fill(r, a)fill(begin(r), end(r), a)
fill_n(r, n, a)fill_n(begin(r), n, a)
generate(r, f)generate(begin(r), end(r), f)
generate_n(r, n, f)generate_n(begin(r), n, f)
remove(r, a)remove(begin(r), end(r), a)
remove_if(r, f)remove_if(begin(r), end(r), f)
remove_copy(r, o, a)remove_copy(begin(r), end(r), o, a)
remove_copy_if(r, o, f)remove_copy_if(begin(r), end(r), o, f)
unique(r[, f])unique(begin(r), end(r)[, f])
unique_copy(r, o[, f])unique_copy(begin(r), end(r), o[, f])
reverse(r)reverse(begin(r), end(r))
reverse_copy(r, o)reverse_copy(begin(r), end(r), o)
rotate(r, m)rotate(begin(r), m, end(r))
rotate_copy(r, m, o)rotate_copy(begin(r), m, end(r), o)
random_shuffle(r[, f])random_shuffle(begin(r), end(r), f)
partition(r, f)partition(begin(r), end(r), f)
stable_partition(r, f)stable_partition(begin(r), end(r), f)
sort(r[, f])sort(begin(r), end(r)[, f])
stable_sort(r[, f])stable_sort(begin(r), end(r)[, f])
partial_sort(r, m[, f])partial_sort(begin(r), m, end(r)[, f])
partial_sort_copy(r1, r2[, f])partial_sort_copy(begin(r1), end(r1), begin(r2), end(r2)[, f])
nth_element(r, n[, f])nth_element(begin(r), n, end(r)[, f])
merge(r1, r2, o[, f])merge(begin(r1), end(r1), begin(r2), end(r2), o[, f])
inplace_merge(r, m[, f])inplace_merge(begin(r), m, end(r)[, f])
set_union(r1, r2, o[, f])set_union(begin(r1), end(r1), begin(r2), end(r2)[, f])
set_intersection(r1, r2, o[, f])set_intersection(begin(r1), end(r1), begin(r2), end(r2)[, f])
set_difference(r1, r2, o[, f])set_difference(begin(r1), end(r1), begin(r2), end(r2)[, f])
set_symmetric_difference(r1, r2, o[, f])set_symmetric_difference(begin(r1), end(r1), begin(r2), end(r2)[, f])
push_heap(r[, f])push_heap(begin(r), end(r)[, f])
pop_heap(r[, f])pop_heap(begin(r), end(r)[, f])
make_heap(r[, f])make_heap(begin(r), end(r)[, f])
sort_heap(r[, f])sort_heap(begin(r), end(r)[, f])
next_permutation(r[, f])next_permutation(begin(r), end(r)[, f])
prev_permutation(r[, f])prev_permutation(begin(r), end(r)[, f])
inner_product(r, o, a[, f1, f2])inner_product(begin(r), end(r), o[, f1, f2])
partial_sum(r, o[, f])partial_sum(begin(r), end(r), o[, f])
adjacent_difference(r, o[, f])adjacent_difference(begin(r), end(r), o[, f])
+
+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/html/phoenix/basics.html b/phoenix/doc/html/phoenix/basics.html new file mode 100644 index 000000000..0191e12f0 --- /dev/null +++ b/phoenix/doc/html/phoenix/basics.html @@ -0,0 +1,243 @@ + + + +Basics + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Basics

+

+ Almost everything is a function in the Phoenix library that can be evaluated + as f(a1, a2, ..., an), + where n is the function's arity, or number of arguments + that the function expects. Operators are also functions. For example, a + b is just a function with arity == 2 (or + binary). a + b is the same as add(a, b), a + b + c is the same as add(add(a, b), c). +

+
+ + +
note Amusingly, functions may even return functions. + We shall see what this means in a short while.
+

+ + Partial Function Application +

+

+ Think of a function as a black box. You pass arguments and it returns something + back. The figure below depicts the typical scenario. +

+

+ fbox +

+

+ A fully evaluated function is one in which all the arguments are given. All + functions in plain C++ are fully evaluated. When you call the sin(x) function, you have to pass a number x. The + function will return a result in return: the sin of x. When you call the add(x, y) function, you have to pass two numbers x and + y. The function will return the sum of the two numbers. The figure below is + a fully evaluated add function. +

+

+ adder +

+

+ A partially applied function, on the other hand, is one in which not all the + arguments are supplied. If we are able to partially apply the function add above, we may pass only the first argument. + In doing so, the function does not have all the required information it needs + to perform its task to compute and return a result. What it returns instead + is another function, a lambda function --another black box. Unlike the original + add function which has an arity + of 2, the resulting lambda function has an arity of 1. Why? because we already + supplied part of the input: 2 +

+

+ add2 +

+

+ Now, when we shove in a number into our lambda function, it will return 2 plus + whatever we pass in. The lambda function essentially remembers 1) the original + function, add, and 2) the partial + input, 2. The figure below illustrates a case where we pass 3 to our lambda + function, which then returns 5: +

+

+ add2_call +

+

+ Obviously, partially applying the add + function, as we see above, cannot be done directly in C++ where we are expected + to supply all the arguments that a function expects. That's where the Phoenix + library comes in. The library provides the facilities to do partial function + application. +

+

+ + STL and higher order functions +

+

+ So, what's all the fuss? What makes partial function application so useful? + Recall our original example in the previous + section: +

+
+find_if(c.begin(), c.end(), arg1 % 2 == 1)
+
+

+ The expression arg1 % 2 == 1 evaluates to a lambda function. arg1 is a placeholder for an argument to + be supplied later. Hence, since there's only one unsupplied argument, the lambda + function has an arity 1. It just so happens that find_if + supplies the unsupplied argument as it loops from c.begin() + to c.end(). +

+
+ + +
note Higher order functions are functions which can take + other functions as arguments, and may also return functions as results. + Higher order functions are functions that are treated like any other + objects and can be used as arguments and return values from functions.
+

+ + Lazy Evaluation +

+

+ In Phoenix, to put it more accurately, function evaluation has two stages: +

+
    +
  1. + Partial application +
  2. +
  3. + Final evaluation +
  4. +
+

+ The first stage is handled by a set of generator functions. These are your + front ends (in the client's perspective). These generators create (through + partial function application), higher order functions that can be passed on + just like any other function pointer or function object. The second stage, + the actual function call, can be invoked or executed anytime in the future, + or not at all; hence "lazy". +

+

+ If we look more closely, the first step involves partial function application: +

+
+arg1 % 2 == 1
+
+

+ The second step is the actual function invocation (done inside the find_if function. These are the back-ends + (often, the final invocation is never actually seen by the client). In our + example, the find_if, if we + take a look inside, we'll see something like: +

+
+template <class InputIterator, class Predicate>
+InputIterator
+find_if(InputIterator first, InputIterator last, Predicate pred)
+{
+    while (first != last && !pred(*first))  // <--- The lambda function is called here
+        ++first;                            //      passing in *first
+    return first;
+}
+
+

+ Again, typically, we, as clients, see only the first step. However, in this + document and in the examples and tests provided, don't be surprised to see + the first and second steps juxtaposed in order to illustrate the complete semantics + of Phoenix expressions. Examples: +

+
+int x = 1;
+int y = 2;
+
+cout << (arg1 % 2 == 1)(x) << endl; // prints 1 or true
+cout << (arg1 % 2 == 1)(y) << endl; // prints 0 or false
+
+

+ + Forwarding Function Problem +

+

+ Usually, we, as clients, write the call-back functions while libraries (such + as STL) provide the callee (e.g. find_if). + In case the role is reversed, e.g. if you have to write an STL algorithm that + takes in a predicate, or develop a GUI library that accepts event handlers, + you have to be aware of a little known problem in C++ called the "Forwarding + Function Problem". +

+

+ Look again at the code above: +

+
+(arg1 % 2 == 1)(x)
+
+

+ Notice that, in the second-stage (the final evaluation), we used a variable + x. Be aware that the second + stage cannot accept non-const temporaries and literal constants. Hence, this + will fail: +

+
+(arg1 % 2 == 1)(123) // Error!
+
+

+ Disallowing non-const rvalues partially solves the "Forwarding + Function Problem" but prohibits code like above. +

+

+ + Polymorphic Functions +

+

+ Unless otherwise noted, Phoenix generated functions are fully polymorphic. + For instance, the add example + above can apply to integers, floating points, user defined complex numbers + or even strings. Example: +

+
+std::string h("Hello");
+char const* w = " World";
+std::string r = add(arg1, arg2)(h, w);
+
+

+ evaluates to std::string("Hello + World"). The observant + reader might notice that this function call in fact takes in heterogeneous + arguments where arg1 is of + type std::string and arg2 + is of type char const*. add + still works because the C++ standard library allows the expression a + b where a + is a std::string and b + is a char const*. +

+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/html/phoenix/composite.html b/phoenix/doc/html/phoenix/composite.html new file mode 100644 index 000000000..5312b9052 --- /dev/null +++ b/phoenix/doc/html/phoenix/composite.html @@ -0,0 +1,1534 @@ + + + +Composite + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Composite

+ +

+ Actors may be combined in a multitude of ways to form composites. Composites + are actors that are composed of zero or more actors. Composition is hierarchical. + An element of the composite can be a primitive or again another composite. + The flexibility to arbitrarily compose hierarchical structures allows us to + form intricate constructions that model complex functions, statements and expressions. +

+

+ A composite is-a tuple of 0..N actors. N is the predefined maximum actors a + composite can take. +

+
+ + +
note You can set PHOENIX_COMPOSITE_LIMIT, + the predefined maximum actors a composite can take. By default, PHOENIX_COMPOSITE_LIMIT is set to + PHOENIX_LIMIT (See Actors).
+

+ As mentioned, each of the actors A0..AN can, in turn, be another composite, + since a composite is itself an actor. This makes the composite a recursive + structure. The actual evaluation is handled by a composite specific eval policy. +

+
+

+Function

+
+#include <boost/spirit/phoenix/function/function.hpp>
+
+

+ The function class template + provides a mechanism for implementing lazily evaluated functions. Syntactically, + a lazy function looks like an ordinary C/C++ function. The function call + looks familiar and feels the same as ordinary C++ functions. However, unlike + ordinary functions, the actual function execution is deferred. +

+

+ Unlike ordinary function pointers or functor objects that need to be explicitly + bound through the bind function (see Bind), + the argument types of these functions are automatically lazily bound. +

+

+ In order to create a lazy function, we need to implement a model of the FunctionEval + concept. For a function that takes N + arguments, a model of FunctionEval must provide: +

+
    +
  • + An operator() + that implements that takes N + arguments, and implements the function logic. +
  • +
  • + A nested metafunction result<A1, ... AN> + that takes the types of the N + arguments to the function and returns the result type of the function. + (There is a special case for function objects that accept no arguments. + Such nullary functors are only required to define a typedef result_type that reflects the return + type of its operator()). +
  • +
+

+ For example, the following type implements the FunctionEval concept, in order + to provide a lazy factorial function: +

+
+struct factorial_impl
+{
+    template <typename Arg>
+    struct result
+    {
+        typedef Arg type;
+    };
+
+    template <typename Arg>
+    Arg operator()(Arg n) const
+    {
+        return (n <= 0) ? 1 : n * this->operator()(n-1);
+    }
+};
+
+

+ (See factorial.cpp) +

+

+ Having implemented the factorial_impl + type, we can declare and instantiate a lazy factorial + function this way: +

+
+function<factorial_impl> factorial;
+
+

+ Invoking a lazy function such as factorial + does not immediately execute the function object factorial_impl. + Instead, an actor object is created + and returned to the caller. Example: +

+
+factorial(arg1)
+
+

+ does nothing more than return an actor. A second function call will invoke + the actual factorial function. Example: +

+
+int i = 4;
+cout << factorial(arg1)(i);
+
+

+ will print out "24". +

+

+ Take note that in certain cases (e.g. for function objects with state), an + instance of the model of FunctionEval may be passed on to the constructor. + Example: +

+
+function<factorial_impl> factorial(ftor);
+
+

+ where ftor is an instance of factorial_impl (this is not necessary in this + case as factorial_impl does + not require any state). +

+
+ + +
alert Take care though when using function objects with + state because they are often copied repeatedly, and state may change + in one of the copies, rather than the original.
+
+
+

+Operator

+

+ This facility provides a mechanism for lazily evaluating operators. Syntactically, + a lazy operator looks and feels like an ordinary C/C++ infix, prefix or postfix + operator. The operator application looks the same. However, unlike ordinary + operators, the actual operator execution is deferred. Samples: +

+
+arg1 + arg2
+1 + arg1 * arg2
+1 / -arg1
+arg1 < 150
+
+

+ We have seen the lazy operators in action (see Quick + Start). Let's go back and examine them a little bit further: +

+
+find_if(c.begin(), c.end(), arg1 % 2 == 1)
+
+

+ Through operator overloading, the expression arg1 % 2 == 1 actually + generates an actor. This actor object is passed on to STL's find_if function. From the viewpoint of + STL, the composite is simply a function object expecting a single argument + of the containers value_type. For each element in c, + the element is passed on as an argument arg1 + to the actor (function object). The actor checks if this is an odd value + based on the expression arg1 % 2 == 1 where + arg1 is replaced by the container's element. +

+

+ Like lazy functions (see function), + lazy operators are not immediately executed when invoked. Instead, an actor + (see actors) object is created and + returned to the caller. Example: +

+
+(arg1 + arg2) * arg3
+
+

+ does nothing more than return an actor. A second function call will evaluate + the actual operators. Example: +

+
+int i = 4, j = 5, k = 6;
+cout << ((arg1 + arg2) * arg3)(i, j, k);
+
+

+ will print out "54". +

+

+ Operator expressions are lazily evaluated following four simple rules: +

+
    +
  1. + A binary operator, except ->* + will be lazily evaluated when at least one of its + operands is an actor object (see actors). +
  2. +
  3. + Unary operators are lazily evaluted if their argument is an actor object. +
  4. +
  5. + Operator ->* is lazily + evaluted if the left hand argument is an actor object. +
  6. +
  7. + The result of a lazy operator is an actor object that can in turn allow + the applications of rules 1 and 2. +
  8. +
+

+ For example, to check the following expression is lazily evaluated: +

+
+-(arg1 + 3 + 6)
+
+
    +
  1. + Following rule 1, arg1 + 3 + is lazily evaluated since arg1 + is an actor (see primitives). +
  2. +
  3. + The result of this arg1 + 3 + expression is an actor object, following rule 4. +
  4. +
  5. + Continuing, arg1 + 3 + 6 + is again lazily evaluated. Rule 2. +
  6. +
  7. + By rule 4 again, the result of arg1 + 3 + 6 + is an actor object. +
  8. +
  9. + As arg1 + 3 + 6 is an actor, -(arg1 + 3 + 6) is lazily + evaluated. Rule 2. +
  10. +
+

+ Lazy-operator application is highly contagious. In most cases, a single + argN actor infects all its + immediate neighbors within a group (first level or parenthesized expression). +

+

+ Note that at least one operand of any operator must be a valid actor for + lazy evaluation to take effect. To force lazy evaluation of an ordinary expression, + we can use ref(x), val(x) or cref(x) + to transform an operand into a valid actor object (see primitives. + For example: +

+
+1 << 3;      // Immediately evaluated
+val(1) << 3; // Lazily evaluated
+
+

+ + Supported operators +

+

+ + Unary operators +

+
+prefix:   ~, !, -, +, ++, --, & (reference), * (dereference)
+postfix:  ++, --
+
+

+ + Binary operators +

+
+=, [], +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=
++, -, *, /, %, &, |, ^, <<, >>
+==, !=, <, >, <=, >=
+&&, ||, ->*
+
+

+ + Ternary operator +

+
+if_else(c, a, b)
+
+

+ The ternary operator deserves special mention. Since C++ does not allow us + to overload the conditional expression: c ? a : b, + the if_else pseudo function is provided for this purpose. The behavior is + identical, albeit in a lazy manner. +

+

+ + Member pointer operator +

+
+a->*member_object_pointer
+a->*member_function_pointer
+
+

+ The left hand side of the member pointer operator must be an actor returning + a pointer type. The right hand side of the member pointer operator may be + either a pointer to member object or pointer to member function. +

+

+ If the right hand side is a member object pointer, the result is an actor + which, when evaluated, returns a reference to that member. For example: +

+
+struct A
+{
+    int member;
+};
+
+A* a = new A;
+...
+
+(arg1->*&A::member)(a); // returns member a->member
+
+

+ If the right hand side is a member function pointer, the result is an actor + which, when invoked, calls the specified member function. For example: +

+
+struct A
+{
+    int func(int);
+};
+
+A* a = new A;
+int i = 0;
+
+(arg1->*&A::func)(arg2)(a, i); // returns a->func(i)
+
+
+

+ + Include Files +

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OperatorsFile
+-, +, ++, + --, +=, + -=, *=, + /=, %=, + *, /, + % +#include <boost/spirit/phoenix/operator/arithmetic.hpp>
+&=, |=, ^=, + <<=, >>=, &, + |, ^, + <<, >> +#include <boost/spirit/phoenix/operator/bitwise.hpp>
+==, !=, <, + <=, >, + >= +#include <boost/spirit/phoenix/operator/comparison.hpp>
+<<, >> +#include <boost/spirit/phoenix/operator/io.hpp>
+!, &&, + || +#include <boost/spirit/phoenix/operator/logical.hpp>
+&x, + *p, + =, [] +#include <boost/spirit/phoenix/operator/self.hpp>
if_else(c, a, b)#include <boost/spirit/phoenix/operator/if_else.hpp>
->*#include <boost/spirit/phoenix/operator/member.hpp>
+
+
+
+

+Statement

+ +

+ Lazy statements... +

+

+ The primitives and composite building blocks presented so far are sufficiently + powerful to construct quite elaborate structures. We have presented lazy- + functions and lazy-operators. How about lazy-statements? First, an appetizer: +

+

+ Print all odd-numbered contents of an STL container using std::for_each + (all_odds.cpp): +

+
+for_each(c.begin(), c.end(),
+    if_(arg1 % 2 == 1)
+    [
+        cout << arg1 << ' '
+    ]
+);
+
+

+ Huh? Is that valid C++? Read on... +

+

+ Yes, it is valid C++. The sample code above is as close as you can get to + the syntax of C++. This stylized C++ syntax differs from actual C++ code. + First, the if has a trailing + underscore. Second, the block uses square brackets instead of the familiar + curly braces {}. +

+
+ + +
note C++ in C++?
+
In as much as Spirit + attempts to mimic EBNF in C++, Phoenix attempts to mimic C++ in C++!!! +
+

+ Here are more examples with annotations. The code almost speaks for itself. +

+
+

+Block Statement

+
+#include <boost/spirit/phoenix/statement/sequence.hpp>
+
+

+ Syntax: +

+
+statement,
+statement,
+....
+statement
+
+

+ Basically, these are comma separated statements. Take note that unlike + the C/C++ semicolon, the comma is a separator put in-between + statements. This is like Pascal's semicolon separator, rather than C/C++'s + semicolon terminator. For example: +

+
+statement,
+statement,
+statement, // ERROR!
+
+

+ Is an error. The last statement should not have a comma. Block statements + can be grouped using the parentheses. Again, the last statement in a group + should not have a trailing comma. +

+
+statement,
+statement,
+(
+    statement,
+    statement
+),
+statement
+
+

+ Outside the square brackets, block statements should be grouped. For example: +

+
+for_each(c.begin(), c.end(),
+    (
+        do_this(arg1),
+        do_that(arg1)
+    )
+);
+
+

+ Wrapping a comma operator chain around a parentheses pair blocks the interpretation + as an argument separator. The reason for the exception for the square bracket + operator is that the operator always takes exactly one argument, so it + "transforms" any attempt at multiple arguments with a comma operator + chain (and spits out an error for zero arguments). +

+
+
+

+if_ Statement

+
+#include <boost/spirit/phoenix/statement/if.hpp>
+
+

+ We have seen the if_ statement. + The syntax is: +

+
+if_(conditional_expression)
+[
+    sequenced_statements
+]
+
+
+
+

+ifelse statement

+
+#include <boost/spirit/phoenix/statement/if.hpp>
+
+

+ The syntax is +

+
+if_(conditional_expression)
+[
+    sequenced_statements
+]
+.else_
+[
+    sequenced_statements
+]
+
+

+ Take note that else has a + leading dot and a trailing underscore: .else_ +

+

+ Example: This code prints out all the elements and appends " > 5", " + == 5" or " < 5" + depending on the element's actual value: +

+
+for_each(c.begin(), c.end(),
+    if_(arg1 > 5)
+    [
+        cout << arg1 << " > 5\n"
+    ]
+    .else_
+    [
+        if_(arg1 == 5)
+        [
+            cout << arg1 << " == 5\n"
+        ]
+        .else_
+        [
+            cout << arg1 << " < 5\n"
+        ]
+    ]
+);
+
+

+ Notice how the if_else_ + statement is nested. +

+
+
+

+switch_ statement

+
+#include <boost/spirit/phoenix/statement/switch.hpp>
+
+

+ The syntax is: +

+
+switch_(integral_expression)
+[
+    case_<integral_value>(sequenced_statements),
+    ...
+    default_<integral_value>(sequenced_statements)
+]
+
+

+ A comma separated list of cases, and an optional default can be provided. + Note unlike a normal switch statement, cases do not fall through. +

+

+ Example: This code prints out "one", + "two" or "other value" depending on the + element's actual value: +

+
+for_each(c.begin(), c.end(),
+    switch_(arg1)
+    [
+        case_<1>(cout << val("one") << '\n'),
+        case_<2>(cout << val("two") << '\n'),
+        default_(cout << val("other value") << '\n')
+    ]
+);
+
+
+
+

+while_ Statement

+
+#include <boost/spirit/phoenix/statement/while.hpp>
+
+

+ The syntax is: +

+
+while_(conditional_expression)
+[
+    sequenced_statements
+]
+
+

+ Example: This code decrements each element until it reaches zero and prints + out the number at each step. A newline terminates the printout of each + value. +

+
+for_each(c.begin(), c.end(),
+    (
+        while_(arg1--)
+        [
+            cout << arg1 << ", "
+        ],
+        cout << val("\n")
+    )
+);
+
+
+
+

+dowhile Statement

+
+#include <boost/spirit/phoenix/statement/do_while.hpp>
+
+

+ The syntax is: +

+
+do_
+[
+    sequenced_statements
+]
+.while_(conditional_expression)
+
+

+ Again, take note that while + has a leading dot and a trailing underscore: .while_ +

+

+ Example: This code is almost the same as the previous example above with + a slight twist in logic. +

+
+for_each(c.begin(), c.end(),
+    (
+        do_
+        [
+            cout << arg1 << ", "
+        ]
+        .while_(arg1--),
+        cout << val("\n")
+    )
+);
+
+
+
+

+for_ Statement

+
+#include <boost/spirit/phoenix/statement/for.hpp>
+
+

+ The syntax is: +

+
+for_(init_statement, conditional_expression, step_statement)
+[
+    sequenced_statements
+]
+
+

+ It is again very similar to the C++ for statement. Take note that the init_statement, + conditional_expression and step_statement are separated by the comma instead + of the semi-colon and each must be present (i.e. for_(,,) is invalid). This is a case where the + nothing actor can be + useful. +

+

+ Example: This code prints each element N times where N is the element's + value. A newline terminates the printout of each value. +

+
+int iii;
+for_each(c.begin(), c.end(),
+    (
+        for_(ref(iii) = 0, ref(iii) < arg1, ++ref(iii))
+        [
+            cout << arg1 << ", "
+        ],
+        cout << val("\n")
+    )
+);
+
+

+ As before, all these are lazily evaluated. The result of such statements + are in fact composites that are passed on to STL's for_each function. In + the viewpoint of for_each, + what was passed is just a functor, no more, no less. +

+
+ + +
note Unlike lazy functions and lazy operators, lazy + statements always return void.
+
+
+

+try_ catch_ Statement

+
+#include <boost/spirit/phoenix/statement/try_catch.hpp>
+
+

+ The syntax is: +

+
+try_
+[
+    sequenced_statements
+]
+.catch_<exception_type>()
+[
+    sequenced_statements
+]
+...
+.catch_all
+[
+    sequenced_statement
+]
+
+

+ Note the usual underscore after try and catch, and the extra parentheses + required after the catch. +

+

+ Example: The following code calls the (lazy) function f + for each element, and prints messages about different exception types it + catches. +

+
+try_
+[
+    f(arg1)
+]
+.catch_<runtime_error>()
+[
+    cout << val("caught runtime error or derived\n")
+]
+.catch_<exception>()
+[
+    cout << val("caught exception or derived\n")
+]
+.catch_all
+[
+    cout << val("caught some other type of exception\n")
+]
+
+
+
+

+throw_

+
+#include <boost/spirit/phoenix/statement/throw.hpp>
+
+

+ As a natural companion to the try/catch support, the statement module provides + lazy throwing and rethrowing of exceptions. +

+

+ The syntax to throw an exception is: +

+
+throw_(exception_expression)
+
+

+ The syntax to rethrow an exception is: +

+
+throw_()
+
+

+ Example: This code extends the try/catch example, rethrowing exceptions + derived from runtime_error or exception, and translating other exception + types to runtime_errors. +

+
+try_
+[
+    f(arg1)
+]
+.catch_<runtime_error>()
+[
+    cout << val("caught runtime error or derived\n"),
+    throw_()
+]
+.catch_<exception>()
+[
+    cout << val("caught exception or derived\n"),
+    throw_()
+]
+.catch_all
+[
+    cout << val("caught some other type of exception\n"),
+    throw_(runtime_error("translated exception"))
+]
+
+
+
+
+

+Object

+

+ The Object module deals with object construction, destruction and conversion. + The module provides "lazy" versions of C++'s + object constructor, new, delete, static_cast, + dynamic_cast, const_cast and reinterpret_cast. +

+

+ + Construction +

+

+ Lazy constructors... +

+
+#include <boost/spirit/phoenix/object/construct.hpp>
+
+

+ Lazily construct an object from an arbitrary set of arguments: +

+
+construct<T>(ctor_arg1, ctor_arg2, ..., ctor_argN);
+
+

+ where the given parameters are the parameters to the contructor of the object + of type T (This implies, that type T is expected to have a constructor with + a corresponding set of parameter types.). +

+

+ Example: +

+
+construct<std::string>(arg1, arg2)
+
+

+ Constructs a std::string from arg1 + and arg2. +

+
+ + +
note The maximum number of actual parameters is limited + by the preprocessor constant PHOENIX_COMPOSITE_LIMIT. Note though, + that this limit should not be greater than PHOENIX_LIMIT. By default, + PHOENIX_COMPOSITE_LIMIT + is set to PHOENIX_LIMIT + (See Actors).
+

+ + New +

+

+ Lazy new... +

+
+#include <boost/spirit/phoenix/object/new.hpp>
+
+

+ Lazily construct an object, on the heap, from an arbitrary set of arguments: +

+
+new_<T>(ctor_arg1, ctor_arg2, ..., ctor_argN);
+
+

+ where the given parameters are the parameters to the contructor of the object + of type T (This implies, that type T is expected to have a constructor with + a corresponding set of parameter types.). +

+

+ Example: +

+
+new_<std::string>(arg1, arg2) // note the spelling of new_ (with trailing underscore)
+
+

+ Creates a std::string from arg1 + and arg2 on the heap. +

+
+ + +
note Again, the maximum number of actual parameters + is limited by the preprocessor constant PHOENIX_COMPOSITE_LIMIT. See + the note above.
+

+ + Delete +

+

+ Lazy delete... +

+
+#include <boost/spirit/phoenix/object/delete.hpp>
+
+

+ Lazily delete an object, from the heap: +

+
+delete_(arg);
+
+

+ where arg is assumed to be a pointer to an object. +

+

+ Example: +

+
+delete_<std::string>(arg1) // note the spelling of delete_ (with trailing underscore)
+
+

+ + Casts +

+

+ Lazy casts... +

+
+#include <boost/spirit/phoenix/object/static_cast.hpp>
+#include <boost/spirit/phoenix/object/dynamic_cast.hpp>
+#include <boost/spirit/phoenix/object/const_cast.hpp>
+#include <boost/spirit/phoenix/object/reinterpret_cast.hpp>
+
+

+ The set of lazy C++ cast template functions provide a way of lazily casting + an object of a certain type to another type. The syntax resembles the well + known C++ casts. Take note however that the lazy versions have a trailing + underscore. +

+
+static_cast_<T>(lambda_expression)
+dynamic_cast_<T>(lambda_expression)
+const_cast_<T>(lambda_expression)
+reinterpret_cast_<T>(lambda_expression)
+
+

+ Example: +

+
+static_cast_<Base*>(&arg1)
+
+

+ Static-casts the address of arg1 + to a Base*. +

+
+
+

+Scope

+

+ Up until now, the most basic ingredient is missing: creation of and access + to local variables in the stack. When recursion comes into play, you will + soon realize the need to have true local variables. It may seem that we do + not need this at all since an unnamed lambda function cannot call itself + anyway; at least not directly. With some sort of arrangement, situations + will arise where a lambda function becomes recursive. A typical situation + occurs when we store a lambda function in a Boost.Function, + essentially naming the unnamed lambda. +

+

+ There will also be situations where a lambda function gets passed as an argument + to another function. This is a more common situation. In this case, the lambda + function assumes a new scope; new arguments and possibly new local variables. +

+

+ This section deals with local variables and nested lambda scopes. +

+

+ + Local Variables +

+
+#include <boost/spirit/phoenix/scope/local_variable.hpp>
+
+

+ We use an instance of: +

+
+actor<local_variable<Key> >
+
+

+ to represent a local variable. The local variable acts as an imaginary data-bin + where a local, stack based data will be placed. Key + is an arbitrary type that is used to identify the local variable. Example: +

+
+struct size_key;
+actor<local_variable<size_key> > size;
+
+

+ + Predefined Local Variables +

+

+ There are a few predefined instances of actor<local_variable<Key> > + named _a.._z + that you can already use. To make use of them, simply use the namespace boost::phoenix::local_names: +

+
+using namespace boost::phoenix::local_names;
+
+

+ + let +

+
+#include <boost/spirit/phoenix/scope/let.hpp>
+
+

+ You declare local variables using the syntax: +

+
+let(local-declarations)
+[
+    let-body
+]
+
+

+ let allows 1..N local variable + declarations (where N == PHOENIX_LOCAL_LIMIT). + Each declaration follows the form: +

+
+local-id = lambda-expression
+
+
+ + +
note You can set PHOENIX_LOCAL_LIMIT, + the predefined maximum local variable declarations in a let expression. + By default, PHOENIX_LOCAL_LIMIT + is set to PHOENIX_LIMIT.
+

+ Example: +

+
+let(_a = 123, _b = 456)
+[
+    _a + _b
+]
+
+

+ + Reference Preservation +

+

+ The type of the local variable assumes the type of the lambda- expression. + Type deduction is reference preserving. For example: +

+
+let(_a = arg1, _b = 456)
+
+

+ _a assumes the type of arg1: a reference to an argument, while + _b has type int. +

+

+ Consider this: +

+
+int i = 1;
+
+let(_a = arg1)
+[
+    cout << --_a << ' '
+]
+(i);
+
+cout << i << endl;
+
+

+ the output of above is : 0 0 +

+

+ While with this: +

+
+int i = 1;
+
+let(_a = val(arg1))
+[
+    cout << --_a << ' '
+]
+(i);
+
+cout << i << endl;
+
+

+ the output is : 0 1 +

+

+ Reference preservation is necessary because we need to have L-value access + to outer lambda-scopes (especially the arguments). args + and refs are L-values. vals are R-values. +

+

+ + Visibility +

+

+ The scope and lifetimes of the local variables is limited within the let-body. + let blocks can be nested. + A local variable may hide an outer local variable. For example: +

+
+let(_x = 1, _y = ", World")
+[
+    // _x here is an int: 1
+
+    let(_x = "Hello") // hides the outer _x
+    [
+        cout << _x << _y // prints "Hello, World"
+    ]
+]
+
+

+ The RHS (right hand side lambda-expression) of each local-declaration cannot + refer to any LHS local-id. At this point, the local-ids are not in scope + yet; they will only be in scope in the let-body. The code below is in error: +

+
+let(
+    _a = 1
+  , _b = _a // Error: _a is not in scope yet
+)
+[
+    // _a and _b's scope starts here
+    /*. body .*/
+]
+
+

+ However, if an outer let scope is available, this will be searched. Since + the scope of the RHS of a local-declaration is the outer scope enclosing + the let, the RHS of a local-declaration can refer to a local variable of + an outer scope: +

+
+let(_a = 1)
+[
+    let(
+        _a = 1
+      , _b = _a // Ok. _a refers to the outer _a
+    )
+    [
+        /*. body .*/
+    ]
+]
+
+

+ + lambda +

+
+#include <boost/spirit/phoenix/scope/lambda.hpp>
+
+

+ A lot of times, you'd want to write a lazy function that accepts one or more + functions (higher order functions). STL algorithms come to mind, for example. + Consider a lazy version of stl::for_each: +

+
+struct for_each_impl
+{
+    template <typename C, typename F>
+    struct result
+    {
+        typedef void type;
+    };
+
+    template <typename C, typename F>
+    void operator()(C& c, F f) const
+    {
+        std::for_each(c.begin(), c.end(), f);
+    }
+};
+
+function<for_each_impl> const for_each = for_each_impl();
+
+

+ Notice that the function accepts another function, f + as an argument. The scope of this function, f, + is limited within the operator(). When f + is called inside std::for_each, it exists in a new scope, along + with new arguments and, possibly, local variables. This new scope is not + at all related to the outer scopes beyond the operator(). +

+

+ Simple syntax: +

+
+lambda
+[
+    lambda-body
+]
+
+

+ Like let, local variables + may be declared, allowing 1..N local variable declarations (where N == PHOENIX_LOCAL_LIMIT): +

+
+lambda(local-declarations)
+[
+    lambda-body
+]
+
+

+ The same restrictions apply with regard to scope and visibility. The RHS + (right hand side lambda-expression) of each local-declaration cannot refer + to any LHS local-id. The local-ids are not in scope yet; they will be in + scope only in the lambda-body: +

+
+lambda(
+    _a = 1
+  , _b = _a // Error: _a is not in scope yet
+)
+
+

+ See let + Visibility above for more information. +

+

+ Example: Using our lazy for_each + let's print all the elements in a container: +

+
+for_each(arg1, lambda[cout << arg1])
+
+

+ As far as the arguments are concerned (arg1..argN), the scope in which the + lambda-body exists is totally new. The left arg1 + refers to the argument passed to for_each + (a container). The right arg1 + refers to the argument passed by std::for_each + when we finally get to call operator() in our for_each_impl + above (a container element). +

+

+ Yet, we may wish to get information from outer scopes. While we do not have + access to arguments in outer scopes, what we still have is access to local + variables from outer scopes. We may only be able to pass argument related + information from outer lambda + scopes through the local variables. +

+
+ + +
note This is a crucial difference between let and lambda: + let does not introduce + new arguments; lambda + does.
+

+ Another example: Using our lazy for_each, + and a lazy push_back: +

+
+struct push_back_impl
+{
+    template <typename C, typename T>
+    struct result
+    {
+        typedef void type;
+    };
+
+    template <typename C, typename T>
+    void operator()(C& c, T& x) const
+    {
+        c.push_back(x);
+    }
+};
+
+function<push_back_impl> const push_back = push_back_impl();
+
+

+ write a lambda expression that accepts: +

+
    +
  1. + a 2-dimensional container (e.g. vector<vector<int> >) +
  2. +
  3. + a container element (e.g. int) +
  4. +
+

+ and pushes-back the element to each of the vector<int>. +

+

+ Solution: +

+
+for_each(arg1, 
+    lambda(_a = arg2)
+    [
+        push_back(arg1, _a)
+    ]
+)
+
+

+ Since we do not have access to the arguments of the outer scopes beyond the + lambda-body, we introduce a local variable _a + that captures the second outer argument: arg2. + Hence: _a = arg2. This local variable is visible inside the lambda scope. +

+

+ (See lambda.cpp) +

+
+
+

+Bind

+

+ Binding is the act of tying together a function to some + arguments for deferred (lazy) evaluation. Named Lazy + functions require a bit of typing. Unlike (unnamed) lambda expressions, + we need to write a functor somewhere offline, detached from the call site. + If you wish to transform a plain function, member function or member variable + to a lambda expression, bind + is your friend. +

+
+ + +
note Take note that binders are monomorphic. Rather + than binding functions, the preferred way is to write true generic + and polymorphic lazy-functions. + However, since most of the time we are dealing with adaptation of exisiting + code, binders get the job done faster.
+

+ There is a set of overloaded bind + template functions. Each bind(x) + function generates a suitable binder object, a composite. +

+

+ + Binding Functions +

+
+#include <boost/spirit/phoenix/bind/bind_function.hpp>
+
+

+ Example, given a function foo: +

+
+void foo(int n) 
+{ 
+    std::cout << n << std::endl; 
+}
+
+

+ Here's how the function foo + may be bound: +

+
+bind(&foo, arg1)
+
+

+ This is now a full-fledged composite + that can finally be evaluated by another function call invocation. A second + function call will invoke the actual foo + function. Example: +

+
+int i = 4;
+bind(&foo, arg1)(i);
+
+

+ will print out "4". +

+

+ + Binding Member Functions +

+
+#include <boost/spirit/phoenix/bind/bind_member_function.hpp>
+
+

+ Binding member functions can be done similarly. A bound member function takes + in a pointer or reference to an object as the first argument. For instance, + given: +

+
+struct xyz 
+{ 
+    void foo(int) const; 
+};
+
+

+ xyz's foo + member function can be bound as: +

+
+bind(&xyz::foo, obj, arg1) // obj is an xyz object
+
+

+ Take note that a lazy-member functions expects the first argument to be a + pointer or reference to an object. Both the object (reference or pointer) + and the arguments can be lazily bound. Examples: +

+
+xyz obj;
+bind(&xyz::foo, arg1, arg2)     // arg1.foo(arg2)
+bind(&xyz::foo, obj, arg1)      // obj.foo(arg1)
+bind(&xyz::foo, obj, 100)       // obj.foo(100)
+
+

+ + Binding Member Variables +

+
+#include <boost/spirit/phoenix/bind/bind_member_variable.hpp>
+
+

+ Member variables can also be bound much like member functions. Member variables + are not functions. Yet, like the ref(x) that acts like a nullary function + returning a reference to the data, member variables, when bound, act like + a unary function, taking in a pointer or reference to an object as its argument + and returning a reference to the bound member variable. For instance, given: +

+
+struct xyz 
+{ 
+    int v; 
+};
+
+

+ xyz::v can be bound as: +

+
+bind(&xyz::v, obj) // obj is an xyz object
+
+

+ As noted, just like the bound member function, a bound member variable also + expects the first (and only) argument to be a pointer or reference to an + object. The object (reference or pointer) can be lazily bound. Examples: +

+
+xyz obj;
+bind(&xyz::v, arg1)             // arg1.v 
+bind(&xyz::v, obj)              // obj.v  
+bind(&xyz::v, arg1)(obj) = 4    // obj.v = 4
+
+
+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/html/phoenix/inside_phoenix.html b/phoenix/doc/html/phoenix/inside_phoenix.html new file mode 100644 index 000000000..7a66d7002 --- /dev/null +++ b/phoenix/doc/html/phoenix/inside_phoenix.html @@ -0,0 +1,750 @@ + + + +Inside Phoenix + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Inside Phoenix

+ +

+ This chapter explains in more detail how the library operates. The information + henceforth should not be necessary to those who are interested in just using + the library. However, a microscopic view might prove to be beneficial to moderate + to advanced programmers who wish to extend the library. +

+
+

+Actors In Detail

+

+ + Actor Concept +

+

+ The main concept is the Actor. + Actors are function objects (that can accept 0 to N arguments (where N is + a predefined maximum). +

+
+ + +
note You can set PHOENIX_LIMIT, + the predefined maximum arity an actor can take. By default, PHOENIX_LIMIT is set to 10.
+

+ + actor template class +

+

+ The actor template class + models the Actor concept: +

+
+template <typename Eval>
+struct actor : Eval
+{
+    typedef Eval eval_type;
+
+    actor();
+    actor(Eval const& base);
+
+    template <typename T0>
+    explicit actor(T0 const& _0);
+
+    template <typename T0, typename T1>
+    actor(T0 const& _0, T1 const& _1);
+    
+    // more constructors
+
+    typename apply_actor<eval_type, basic_environment<> >::type
+    operator()() const;
+
+    template <typename T0>
+    typename apply_actor<eval_type, basic_environment<T0> >::type
+    operator()(T0& _0) const;
+
+    template <typename T0, typename T1>
+    typename apply_actor<eval_type, basic_environment<T0, T1> >::type
+    operator()(T0& _0, T1& _1) const;
+
+    // function call operators
+};
+
+
+

+ + Actor Concept Requirements +

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + +
ExpressionResult/Semantics
T::eval_typeThe actor's + Eval type
T()Default + Constructor
T(base)Constructor + from Eval
T(arg0, arg1, ..., argN)Pass through constructors
x(arg0, arg1, ..., argN)Function call operators
+
+

+ + Eval Concept +

+

+ The actor template class + has a single template parameter, Eval, + from which it derives from. While the Actor + concept represents a function, the Eval + concept represents the function body. The requirements for Eval are intentionally kept simple, to + make it easy to write models of the concept. We shall see an example in the + next section. +

+
+

+ + Eval Concept Requirements +

+ ++++ + + + + + + + + + + + + + + +
ExpressionResult/Semantics
return x.eval(env)Evaluates the function + (see Environment below)
T::result<Env>::typeThe return type + of eval (see Environment below)
+
+

+ + Constructors +

+

+ In addition to a default constructor and an constructor from a Eval object, + there are templated (pass through) constructors for 1 to N arguments (N == + PHOENIX_LIMIT). These constructors + simply forward the arguments to the base. +

+
+ + +
note Parametric Base Class + Pattern

Notice that actor derives from its + template argument Eval. This is the inverse of the curiously recurring + template pattern (CRTP). With the CRTP, a class, T, has a Derived template + parameter that is assumed to be its subclass. The "parametric + base class pattern" (PBCP), on the other hand, inverses the inheritance + and makes a class, T, the derived class. Both CRTP and PBCP techniques + have its pros and cons, which is outside the scope of this document. + CRTP should really be renamed "parametric subclass pattern (PSCP), + but again, that's another story.
+

+ + Function Call Operators +

+

+ There are N function call operators for 0 to N arguments (N == PHOENIX_LIMIT). The actor class accepts + the arguments and forwards the arguments to the actor's base Eval for evaluation. +

+
+ + +
note Forwarding Function + Problem

The function call operators cannot + accept non-const temporaries and literal constants. There is a known + issue with current C++ called the "Forwarding + Function Problem". The problem is that given an arbitrary + function F, using current + C++ language rules, one cannot create a forwarding function FF that transparently assumes the + arguments of F. Disallowing + non-const rvalues arguments partially solves the problem but prohibits + code such as f(1, 2, 3);. +
+

+ + Environment +

+

+ On an actor function call, before calling the actor's Eval::eval + for evaluation, the actor creates an environment. + Basically, the environment packages the arguments in a tuple. The Environment is a concept, of which, the + basic_environment template + class is a model of. +

+
+

+ + Environment Concept Requirements +

+ ++++ + + + + + + + + + + + + + + + + + + +
ExpressionResult/Semantics
x.args()The + arguments in a tie (a tuple of references)
T::args_typeThe arguments' + types in an MPL sequence
T::tie_typeThe tie (tuple + of references) type
+
+

+ Schematically: +

+

+ funnel_in +

+

+ Other parts of the library (e.g. the scope module) extends the Environment concept to hold other information + such as local variables, etc. +

+

+ + apply_actor +

+

+ apply_actor is a standard + MPL style metafunction that simply calls the Action's result + nested metafunction: +

+
+template <typename Action, typename Env>
+struct apply_actor
+{
+    typedef typename Action::template result<Env>::type type;
+};
+
+

+ After evaluating the arguments and doing some computation, the eval member function returns something + back to the client. To do this, the forwarding function (the actor's operator()) + needs to know the return type of the eval member function that it is calling. + For this purpose, models of Eval + are required to provide a nested template class: +

+
+template <typename Env>
+struct result;
+
+

+ This nested class provides the result type information returned by the Eval's eval + member function. The nested template class result + should have a typedef type + that reflects the return type of its member function eval. +

+

+ For reference, here's a typical actor::operator() that accepts two arguments: +

+
+template <typename T0, typename T1>
+typename apply_actor<eval_type, basic_environment<T0, T1> >::type
+operator()(T0& _0, T1& _1) const
+{
+    return eval_type::eval(basic_environment<T0, T1>(_0, _1));
+}
+
+

+ + actor_result +

+

+ For reasons of symmetry to the family of actor::operator() there is a special metafunction usable + for actor result type calculation named actor_result. + This metafunction allows us to directly to specify the types of the parameters + to be passed to the actor::operator() function. Here's a typical actor_result that accepts two arguments: +

+
+template <typename Action, typename T0, typename T1>
+struct actor_result
+{
+    typedef basic_environment<T0, T1> env_type;
+    typedef typename Action::template result<env_type>::type type;
+};
+
+
+
+

+Actor Example

+

+ Let us see a very simple prototypical example of an actor. This is not a + toy example. This is actually part of the library. Remember the reference?. +

+

+ First, we have a model of the Eval + concept: the reference: +

+
+template <typename T>
+struct reference
+{
+    template <typename Env>
+    struct result
+    {
+        typedef T& type;
+    };
+
+    reference(T& arg)
+        : ref(arg) {}
+
+    template <typename Env>
+    T& eval(Env const&) const
+    {
+        return ref;
+    }
+
+    T& ref;
+};
+
+

+ Models of Eval are never + created directly and its instances never exist alone. We have to wrap it + inside the actor template + class to be useful. The ref + template function does this for us: +

+
+template <typename T>
+actor<reference<T> > const
+ref(T& v)
+{
+    return reference<T>(v);
+}
+
+

+ The reference template class + conforms to the Eval concept. It has a nested result metafunction that reflects the return + type of its eval member function, + which peforms the actual function. reference<T> + stores a reference to a T. + Its eval member function + simply returns the reference. It does not make use of the environment Env. +

+

+ Pretty simple... +

+
+
+

+Composites In Detail

+

+ We stated before that composites are actors that are composed of zero or + more actors (see Composite). This + is not quite accurate. The definition was sufficient at that point where + we opted to keep things simple and not bury the reader with details which + she might not need anyway. +

+

+ Actually, a composite is a model of the Eval concept (more on this later). + At the same time, it is also composed of 0..N (where N is a predefined maximum) + Eval instances and an eval policy. + The individual Eval instances are stored in a tuple. +

+
+ + +
note In a sense, the original definition of "composite", + more or less, will do just fine because Eval instances never exist + alone and are always wrapped in an actor + template class which inherits from it anyway. The resulting actor IS-AN + Eval.
+
+ + +
note You can set PHOENIX_COMPOSITE_LIMIT, + the predefined maximum Evals + (actors) a composite can take. By default, PHOENIX_COMPOSITE_LIMIT + is set to PHOENIX_LIMIT + (See Actors).
+

+ + composite template class +

+
+template <typename EvalPolicy, typename EvalTuple>
+struct composite : EvalTuple
+{
+    typedef EvalTuple base_type;
+    typedef EvalPolicy eval_policy_type;
+
+    template <typename Env>
+    struct result
+    {
+        typedef implementation-defined type;
+    };
+
+    composite();
+    composite(base_type const& actors);
+
+    template <typename U0>
+    composite(U0 const& _0);
+
+    template <typename U0, typename U1>
+    composite(U0 const& _0, U1 const& _1);
+    
+    // more constructors
+
+    template <typename Env>
+    typename result<Env>::type
+    eval(Env const& env) const;
+};
+
+

+ + EvalTuple +

+

+ EvalTuple, holds all the + Eval instances. The composite template class inherits from + it. In addition to a default constructor and a constructor from an EvalTuple object, there are templated (pass + through) constructors for 1 to N arguments (again, where N == PHOENIX_COMPOSITE_LIMIT). These constructors + simply forward the arguments to the EvalTuple + base class. +

+

+ + EvalPolicy +

+

+ The composite's eval member + function calls its EvalPolicy's + eval member function (a static + member function) passing in the environment + and each of its actors, in parallel. The following diagram illustrates what's + happening: +

+

+ funnel_out +

+
+

+ + EvalPolicy Requirements +

+ ++++ + + + + + + + + + + + + + + +
ExpressionResult/Semantics
x.eval<RT>(env, eval0, eval1, ..., evalN)Evaluate the composite
T::result<Env, Eval0, Eval1, Eval2, ..., EvalN>::typeThe + return type of eval
+
+

+ The EvalPolicy is expected + to have a nested template class result + which has a typedef type + that reflects the return type of its member function eval. + Here's a typical example of the composite's eval member function for a 2-actor + composite: +

+
+template <typename Env>
+typename result<Env>::type
+eval(Env const& env) const
+{
+    typedef typename result<Env>::type return_type;
+    return EvalPolicy::template 
+        eval<return_type>(
+            env
+          , get<0>(*this)   // gets the 0th element from EvalTuple
+          , get<1>(*this)); // gets the 1st element from EvalTuple    
+}
+
+
+
+

+Composing

+ +

+ Composites are never instantiated directly. Front end expression templates + are used to generate the composites. Using expression templates, we implement + a DSEL (Domain Specific Embedded Language) that mimicks native C++. You've + seen this DSEL in action in the preceding sections. It is most evident in + the Statement section. +

+

+ There are some facilities in the library to make composition of composites + easier. We have a set of overloaded compose + functions and an as_composite + metafunction. Together, these helpers make composing a breeze. We'll provide + an example + of a composite later to see why. +

+
+

+compose

+
+compose<EvalPolicy>(arg0, arg1, arg2, ..., argN);
+
+

+ Given an EvalPolicy and some arguments + arg0...argN, returns a + proper composite. The arguments + may or may not be phoenix actors (primitives of composites). If not, the + arguments are converted to actors appropriately. For example: +

+
+compose<X>(3)
+
+

+ converts the argument 3 to + an actor<value<int> >(3). +

+
+
+

+as_composite

+
+as_composite<EvalPolicy, Arg0, Arg1, Arg2, ..., ArgN>::type
+
+

+ This is the metafunction counterpart of compose. + Given an EvalPolicy and some argument types + Arg0...ArgN, returns a + proper composite type. + For example: +

+
+as_composite<X, int>::type
+
+

+ is the composite type of the compose<X>(3) + expression above. +

+
+
+

+Composite Example

+

+ Now, let's examine an example. Again, this is not a toy example. This is + actually part of the library. Remember the while_ lazy statement? Putting + together everything we've learned so far, we will present it here in its + entirety (verbatim): +

+
+struct while_eval
+{
+    template <typename Env, typename Cond, typename Do>
+    struct result
+    {
+        typedef void type;
+    };
+
+    template <typename RT, typename Env, typename Cond, typename Do>
+    static void
+    eval(Env const& env, Cond& cond, Do& do_)
+    {
+        while (cond.eval(env))
+            do_.eval(env);
+    }
+};
+
+template <typename Cond>
+struct while_gen
+{
+    while_gen(Cond const& cond)
+        : cond(cond) {}
+
+    template <typename Do>
+    actor<typename as_composite<while_eval, Cond, Do>::type>
+    operator[](Do const& do_) const
+    {
+        return compose<while_eval>(cond, do_);
+    }
+
+    Cond cond;
+};
+
+template <typename Cond>
+while_gen<Cond>
+while_(Cond const& cond)
+{
+    return while_gen<Cond>(cond);
+}
+
+

+ while_eval is an example + of an EvalPolicy. while_gen + and while_ are the expression + template front ends. Let's break this apart to understand what's happening. + Let's start at the bottom. It's easier that way. +

+

+ When you write: +

+
+while_(cond)
+
+

+ we generate an instance of while_gen<Cond>, where Cond + is the type of cond. cond can be an arbitrarily complex actor + expression. The while_gen + template class has an operator[] accepting another expression. If we write: +

+
+while_(cond)
+[
+    do_
+]
+
+

+ it will generate a proper composite with the type: +

+
+as_composite<while_eval, Cond, Do>::type
+
+

+ where Cond is the type + of cond and Do is the type of do_. + Notice how we are using phoenix's composition + (compose and as_composite) mechanisms here +

+
+template <typename Do>
+actor<typename as_composite<while_eval, Cond, Do>::type>
+operator[](Do const& do_) const
+{
+    return compose<while_eval>(cond, do_);
+}
+
+

+ Finally, the while_eval + does its thing: +

+
+while (cond.eval(env))
+    do_.eval(env);
+
+

+ cond and do_, at this point, are instances of + Eval. cond + and do_ are the Eval elements held by the composite's + EvalTuple. env + is the Environment. +

+
+
+
+

+Extending

+

+ We've shown how it is very easy to extend phoenix by writing new primitives + and composites. The modular design of Phoenix makes it extremely extensible. + We have seen that layer upon layer, the whole library is built on a solid + foundation. There are only a few simple well designed concepts that are laid + out like bricks. Overall, the library is designed to be extended. Everything + above the core layer can in fact be considered just as extensions to the + library. This modular design was inherited from the Spirit + inline parser library. +

+

+ Extension is non-intrusive. And, whenever a component or module is extended, + the new extension automatically becomes a first class citizen and is automatically + recognized by all modules and components in the library. +

+
+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/html/phoenix/intrinsic.html b/phoenix/doc/html/phoenix/intrinsic.html new file mode 100644 index 000000000..3720bb39f --- /dev/null +++ b/phoenix/doc/html/phoenix/intrinsic.html @@ -0,0 +1,320 @@ + + + +Intrinsic + + + + + + + + + + + + + + + +
boost.png (6897 bytes)HomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Intrinsic

+
+#include <boost/spirit/phoenix/intrinsic.hpp>
+
+

+ The intrinsic module predefines a set of lazy functions that work on STL sequences. + These functions provide a mechanism for the lazy evaluation of the public member + functions of the STL containers. The lazy functions are thin wrappers that + simply forward to their respective counterparts in the STL library. +

+

+ Lazy functions are provided for all of the member functions of the following + containers: +

+
    +
  • + deque +
  • +
  • + list +
  • +
  • + map +
  • +
  • + multimap +
  • +
  • + vector +
  • +
+

+ Indeed, should your class have member functions with the same names and signatures + as those listed below, then it will automatically be supported. To summarize, + lazy functions are provided for member functions: +

+
    +
  • + assign +
  • +
  • + at +
  • +
  • + back +
  • +
  • + begin +
  • +
  • + capacity +
  • +
  • + clear +
  • +
  • + empty +
  • +
  • + end +
  • +
  • + erase +
  • +
  • + front +
  • +
  • + get_allocator +
  • +
  • + insert +
  • +
  • + key_comp +
  • +
  • + max_size +
  • +
  • + pop_back +
  • +
  • + pop_front +
  • +
  • + push_back +
  • +
  • + push_front +
  • +
  • + rbegin +
  • +
  • + rend +
  • +
  • + reserve +
  • +
  • + resize +
  • +
  • + size +
  • +
  • + splice +
  • +
  • + value_comp +
  • +
+

+ The lazy functions' names are the same as the corresponding member function. + The difference is that the lazy functions are free functions and therefore + does not use the member "dot" syntax. +

+
+

+Sample usage +

+ ++++ + + + + + + + + + + + + + + + + + + +
"Normal" version"Lazy" version
my_vector.at(5)at(arg1, 5)
my_list.size()size(arg1)
my_vector1.swap(my_vector2)swap(arg1, arg2)
+
+

+ Notice that member functions with names that clash with stl algorithms are + absent. This will be provided in Phoenix's algorithm module. +

+

+ No support is provided here for lazy versions of operator+=, operator[] etc. Such operators are not specific to STL + containers and lazy versions can therefore be found in operators. +

+
+

+Lazy STL Intrinsic Functions +

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FunctionSemantics
assign(c, a[, b, c])c.assign(a[, b, c])
at(c, i)c.at(i)
back(c)c.back()
begin(c)c.begin()
capacity(c)c.capacity()
clear(c)c.clear()
empty(c)c.empty()
end(c)c.end()
erase(c, a[, b])c.erase(a[, b])
front(c)c.front()
get_allocator(c)c.get_allocator()
insert(c, a[, b, c])c.insert(a[, b, c])
key_comp(c)c.key_comp()
max_size(c)c.max_size()
pop_back(c)c.pop_back()
pop_front(c)c.pop_front()
push_back(c, d)c.push_back(d)
push_front(c, d)c.push_front(d)
pop_front(c)c.pop_front()
rbegin(c)c.rbegin()
rend(c)c.rend()
reserve(c, n)c.reserve(n)
resize(c, a[, b])c.resize(a[, b])
size(c)c.size()
splice(c, a[, b, c, d])c.splice(a[, b, c, d])
value_comp(c)c.value_comp()
+
+
+ + +
+ + Arguments in brackets denote optional parameters.
+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/html/phoenix/introduction.html b/phoenix/doc/html/phoenix/introduction.html new file mode 100644 index 000000000..0d3e921cb --- /dev/null +++ b/phoenix/doc/html/phoenix/introduction.html @@ -0,0 +1,66 @@ + + + +Introduction + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Introduction

+

+ banner +

+

+ The Phoenix library enables FP techniques such as higher order functions, + lambda (unnamed functions), currying + (partial function application) and lazy evaluation in C++. The focus is more + on usefulness and practicality than purity, elegance and strict adherence to + FP principles. +

+

+ FP is a programming discipline that is not at all tied to a specific language. + FP as a programming discipline can, in fact, be applied to many programming + languages. In the realm of C++ for instance, we are seeing more FP techniques + being applied. C++ is sufficiently rich to support at least some of the most + important facets of FP. C++ is a multiparadigm programming language. It is + not only procedural. It is not only object oriented. Beneath the core of the + standard C++ library, a closer look into STL gives us a glimpse of FP already + in place. It is obvious that the authors of STL know and practice FP. In the + near future, we shall surely see more FP trickle down into the mainstream. +

+

+ The truth is, most of the FP techniques can coexist quite well with the standard + object oriented and imperative programming paradigms. When we are using STL + algorithms and functors (function objects) for example, we are already doing + FP. Phoenix is an evolutionary next step. +

+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/html/phoenix/organization.html b/phoenix/doc/html/phoenix/organization.html new file mode 100644 index 000000000..dd916780e --- /dev/null +++ b/phoenix/doc/html/phoenix/organization.html @@ -0,0 +1,208 @@ + + + +Organization + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Organization

+

+ Care and attention to detail was given, painstakingly, to the design and implementation + of Phoenix. +

+

+ The library is organized in four layers: +

+

+ organization +

+

+ The modules are orthogonal, with no cyclic dependencies. Lower layers do not + depend on higher layers. Modules in a layer do not depend on other modules + in the same layer. This means, for example, that Bind can be completely discarded + if it is not required; or one could perhaps take out Operator and Statement + and just use Function, which may be desireable in a pure FP application. +

+

+ The library has grown from the original Phoenix but still comprises only header + files. There are no object files to link against. +

+

+ + Core +

+

+ The lowest two layers comprise the core. +

+

+ The Actor is the main concept + behind the library. Lazy functions are abstracted as actors. There are only + 2 kinds of actors: +

+
    +
  1. + Primitives +
  2. +
  3. + Composites +
  4. +
+

+ Primitives provide the basic building blocks of functionality within Phoenix. + Composites are used to combine these primitives together to provide more powerful + functionality. +

+

+ Composites are composed of zero or more actors. Each actor in a composite can + again be another composite. +

+
+

+ + Modules +

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ModuleDescription
FunctionLazy functions support (e.g. add)
OperatorLazy operators support (e.g. +)
StatementLazy statments (e.g. if_, + while_)
ObjectLazy casts (e.g. static_cast_), + object creation destruction (e.g. new_, + delete_)
ScopeSupport for scopes, local variables and lambda-lambda
BindLazy functions from free functions, member + functions or member variables.
ContainerSet of predefined "lazy" functions + that work on STL containers and sequences (e.g. push_back).
AlgorithmSet of predefined "lazy" versions + of the STL algorithms (e.g. find_if).
+
+

+ Each module is defined in a header file with the same name. For example, the + core module is defined in <boost/spirit/phoenix/core.hpp>. +

+
+

+ + Includes +

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ModuleFile
Core#include <boost/spirit/phoenix/core.hpp>
Function#include <boost/spirit/phoenix/function.hpp>
Operator#include <boost/spirit/phoenix/operator.hpp>
Statement#include <boost/spirit/phoenix/statement.hpp>
Object#include <boost/spirit/phoenix/object.hpp>
Scope#include <boost/spirit/phoenix/scope.hpp>
Bind#include <boost/spirit/phoenix/bind.hpp>
Container#include <boost/spirit/phoenix/container.hpp>
Algorithm#include <boost/spirit/phoenix/algorithm.hpp>
+
+
+ + +
tip Finer grained include files are available per feature; + see the succeeding sections.
+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/html/phoenix/primitives.html b/phoenix/doc/html/phoenix/primitives.html new file mode 100644 index 000000000..aec57b353 --- /dev/null +++ b/phoenix/doc/html/phoenix/primitives.html @@ -0,0 +1,329 @@ + + + +Primitives + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Primitives

+ +

+ Actors are composed to create more complex actors in a tree-like hierarchy. + The primitives are atomic entities that are like the leaves in the tree. Phoenix + is extensible. New primitives can be added anytime. Right out of the box, there + are only a few primitives. This section shall deal with these preset primitives. +

+
+

+Arguments

+
+#include <boost/spirit/phoenix/core/argument.hpp>
+
+

+ We use an instance of: +

+
+actor<argument<N> >
+
+

+ to represent the Nth function argument. The argument placeholder acts as + an imaginary data-bin where a function argument will be placed. +

+

+ + Predefined Arguments +

+

+ There are a few predefined instances of actor<argument<N> > named arg1..argN, and its BLL + counterpart _1.._N. (where N is a predefined maximum). +

+

+ Here are some sample preset definitions of arg1..argN +

+
+actor<argument<0> > const arg1 = argument<0>();
+actor<argument<1> > const arg2 = argument<1>();
+actor<argument<2> > const arg3 = argument<2>();
+
+

+ and its BLL _1.._N + style counterparts: +

+
+actor<argument<0> > const _1 = argument<0>();
+actor<argument<1> > const _2 = argument<1>();
+actor<argument<2> > const _3 = argument<2>();
+
+
+ + +
note You can set PHOENIX_ARG_LIMIT, + the predefined maximum placeholder index. By default, PHOENIX_ARG_LIMIT is set to PHOENIX_LIMIT (See Actors).
+

+ + User Defined Arguments +

+

+ When appropriate, you can define your own argument<N> + names. For example: +

+
+actor<argument<0> > x; // note zero based index
+
+

+ x may now be used as a parameter + to a lazy function: +

+
+add(x, 6)
+
+

+ which is equivalent to: +

+
+add(arg1, 6)
+
+

+ + Evaluating an Argument +

+

+ An argument, when evaluated, selects the Nth argument from the those passed + in by the client. +

+

+ For example: +

+
+char        c = 'A';
+int         i = 123;
+const char* s = "Hello World";
+
+cout << arg1(c) << endl;        //  Get the 1st argument: c
+cout << arg1(i, s) << endl;     //  Get the 1st argument: i
+cout << arg2(i, s) << endl;     //  Get the 2nd argument: s
+
+

+ will print out: +

+
+A
+123
+Hello World
+
+

+ + Extra Arguments +

+

+ In C and C++, a function can have extra arguments that are not at all used + by the function body itself. These extra arguments are simply ignored. +

+

+ Phoenix also allows extra arguments to be passed. For example, recall our + original add function: +

+
+add(arg1, arg2)
+
+

+ We know now that partially applying this function results to a function that + expects 2 arguments. However, the library is a bit more lenient and allows + the caller to supply more arguments than is actually required. Thus, add actually allows 2 or more + arguments. For instance, with: +

+
+add(arg1, arg2)(x, y, z)
+
+

+ the third argument z is ignored. + Taking this further, in-between arguments are also ignored. Example: +

+
+add(arg1, arg5)(a, b, c, d, e)
+
+

+ Here, arguments b, c, and d are ignored. The function add + takes in the first argument (arg1) + and the fifth argument (arg5). +

+
+ + +
note There are a few reasons why enforcing strict arity + is not desireable. A case in point is the callback function. Typical + callback functions provide more information than is actually needed. + Lambda functions are often used as callbacks.
+
+
+

+Values

+
+#include <boost/spirit/phoenix/core/value.hpp>
+
+

+ Whenever we see a constant in a partially applied function, an +

+
+actor<value<T> > 
+
+

+ (where T is the type of the constant) is automatically created for us. For + instance: +

+
+add(arg1, 6)
+
+

+ Passing a second argument, 6, + an actor<value<int> > is implicitly created behind the scenes. + This is also equivalent to: +

+
+add(arg1, val(6))
+
+

+ val(x) generates + an actor<value<T> > where T + is the type of x. In most + cases, there's no need to explicitly use val, + but, as we'll see later on, there are situations where this is unavoidable. +

+

+ + Evaluating a Value +

+

+ Like arguments, values are also actors. As such, values can be evaluated. + Invoking a value gives the value's identity. Example: +

+
+cout << val(3)() << val("Hello World")();
+
+

+ prints out "3 Hello World". +

+
+
+

+References

+
+#include <boost/spirit/phoenix/core/reference.hpp>
+
+

+ Values are immutable constants. Attempting to modify a value will result + in a compile time error. When we want the function to modify the parameter, + we use a reference instead. For instance, imagine a lazy function add_assign: +

+
+void add_assign(T& x, T y) { x += y; } // pseudo code
+
+

+ Here, we want the first function argument, x, to be mutable. Obviously, we + cannot write: +

+
+add_assign(1, 2) // error first argument is immutable
+
+

+ In C++, we can pass in a reference to a variable as the first argument in + our example above. Yet, by default, the library forces arguments passed to + partially applied functions functions to be immutable values (see Values). + To achieve our intent, we use: +

+
+actor<reference<T> >
+
+

+ This is similar to actor<value<T> > above but instead holds a reference to + a variable. +

+

+ We normally don't instantiate actor<reference<T> > objects directly. Instead we use ref. For example (where i + is an int variable): +

+
+add_assign(ref(i), 2)
+
+

+ + Evaluating a Reference +

+

+ References are actors. Hence, references can be evaluated. Such invocation + gives the references's identity. Example: +

+
+int i = 3;
+char const* s = "Hello World";
+cout << ref(i)() << ref(s)();
+
+

+ prints out "3 Hello World" +

+
+
+

+Constant References

+
+#include <boost/spirit/phoenix/core/reference.hpp>
+
+

+ Another free function cref(cv) + may also be used. cref(cv) creates + an actor<reference<T const&> > + object. This is similar to actor<value<T> > but when the data to be passed as argument + to a function is heavy and expensive to copy by value, the cref(cv) offers a lighter alternative. +

+
+
+

+Nothing

+
+#include <boost/spirit/phoenix/core/nothing.hpp>
+
+

+ Finally, the actor<null_actor> + does nothing; (a "bum", if you will :-). There's a sole actor<null_actor> + instance named "nothing". This actor is actually useful in situations + where we don't want to do anything. (See for_ + Statement for example). +

+
+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/html/phoenix/references.html b/phoenix/doc/html/phoenix/references.html new file mode 100644 index 000000000..0feb9eeaf --- /dev/null +++ b/phoenix/doc/html/phoenix/references.html @@ -0,0 +1,86 @@ + + + +References + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHome +
+
+

+References

+
    +
  1. + Why Functional Programming Matters, John Hughes, 1989. Available online at + http://www.math.chalmers.se/~rjmh/Papers/whyfp.html. +
  2. +
  3. + Boost.Lambda library, Jaakko Jarvi, 1999-2004 Jaakko Jarvi, Gary Powell. + Available online at http://www.boost.org/libs/lambda/. +
  4. +
  5. + Functional Programming in C++ using the FC++ Library: a short article introducing + FC++, Brian McNamara and Yannis Smaragdakis, August 2003. Available online + at http://www.cc.gatech.edu/~yannis/fc++/. +
  6. +
  7. + Side-effects and partial function application in C++, Jaakko Jarvi and Gary + Powell, 2001. Available online at http://osl.iu.edu/~jajarvi/publications/papers/mpool01.pdf. +
  8. +
  9. + Spirit Version 1.8.1, Joel de Guzman, Nov 2004. Available online at http://www.boost.org/libs/spirit/. +
  10. +
  11. + The Boost MPL Library, Aleksey Gurtovoy and David Abrahams, 2002-2004. Available + online at http://www.boost.org/libs/mpl/. +
  12. +
  13. + Generic Programming Redesign of Patterns, Proceedings of the 5th European + Conference on Pattern Languages of Programs, (EuroPLoP'2000) Irsee, Germany, + July 2000. Available online at http://www.coldewey.com/europlop2000/papers/geraud%2Bduret.zip. +
  14. +
  15. + A Gentle Introduction to Haskell, Paul Hudak, John Peterson and Joseph Fasel, + 1999. Available online at http://www.haskell.org/tutorial/. +
  16. +
  17. + Large scale software design, John Lackos, ISBN 0201633620, Addison-Wesley, + July 1996. +
  18. +
  19. + Design Patterns, Elements of Reusable Object-Oriented Software, Erich Gamma, + Richard Helm, Ralph Jhonson, and John Vlissides, Addison-Wesley, 1995. +
  20. +
  21. + The Forwarding Problem: Arguments Peter Dimov, Howard E. Hinnant, Dave Abrahams, + September 09, 2002. Available online: Forwarding + Function Problem. +
  22. +
+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHome +
+ + diff --git a/phoenix/doc/html/phoenix/starter_kit.html b/phoenix/doc/html/phoenix/starter_kit.html new file mode 100644 index 000000000..0b3cb662f --- /dev/null +++ b/phoenix/doc/html/phoenix/starter_kit.html @@ -0,0 +1,503 @@ + + + +Starter Kit + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Starter Kit

+ +

+ Most "quick starts" only get you a few blocks from where you are. + From there, you are on your own. Yet, typically, you'd want to get to the next + city. This starter kit shall be as minimal as possible, yet packed as much + power as possible. +

+

+ So you are busy and always on the go. You do not wish to spend a lot of time + studying the library. You wish to be spared the details for later when you + need it. For now, all you need to do is to get up to speed as quickly as possible + and start using the library. If this is the case, this is the right place to + start. +

+

+ This chapter is by no means a thorough discourse of the library. For more information + on Phoenix, please take some time to read the rest of the User's Guide. Yet, + if you just want to use the library quickly, now, this chapter will probably + suffice. Rather than taking you to the details of the library, we shall try + to provide you with annotated exemplars instead. Hopefully, this will get you + into high gear quickly. +

+

+ + Functors everywhere +

+

+ Phoenix is built on function objects (functors). The functor is the main building + block. We compose functors to build more complex functors... to build more + complex functors... and so on. Almost everything is a functor. +

+
+ + +
note Functors are so ubiquitous in Phoenix that, in the + manual, the words "functor" and "function" + are used interchangeably.
+
+

+Values

+

+ Values are functions! Examples: +

+
+val(3)
+val("Hello, World")
+
+

+ The first evaluates to a nullary function (a function taking no arguments) + that returns an int, 3. The second evaluates to a nullary function + that returns a char const(&)[13], "Hello, + World". +

+

+ + Lazy Evaluation +

+

+ Confused? val(3) is a unary + function, you say? Yes it is. However, read carefully: "evaluates + to a nullary function". val(3) + evaluates to (returns) a nullary function. Aha! val(3) + returns a function! So, since val(3) + returns a function, you can invoke it. Example: +

+
+cout << val(3)() << endl;
+
+

+ (See values.cpp) +

+
+ + +
tip Learn more about values here. +
+

+ The second function call (the one with no arguments) calls the nullary function + which then returns 3. The need + for a second function call is the reason why the function is said to be + Lazily Evaluated. The + first call doesn't do anything. You need a second call to finally evaluate + the thing. The first call lazily evaluates the function; i.e. doesn't do + anything and defers the evaluation for later. +

+

+ + Callbacks +

+

+ It may not be immediately apparent how lazy evaluation can be useful by just + looking at the example above. Putting the first and second function call + in a single line is really not very useful. However, thinking of val(3) as a callback function (and in most cases + they are actually used that way), will make it clear. Example: +

+
+template <typename F>
+void print(F f)
+{
+    cout << f() << endl;
+}
+
+int
+main()
+{
+    print(val(3));
+    print(val("Hello World"));
+    return 0;
+}
+
+

+ (See callback.cpp) +

+
+
+

+References

+

+ References are functions. They hold a reference to a value stored somehere. + For example, given: +

+
+int i = 3;
+char const* s = "Hello World";
+
+

+ we create references to + i and s + this way: +

+
+ref(i)
+ref(s)
+
+

+ Like val, the expressions + above evaluates to a nullary function; the first one returning an int&, + and the second one returning a char const*&. +

+

+ (See references.cpp) +

+
+ + +
tip Learn more about references here. +
+
+
+

+Arguments

+

+ Arguments are also functions? You bet! +

+

+ Until now, we have been dealing with expressions returning a nullary function. + Arguments, on the other hand, evaluate to an N-ary function. An argument + represents the Nth argument. There are a few predefined arguments arg1, arg2, + arg3, arg4 and so on (and it's BLL + counterparts: _1, _2, _3, _4 and so on). Examples: +

+
+arg1 // one-or-more argument function that returns its first argument
+arg2 // two-or-more argument function that returns its second argument
+arg3 // three-or-more argument function that returns its third argument
+
+

+ argN returns the Nth argument. + Examples: +

+
+int i = 3;
+char const* s = "Hello World";
+cout << arg1(i) << endl;        // prints 3
+cout << arg2(i, s) << endl;     // prints "Hello World"
+
+

+ (See arguments.cpp) +

+
+ + +
tip Learn more about arguments here. +
+
+
+

+Composites

+

+ What we have seen so far, are what are called primitives. + You can think of primitives (such as values, references and arguments) as + atoms. +

+

+ Things start to get interesting when we start composing + primitives to form composites. The composites + can, in turn, be composed to form even more complex composites. +

+
+
+

+Lazy Operators

+

+ You can use the usual set of operators to form composites. Examples: +

+
+arg1 * arg1
+ref(x) = arg1 + ref(z)
+arg1 = arg2 + (3 * arg3)
+ref(x) = arg1[arg2] // assuming arg1 is indexable and arg2 is a valid index
+
+

+ Note the expression: 3 * arg3. + This expression is actually a short-hand equivalent to: val(3) * arg3. + In most cases, like above, you can get away with it. But in some cases, you + will have to explicitly wrap your values in val. + Rules of thumb: +

+
    +
  • + In a binary expression (e.g. 3 * arg3), + at least one of the operands must be a phoenix primitive or composite. +
  • +
  • + In a unary expression (e.g. arg1++), the single operand must be a phoenix + primitive or composite. +
  • +
+

+ If these basic rules are not followed, the result is either in error, or + is immediately evaluated. Some examples: +

+
+ref(x) = 123    // lazy
+x = 123         // immediate
+
+ref(x)[0]       // lazy
+x[0]            // immediate
+
+ref(x)[ref(i)]  // lazy
+ref(x)[i]       // lazy (equivalent to ref(x)[val(i)])
+x[ref(i)]       // illegal (x is not a phoenix primitive or composite)
+ref(x[ref(i)])  // illegal (x is not a phoenix primitive or composite)
+
+
+ + +
tip Learn more about operators here. +
+

+ + First Practical Example +

+

+ We've covered enough ground to present a real world example. We want to find + the first odd number in an STL container. Normally we use a functor (function + object) or a function pointer and pass that in to STL's find_if + generic function: +

+

+ Write a function: +

+
+bool
+is_odd(int arg1)
+{
+    return arg1 % 2 == 1;
+}
+
+

+ Pass a pointer to the function to STL's find_if + algorithm: +

+
+find_if(c.begin(), c.end(), &is_odd)
+
+

+ Using Phoenix, the same can be achieved directly with a one-liner: +

+
+find_if(c.begin(), c.end(), arg1 % 2 == 1)
+
+

+ The expression arg1 % 2 == 1 automagically + creates a functor with the expected behavior. In FP, this unnamed function + is called a lambda function. Unlike the function pointer version, which is + monomorphic (expects and works only with a fixed type int argument), the + Phoenix version is fully polymorphic and works with any container (of ints, + of longs, of bignum, etc.) as long as its elements can handle the arg1 % 2 == 1 expression. +

+

+ (See find_if.cpp) +

+
+ + +
tip ...That's it, we're done. + Well if you wish to know a little bit more, read on...
+
+
+

+Lazy Statements

+

+ Lazy statements? Sure. There are lazy versions of the C++ statements we all + know and love. For example: +

+
+if_(arg1 > 5)
+    cout << arg1
+
+

+ Say, for example, we wish to print all the elements that are greater than + 5 (separated by a comma) in a vector. Here's how we write it: +

+
+for_each(v.begin(), v.end(),
+    if_(arg1 > 5)
+    [
+        cout << arg1 << ", "
+    ]
+);
+
+

+ (See if.cpp) +

+
+ + +
tip Learn more about statements here. +
+
+
+

+Construct, New, Delete, Casts

+

+ You'll probably want to work with objects. There are lazy versions of constructor + calls, new, delete + and the suite of C++ casts. Examples: +

+
+construct<std::string>(arg1, arg2)  // constructs a std::string from arg1, arg2
+new_<std::string>(arg1, arg2)       // makes a new std::string from arg1, arg2
+delete_(arg1)                       // deletes arg1 (assumed to be a pointer)
+static_cast_<int*>(arg1)            // static_cast's arg1 to an int*
+
+
+ + +
note Take note that, by convention, names that conflict + with C++ reserved words are appended with a single trailing underscore + '_' +
+
+ + +
tip Learn more about this here. +
+
+
+

+Lazy Functions

+

+ As you write more lambda functions, you'll notice certain patterns that you + wish to refactor as reusable functions. When you reach that point, you'll + wish that ordinary functions can co-exist with phoenix functions. Unfortunately, + the immediate nature of plain C++ functions make them + incompatible. +

+

+ Lazy functions are your friends. The library provides a facility to make + lazy functions. The code below is a rewrite of the is_odd + function using the facility: +

+
+struct is_odd_impl
+{
+    template <typename Arg>
+    struct result 
+    { 
+        typedef bool type; 
+    };
+
+    template <typename Arg>
+    bool operator()(Arg arg1) const
+    { 
+        return arg1 % 2 == 1; 
+    }
+};
+
+function<is_odd_impl> is_odd;
+
+

+ + Things to note: +

+
    +
  • +result is a nested metafunction + that reflects the return type of the function (in this case, bool). This + makes the function fully polymorphic: It can work with arbitrary Arg types. +
  • +
  • + There are as many Args in the result + metafunction as in the actual operator(). +
  • +
  • +is_odd_impl implements + the function. +
  • +
  • +is_odd, an instance of + function<is_odd_impl>, + is the lazy function. +
  • +
+

+ Now, is_odd is a truly lazy + function that we can use in conjunction with the rest of phoenix. Example: +

+
+find_if(c.begin(), c.end(), is_odd(arg1));
+
+

+ (See function.cpp) +

+

+ + Predefined Lazy Functions +

+

+ The library is chock full of STL savvy, predefined lazy functions covering + the whole of the STL containers, iterators and algorithms. For example, there + are lazy versions of container related operations such as assign, at, back, + begin, pop_back, pop_front, push_back, push_front, etc. (See Container). +

+
+
+

+More

+

+ As mentioned earlier, this chapter is not a thorough discourse of the library. + It is meant only to cover enough ground to get you into high gear as quickly + as possible. Some advanced stuff is not discussed here (e.g. Scopes); + nor are features that provide alternative (short-hand) ways to do the same + things (e.g. Bind vs. Lazy + Functions). +

+
+ + +
tip ...If you still wish to + learn more, the read on... +
+
+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/html/phoenix/wrap_up.html b/phoenix/doc/html/phoenix/wrap_up.html new file mode 100644 index 000000000..43050225d --- /dev/null +++ b/phoenix/doc/html/phoenix/wrap_up.html @@ -0,0 +1,72 @@ + + + +Wrap Up + + + + + + + + + + + + + + + +
Boost C++ LibrariesHomeLibrariesPeopleFAQMore
+
+
+PrevUpHomeNext +
+
+

+Wrap Up

+

+ Sooner or later more FP techniques become standard practice as people find + the true value of this programming discipline outside the academe and into + the mainstream. In as much as structured programming of the 70s and object + oriented programming in the 80s and generic programming in the 90s shaped our + thoughts towards a more robust sense of software engineering, FP will certainly + be a paradigm that will catapult us towards more powerful software design and + engineering onward into the new millenium. +

+

+ Let me quote Doug Gregor of Boost.org. About functional style programming libraries: +

+

+ They're gaining acceptance, but are somewhat stunted by the ubiquitousness + of broken compilers. The C++ community is moving deeper into the so-called + "STL- style" programming paradigm, which brings many aspects of + functional programming into the fold. Look at, for instance, the Spirit parser + to see how such function objects can be used to build Yacc-like grammars + with semantic actions that can build abstract syntax trees on the fly. This + type of functional composition is gaining momentum. +

+

+ Indeed. Phoenix is another attempt to introduce more FP techniques into the + mainstream. Not only is it a tool that will make life easier for the programmer. + In its own right, the actual design of the library itself is a model of true + C++ FP in action. The library is designed and structured in a strict but clear + and well mannered FP sense. By all means, use the library as a tool. But for + those who want to learn more about FP in C++, don't stop there, I invite you + to take a closer look at the design of the library itself. +

+

+ So there you have it. Have fun! See you in the FP world. +

+
+ + + +
Copyright © 2002-2005 Joel + de Guzman, Dan Marsden
+
+
+PrevUpHomeNext +
+ + diff --git a/phoenix/doc/users_manual.qbk b/phoenix/doc/users_manual.qbk new file mode 100644 index 000000000..64e861635 --- /dev/null +++ b/phoenix/doc/users_manual.qbk @@ -0,0 +1,2769 @@ +[library Phoenix + [quickbook 1.3] + [version 2.0] + [authors [de Guzman, Joel], [Marsden, Dan]] + [copyright 2002 2003 2004 2005 Joel de Guzman, Dan Marsden] + [category string-text] + [purpose Lambda Expressions in C++] + [license + Distributed under the Boost Software License, Version 1.0. + (See accompanying file LICENSE_1_0.txt or copy at + [@http://www.boost.org/LICENSE_1_0.txt]) + ] +] + +[/ September 2002] +[/ September 2004] +[/ September 2005] + +[/ Some links] + +[def __note__ [$images/note.png]] +[def __alert__ [$images/alert.png]] +[def __tip__ [$images/tip.png]] + +[def __spirit__ [@http://spirit.sourceforge.net Spirit]] +[def __haskell__ [@http://www.haskell.org Haskell]] +[def __mpl__ [@http://www.boost.org/libs/mpl/index.html MPL]] +[def __bll__ [@http://www.boost.org/libs/lambda/doc/index.html BLL]] +[def __fcpp__ [@http://www.cc.gatech.edu/~yannis/fc++/ FC++]] +[def __spirit_repo__ [@http://spirit.sourceforge.net/repository/applications/show_contents.php Spirit Repository]] +[def __spirit_list__ [@https://lists.sourceforge.net/lists/listinfo/spirit-general Spirit Mailing List]] +[def __spirit_general__ [@news://news.gmane.org/gmane.comp.spirit.general Spirit General NNTP news portal]] +[def __gmane__ [@http://www.gmane.org Gmane]] +[def __mlist_archive__ [@http://news.gmane.org/gmane.comp.parsers.spirit.general]] +[def __forwarding__ [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1385.htm Forwarding Function Problem]] +[def __boost_mpl__ [@http://boost.org/libs/mpl/doc/index.html Boost.MPL]] +[def __boost_range__ [@http://boost.org/libs/range/index.html Boost.Range]] + +[section Preface] + +[:['Functional programming is so called because a program consists entirely of +functions. The main program itself is written as a function which receives the +program's input as its argument and delivers the program's output as its result. +Typically the main function is defined in terms of other functions, which in +turn are defined in terms of still more functions until at the bottom level the +functions are language primitives.]] + +[:*John Hughes*-- /Why Functional Programming Matters/] + +[$images/lambda_cpp.png] + +[h2 Description] + +Phoenix enables Functional Programming (FP) in C++. The design and +implementation of Phoenix is highly influenced by __fcpp__ by Yannis Smaragdakis +and Brian McNamara and the __bll__ (Boost Lambda Library) by Jaakko Jaarvi and +Gary Powell. Phoenix is a blend of FC++ and BLL using the implementation +techniques used in the __spirit__ inline parser. Phoenix version 2, this +version, will probably be the last release of the library. Phoenix v2 will be +the basis of the Phoenix and __bll__ merger. + +Phoenix is a header only library. It is extremely modular by design. One can +extract and use only a small subset of the full library, literally tearing the +library into small pieces, without fear that the pieces won't work anymore. The +library is organized in highly independent modules and layers. + +[h2 How to use this manual] + +The Phoenix library is organized in logical modules. This documentation +provides a user's guide and reference for each module in the library. A simple +and clear code example is worth a hundred lines of documentation; therefore, the +user's guide is presented with abundant examples annotated and explained in +step-wise manner. The user's guide is based on examples: lots of them. + +As much as possible, forward information (i.e. citing a specific piece of +information that has not yet been discussed) is avoided in the user's manual +portion of each module. In many cases, though, it is unavoidable that advanced +but related topics not be interspersed with the normal flow of discussion. To +alleviate this problem, topics categorized as "advanced" may be skipped at first +reading. + +Some icons are used to mark certain topics indicative of their relevance. These +icons precede some text to indicate: + +[table Icons + [[Icon] [Name] [Meaning]] + [[__note__] [Note] [Information provided is auxiliary but will + give the reader a deeper insight into a specific + topic. May be skipped.]] + [[__alert__] [Alert] [Information provided is of utmost importance.]] + [[__tip__] [Tip] [A potentially useful and helpful piece of + information.]] +] + +This documentation is automatically generated by Spirit QuickBook documentation +tool. QuickBook can be found in the __spirit_repo__. + +[h2 Support] + +Please direct all questions to Spirit's mailing list. You can subscribe to the +__spirit_list__. The mailing list has a searchable archive. A search link to +this archive is provided in __spirit__'s home page. You may also read and post +messages to the mailing list through __spirit_general__ (thanks to __gmane__). +The news group mirrors the mailing list. Here is a link to the archives: +__mlist_archive__. + +[h2 [*/...To my dear daughter, Phoenix/]] + +[endsect] + +[section Introduction] + +[$images/banner.png] + +The Phoenix library enables FP techniques such as higher order functions, +/lambda/ (unnamed functions), /currying/ (partial function application) and lazy +evaluation in C++. The focus is more on usefulness and practicality than purity, +elegance and strict adherence to FP principles. + +FP is a programming discipline that is not at all tied to a specific language. +FP as a programming discipline can, in fact, be applied to many programming +languages. In the realm of C++ for instance, we are seeing more FP techniques +being applied. C++ is sufficiently rich to support at least some of the most +important facets of FP. C++ is a multiparadigm programming language. It is not +only procedural. It is not only object oriented. Beneath the core of the +standard C++ library, a closer look into STL gives us a glimpse of FP already in +place. It is obvious that the authors of STL know and practice FP. In the near +future, we shall surely see more FP trickle down into the mainstream. + +The truth is, most of the FP techniques can coexist quite well with the standard +object oriented and imperative programming paradigms. When we are using STL +algorithms and functors (function objects) for example, we are already doing FP. +Phoenix is an evolutionary next step. + +[endsect] + +[section Starter Kit] + +Most "quick starts" only get you a few blocks from where you are. From there, +you are on your own. Yet, typically, you'd want to get to the next city. This +starter kit shall be as minimal as possible, yet packed as much power as +possible. + +So you are busy and always on the go. You do not wish to spend a lot of time +studying the library. You wish to be spared the details for later when you need +it. For now, all you need to do is to get up to speed as quickly as possible and +start using the library. If this is the case, this is the right place to start. + +This chapter is by no means a thorough discourse of the library. For more +information on Phoenix, please take some time to read the rest of the User's +Guide. Yet, if you just want to use the library quickly, now, this chapter will +probably suffice. Rather than taking you to the details of the library, we shall +try to provide you with annotated exemplars instead. Hopefully, this will get +you into high gear quickly. + +[h2 Functors everywhere] + +Phoenix is built on function objects (functors). The functor is the main +building block. We compose functors to build more complex functors... to build +more complex functors... and so on. Almost everything is a functor. + +[blurb __note__ Functors are so ubiquitous in Phoenix that, in the manual, the +words /"functor"/ and /"function"/ are used interchangeably.] + +[section Values] + +Values are functions! Examples: + + val(3) + val("Hello, World") + +The first evaluates to a nullary function (a function taking no arguments) that +returns an `int`, `3`. The second evaluates to a nullary function that returns +a `char const(&)[13]`, `"Hello, World"`. + +[h2 Lazy Evaluation] + +Confused? `val(3)` is a unary function, you say? Yes it is. However, read +carefully: /"evaluates to a nullary function"/. `val(3)` evaluates to (returns) a +nullary function. Aha! `val(3)` returns a function! So, since `val(3)` returns a +function, you can invoke it. Example: + + cout << val(3)() << endl; + +(See [@../../example/users_manual/values.cpp values.cpp]) + +[blurb __tip__ Learn more about values [link phoenix.primitives.values here.]] + +The second function call (the one with no arguments) calls the nullary function +which then returns `3`. The need for a second function call is the reason why +the function is said to be [*/Lazily Evaluated/]. The first call doesn't do +anything. You need a second call to finally evaluate the thing. The first call +lazily evaluates the function; i.e. doesn't do anything and defers the evaluation +for later. + +[h2 Callbacks] + +It may not be immediately apparent how lazy evaluation can be useful by just +looking at the example above. Putting the first and second function call in a +single line is really not very useful. However, thinking of `val(3)` as a +callback function (and in most cases they are actually used that way), will make +it clear. Example: + + template + void print(F f) + { + cout << f() << endl; + } + + int + main() + { + print(val(3)); + print(val("Hello World")); + return 0; + } + +(See [@../../example/users_manual/callback.cpp callback.cpp]) + +[endsect] +[section References] + +References are functions. They hold a reference to a value stored somehere. +For example, given: + + int i = 3; + char const* s = "Hello World"; + +we create `references` to `i` and `s` this way: + + ref(i) + ref(s) + +Like `val`, the expressions above evaluates to a nullary function; the first one +returning an `int&`, and the second one returning a `char const*&`. + +(See [@../../example/users_manual/references.cpp references.cpp]) + +[blurb __tip__ Learn more about references [link phoenix.primitives.references here.]] + +[endsect] +[section Arguments] + +Arguments are also functions? You bet! + +Until now, we have been dealing with expressions returning a nullary function. +Arguments, on the other hand, evaluate to an N-ary function. An argument +represents the Nth argument. There are a few predefined arguments arg1, +arg2, arg3, arg4 and so on (and it's __bll__ counterparts: _1, _2, _3, _4 and so +on). Examples: + + arg1 // one-or-more argument function that returns its first argument + arg2 // two-or-more argument function that returns its second argument + arg3 // three-or-more argument function that returns its third argument + +`argN` returns the Nth argument. Examples: + + int i = 3; + char const* s = "Hello World"; + cout << arg1(i) << endl; // prints 3 + cout << arg2(i, s) << endl; // prints "Hello World" + +(See [@../../example/users_manual/arguments.cpp arguments.cpp]) + +[blurb __tip__ Learn more about arguments [link phoenix.primitives.arguments here.]] + +[endsect] +[section Composites] + +What we have seen so far, are what are called *primitives*. You can think of +primitives (such as values, references and arguments) as atoms. + +Things start to get interesting when we start /composing/ primitives to form +*composites*. The composites can, in turn, be composed to form even more complex +composites. + +[endsect] +[section Lazy Operators] + +You can use the usual set of operators to form composites. Examples: + + arg1 * arg1 + ref(x) = arg1 + ref(z) + arg1 = arg2 + (3 * arg3) + ref(x) = arg1[arg2] // assuming arg1 is indexable and arg2 is a valid index + +Note the expression: `3 * arg3`. This expression is actually a short-hand +equivalent to: `val(3) * arg3`. In most cases, like above, you can get away with +it. But in some cases, you will have to explicitly wrap your values in `val`. +Rules of thumb: + +* In a binary expression (e.g. `3 * arg3`), at least one of the operands must be + a phoenix primitive or composite. +* In a unary expression (e.g. `arg1++`), the single operand must be a phoenix + primitive or composite. + +If these basic rules are not followed, the result is either in error, or is +immediately evaluated. Some examples: + + ref(x) = 123 // lazy + x = 123 // immediate + + ref(x)[0] // lazy + x[0] // immediate + + ref(x)[ref(i)] // lazy + ref(x)[i] // lazy (equivalent to ref(x)[val(i)]) + x[ref(i)] // illegal (x is not a phoenix primitive or composite) + ref(x[ref(i)]) // illegal (x is not a phoenix primitive or composite) + +[blurb __tip__ Learn more about operators [link phoenix.composite.operator here.]] + +[h2 First Practical Example] + +We've covered enough ground to present a real world example. We want to find the +first odd number in an STL container. Normally we use a functor (function +object) or a function pointer and pass that in to STL's `find_if` generic +function: + +Write a function: + + bool + is_odd(int arg1) + { + return arg1 % 2 == 1; + } + +Pass a pointer to the function to STL's `find_if` algorithm: + + find_if(c.begin(), c.end(), &is_odd) + +Using Phoenix, the same can be achieved directly with a one-liner: + + find_if(c.begin(), c.end(), arg1 % 2 == 1) + +The expression `arg1 % 2 == 1` automagically creates a functor with the expected +behavior. In FP, this unnamed function is called a lambda function. Unlike the +function pointer version, which is monomorphic (expects and works only with a +fixed type int argument), the Phoenix version is fully polymorphic and works +with any container (of ints, of longs, of bignum, etc.) as long as its elements +can handle the `arg1 % 2 == 1` expression. + +(See [@../../example/users_manual/find_if.cpp find_if.cpp]) + +[blurb __tip__ ...[*That's it, we're done]. Well if you wish to know a little bit +more, read on...] + +[endsect] +[section Lazy Statements] + +Lazy statements? Sure. There are lazy versions of the C++ statements we all know +and love. For example: + + if_(arg1 > 5) + cout << arg1 + +Say, for example, we wish to print all the elements that are greater than 5 +(separated by a comma) in a vector. Here's how we write it: + + for_each(v.begin(), v.end(), + if_(arg1 > 5) + [ + cout << arg1 << ", " + ] + ); + +(See [@../../example/users_manual/if.cpp if.cpp]) + +[blurb __tip__ Learn more about statements [link phoenix.composite.statement here.]] + +[endsect] +[section Construct, New, Delete, Casts] + +You'll probably want to work with objects. There are lazy versions of +constructor calls, `new`, `delete` and the suite of C++ casts. Examples: + + construct(arg1, arg2) // constructs a std::string from arg1, arg2 + new_(arg1, arg2) // makes a new std::string from arg1, arg2 + delete_(arg1) // deletes arg1 (assumed to be a pointer) + static_cast_(arg1) // static_cast's arg1 to an int* + +[blurb __note__ Take note that, by convention, names that conflict with C++ +reserved words are appended with a single trailing underscore `'_'`] + +[blurb __tip__ Learn more about this [link phoenix.composite.object here.]] + +[endsect] +[section Lazy Functions] + +As you write more lambda functions, you'll notice certain patterns that you wish +to refactor as reusable functions. When you reach that point, you'll wish that +ordinary functions can co-exist with phoenix functions. Unfortunately, the +/immediate/ nature of plain C++ functions make them incompatible. + +Lazy functions are your friends. The library provides a facility to make lazy +functions. The code below is a rewrite of the `is_odd` function using the +facility: + + struct is_odd_impl + { + template + struct result + { + typedef bool type; + }; + + template + bool operator()(Arg arg1) const + { + return arg1 % 2 == 1; + } + }; + + function is_odd; + +[h2 Things to note:] + +* `result` is a nested metafunction that reflects the return type of the + function (in this case, bool). This makes the function fully polymorphic: + It can work with arbitrary `Arg` types. +* There are as many Args in the `result` metafunction as in the actual + `operator()`. +* `is_odd_impl` implements the function. +* `is_odd`, an instance of `function`, is the lazy function. + +Now, `is_odd` is a truly lazy function that we can use in conjunction with the +rest of phoenix. Example: + + find_if(c.begin(), c.end(), is_odd(arg1)); + +(See [@../../example/users_manual/function.cpp function.cpp]) + +[h2 Predefined Lazy Functions] + +The library is chock full of STL savvy, predefined lazy functions covering the +whole of the STL containers, iterators and algorithms. For example, there are lazy +versions of container related operations such as assign, at, back, begin, +pop_back, pop_front, push_back, push_front, etc. (See [link phoenix.container +Container]). + +[endsect] +[section More] + +As mentioned earlier, this chapter is not a thorough discourse of the library. +It is meant only to cover enough ground to get you into high gear as quickly as +possible. Some advanced stuff is not discussed here (e.g. [link phoenix.composite.scope +Scopes]); nor are features that provide alternative (short-hand) ways to do the +same things (e.g. [link phoenix.composite.bind Bind] vs. Lazy Functions). + +[blurb __tip__ ...*If you still wish to learn more, the read on...*] + +[endsect] +[endsect] + +[section Basics] + +[def __constant_n__ /n/] +[def __argument_n__ a/n/] + +Almost everything is a function in the Phoenix library that can be evaluated as +`f(a1, a2, ..., __argument_n__)`, where __constant_n__ is the function's arity, or number of arguments that the +function expects. Operators are also functions. For example, `a + b` is just a +function with arity == 2 (or binary). `a + b` is the same as `add(a, b)`, `a + b ++ c` is the same as `add(add(a, b), c)`. + +[blurb __note__ Amusingly, functions may even return functions. We shall see +what this means in a short while.] + +[h2 Partial Function Application] + +Think of a function as a black box. You pass arguments and it returns something +back. The figure below depicts the typical scenario. + +[$images/fbox.png] + +A fully evaluated function is one in which all the arguments are given. All +functions in plain C++ are fully evaluated. When you call the `sin(x)` function, +you have to pass a number x. The function will return a result in return: the +sin of x. When you call the `add(x, y)` function, you have to pass two numbers x +and y. The function will return the sum of the two numbers. The figure below is +a fully evaluated `add` function. + +[$images/adder.png] + +A partially applied function, on the other hand, is one in which not all the +arguments are supplied. If we are able to partially apply the function `add` +above, we may pass only the first argument. In doing so, the function does not +have all the required information it needs to perform its task to compute and +return a result. What it returns instead is another function, a lambda function +--another black box. Unlike the original `add` function which has an arity of 2, +the resulting lambda function has an arity of 1. Why? because we already +supplied part of the input: `2` + +[$images/add2.png] + +Now, when we shove in a number into our lambda function, it will return 2 plus +whatever we pass in. The lambda function essentially remembers 1) the original +function, `add`, and 2) the partial input, 2. The figure below illustrates a +case where we pass 3 to our lambda function, which then returns 5: + +[$images/add2_call.png] + +Obviously, partially applying the `add` function, as we see above, cannot be +done directly in C++ where we are expected to supply all the arguments that a +function expects. That's where the Phoenix library comes in. The library +provides the facilities to do partial function application. + +[h2 STL and higher order functions] + +So, what's all the fuss? What makes partial function application so useful? +Recall our original example in the [link phoenix.starter_kit previous section]: + + find_if(c.begin(), c.end(), arg1 % 2 == 1) + +The expression `arg1 % 2 == 1` evaluates to a lambda function. `arg1` is a placeholder +for an argument to be supplied later. Hence, since there's only one unsupplied argument, the +lambda function has an arity 1. It just so happens that `find_if` supplies the +unsupplied argument as it loops from `c.begin()` to `c.end()`. + +[blurb __note__ Higher order functions are functions which can take other +functions as arguments, and may also return functions as results. Higher order +functions are functions that are treated like any other objects and can be used as +arguments and return values from functions.] + +[h2 Lazy Evaluation] + +In Phoenix, to put it more accurately, function evaluation has two stages: + +# Partial application +# Final evaluation + +The first stage is handled by a set of generator functions. These are your front +ends (in the client's perspective). These generators create (through partial +function application), higher order functions that can be passed on just like +any other function pointer or function object. The second stage, the actual +function call, can be invoked or executed anytime in the future, or not at all; +hence /"lazy"/. + +If we look more closely, the first step involves partial function application: + + arg1 % 2 == 1 + +The second step is the actual function invocation (done inside the `find_if` +function. These are the back-ends (often, the final invocation is never actually +seen by the client). In our example, the `find_if`, if we take a look inside, +we'll see something like: + + template + InputIterator + find_if(InputIterator first, InputIterator last, Predicate pred) + { + while (first != last && !pred(*first)) // <--- The lambda function is called here + ++first; // passing in *first + return first; + } + +Again, typically, we, as clients, see only the first step. However, in this +document and in the examples and tests provided, don't be surprised to see the +first and second steps juxtaposed in order to illustrate the complete semantics +of Phoenix expressions. Examples: + + int x = 1; + int y = 2; + + cout << (arg1 % 2 == 1)(x) << endl; // prints 1 or true + cout << (arg1 % 2 == 1)(y) << endl; // prints 0 or false + + +[h2 Forwarding Function Problem] + +Usually, we, as clients, write the call-back functions while libraries (such as +STL) provide the callee (e.g. `find_if`). In case the role is reversed, e.g. +if you have to write an STL algorithm that takes in a predicate, or develop a +GUI library that accepts event handlers, you have to be aware of a little known +problem in C++ called the "__forwarding__". + +Look again at the code above: + + (arg1 % 2 == 1)(x) + +Notice that, in the second-stage (the final evaluation), we used a variable `x`. +Be aware that the second stage cannot accept non-const temporaries and literal +constants. Hence, this will fail: + + (arg1 % 2 == 1)(123) // Error! + +Disallowing non-const rvalues partially solves the "__forwarding__" but +prohibits code like above. + +[h2 Polymorphic Functions] + +Unless otherwise noted, Phoenix generated functions are fully polymorphic. For +instance, the `add` example above can apply to integers, floating points, user +defined complex numbers or even strings. Example: + + std::string h("Hello"); + char const* w = " World"; + std::string r = add(arg1, arg2)(h, w); + +evaluates to `std::string("Hello World")`. The observant reader might notice +that this function call in fact takes in heterogeneous arguments where `arg1` is +of type `std::string` and `arg2` is of type `char const*`. `add` still works +because the C++ standard library allows the expression `a + b` where `a` is a +`std::string` and `b` is a `char const*`. + +[endsect] + +[section Organization] + +Care and attention to detail was given, painstakingly, to the design and +implementation of Phoenix. + +The library is organized in four layers: + +[$images/organization.png] + +The modules are orthogonal, with no cyclic dependencies. +Lower layers do not depend on higher layers. Modules in a layer do not depend on other modules in the same layer. +This means, for example, that Bind can be completely discarded if it is +not required; or one could perhaps take out Operator and Statement and just use Function, +which may be desireable in a pure FP application. + +The library has grown from the original Phoenix but still comprises only +header files. There are no object files to link against. + +[h2 Core] + +The lowest two layers comprise the core. + +The `Actor` is the main concept behind the library. Lazy functions are +abstracted as actors. There are only 2 +kinds of actors: + +# Primitives +# Composites + +Primitives provide the basic building blocks of functionality within Phoenix. +Composites are used to combine these primitives together to provide more +powerful functionality. + +Composites are composed of zero or more actors. Each actor in a composite can +again be another composite. + +[table Modules + [[Module] [Description]] + [[Function] [Lazy functions support (e.g. `add`)]] + [[Operator] [Lazy operators support (e.g. `+`)]] + [[Statement] [Lazy statments (e.g. `if_`, `while_`)]] + [[Object] [Lazy casts (e.g. `static_cast_`), + object creation destruction (e.g. + `new_`, `delete_`)]] + [[Scope] [Support for scopes, local variables and lambda-lambda]] + [[Bind] [Lazy functions from free functions, member functions or member variables.]] + [[Container] [Set of predefined "lazy" functions that work on STL + containers and sequences (e.g. `push_back`).]] + [[Algorithm] [Set of predefined "lazy" versions of the STL algorithms + (e.g. `find_if`).]] +] + +Each module is defined in a header file with the same name. For example, +the core module is defined in ``. + +[table Includes + [[Module] [File]] + [[Core] [`#include `]] + [[Function] [`#include `]] + [[Operator] [`#include `]] + [[Statement] [`#include `]] + [[Object] [`#include `]] + [[Scope] [`#include `]] + [[Bind] [`#include `]] + [[Container] [`#include `]] + [[Algorithm] [`#include `]] +] + +[blurb __tip__ Finer grained include files are available per feature; see the +succeeding sections.] + +[endsect] + +[section Actors] + +The `Actor` is the main concept behind the library. Actors are function objects. +An actor can accept 0 to `PHOENIX_LIMIT` arguments. + +[blurb __note__ You can set `PHOENIX_LIMIT`, the predefined maximum arity an +actor can take. By default, `PHOENIX_LIMIT` is set to 10.] + +Phoenix supplies an `actor` class template whose specializations +model the `Actor` concept. `actor` has one template parameter, `Eval`, +that supplies the smarts to evaluate the resulting function. + + template + struct actor : Eval + { + return_type + operator()() const; + + template + return_type + operator()(T0& _0) const; + + template + return_type + operator()(T0& _0, T1& _1) const; + + //... + }; + +The actor class accepts the arguments through a set of function call operators +for 0 to `PHOENIX_LIMIT` arities (Don't worry about the details, for now. Note, for example, +that we skimp over the details regarding `return_type`). The arguments +are then forwarded to the actor's `Eval` for evaluation. + +[endsect] + +[section Primitives] + +Actors are composed to create more complex actors in a tree-like hierarchy. The +primitives are atomic entities that are like the leaves in the tree. Phoenix is +extensible. New primitives can be added anytime. Right out of the box, there are +only a few primitives. This section shall deal with these preset primitives. + +[section Arguments] + + #include + +We use an instance of: + + actor > + +to represent the Nth function argument. The argument placeholder acts as an +imaginary data-bin where a function argument will be placed. + +[h2 Predefined Arguments] + +There are a few predefined instances of `actor >` named +`arg1`..`argN`, and its __bll__ counterpart `_1`..`_N`. (where N is a predefined +maximum). + +Here are some sample preset definitions of `arg1`..`argN` + + actor > const arg1 = argument<0>(); + actor > const arg2 = argument<1>(); + actor > const arg3 = argument<2>(); + +and its __bll__ `_1`..`_N` style counterparts: + + actor > const _1 = argument<0>(); + actor > const _2 = argument<1>(); + actor > const _3 = argument<2>(); + +[blurb __note__ You can set `PHOENIX_ARG_LIMIT`, the predefined maximum +placeholder index. By default, `PHOENIX_ARG_LIMIT` is set to `PHOENIX_LIMIT` +(See [link phoenix.actors Actors]).] + +[h2 User Defined Arguments] + +When appropriate, you can define your own `argument` names. For example: + + actor > x; // note zero based index + +`x` may now be used as a parameter to a lazy function: + + add(x, 6) + +which is equivalent to: + + add(arg1, 6) + +[h2 Evaluating an Argument] + +An argument, when evaluated, selects the Nth argument from the those passed +in by the client. + +For example: + + char c = 'A'; + int i = 123; + const char* s = "Hello World"; + + cout << arg1(c) << endl; // Get the 1st argument: c + cout << arg1(i, s) << endl; // Get the 1st argument: i + cout << arg2(i, s) << endl; // Get the 2nd argument: s + +will print out: + + A + 123 + Hello World + +[h2 Extra Arguments] + +In C and C++, a function can have extra arguments that are not at all used by +the function body itself. These extra arguments are simply ignored. + +Phoenix also allows extra arguments to be passed. For example, recall our +original `add` function: + + add(arg1, arg2) + +We know now that partially applying this function results to a function that +expects 2 arguments. However, the library is a bit more lenient and allows the +caller to supply more arguments than is actually required. Thus, `add` actually +allows 2 /or more/ arguments. For instance, with: + + add(arg1, arg2)(x, y, z) + +the third argument `z` is ignored. Taking this further, in-between arguments are +also ignored. Example: + + add(arg1, arg5)(a, b, c, d, e) + +Here, arguments b, c, and d are ignored. The function `add` takes in the first +argument (`arg1`) and the fifth argument (`arg5`). + +[blurb __note__ There are a few reasons why enforcing strict arity is not +desireable. A case in point is the callback function. Typical callback functions +provide more information than is actually needed. Lambda functions are often +used as callbacks.] + +[endsect] + +[section Values] + + #include + +Whenever we see a constant in a partially applied function, an + + actor > + +(where T is the type of the constant) is automatically created for +us. For instance: + + add(arg1, 6) + +Passing a second argument, `6`, an `actor >` is implicitly created +behind the scenes. This is also equivalent to: + + add(arg1, val(6)) + +`val(x)` generates an `actor >` where `T` is the type of `x`. In most +cases, there's no need to explicitly use `val`, but, as we'll see later on, +there are situations where this is unavoidable. + +[h2 Evaluating a Value] + +Like arguments, values are also actors. As such, values can be evaluated. +Invoking a value gives the value's identity. Example: + + cout << val(3)() << val("Hello World")(); + +prints out "3 Hello World". + +[endsect] + +[section References] + + #include + +Values are immutable constants. Attempting to modify a value will result in a +compile time error. When we want the function to modify the parameter, we use a +reference instead. For instance, imagine a lazy function `add_assign`: + + void add_assign(T& x, T y) { x += y; } // pseudo code + +Here, we want the first function argument, x, to be mutable. Obviously, we +cannot write: + + add_assign(1, 2) // error first argument is immutable + +In C++, we can pass in a reference to a variable as the first argument in our +example above. Yet, by default, the library forces arguments passed to partially +applied functions functions to be immutable values (see [link phoenix.primitives.values +Values]). To achieve our intent, we use: + + actor > + +This is similar to `actor >` above but instead holds a reference to a +variable. + +We normally don't instantiate `actor >` objects directly. Instead we +use `ref`. For example (where `i` is an `int` variable): + + add_assign(ref(i), 2) + +[h2 Evaluating a Reference] + +References are actors. Hence, references can be evaluated. Such invocation gives +the references's identity. Example: + + int i = 3; + char const* s = "Hello World"; + cout << ref(i)() << ref(s)(); + +prints out "3 Hello World" + +[endsect] +[section Constant References] + + #include + +Another free function `cref(cv)` may also be used. `cref(cv)` creates an +`actor >` object. This is similar to `actor >` but +when the data to be passed as argument to a function is heavy and expensive to +copy by value, the `cref(cv)` offers a lighter alternative. + +[endsect] +[section Nothing] + + #include + +Finally, the `actor` does nothing; (a "bum", if you will :-). +There's a sole `actor` instance named "nothing". This actor is +actually useful in situations where we don't want to do anything. (See +[link phoenix.composite.statement.for__statement for_ Statement] for example). + +[endsect] + +[endsect] + +[section Composite] + +Actors may be combined in a multitude of ways to form composites. Composites are +actors that are composed of zero or more actors. Composition is hierarchical. An +element of the composite can be a primitive or again another composite. The +flexibility to arbitrarily compose hierarchical structures allows us to form +intricate constructions that model complex functions, statements and +expressions. + +A composite is-a tuple of 0..N actors. N is the predefined maximum actors a +composite can take. + +[blurb __note__ You can set `PHOENIX_COMPOSITE_LIMIT`, the predefined maximum +actors a composite can take. By default, `PHOENIX_COMPOSITE_LIMIT` is set to +`PHOENIX_LIMIT` (See [link phoenix.actors Actors]).] + +As mentioned, each of the actors A0..AN can, in turn, be another composite, +since a composite is itself an actor. This makes the composite a recursive +structure. The actual evaluation is handled by a composite specific eval policy. + +[section Function] + + #include + +The `function` class template provides a mechanism for implementing lazily +evaluated functions. Syntactically, a lazy function looks like an ordinary C/C++ function. +The function call looks familiar and feels the same as ordinary C++ functions. +However, unlike ordinary functions, the actual function execution is deferred. + +Unlike ordinary function pointers or functor objects that need to be explicitly bound through the bind function (see [link phoenix.composite.bind Bind]), +the argument types of these functions are automatically lazily bound. + +In order to create a lazy function, we need to implement a model of the +FunctionEval concept. For a function that takes `N` arguments, a model of FunctionEval must +provide: + +* An `operator()` that implements that takes `N` arguments, and implements +the function logic. +* A nested metafunction `result` that takes the types of the `N` arguments to +the function and returns the result type of the function. (There is a special case for function +objects that accept no arguments. Such nullary functors are only required to define a typedef +`result_type` that reflects the return type of its `operator()`). + +For example, the following type implements the FunctionEval concept, in order to provide a +lazy factorial function: + + struct factorial_impl + { + template + struct result + { + typedef Arg type; + }; + + template + Arg operator()(Arg n) const + { + return (n <= 0) ? 1 : n * this->operator()(n-1); + } + }; + +(See [@../../example/users_manual/factorial.cpp factorial.cpp]) + +Having implemented the `factorial_impl` type, we can declare and instantiate a lazy +`factorial` function this way: + + function factorial; + +Invoking a lazy function such as `factorial` does not immediately execute the function +object `factorial_impl`. Instead, an [link phoenix.actors actor] object is +created and returned to the caller. Example: + + factorial(arg1) + +does nothing more than return an actor. A second function call will invoke +the actual factorial function. Example: + + int i = 4; + cout << factorial(arg1)(i); + +will print out "24". + +Take note that in certain cases (e.g. for function objects with state), an +instance of the model of FunctionEval may be passed on to the constructor. Example: + + function factorial(ftor); + +where ftor is an instance of factorial_impl (this is not necessary in this case +as `factorial_impl` does not require any state). + +[blurb __alert__ Take care though when using function objects with state because they are +often copied repeatedly, and state may change in one of the copies, rather than the +original.] + +[endsect] + +[section Operator] + +This facility provides a mechanism for lazily evaluating operators. +Syntactically, a lazy operator looks and feels like an ordinary C/C++ infix, +prefix or postfix operator. The operator application looks the same. However, +unlike ordinary operators, the actual operator execution is deferred. Samples: + + arg1 + arg2 + 1 + arg1 * arg2 + 1 / -arg1 + arg1 < 150 + +We have seen the lazy operators in action (see [link phoenix.starter_kit +Quick Start]). Let's go back and examine them a little bit further: + + find_if(c.begin(), c.end(), arg1 % 2 == 1) + +Through operator overloading, the expression `arg1 % 2 == 1` actually generates +an actor. This actor object is passed on to STL's `find_if` function. From +the viewpoint of STL, the composite is simply a function object expecting a +single argument of the containers value_type. For each element in `c`, +the element is passed on as an argument `arg1` to the actor (function +object). The actor checks if this is an odd value based on the expression +`arg1 % 2 == 1` where arg1 is replaced by the container's element. + +Like lazy functions (see +[link phoenix.composite.function function]), lazy operators are not immediately executed +when invoked. Instead, an actor (see [link phoenix.actors actors]) +object is created and returned to the caller. Example: + + (arg1 + arg2) * arg3 + +does nothing more than return an actor. A second function call will evaluate +the actual operators. Example: + + int i = 4, j = 5, k = 6; + cout << ((arg1 + arg2) * arg3)(i, j, k); + +will print out "54". + +Operator expressions are lazily evaluated following four simple rules: + +# A binary operator, except `->*` will be lazily evaluated when + /at least/ one of its operands is an actor object + (see [link phoenix.actors actors]). +# Unary operators are lazily evaluted if their argument is an actor object. +# Operator `->*` is lazily evaluted if the left hand argument is an actor object. +# The result of a lazy operator is an actor object that can in turn allow the + applications of rules 1 and 2. + +For example, to check the following expression is lazily evaluated: + + -(arg1 + 3 + 6) + +# Following rule 1, `arg1 + 3` is lazily evaluated since `arg1` is an actor + (see [link phoenix.primitives primitives]). +# The result of this `arg1 + 3` expression is an actor object, following rule 4. +# Continuing, `arg1 + 3 + 6` is again lazily evaluated. + Rule 2. +# By rule 4 again, the result of `arg1 + 3 + 6` is an actor object. +# As `arg1 + 3 + 6` is an actor, `-(arg1 + 3 + 6)` is lazily evaluated. Rule 2. + +Lazy-operator application is highly contagious. In most cases, a single `argN` +actor infects all its immediate neighbors within a group (first level or +parenthesized expression). + +Note that at least one operand of any operator must be a valid actor +for lazy evaluation to take effect. To force lazy evaluation of an +ordinary expression, we can use `ref(x)`, `val(x)` or `cref(x)` to +transform an operand into a valid actor object (see [link phoenix.primitives primitives]. +For example: + + 1 << 3; // Immediately evaluated + val(1) << 3; // Lazily evaluated + +[h2 Supported operators] + +[h3 Unary operators] + + prefix: ~, !, -, +, ++, --, & (reference), * (dereference) + postfix: ++, -- + +[h3 Binary operators] + + =, [], +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>= + +, -, *, /, %, &, |, ^, <<, >> + ==, !=, <, >, <=, >= + &&, ||, ->* + +[h3 Ternary operator] + + if_else(c, a, b) + +The ternary operator deserves special mention. Since C++ does not allow us to +overload the conditional expression: `c ? a : b`, the if_else pseudo function is +provided for this purpose. The behavior is identical, albeit in a lazy manner. + +[h3 Member pointer operator] + + a->*member_object_pointer + a->*member_function_pointer + +The left hand side of the member pointer operator must be an actor returning a pointer +type. The right hand side of the member pointer operator may be either a pointer to member +object or pointer to member function. + +If the right hand side is a member object pointer, the result is an actor which, when evaluated, +returns a reference to that member. For example: + + struct A + { + int member; + }; + + A* a = new A; + ... + + (arg1->*&A::member)(a); // returns member a->member + +If the right hand side is a member function pointer, the result is an actor which, when invoked, calls the specified member function. For example: + + struct A + { + int func(int); + }; + + A* a = new A; + int i = 0; + + (arg1->*&A::func)(arg2)(a, i); // returns a->func(i) + +[table Include Files + [[Operators] [File]] + [[`-`, `+`, `++`, `--`, `+=`, + `-=`, `*=`, `/=`, `%=`, + `*`, `/`, `%`] [`#include `]] + [[`&=`, `|=`, `^=`, `<<=`, + `>>=`, `&`, `|`, `^`, `<<`, + `>>`] [`#include `]] + [[`==`, `!=`, `<`, + `<=`, `>`, `>=`] [`#include `]] + [[`<<`, `>>`] [`#include `]] + [[`!`, &&, `||`] [`#include `]] + [[`&x`, `*p`, `=`, `[]`] [`#include `]] + [[`if_else(c, a, b)`] [`#include `]] + [[`->*`] [`#include `]] +] + +[endsect] + +[section Statement] + +[*/Lazy statements.../] + +The primitives and composite building blocks presented so far are sufficiently +powerful to construct quite elaborate structures. We have presented lazy- +functions and lazy-operators. How about lazy-statements? First, an appetizer: + +Print all odd-numbered contents of an STL container using `std::for_each` +([@../../example/users_manual/all_odds.cpp all_odds.cpp]): + + for_each(c.begin(), c.end(), + if_(arg1 % 2 == 1) + [ + cout << arg1 << ' ' + ] + ); + +Huh? Is that valid C++? Read on... + +Yes, it is valid C++. The sample code above is as close as you can get to the +syntax of C++. This stylized C++ syntax differs from actual C++ code. First, the +`if` has a trailing underscore. Second, the block uses square brackets instead +of the familiar curly braces {}. + +[blurb __note__ *C++ in C++?*\n\n + In as much as __spirit__ attempts to mimic EBNF in C++, + Phoenix attempts to mimic C++ in C++!!! +] + +Here are more examples with annotations. The code almost speaks for itself. + +[section Block Statement] + + #include + +Syntax: + + statement, + statement, + .... + statement + +Basically, these are comma separated statements. Take note that unlike the C/C++ +semicolon, the comma is a separator put *in-between* statements. This is like +Pascal's semicolon separator, rather than C/C++'s semicolon terminator. For +example: + + statement, + statement, + statement, // ERROR! + +Is an error. The last statement should not have a comma. Block statements can be +grouped using the parentheses. Again, the last statement in a group should not +have a trailing comma. + + statement, + statement, + ( + statement, + statement + ), + statement + +Outside the square brackets, block statements should be grouped. For example: + + for_each(c.begin(), c.end(), + ( + do_this(arg1), + do_that(arg1) + ) + ); + +Wrapping a comma operator chain around a parentheses pair blocks the +interpretation as an argument separator. The reason for the exception for +the square bracket operator is that the operator always takes exactly one +argument, so it "transforms" any attempt at multiple arguments with a comma +operator chain (and spits out an error for zero arguments). + +[endsect] +[section if_ Statement] + + #include + +We have seen the `if_` statement. The syntax is: + + if_(conditional_expression) + [ + sequenced_statements + ] + +[endsect] +[section if_else_ statement] + + #include + +The syntax is + + if_(conditional_expression) + [ + sequenced_statements + ] + .else_ + [ + sequenced_statements + ] + +Take note that `else` has a leading dot and a trailing underscore: `.else_` + +Example: This code prints out all the elements and appends `" > 5"`, `" == 5"` +or `" < 5"` depending on the element's actual value: + + for_each(c.begin(), c.end(), + if_(arg1 > 5) + [ + cout << arg1 << " > 5\n" + ] + .else_ + [ + if_(arg1 == 5) + [ + cout << arg1 << " == 5\n" + ] + .else_ + [ + cout << arg1 << " < 5\n" + ] + ] + ); + +Notice how the `if_else_` statement is nested. + +[endsect] +[section switch_ statement] + + #include + +The syntax is: + + switch_(integral_expression) + [ + case_(sequenced_statements), + ... + default_(sequenced_statements) + ] + +A comma separated list of cases, and an optional default can be provided. Note unlike +a normal switch statement, cases do not fall through. + +Example: This code prints out `"one"`, `"two"` or `"other value"` depending on the +element's actual value: + + for_each(c.begin(), c.end(), + switch_(arg1) + [ + case_<1>(cout << val("one") << '\n'), + case_<2>(cout << val("two") << '\n'), + default_(cout << val("other value") << '\n') + ] + ); + +[endsect] +[section while_ Statement] + + #include + +The syntax is: + + while_(conditional_expression) + [ + sequenced_statements + ] + +Example: This code decrements each element until it reaches zero and prints out +the number at each step. A newline terminates the printout of each value. + + for_each(c.begin(), c.end(), + ( + while_(arg1--) + [ + cout << arg1 << ", " + ], + cout << val("\n") + ) + ); + +[endsect] +[section do_while_ Statement] + + #include + +The syntax is: + + do_ + [ + sequenced_statements + ] + .while_(conditional_expression) + +Again, take note that `while` has a leading dot and a trailing underscore: +`.while_` + +Example: This code is almost the same as the previous example above with a +slight twist in logic. + + for_each(c.begin(), c.end(), + ( + do_ + [ + cout << arg1 << ", " + ] + .while_(arg1--), + cout << val("\n") + ) + ); + +[endsect] +[section for_ Statement] + + #include + +The syntax is: + + for_(init_statement, conditional_expression, step_statement) + [ + sequenced_statements + ] + +It is again very similar to the C++ for statement. Take note that the +init_statement, conditional_expression and step_statement are separated by the +comma instead of the semi-colon and each must be present (i.e. `for_(,,)` is +invalid). This is a case where the [link phoenix.primitives.nothing nothing] +actor can be useful. + +Example: This code prints each element N times where N is the element's value. A +newline terminates the printout of each value. + + int iii; + for_each(c.begin(), c.end(), + ( + for_(ref(iii) = 0, ref(iii) < arg1, ++ref(iii)) + [ + cout << arg1 << ", " + ], + cout << val("\n") + ) + ); + +As before, all these are lazily evaluated. The result of such statements are in +fact composites that are passed on to STL's for_each function. In the viewpoint +of `for_each`, what was passed is just a functor, no more, no less. + +[blurb __note__ Unlike lazy functions and lazy operators, lazy statements always +return void.] + +[endsect] +[section try_ catch_ Statement] + + #include + +The syntax is: + + try_ + [ + sequenced_statements + ] + .catch_() + [ + sequenced_statements + ] + ... + .catch_all + [ + sequenced_statement + ] + +Note the usual underscore after try and catch, and the extra parentheses required +after the catch. + +Example: The following code calls the (lazy) function `f` for each element, and +prints messages about different exception types it catches. + + try_ + [ + f(arg1) + ] + .catch_() + [ + cout << val("caught runtime error or derived\n") + ] + .catch_() + [ + cout << val("caught exception or derived\n") + ] + .catch_all + [ + cout << val("caught some other type of exception\n") + ] + +[endsect] +[section throw_] + + #include + +As a natural companion to the try/catch support, the statement module provides +lazy throwing and rethrowing of exceptions. + +The syntax to throw an exception is: + + throw_(exception_expression) + +The syntax to rethrow an exception is: + + throw_() + +Example: This code extends the try/catch example, rethrowing exceptions derived from +runtime_error or exception, and translating other exception types to runtime_errors. + + try_ + [ + f(arg1) + ] + .catch_() + [ + cout << val("caught runtime error or derived\n"), + throw_() + ] + .catch_() + [ + cout << val("caught exception or derived\n"), + throw_() + ] + .catch_all + [ + cout << val("caught some other type of exception\n"), + throw_(runtime_error("translated exception")) + ] + +[endsect] +[endsect] + +[section Object] + +The Object module deals with object construction, destruction and conversion. +The module provides /"lazy"/ versions of C++'s object constructor, `new`, +`delete`, `static_cast`, `dynamic_cast`, `const_cast` and `reinterpret_cast`. + +[h2 Construction] + +[*/Lazy constructors.../] + + #include + +Lazily construct an object from an arbitrary set of arguments: + + construct(ctor_arg1, ctor_arg2, ..., ctor_argN); + +where the given parameters are the parameters to the contructor of the object of +type T (This implies, that type T is expected to have a constructor with a +corresponding set of parameter types.). + +Example: + + construct(arg1, arg2) + +Constructs a `std::string` from `arg1` and `arg2`. + +[blurb __note__ The maximum number of actual parameters is limited by the +preprocessor constant PHOENIX_COMPOSITE_LIMIT. Note though, that this limit +should not be greater than PHOENIX_LIMIT. By default, `PHOENIX_COMPOSITE_LIMIT` +is set to `PHOENIX_LIMIT` (See [link phoenix.actors Actors]).] + +[h2 New] + +[*/Lazy new.../] + + #include + +Lazily construct an object, on the heap, from an arbitrary set of arguments: + + new_(ctor_arg1, ctor_arg2, ..., ctor_argN); + +where the given parameters are the parameters to the contructor of the object of +type T (This implies, that type T is expected to have a constructor with a +corresponding set of parameter types.). + +Example: + + new_(arg1, arg2) // note the spelling of new_ (with trailing underscore) + +Creates a `std::string` from `arg1` and `arg2` on the heap. + +[blurb __note__ Again, the maximum number of actual parameters is limited by the +preprocessor constant PHOENIX_COMPOSITE_LIMIT. See the note above.] + +[h2 Delete] + +[*/Lazy delete.../] + + #include + +Lazily delete an object, from the heap: + + delete_(arg); + +where arg is assumed to be a pointer to an object. + +Example: + + delete_(arg1) // note the spelling of delete_ (with trailing underscore) + +[h2 Casts] + +[*/Lazy casts.../] + + #include + #include + #include + #include + +The set of lazy C++ cast template functions provide a way of lazily casting an +object of a certain type to another type. The syntax resembles the well known +C++ casts. Take note however that the lazy versions have a trailing underscore. + + static_cast_(lambda_expression) + dynamic_cast_(lambda_expression) + const_cast_(lambda_expression) + reinterpret_cast_(lambda_expression) + +Example: + + static_cast_(&arg1) + +Static-casts the address of `arg1` to a `Base*`. + +[endsect] + +[section Scope] + +Up until now, the most basic ingredient is missing: creation of and access to +local variables in the stack. When recursion comes into play, you will soon +realize the need to have true local variables. It may seem that we do not need +this at all since an unnamed lambda function cannot call itself anyway; at least +not directly. With some sort of arrangement, situations will arise where a +lambda function becomes recursive. A typical situation occurs when we store a +lambda function in a [@http://www.boost.org/libs/function Boost.Function], +essentially naming the unnamed lambda. + +There will also be situations where a lambda function gets passed as an argument +to another function. This is a more common situation. In this case, the lambda +function assumes a new scope; new arguments and possibly new local variables. + +This section deals with local variables and nested lambda scopes. + +[h2 Local Variables] + + #include + +We use an instance of: + + actor > + +to represent a local variable. The local variable acts as an imaginary data-bin +where a local, stack based data will be placed. `Key` is an arbitrary type that +is used to identify the local variable. Example: + + struct size_key; + actor > size; + +[h2 Predefined Local Variables] + +There are a few predefined instances of `actor >` +named `_a`..`_z` that you can already use. To make use of them, simply use the +`namespace boost::phoenix::local_names`: + + using namespace boost::phoenix::local_names; + +[h2 let] + + #include + +You declare local variables using the syntax: + + let(local-declarations) + [ + let-body + ] + +`let` allows 1..N local variable declarations (where N == +`PHOENIX_LOCAL_LIMIT`). Each declaration follows the form: + + local-id = lambda-expression + +[blurb __note__ You can set `PHOENIX_LOCAL_LIMIT`, the predefined maximum local +variable declarations in a let expression. By default, `PHOENIX_LOCAL_LIMIT` is +set to `PHOENIX_LIMIT`.] + +Example: + + let(_a = 123, _b = 456) + [ + _a + _b + ] + +[h2 Reference Preservation] + +The type of the local variable assumes the type of the lambda- expression. Type +deduction is reference preserving. For example: + + let(_a = arg1, _b = 456) + +`_a` assumes the type of `arg1`: a reference to an argument, while `_b` has type +`int`. + +Consider this: + + int i = 1; + + let(_a = arg1) + [ + cout << --_a << ' ' + ] + (i); + + cout << i << endl; + +the output of above is : 0 0 + +While with this: + + int i = 1; + + let(_a = val(arg1)) + [ + cout << --_a << ' ' + ] + (i); + + cout << i << endl; + +the output is : 0 1 + +Reference preservation is necessary because we need to have L-value access to +outer lambda-scopes (especially the arguments). `arg`s and `ref`s are L-values. +`val`s are R-values. + +[h2 Visibility] + +The scope and lifetimes of the local variables is limited within the let-body. +`let` blocks can be nested. A local variable may hide an outer local variable. +For example: + + let(_x = 1, _y = ", World") + [ + // _x here is an int: 1 + + let(_x = "Hello") // hides the outer _x + [ + cout << _x << _y // prints "Hello, World" + ] + ] + +The RHS (right hand side lambda-expression) of each local-declaration cannot +refer to any LHS local-id. At this point, the local-ids are not in scope yet; +they will only be in scope in the let-body. The code below is in error: + + let( + _a = 1 + , _b = _a // Error: _a is not in scope yet + ) + [ + // _a and _b's scope starts here + /*. body .*/ + ] + +However, if an outer let scope is available, this will be searched. Since +the scope of the RHS of a local-declaration is the outer scope enclosing +the let, the RHS of a local-declaration can refer to a local variable of +an outer scope: + + let(_a = 1) + [ + let( + _a = 1 + , _b = _a // Ok. _a refers to the outer _a + ) + [ + /*. body .*/ + ] + ] + +[h2 lambda] + + #include + +A lot of times, you'd want to write a lazy function that accepts one or more +functions (higher order functions). STL algorithms come to mind, for example. +Consider a lazy version of `stl::for_each`: + + struct for_each_impl + { + template + struct result + { + typedef void type; + }; + + template + void operator()(C& c, F f) const + { + std::for_each(c.begin(), c.end(), f); + } + }; + + function const for_each = for_each_impl(); + +Notice that the function accepts another function, `f` as an argument. The scope +of this function, `f`, is limited within the `operator()`. When `f` is called +inside `std::for_each`, it exists in a new scope, along with new arguments and, +possibly, local variables. This new scope is not at all related to the outer +scopes beyond the `operator()`. + +Simple syntax: + + lambda + [ + lambda-body + ] + +Like `let`, local variables may be declared, allowing 1..N local variable +declarations (where N == `PHOENIX_LOCAL_LIMIT`): + + lambda(local-declarations) + [ + lambda-body + ] + +The same restrictions apply with regard to scope and visibility. The RHS +(right hand side lambda-expression) of each local-declaration cannot refer +to any LHS local-id. The local-ids are not in scope yet; they will be in +scope only in the lambda-body: + + lambda( + _a = 1 + , _b = _a // Error: _a is not in scope yet + ) + +See [link phoenix.composite.scope.visibility `let` Visibility] above for more information. + +Example: Using our lazy `for_each` let's print all the elements in a container: + + for_each(arg1, lambda[cout << arg1]) + +As far as the arguments are concerned (arg1..argN), the scope in which the +lambda-body exists is totally new. The left `arg1` refers to the argument passed +to `for_each` (a container). The right `arg1` refers to the argument passed by +`std::for_each` when we finally get to call `operator()` in our `for_each_impl` +above (a container element). + +Yet, we may wish to get information from outer scopes. While we do not have +access to arguments in outer scopes, what we still have is access to local +variables from outer scopes. We may only be able to pass argument related +information from outer `lambda` scopes through the local variables. + +[blurb __note__ This is a crucial difference between `let` and `lambda`: `let` +does not introduce new arguments; `lambda` does.] + +Another example: Using our lazy `for_each`, and a lazy `push_back`: + + struct push_back_impl + { + template + struct result + { + typedef void type; + }; + + template + void operator()(C& c, T& x) const + { + c.push_back(x); + } + }; + + function const push_back = push_back_impl(); + +write a lambda expression that accepts: + +# a 2-dimensional container (e.g. `vector >`) +# a container element (e.g. `int`) + +and pushes-back the element to each of the `vector`. + +Solution: + + for_each(arg1, + lambda(_a = arg2) + [ + push_back(arg1, _a) + ] + ) + +Since we do not have access to the arguments of the outer scopes beyond the +lambda-body, we introduce a local variable `_a` that captures the second outer +argument: `arg2`. Hence: _a = arg2. This local variable is visible inside the +lambda scope. + +(See [@../../example/users_manual/lambda.cpp lambda.cpp]) + +[endsect] + +[section Bind] + +['Binding] is the act of tying together a function to some arguments for +deferred (lazy) evaluation. Named [link phoenix.composite.function Lazy functions] +require a bit of typing. Unlike (unnamed) lambda expressions, we need to write a +functor somewhere offline, detached from the call site. If you wish to transform a +plain function, member function or member variable to a lambda expression, `bind` +is your friend. + +[blurb __note__ Take note that binders are monomorphic. Rather than binding +functions, the preferred way is to write true generic and polymorphic [link +phoenix.composite.function lazy-functions]. However, since most of the time we +are dealing with adaptation of exisiting code, binders get the job done faster.] + +There is a set of overloaded `bind` template functions. Each `bind(x)` +function generates a suitable binder object, a [link phoenix.composite +composite]. + +[h2 Binding Functions] + + #include + +Example, given a function `foo`: + + void foo(int n) + { + std::cout << n << std::endl; + } + +Here's how the function `foo` may be bound: + + bind(&foo, arg1) + +This is now a full-fledged [link phoenix.composite composite] that can finally +be evaluated by another function call invocation. A second function call will +invoke the actual `foo` function. Example: + + int i = 4; + bind(&foo, arg1)(i); + +will print out "4". + +[h2 Binding Member Functions] + + #include + +Binding member functions can be done similarly. A bound member function takes in +a pointer or reference to an object as the first argument. For instance, given: + + struct xyz + { + void foo(int) const; + }; + +`xyz`'s `foo` member function can be bound as: + + bind(&xyz::foo, obj, arg1) // obj is an xyz object + +Take note that a lazy-member functions expects the first argument to be a +pointer or reference to an object. Both the object (reference or pointer) and +the arguments can be lazily bound. Examples: + + xyz obj; + bind(&xyz::foo, arg1, arg2) // arg1.foo(arg2) + bind(&xyz::foo, obj, arg1) // obj.foo(arg1) + bind(&xyz::foo, obj, 100) // obj.foo(100) + +[h2 Binding Member Variables] + + #include + +Member variables can also be bound much like member functions. Member variables +are not functions. Yet, like the [link phoenix.primitives.references `ref(x)`] that +acts like a nullary function returning a reference to the data, member variables, +when bound, act like a unary function, taking in a pointer or reference to an +object as its argument and returning a reference to the bound member variable. +For instance, given: + + struct xyz + { + int v; + }; + +`xyz::v` can be bound as: + + bind(&xyz::v, obj) // obj is an xyz object + +As noted, just like the bound member function, a bound member variable also +expects the first (and only) argument to be a pointer or reference to an object. +The object (reference or pointer) can be lazily bound. Examples: + + xyz obj; + bind(&xyz::v, arg1) // arg1.v + bind(&xyz::v, obj) // obj.v + bind(&xyz::v, arg1)(obj) = 4 // obj.v = 4 + +[endsect] + +[endsect] +[section Container] + + #include + +The container module predefines a set of lazy functions that work on STL +containers. These functions provide a mechanism for the lazy evaluation of the +public member functions of the STL containers. The lazy functions are thin +wrappers that simply forward to their respective counterparts in the STL +library. + +Lazy functions are provided for all of the member functions of the following +containers: + +* deque +* list +* map +* multimap +* vector + +Indeed, should your class have member functions with the same names and +signatures as those listed below, then it will automatically be supported. To +summarize, lazy functions are provided for member functions: + +* assign +* at +* back +* begin +* capacity +* clear +* empty +* end +* erase +* front +* get_allocator +* insert +* key_comp +* max_size +* pop_back +* pop_front +* push_back +* push_front +* rbegin +* rend +* reserve +* resize +* size +* splice +* value_comp + +The lazy functions' names are the same as the corresponding member function. The +difference is that the lazy functions are free functions and therefore does not +use the member "dot" syntax. + +[table Sample usage + [["Normal" version] ["Lazy" version]] + [[`my_vector.at(5)`] [`at(arg1, 5)`]] + [[`my_list.size()`] [`size(arg1)`]] + [[`my_vector1.swap(my_vector2)`] [`swap(arg1, arg2)`]] +] + +Notice that member functions with names that clash with stl algorithms are +absent. This will be provided in Phoenix's algorithm module. + +No support is provided here for lazy versions of `operator+=`, `operator[]` etc. +Such operators are not specific to STL containers and lazy versions can +therefore be found in [link phoenix.composite.operator operators]. + +The following table describes the container functions and their semantics. + +[blurb __tip__ Arguments in brackets denote optional parameters.] + +[table Lazy STL Container Functions + [[Function] [Semantics]] + [[`assign(c, a[, b, c])`] [`c.assign(a[, b, c])`]] + [[`at(c, i)`] [`c.at(i)`]] + [[`back(c)`] [`c.back()`]] + [[`begin(c)`] [`c.begin()`]] + [[`capacity(c)`] [`c.capacity()`]] + [[`clear(c)`] [`c.clear()`]] + [[`empty(c)`] [`c.empty()`]] + [[`end(c)`] [`c.end()`]] + [[`erase(c, a[, b])`] [`c.erase(a[, b])`]] + [[`front(c)`] [`c.front()`]] + [[`get_allocator(c)`] [`c.get_allocator()`]] + [[`insert(c, a[, b, c])`] [`c.insert(a[, b, c])`]] + [[`key_comp(c)`] [`c.key_comp()`]] + [[`max_size(c)`] [`c.max_size()`]] + [[`pop_back(c)`] [`c.pop_back()`]] + [[`pop_front(c)`] [`c.pop_front()`]] + [[`push_back(c, d)`] [`c.push_back(d)`]] + [[`push_front(c, d)`] [`c.push_front(d)`]] + [[`pop_front(c)`] [`c.pop_front()`]] + [[`rbegin(c)`] [`c.rbegin()`]] + [[`rend(c)`] [`c.rend()`]] + [[`reserve(c, n)`] [`c.reserve(n)`]] + [[`resize(c, a[, b])`] [`c.resize(a[, b])`]] + [[`size(c)`] [`c.size()`]] + [[`splice(c, a[, b, c, d])`] [`c.splice(a[, b, c, d])`]] + [[`value_comp(c)`] [`c.value_comp()`]] +] + +[endsect] + +[section Algorithm] + + #include + +The algorithm module provides wrappers for the standard algorithms in the +`` and `` headers. + +The algorithms are divided into the categories iteration, transformation and querying, +modelling the __boost_mpl__ library. The different algorithm classes can be +included using the headers: + + #include + #include + #include + +The functions of the algorithm module take ranges as arguments where +appropriate. This is different to the standard +library, but easy enough to pick up. Ranges are described in detail in the +__boost_range__ library. + +For example, using the standard copy algorithm to copy between 2 arrays: + + int array[] = {1, 2, 3}; + int output[3]; + std::copy(array, array + 3, output); // We have to provide iterators + // to both the start and end of array + +The analogous code using the phoenix algorithm module is: + + int array[] = {1, 2, 3}; + int output[3]; + copy(arg1, arg2)(array, output); // Notice only 2 arguments, the end of + // array is established automatically + +The __boost_range__ library provides support for standard containers, strings and +arrays, and can be extended to support additional types. + +The following tables describe the different categories of algorithms, and their +semantics. + +[blurb __tip__ Arguments in brackets denote optional parameters.] + +[table Iteration Algorithms + [[Function] [stl Semantics]] + [[`for_each(r, c)`] [`for_each(begin(r), end(r), f)`]] + [[`accumulate(r, o[, f])`] [`accumulate(begin(r), end(r), o[, f])`]] +] + +[table Querying Algorithms + [[Function] [stl Semantics]] + [[`find(r, a)`] [`find(begin(r), end(r), a)`]] + [[`find_if(r, f)`] [`find_if(begin(r), end(r), f)`]] + [[`find_end(r1, r2[, f])`] [`find_end(begin(r1), end(r1), begin(r2), end(r2)[, f])`]] + [[`find_first_of(r1, r2[, f])`] [`find_first_of(begin(r1), end(r1), begin(r2), end(r2)[, f])`]] + [[`adjacent_find(r[, f])`] [`adjacent_find(begin(r), end(r)[, f])`]] + [[`count(r, a)`] [`count(begin(r), end(r), a)`]] + [[`count_if(r, f)`] [`count_if(begin(r), end(r), f)`]] + [[`distance(r)`] [`distance(begin(r), end(r))`]] + [[`mismatch(r, i[, f])`] [`mismatch(begin(r), end(r), i[, f])`]] + [[`equal(r, i[, f])`] [`equal(begin(r), end(r), i[, f])`]] + [[`search(r1, r2[, f])`] [`search(begin(r1), end(r1), begin(r2), end(r2)[, f])`]] + [[`lower_bound(r, a[, f])`] [`lower_bound(begin(r), end(r), a[, f])`]] + [[`upper_bound(r, a[, f])`] [`upper_bound(begin(r), end(r), a[, f])`]] + [[`equal_range(r, a[, f])`] [`equal_range(begin(r), end(r), a[, f])`]] + [[`binary_search(r, a[, f])`] [`binary_search(begin(r), end(r), a[, f])`]] + [[`includes(r1, r2[, f])`] [`includes(begin(r1), end(r1), begin(r2), end(r2)[, f])`]] + [[`min_element(r[, f])`] [`min_element(begin(r), end(r)[, f])`]] + [[`max_element(r[, f])`] [`max_element(begin(r), end(r)[, f])`]] + [[`lexicographical_compare(r1, r2[, f])`] [`lexicographical_compare(begin(r1), end(r1), begin(r2), end(r2)[, f])`]] +] + +[table Transformation Algorithms + [[Function] [stl Semantics]] + [[`copy(r, o)`] [`copy(begin(r), end(r), o)`]] + [[`copy_backward(r, o)`] [`copy_backward(begin(r), end(r), o)`]] + [[`transform(r, o, f)`] [`transform(begin(r), end(r), o, f)`]] + [[`transform(r, i, o, f)`] [`transform(begin(r), end(r), i, o, f)`]] + [[`replace(r, a, b)`] [`replace(begin(r), end(r), a, b)`]] + [[`replace_if(r, f, a)`] [`replace(begin(r), end(r), f, a)`]] + [[`replace_copy(r, o, a, b)`] [`replace_copy(begin(r), end(r), o, a, b)`]] + [[`replace_copy_if(r, o, f, a)`] [`replace_copy_if(begin(r), end(r), o, f, a)`]] + [[`fill(r, a)`] [`fill(begin(r), end(r), a)`]] + [[`fill_n(r, n, a)`] [`fill_n(begin(r), n, a)`]] + [[`generate(r, f)`] [`generate(begin(r), end(r), f)`]] + [[`generate_n(r, n, f)`] [`generate_n(begin(r), n, f)`]] + [[`remove(r, a)`] [`remove(begin(r), end(r), a)`]] + [[`remove_if(r, f)`] [`remove_if(begin(r), end(r), f)`]] + [[`remove_copy(r, o, a)`] [`remove_copy(begin(r), end(r), o, a)`]] + [[`remove_copy_if(r, o, f)`] [`remove_copy_if(begin(r), end(r), o, f)`]] + [[`unique(r[, f])`] [`unique(begin(r), end(r)[, f])`]] + [[`unique_copy(r, o[, f])`] [`unique_copy(begin(r), end(r), o[, f])`]] + [[`reverse(r)`] [`reverse(begin(r), end(r))`]] + [[`reverse_copy(r, o)`] [`reverse_copy(begin(r), end(r), o)`]] + [[`rotate(r, m)`] [`rotate(begin(r), m, end(r))`]] + [[`rotate_copy(r, m, o)`] [`rotate_copy(begin(r), m, end(r), o)`]] + [[`random_shuffle(r[, f])`] [`random_shuffle(begin(r), end(r), f)`]] + [[`partition(r, f)`] [`partition(begin(r), end(r), f)`]] + [[`stable_partition(r, f)`] [`stable_partition(begin(r), end(r), f)`]] + [[`sort(r[, f])`] [`sort(begin(r), end(r)[, f])`]] + [[`stable_sort(r[, f])`] [`stable_sort(begin(r), end(r)[, f])`]] + [[`partial_sort(r, m[, f])`] [`partial_sort(begin(r), m, end(r)[, f])`]] + [[`partial_sort_copy(r1, r2[, f])`] [`partial_sort_copy(begin(r1), end(r1), begin(r2), end(r2)[, f])`]] + [[`nth_element(r, n[, f])`] [`nth_element(begin(r), n, end(r)[, f])`]] + [[`merge(r1, r2, o[, f])`] [`merge(begin(r1), end(r1), begin(r2), end(r2), o[, f])`]] + [[`inplace_merge(r, m[, f])`] [`inplace_merge(begin(r), m, end(r)[, f])`]] + [[`set_union(r1, r2, o[, f])`] [`set_union(begin(r1), end(r1), begin(r2), end(r2)[, f])`]] + [[`set_intersection(r1, r2, o[, f])`] [`set_intersection(begin(r1), end(r1), begin(r2), end(r2)[, f])`]] + [[`set_difference(r1, r2, o[, f])`] [`set_difference(begin(r1), end(r1), begin(r2), end(r2)[, f])`]] + [[`set_symmetric_difference(r1, r2, o[, f])`] [`set_symmetric_difference(begin(r1), end(r1), begin(r2), end(r2)[, f])`]] + [[`push_heap(r[, f])`] [`push_heap(begin(r), end(r)[, f])`]] + [[`pop_heap(r[, f])`] [`pop_heap(begin(r), end(r)[, f])`]] + [[`make_heap(r[, f])`] [`make_heap(begin(r), end(r)[, f])`]] + [[`sort_heap(r[, f])`] [`sort_heap(begin(r), end(r)[, f])`]] + [[`next_permutation(r[, f])`] [`next_permutation(begin(r), end(r)[, f])`]] + [[`prev_permutation(r[, f])`] [`prev_permutation(begin(r), end(r)[, f])`]] + + [[`inner_product(r, o, a[, f1, f2])`] [`inner_product(begin(r), end(r), o[, f1, f2])`]] + [[`partial_sum(r, o[, f])`] [`partial_sum(begin(r), end(r), o[, f])`]] + [[`adjacent_difference(r, o[, f])`] [`adjacent_difference(begin(r), end(r), o[, f])`]] +] + +[endsect] + +[section Inside Phoenix] + +[def __eval_policy__ [link phoenix.inside_phoenix.composites_in_detail.evalpolicy `EvalPolicy`]] +[def __eval__ [link phoenix.inside_phoenix.actors_in_detail.eval_concept `Eval`]] +[def __eval_tuple__ [link phoenix.inside_phoenix.composites_in_detail.evaltuple `EvalTuple`]] +[def __environment__ [link phoenix.inside_phoenix.actors_in_detail.environment `Environment`]] + +This chapter explains in more detail how the library operates. The information +henceforth should not be necessary to those who are interested in just using the +library. However, a microscopic view might prove to be beneficial to moderate +to advanced programmers who wish to extend the library. + +[section Actors In Detail] + +[h3 Actor Concept] + +The main concept is the `Actor`. Actors are function objects (that can accept 0 +to N arguments (where N is a predefined maximum). + +[blurb __note__ You can set `PHOENIX_LIMIT`, the predefined maximum arity an +actor can take. By default, `PHOENIX_LIMIT` is set to 10.] + +[h3 actor template class] + +The `actor` template class models the `Actor` concept: + + template + struct actor : Eval + { + typedef Eval eval_type; + + actor(); + actor(Eval const& base); + + template + explicit actor(T0 const& _0); + + template + actor(T0 const& _0, T1 const& _1); + + // more constructors + + typename apply_actor >::type + operator()() const; + + template + typename apply_actor >::type + operator()(T0& _0) const; + + template + typename apply_actor >::type + operator()(T0& _0, T1& _1) const; + + // function call operators + }; + +[table Actor Concept Requirements + [ [Expression] [Result/Semantics] ] + [ [`T::eval_type`] [The actor's Eval type] ] + [ [`T()`] [Default Constructor] ] + [ [`T(base)`] [Constructor from Eval] ] + [ [`T(arg0, arg1, ..., argN)`] [Pass through constructors] ] + [ [`x(arg0, arg1, ..., argN)`] [Function call operators] ] +] + +[h3 Eval Concept] + +The `actor` template class has a single template parameter, `Eval`, from which +it derives from. While the `Actor` concept represents a function, the `Eval` +concept represents the function body. The requirements for `Eval` are +intentionally kept simple, to make it easy to write models of the concept. We +shall see an example in the [link phoenix.inside_phoenix.actor_example next section]. + +[table Eval Concept Requirements + [ [Expression] [Result/Semantics] ] + [ [`return x.eval(env)`] [Evaluates the function (see Environment below)] ] + [ [`T::result::type`] [The return type of eval (see Environment below)] ] +] + +[h3 Constructors] + +In addition to a default constructor and an constructor from a Eval object, +there are templated (pass through) constructors for 1 to N arguments (N == +`PHOENIX_LIMIT`). These constructors simply forward the arguments to the `base`. + +[blurb __note__ *Parametric Base Class Pattern*\n\n + Notice that actor derives from its template argument Eval. This is the + inverse of the curiously recurring template pattern (CRTP). With the CRTP, a + class, T, has a Derived template parameter that is assumed to be its + subclass. The "parametric base class pattern" (PBCP), on the other hand, + inverses the inheritance and makes a class, T, the derived class. Both CRTP + and PBCP techniques have its pros and cons, which is outside the scope of + this document. CRTP should really be renamed "parametric subclass pattern + (PSCP), but again, that's another story. +] + +[h3 Function Call Operators] + +There are N function call operators for 0 to N arguments (N == `PHOENIX_LIMIT`). +The actor class accepts the arguments and forwards the arguments to the actor's +base `Eval` for evaluation. + +[def [$http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1385.htm] + +[blurb __note__ *Forwarding Function Problem*\n\n + The function call operators cannot accept non-const temporaries and literal + constants. There is a known issue with current C++ called the + "__forwarding__". The problem is that given an arbitrary function `F`, using + current C++ language rules, one cannot create a forwarding function `FF` + that transparently assumes the arguments of `F`. Disallowing non-const + rvalues arguments partially solves the problem but prohibits code such as + `f(1, 2, 3);`. +] + +[h3 Environment] + +On an actor function call, before calling the actor's `Eval::eval` for +evaluation, the actor creates an ['*environment*]. Basically, the environment +packages the arguments in a tuple. The `Environment` is a concept, of which, the +`basic_environment` template class is a model of. + +[table Environment Concept Requirements + [ [Expression] [Result/Semantics] ] + [ [`x.args()`] [The arguments in a tie (a tuple of references)] ] + [ [`T::args_type`] [The arguments' types in an MPL sequence] ] + [ [`T::tie_type`] [The tie (tuple of references) type] ] +] + +Schematically: + +[$images/funnel_in.png] + +Other parts of the library (e.g. the scope module) extends the `Environment` +concept to hold other information such as local variables, etc. + +[h3 apply_actor] + +`apply_actor` is a standard MPL style metafunction that simply calls the +Action's `result` nested metafunction: + + template + struct apply_actor + { + typedef typename Action::template result::type type; + }; + +After evaluating the arguments and doing some computation, the `eval` member +function returns something back to the client. To do this, the forwarding +function (the actor's `operator()`) needs to know the return type of the eval +member function that it is calling. For this purpose, models of `Eval` are +required to provide a nested template class: + + template + struct result; + +This nested class provides the result type information returned by the `Eval`'s +`eval` member function. The nested template class `result` should have a typedef +`type` that reflects the return type of its member function `eval`. + +For reference, here's a typical `actor::operator()` that accepts two arguments: + + template + typename apply_actor >::type + operator()(T0& _0, T1& _1) const + { + return eval_type::eval(basic_environment(_0, _1)); + } + +[h3 actor_result] + +For reasons of symmetry to the family of `actor::operator()` there is a special +metafunction usable for actor result type calculation named `actor_result`. This +metafunction allows us to directly to specify the types of the parameters to be +passed to the `actor::operator()` function. Here's a typical `actor_result` that +accepts two arguments: + + template + struct actor_result + { + typedef basic_environment env_type; + typedef typename Action::template result::type type; + }; + +[endsect] +[section Actor Example] + +Let us see a very simple prototypical example of an actor. This is not a toy +example. This is actually part of the library. Remember the [link +phoenix.primitives.references `reference`]?. + +First, we have a model of the `Eval` concept: the `reference`: + + template + struct reference + { + template + struct result + { + typedef T& type; + }; + + reference(T& arg) + : ref(arg) {} + + template + T& eval(Env const&) const + { + return ref; + } + + T& ref; + }; + +Models of `Eval` are never created directly and its instances never exist alone. +We have to wrap it inside the `actor` template class to be useful. The `ref` +template function does this for us: + + template + actor > const + ref(T& v) + { + return reference(v); + } + +The `reference` template class conforms to the __eval__ concept. It has a nested +`result` metafunction that reflects the return type of its `eval` member +function, which peforms the actual function. `reference` stores a reference to +a `T`. Its `eval` member function simply returns the reference. It does not make +use of the environment `Env`. + +/Pretty simple.../ + +[endsect] +[section Composites In Detail] + +We stated before that composites are actors that are composed of zero or more +actors (see [link phoenix.composite Composite]). This is not quite accurate. The +definition was sufficient at that point where we opted to keep things simple and +not bury the reader with details which she might not need anyway. + +Actually, a composite is a model of the __eval__ concept (more on this later). +At the same time, it is also composed of 0..N (where N is a predefined maximum) +__eval__ instances and an eval policy. The individual __eval__ instances are +stored in a tuple. + +[blurb __note__ In a sense, the original definition of "composite", more or +less, will do just fine because __eval__ instances never exist alone and are +always wrapped in an `actor` template class which inherits from it anyway. The +resulting actor IS-AN __eval__.] + +[blurb __note__ You can set `PHOENIX_COMPOSITE_LIMIT`, the predefined maximum +`Eval`s (actors) a composite can take. By default, `PHOENIX_COMPOSITE_LIMIT` is +set to `PHOENIX_LIMIT` (See [link phoenix.actors Actors]).] + +[h2 composite template class] + + template + struct composite : EvalTuple + { + typedef EvalTuple base_type; + typedef EvalPolicy eval_policy_type; + + template + struct result + { + typedef implementation-defined type; + }; + + composite(); + composite(base_type const& actors); + + template + composite(U0 const& _0); + + template + composite(U0 const& _0, U1 const& _1); + + // more constructors + + template + typename result::type + eval(Env const& env) const; + }; + +[h2 EvalTuple] + +`EvalTuple`, holds all the __eval__ instances. The `composite` template class +inherits from it. In addition to a default constructor and a constructor from an +`EvalTuple` object, there are templated (pass through) constructors for 1 to N +arguments (again, where N == `PHOENIX_COMPOSITE_LIMIT`). These constructors +simply forward the arguments to the `EvalTuple` base class. + +[h2 EvalPolicy] + +The composite's `eval` member function calls its `EvalPolicy`'s `eval` member +function (a static member function) passing in the [link +phoenix.inside_phoenix.actors_in_detail.environment environment] and each of +its actors, in parallel. The following diagram illustrates what's happening: + +[$images/funnel_out.png] + +[table EvalPolicy Requirements + [ [Expression] [Result/Semantics] ] + [ [`x.eval(env, eval0, eval1, ..., evalN)`] [Evaluate the composite] ] + [ [`T::result::type`] [The return type of eval] ] +] + +The `EvalPolicy` is expected to have a nested template class `result` which has a +typedef `type` that reflects the return type of its member function `eval`. +Here's a typical example of the composite's eval member function for a 2-actor +composite: + + template + typename result::type + eval(Env const& env) const + { + typedef typename result::type return_type; + return EvalPolicy::template + eval( + env + , get<0>(*this) // gets the 0th element from EvalTuple + , get<1>(*this)); // gets the 1st element from EvalTuple + } + +[endsect] +[section Composing] + +Composites are never instantiated directly. Front end expression templates are +used to generate the composites. Using expression templates, we implement a DSEL +(Domain Specific Embedded Language) that mimicks native C++. You've seen this +DSEL in action in the preceding sections. It is most evident in the +[link phoenix.composite.statement Statement] section. + +There are some facilities in the library to make composition of composites +easier. We have a set of overloaded `compose` functions and an `as_composite` +metafunction. Together, these helpers make composing a breeze. We'll provide an +[link phoenix.inside_phoenix.composing.composite_example example of a +composite] later to see why. + +[section compose] + + compose(arg0, arg1, arg2, ..., argN); + +Given an __eval_policy__ and some arguments `arg0`...argN, returns a proper +`composite`. The arguments may or may not be phoenix actors (primitives of +composites). If not, the arguments are converted to actors appropriately. For +example: + + compose(3) + +converts the argument `3` to an `actor >(3)`. + +[endsect] + +[section as_composite] + + as_composite::type + +This is the metafunction counterpart of `compose`. Given an __eval_policy__ and +some argument types `Arg0`...ArgN, returns a proper `composite` type. For +example: + + as_composite::type + +is the composite type of the `compose(3)` expression above. + +[endsect] + +[section Composite Example] + +Now, let's examine an example. Again, this is not a toy example. This is actually +part of the library. Remember the [link phoenix.composite.statement.while__statement +`while_`] lazy statement? Putting together everything we've learned so far, we +will present it here in its entirety (verbatim): + + struct while_eval + { + template + struct result + { + typedef void type; + }; + + template + static void + eval(Env const& env, Cond& cond, Do& do_) + { + while (cond.eval(env)) + do_.eval(env); + } + }; + + template + struct while_gen + { + while_gen(Cond const& cond) + : cond(cond) {} + + template + actor::type> + operator[](Do const& do_) const + { + return compose(cond, do_); + } + + Cond cond; + }; + + template + while_gen + while_(Cond const& cond) + { + return while_gen(cond); + } + +`while_eval` is an example of an __eval_policy__. `while_gen` and `while_` are +the expression template front ends. Let's break this apart to understand what's +happening. Let's start at the bottom. It's easier that way. + +When you write: + + while_(cond) + +we generate an instance of `while_gen`, where `Cond` is the type of +`cond`. `cond` can be an arbitrarily complex actor expression. The `while_gen` +template class has an `operator[]` accepting another expression. If we write: + + while_(cond) + [ + do_ + ] + +it will generate a proper composite with the type: + + as_composite::type + +where `Cond` is the type of `cond` and `Do` is the type of `do_`. Notice how we +are using phoenix's [link phoenix.inside_phoenix.composing composition] (`compose` +and `as_composite`) mechanisms here + + template + actor::type> + operator[](Do const& do_) const + { + return compose(cond, do_); + } + +Finally, the `while_eval` does its thing: + + while (cond.eval(env)) + do_.eval(env); + +`cond` and `do_`, at this point, are instances of __eval__. `cond` and `do_` are +the __eval__ elements held by the composite's __eval_tuple__. `env` is the +__environment__. + +[endsect] + +[endsect] + +[section Extending] + +We've shown how it is very easy to extend phoenix by writing new primitives and +composites. The modular design of Phoenix makes it extremely extensible. We have +seen that layer upon layer, the whole library is built on a solid foundation. +There are only a few simple well designed concepts that are laid out like +bricks. Overall, the library is designed to be extended. Everything above the +core layer can in fact be considered just as extensions to the library. This +modular design was inherited from the __spirit__ inline parser library. + +Extension is non-intrusive. And, whenever a component or module is extended, the +new extension automatically becomes a first class citizen and is automatically +recognized by all modules and components in the library. + +[endsect] + +[endsect] + +[section Wrap Up] + +Sooner or later more FP techniques become standard practice as people find the +true value of this programming discipline outside the academe and into the +mainstream. In as much as structured programming of the 70s and object oriented +programming in the 80s and generic programming in the 90s shaped our thoughts +towards a more robust sense of software engineering, FP will certainly be a +paradigm that will catapult us towards more powerful software design and +engineering onward into the new millenium. + +Let me quote Doug Gregor of Boost.org. About functional style programming +libraries: + +[:['They're gaining acceptance, but are somewhat stunted by the ubiquitousness +of broken compilers. The C++ community is moving deeper into the so-called "STL- +style" programming paradigm, which brings many aspects of functional programming +into the fold. Look at, for instance, the Spirit parser to see how such function +objects can be used to build Yacc-like grammars with semantic actions that can +build abstract syntax trees on the fly. This type of functional composition is +gaining momentum.]] + +Indeed. Phoenix is another attempt to introduce more FP techniques into the +mainstream. Not only is it a tool that will make life easier for the programmer. +In its own right, the actual design of the library itself is a model of true C++ +FP in action. The library is designed and structured in a strict but clear and +well mannered FP sense. By all means, use the library as a tool. But for those +who want to learn more about FP in C++, don't stop there, I invite you to take a +closer look at the design of the library itself. + +So there you have it. Have fun! See you in the FP world. + +[endsect] + +[section Acknowledgement] + +# Hartmut Kaiser implemented the original lazy casts and constructors based on + his original work on Spirit SE "semantic expressions" (the precursor to + Phoenix). +# Angus Leeming implemented the container functions on Phoenix-1 which I then + ported to Phoenix-2. +# Daniel Wallin helped with the scope module, local variables, let and lambda + and the algorithms. I frequently discuss design issues with Daniel on Yahoo Messenger. +# Jaakko Jarvi. DA Lambda MAN! +# Dave Abrahams, for his constant presence, wherever, whenever. +# Aleksey Gurtovoy, DA MPL MAN! +# Doug Gregor, always a source of inpiration. +# Dan Marsden, did almost all the work in bringing Phoenix-2 out the door. +# Eric Niebler did a 2.0 pre-release review and wrote some range related code + that Phoenix stole and used in the algorithms. +# Thorsten Ottosen; Eric's range_ex code began life as "container_algo" in the + old boost sandbox, by Thorsten in 2002-2003. +# Jeremy Siek, even prior to Thorsten, in 2001, started the "container_algo". +# Vladimir Prus wrote the mutating algorithms code from the Boost Wiki. +# Daryle Walker did a 2.0 pre-release review. + +[endsect] + +[section References] + +# Why Functional Programming Matters, John Hughes, 1989. + Available online at [@http://www.math.chalmers.se/~rjmh/Papers/whyfp.html]. +# Boost.Lambda library, Jaakko Jarvi, 1999-2004 Jaakko Jarvi, Gary Powell. + Available online at [@http://www.boost.org/libs/lambda/]. +# Functional Programming in C++ using the FC++ Library: a short article + introducing FC++, Brian McNamara and Yannis Smaragdakis, August 2003. Available + online at [@http://www.cc.gatech.edu/~yannis/fc++/]. +# Side-effects and partial function application in C++, Jaakko Jarvi and Gary + Powell, 2001. Available online at + [@http://osl.iu.edu/~jajarvi/publications/papers/mpool01.pdf]. +# Spirit Version 1.8.1, Joel de Guzman, Nov 2004. Available online at + [@http://www.boost.org/libs/spirit/]. +# The Boost MPL Library, Aleksey Gurtovoy and David Abrahams, 2002-2004. + Available online at [@http://www.boost.org/libs/mpl/]. +# Generic Programming Redesign of Patterns, Proceedings of the 5th European + Conference on Pattern Languages of Programs, (EuroPLoP'2000) Irsee, Germany, + July 2000. Available online at + [@http://www.coldewey.com/europlop2000/papers/geraud%2Bduret.zip]. +# A Gentle Introduction to Haskell, Paul Hudak, John Peterson and Joseph Fasel, + 1999. Available online at [@http://www.haskell.org/tutorial/]. +# Large scale software design, John Lackos, ISBN 0201633620, Addison-Wesley, July + 1996. +# Design Patterns, Elements of Reusable Object-Oriented Software, Erich Gamma, + Richard Helm, Ralph Jhonson, and John Vlissides, Addison-Wesley, 1995. +# The Forwarding Problem: Arguments Peter Dimov, Howard E. Hinnant, Dave + Abrahams, September 09, 2002. Available online: __forwarding__. + +[endsect] + diff --git a/phoenix/example/Jamfile.v2 b/phoenix/example/Jamfile.v2 new file mode 100644 index 000000000..c72426ade --- /dev/null +++ b/phoenix/example/Jamfile.v2 @@ -0,0 +1,24 @@ +#============================================================================== +# Copyright (c) 2003, 2005 Joel de Guzman +# +# Use, modification and distribution is subject to the Boost Software +# License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +#============================================================================== + +# bring in rules for testing +import testing ; + +test-suite users_manual : + [ run users_manual/all_odds.cpp ] + [ run users_manual/arguments.cpp ] + [ run users_manual/callback.cpp ] + [ run users_manual/factorial.cpp ] + [ run users_manual/find_if.cpp ] + [ run users_manual/function.cpp ] + [ run users_manual/if.cpp ] + [ run users_manual/lambda.cpp ] + [ run users_manual/values.cpp ] + [ run users_manual/references.cpp ] + ; + diff --git a/phoenix/example/users_manual/algorithm.cpp b/phoenix/example/users_manual/algorithm.cpp new file mode 100644 index 000000000..6eaddeea1 --- /dev/null +++ b/phoenix/example/users_manual/algorithm.cpp @@ -0,0 +1,21 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; + +int +main() +{ + int array[] = {1, 2, 3}; + int output[3]; + copy(arg1, arg2)(array, output); + return 0; +} diff --git a/phoenix/example/users_manual/all_odds.cpp b/phoenix/example/users_manual/all_odds.cpp new file mode 100644 index 000000000..b8c932bd1 --- /dev/null +++ b/phoenix/example/users_manual/all_odds.cpp @@ -0,0 +1,35 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + int init[] = { 2, 10, 4, 5, 1, 6, 8, 3, 9, 7 }; + vector c(init, init + 10); + typedef vector::iterator iterator; + + // Print all odd contents of an stl container c + for_each(c.begin(), c.end(), + if_(arg1 % 2 == 1) + [ + cout << arg1 << ' ' + ] + ); + + return 0; +} diff --git a/phoenix/example/users_manual/arguments.cpp b/phoenix/example/users_manual/arguments.cpp new file mode 100644 index 000000000..0997a8f1e --- /dev/null +++ b/phoenix/example/users_manual/arguments.cpp @@ -0,0 +1,22 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + int i = 3; + char const* s = "Hello World"; + cout << arg1(i) << endl; // prints 3 + cout << arg2(i, s) << endl; // prints "Hello World" + return 0; +} diff --git a/phoenix/example/users_manual/callback.cpp b/phoenix/example/users_manual/callback.cpp new file mode 100644 index 000000000..375117545 --- /dev/null +++ b/phoenix/example/users_manual/callback.cpp @@ -0,0 +1,26 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +template +void print(F f) +{ + cout << f() << endl; +} + +int +main() +{ + print(val(3)); + print(val("Hello World")); + return 0; +} diff --git a/phoenix/example/users_manual/factorial.cpp b/phoenix/example/users_manual/factorial.cpp new file mode 100644 index 000000000..a093edc29 --- /dev/null +++ b/phoenix/example/users_manual/factorial.cpp @@ -0,0 +1,40 @@ +/*============================================================================= + Copyright (c) 2001-2003 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +struct factorial_impl +{ + template + struct result + { + typedef Arg type; + }; + + template + Arg operator()(Arg n) const + { + return (n <= 0) ? 1 : n * this->operator()(n-1); + } +}; + +function factorial; + +int +main() +{ + int i = 4; + cout << factorial(arg1)(i) << endl; + return 0; +} diff --git a/phoenix/example/users_manual/find_if.cpp b/phoenix/example/users_manual/find_if.cpp new file mode 100644 index 000000000..d008b3977 --- /dev/null +++ b/phoenix/example/users_manual/find_if.cpp @@ -0,0 +1,30 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + int init[] = { 2, 10, 4, 5, 1, 6, 8, 3, 9, 7 }; + vector c(init, init + 10); + typedef vector::iterator iterator; + + // Find the first odd number in container c + iterator it = find_if(c.begin(), c.end(), arg1 % 2 == 1); + + if (it != c.end()) + cout << *it; // if found, print the result + return 0; +} diff --git a/phoenix/example/users_manual/function.cpp b/phoenix/example/users_manual/function.cpp new file mode 100644 index 000000000..9701703a3 --- /dev/null +++ b/phoenix/example/users_manual/function.cpp @@ -0,0 +1,47 @@ +/*============================================================================= + Copyright (c) 2001-2003 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +struct is_odd_ +{ + template + struct result + { + typedef bool type; + }; + + template + bool operator()(Arg arg1) const + { + return arg1 % 2 == 1; + } +}; + +function is_odd; + +int +main() +{ + int init[] = { 2, 10, 4, 5, 1, 6, 8, 3, 9, 7 }; + vector c(init, init + 10); + typedef vector::iterator iterator; + + // Find the first odd number in container c + iterator it = find_if(c.begin(), c.end(), is_odd(arg1)); + + if (it != c.end()) + cout << *it; // if found, print the result + return 0; +} diff --git a/phoenix/example/users_manual/if.cpp b/phoenix/example/users_manual/if.cpp new file mode 100644 index 000000000..967d6535e --- /dev/null +++ b/phoenix/example/users_manual/if.cpp @@ -0,0 +1,35 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + int init[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + vector v(init, init+10); + + cout << dec; + int x = 0; + + for_each(v.begin(), v.end(), + if_(arg1 > 5) + [ + cout << arg1 << ", " + ] + ); + + return 0; +} diff --git a/phoenix/example/users_manual/lambda.cpp b/phoenix/example/users_manual/lambda.cpp new file mode 100644 index 000000000..52c6d4813 --- /dev/null +++ b/phoenix/example/users_manual/lambda.cpp @@ -0,0 +1,79 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include + +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace boost::phoenix::local_names; +using namespace std; + +namespace lazy_stuff +{ + struct for_each_impl + { + template + struct result + { + typedef void type; + }; + + template + void operator()(C& c, F f) const + { + std::for_each(c.begin(), c.end(), f); + } + }; + + function const for_each = for_each_impl(); + + struct push_back_impl + { + template + struct result + { + typedef void type; + }; + + template + void operator()(C& c, T& x) const + { + c.push_back(x); + } + }; + + function const push_back = push_back_impl(); +} + +int +main() +{ + { + using lazy_stuff::for_each; + using lazy_stuff::push_back; + + int x = 10; + std::vector > v(10); + + for_each(arg1, + lambda(_a = arg2) + [ + push_back(arg1, _a) + ] + ) + (v, x); + } + + return 0; +} + diff --git a/phoenix/example/users_manual/references.cpp b/phoenix/example/users_manual/references.cpp new file mode 100644 index 000000000..28f3cf1ed --- /dev/null +++ b/phoenix/example/users_manual/references.cpp @@ -0,0 +1,22 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + int i = 3; + char const* s = "Hello World"; + cout << ref(i)() << endl; + cout << ref(s)() << endl; + return 0; +} diff --git a/phoenix/example/users_manual/values.cpp b/phoenix/example/users_manual/values.cpp new file mode 100644 index 000000000..bb196d7ed --- /dev/null +++ b/phoenix/example/users_manual/values.cpp @@ -0,0 +1,20 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + cout << val(3)() << endl; + cout << val("Hello World")() << endl; + return 0; +} diff --git a/phoenix/index.html b/phoenix/index.html new file mode 100644 index 000000000..b6ce29ba0 --- /dev/null +++ b/phoenix/index.html @@ -0,0 +1,10 @@ + + + + + + + Automatic redirection failed, click this + link + + diff --git a/phoenix/test/Jamfile.v2 b/phoenix/test/Jamfile.v2 new file mode 100644 index 000000000..62d5182bf --- /dev/null +++ b/phoenix/test/Jamfile.v2 @@ -0,0 +1,90 @@ +#============================================================================== +# Copyright (c) 2003-2006 Joel de Guzman +# +# Use, modification and distribution is subject to the Boost Software +# License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +#============================================================================== + +# bring in rules for testing +import testing ; + +local multi-threading = /boost/thread//boost_thread + multi BOOST_ALL_NO_LIB=1 ; + +test-suite phoenix_detail : + [ run detail/type_deduction_tests.cpp ] + ; + +test-suite phoenix_core : + [ run core/primitives_tests.cpp ] + [ run core/compose_tests.cpp ] + ; + +test-suite phoenix_operator : + [ run operator/arithmetic_tests.cpp ] + [ run operator/bitwise_tests.cpp ] + [ run operator/comparison_tests.cpp ] + [ run operator/if_else_tests.cpp ] + [ run operator/io_tests.cpp ] + [ run operator/logical_tests.cpp ] + [ run operator/misc_binary_tests.cpp ] + [ run operator/self_tests.cpp ] + [ run operator/unary_tests.cpp ] + [ run operator/member.cpp ] + ; + +test-suite phoenix_object : + [ run object/cast_tests.cpp ] + [ run object/new_delete_tests.cpp ] + ; + +test-suite phoenix_function : + [ run function/function_tests.cpp ] + ; + +test-suite phoenix_bind : + [ run bind/bind_function_tests.cpp ] + [ run bind/bind_function_object_tests.cpp ] + [ run bind/bind_member_function_tests.cpp ] + [ run bind/bind_member_variable_tests.cpp ] + ; + +test-suite phoenix_statement : + [ run statement/if_tests.cpp ] + [ run statement/loops_tests.cpp ] + [ run statement/switch_tests.cpp ] + [ run statement/exceptions.cpp ] + ; + +test-suite phoenix_container : + [ run container/container_tests1a.cpp ] + [ run container/container_tests1b.cpp ] + [ run container/container_tests2a.cpp ] + [ run container/container_tests2b.cpp ] + [ run container/container_tests3a.cpp ] + [ run container/container_tests3b.cpp ] + [ run container/container_tests4a.cpp ] + [ run container/container_tests4b.cpp ] + [ run container/container_tests5a.cpp ] + [ run container/container_tests5b.cpp ] + [ run container/container_tests6a.cpp ] + [ run container/container_tests6b.cpp ] + ; + +test-suite phoenix_scope : + [ run scope/lambda_tests.cpp ] + [ run scope/let_tests.cpp ] + [ run scope/dynamic_tests.cpp ] + [ run scope/bug_000008.cpp : : : $(multi-threading) ] + ; + +test-suite phoenix_algorithm : + [ run algorithm/iteration.cpp ] + [ run algorithm/transformation1.cpp ] + [ run algorithm/transformation2.cpp ] + [ run algorithm/transformation3.cpp ] + [ run algorithm/transformation4.cpp ] + [ run algorithm/querying.cpp ] + [ run algorithm/querying2.cpp ] + ; diff --git a/phoenix/test/algorithm/iteration.cpp b/phoenix/test/algorithm/iteration.cpp new file mode 100644 index 000000000..e1b12f795 --- /dev/null +++ b/phoenix/test/algorithm/iteration.cpp @@ -0,0 +1,57 @@ +/*============================================================================= + Copyright (c) 2005-2007 Dan Marsden + Copyright (c) 2005-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#include +#include +#include + +#include + +namespace +{ + struct for_each_tester + { + int value_; + for_each_tester() : value_(0) { } + void operator()( + int& i) + { + value_ += i++; + return; + } + }; + + void for_each_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + BOOST_TEST(for_each(arg1, for_each_tester())(array).value_ == 6); + BOOST_TEST(array[0] == 2); + BOOST_TEST(array[1] == 3); + BOOST_TEST(array[2] == 4); + return; + } + + void accumulate_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + BOOST_TEST(accumulate(arg1, 0)(array) == 6); + BOOST_TEST(boost::phoenix::accumulate(arg1, 0, std::minus())(array) == -6); + return; + } +} + +int main() +{ + for_each_test(); + accumulate_test(); + boost::report_errors(); +} diff --git a/phoenix/test/algorithm/querying.cpp b/phoenix/test/algorithm/querying.cpp new file mode 100644 index 000000000..04bb4f678 --- /dev/null +++ b/phoenix/test/algorithm/querying.cpp @@ -0,0 +1,270 @@ +/*============================================================================= + Copyright (c) 2005 Dan Marsden + Copyright (c) 2005-2007 Joel de Guzman + Copyright (c) 2007 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#include +#include +#include + +#include +#include + +namespace +{ + struct even + { + bool operator()(const int i) const + { + return i % 2 == 0; + } + }; + + struct mod_2_comparison + { + bool operator()( + const int lhs, + const int rhs) + { + return lhs % 2 == rhs % 2; + }; + }; + + void find_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + BOOST_TEST(find(arg1,2)(array) == array + 1); + + std::set s(array, array + 3); + BOOST_TEST(find(arg1, 2)(s) == s.find(2)); + + return; + } + + + void find_if_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + BOOST_TEST(find_if(arg1, even())(array) == array + 1); + return; + } + + void find_end_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3,1,2,3,1}; + int pattern[] = {1,2,3}; + BOOST_TEST(find_end(arg1, arg2)(array, pattern) == array + 3); + int pattern2[] = {5,6,5}; + BOOST_TEST(find_end(arg1, arg2, mod_2_comparison())(array, pattern2) == array + 3); + return; + } + + void find_first_of_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int search_for[] = {2,3,4}; + BOOST_TEST(find_first_of(arg1, arg2)(array, search_for) == array + 1); + + int search_for2[] = {0}; + BOOST_TEST(find_first_of(arg1, arg2, mod_2_comparison())(array, search_for2) == array + 1); + return; + } + + void adjacent_find_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {0,1,3,4,4}; + BOOST_TEST(adjacent_find(arg1)(array) == array + 3); + BOOST_TEST(adjacent_find(arg1, mod_2_comparison())(array) == array + 1); + return; + } + + void count_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,1,0,1,1}; + BOOST_TEST(count(arg1, 1)(array) == 4); + return; + } + + void count_if_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3,4,5}; + BOOST_TEST(count_if(arg1, even())(array) == 2); + return; + } + + void distance_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,1,0,1,1}; + BOOST_TEST(distance(arg1)(array) == 5); + return; + } + + void mismatch_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3,4,5}; + int search[] = {1,2,4}; + + BOOST_TEST( + mismatch(arg1, arg2)(array, search) == + std::make_pair(array + 2, search + 2)); + int search2[] = {1,2,1,1}; + BOOST_TEST( + mismatch(arg1, arg2, mod_2_comparison())(array, search2) + == std::make_pair(array + 3, search2 + 3)); + + return; + } + + void equal_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[] = {1,2,3}; + int array3[] = {1,2,4}; + BOOST_TEST( + equal(arg1, arg2)(array, array2)); + BOOST_TEST( + !equal(arg1, arg2)(array, array3)); + + BOOST_TEST( + equal(arg1, arg2, mod_2_comparison())(array, array2)); + BOOST_TEST( + !equal(arg1, arg2, mod_2_comparison())(array, array3)); + return; + } + + void search_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3,1,2,3}; + int pattern[] = {2,3}; + BOOST_TEST( + search(arg1, arg2)(array, pattern) == array + 1); + int pattern2[] = {1,1}; + BOOST_TEST( + search(arg1, arg2, mod_2_comparison())(array, pattern2) == array + 2); + return; + } + + void lower_bound_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + const std::set test_set(array, array + 3); + BOOST_TEST(lower_bound(arg1, 2)(array) == array + 1); + BOOST_TEST(lower_bound(arg1, 2)(test_set) == test_set.lower_bound(2)); + + int array2[] = {3,2,1}; + const std::set > test_set2(array2, array2 + 3); + BOOST_TEST(boost::phoenix::lower_bound(arg1, 2, std::greater())(array2) == + array2 + 1); + BOOST_TEST(boost::phoenix::lower_bound(arg1, 2, std::greater())(test_set2) == + test_set2.lower_bound(2)); + return; + } + + void upper_bound_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + const std::set test_set(array, array + 3); + BOOST_TEST(upper_bound(arg1, 2)(array) == array + 2); + BOOST_TEST(upper_bound(arg1, 2)(test_set) == test_set.upper_bound(2)); + + int array2[] = {3,2,1}; + const std::set > test_set2(array2, array2 + 3); + BOOST_TEST(boost::phoenix::upper_bound(arg1, 2, std::greater())(array2) == + array2 + 2); + BOOST_TEST(boost::phoenix::upper_bound(arg1, 2, std::greater())(test_set2) == + test_set2.upper_bound(2)); + return; + } + + void equal_range_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,2,3}; + const std::set test_set(array, array + 4); + BOOST_TEST(equal_range(arg1, 2)(array).first == + array + 1); + BOOST_TEST(equal_range(arg1, 2)(array).second == + array + 3); + + BOOST_TEST(equal_range(arg1, 2)(test_set).first == + test_set.equal_range(2).first); + BOOST_TEST(equal_range(arg1, 2)(test_set).second == + test_set.equal_range(2).second); + + int array2[] = {3,2,2,1}; + const std::set > test_set2(array2, array2 + 4); + BOOST_TEST(boost::phoenix::equal_range(arg1, 2, std::greater())(array2).first == + array2 + 1); + BOOST_TEST(boost::phoenix::equal_range(arg1, 2, std::greater())(array2).second == + array2 + 3); + + BOOST_TEST(boost::phoenix::equal_range(arg1, 2, std::greater())(test_set2).first == + test_set2.equal_range(2).first); + BOOST_TEST(boost::phoenix::equal_range(arg1, 2, std::greater())(test_set2).second == + test_set2.equal_range(2).second); + + return; + } + + void binary_search_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + BOOST_TEST(binary_search(arg1, 2)(array)); + BOOST_TEST(!binary_search(arg1, 4)(array)); + return; + } + +} + +int main() +{ + find_test(); + find_if_test(); + find_end_test(); + find_first_of_test(); + adjacent_find_test(); + count_test(); + count_if_test(); + distance_test(); + mismatch_test(); + equal_test(); + search_test(); + lower_bound_test(); + upper_bound_test(); + equal_range_test(); + binary_search_test(); + return boost::report_errors(); +} diff --git a/phoenix/test/algorithm/querying2.cpp b/phoenix/test/algorithm/querying2.cpp new file mode 100644 index 000000000..a11bca955 --- /dev/null +++ b/phoenix/test/algorithm/querying2.cpp @@ -0,0 +1,83 @@ +/*============================================================================= + Copyright (c) 2005-2007 Dan Marsden + Copyright (c) 2005-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#include +#include +#include + +#include + +#include + +namespace +{ + void includes_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[] = {1,2}; + BOOST_TEST(includes(arg1, arg2)(array, array2)); + boost::iterator_range rng(array + 1, array + 3); + BOOST_TEST(!includes(arg1, arg2)(rng, array2)); + + int array3[] = {3,2,1}; + int array4[] = {2,1}; + BOOST_TEST(boost::phoenix::includes(arg1, arg2, std::greater())(array3, array4)); + boost::iterator_range rng2(array3, array3 + 2); + BOOST_TEST(!boost::phoenix::includes(arg1, arg2, std::greater())(rng2, array4)); + return; + } + + void min_element_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,3,2}; + BOOST_TEST(min_element(arg1)(array) == array); + BOOST_TEST(boost::phoenix::min_element(arg1, std::greater())(array) == array + 1); + return; + } + + void max_element_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,3,2}; + BOOST_TEST(max_element(arg1)(array) == array + 1); + BOOST_TEST(boost::phoenix::max_element(arg1, std::greater())(array) == array); + return; + } + + void lexicographical_compare_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[] = {1,2,4}; + + BOOST_TEST(lexicographical_compare(arg1, arg2)(array, array2)); + BOOST_TEST(!lexicographical_compare(arg1, arg2)(array2, array)); + BOOST_TEST(!lexicographical_compare(arg1, arg2)(array, array)); + + BOOST_TEST(!boost::phoenix::lexicographical_compare(arg1, arg2, std::greater())(array, array2)); + BOOST_TEST(boost::phoenix::lexicographical_compare(arg1, arg2, std::greater())(array2, array)); + BOOST_TEST(!boost::phoenix::lexicographical_compare(arg1, arg2, std::greater())(array, array)); + + return; + } +} + +int main() +{ + includes_test(); + min_element_test(); + max_element_test(); + lexicographical_compare_test(); + return boost::report_errors(); +} diff --git a/phoenix/test/algorithm/transformation1.cpp b/phoenix/test/algorithm/transformation1.cpp new file mode 100644 index 000000000..cca0858c2 --- /dev/null +++ b/phoenix/test/algorithm/transformation1.cpp @@ -0,0 +1,390 @@ +/*============================================================================= + Copyright (c) 2005-2007 Dan Marsden + Copyright (c) 2005-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#include +#include +#include + +#include +#include + +namespace +{ + struct even + { + bool operator()(const int i) const + { + return i % 2 == 0; + } + }; + + struct mod_2_comparison + { + bool operator()( + const int lhs, + const int rhs) + { + return lhs % 2 == rhs % 2; + }; + }; + + void swap_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int a = 123; + int b = 456; + swap(ref(a), ref(b))(); + BOOST_TEST(a == 456 && b == 123); + swap(ref(a), _1)(b); + BOOST_TEST(a == 123 && b == 456); + swap(_1, _2)(a, b); + BOOST_TEST(a == 456 && b == 123); + return; + } + + void copy_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int output[4]; + BOOST_TEST( + copy(arg1, arg2)(array, output) == output + 3); + BOOST_TEST(output[0] == 1); + BOOST_TEST(output[1] == 2); + BOOST_TEST(output[2] == 3); + return; + } + + void copy_backward_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int output[4]; + int* output_end = output + 3; + BOOST_TEST( + copy_backward(arg1, arg2)(array, output_end) == output); + BOOST_TEST(output[0] == 1); + BOOST_TEST(output[1] == 2); + BOOST_TEST(output[2] == 3); + return; + } + + struct increment + { + int operator()( + int i) const + { + return i+1; + } + }; + + void transform_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + BOOST_TEST( + transform(arg1, arg2, increment())(array, array) == + array + 3); + BOOST_TEST(array[0] == 2); + BOOST_TEST(array[1] == 3); + BOOST_TEST(array[2] == 4); + + int array2[] = {1,2,3}; + BOOST_TEST( + boost::phoenix::transform(arg1, arg2, arg3, std::plus())(array, array2, array) == + array +3); + BOOST_TEST(array[0] == 2 + 1); + BOOST_TEST(array[1] == 3 + 2); + BOOST_TEST(array[2] == 4 + 3); + return; + } + + void replace_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + replace(arg1,2,4)(array); + BOOST_TEST(array[0] == 1); + BOOST_TEST(array[1] == 4); + BOOST_TEST(array[2] == 3); + return; + } + + void replace_if_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + replace_if(arg1, even(), 4)(array); + BOOST_TEST(array[0] == 1); + BOOST_TEST(array[1] == 4); + BOOST_TEST(array[2] == 3); + return; + } + + void replace_copy_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int input[] = {1,2,3}; + int output[3]; + replace_copy(arg1, arg2, 2, 4)(input, output); + BOOST_TEST(output[0] == 1); + BOOST_TEST(output[1] == 4); + BOOST_TEST(output[2] == 3); + return; + } + + void replace_copy_if_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int input[] = {1,2,3}; + int output[3]; + replace_copy_if(arg1, arg2, even(), 4)(input, output); + BOOST_TEST(output[0] == 1); + BOOST_TEST(output[1] == 4); + BOOST_TEST(output[2] == 3); + return; + } + + void fill_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {0,0,0}; + fill(arg1, 1)(array); + BOOST_TEST(array[0] == 1); + BOOST_TEST(array[1] == 1); + BOOST_TEST(array[2] == 1); + return; + } + + void fill_n_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {0,0,0}; + fill_n(arg1, 2, 1)(array); + BOOST_TEST(array[0] == 1); + BOOST_TEST(array[1] == 1); + BOOST_TEST(array[2] == 0); + return; + } + + class int_seq + { + public: + int_seq() : val_(0) { } + + int operator()() + { + return val_++; + } + private: + int val_; + }; + + void generate_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[3]; + generate(arg1, int_seq())(array); + BOOST_TEST(array[0] == 0); + BOOST_TEST(array[1] == 1); + BOOST_TEST(array[2] == 2); + return; + } + + void generate_n_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {0,0,1}; + generate_n(arg1, 2, int_seq())(array); + BOOST_TEST(array[0] == 0); + BOOST_TEST(array[1] == 1); + BOOST_TEST(array[2] == 1); + return; + } + + + void remove_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + std::list test_list(array, array + 3); + BOOST_TEST(boost::phoenix::remove(arg1, 2)(array) == array + 2); + BOOST_TEST(array[0] == 1); + BOOST_TEST(array[1] == 3); + BOOST_TEST(boost::phoenix::remove(arg1, 2)(test_list) == test_list.end()); + std::list::const_iterator it(test_list.begin()); + BOOST_TEST(*it++ == 1); + BOOST_TEST(*it++ == 3); + return; + } + + void remove_if_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + std::list test_list(array, array + 3); + BOOST_TEST(boost::phoenix::remove_if(arg1, even())(array) == array + 2); + BOOST_TEST(array[0] == 1); + BOOST_TEST(array[1] == 3); + BOOST_TEST(boost::phoenix::remove_if(arg1, even())(test_list) == test_list.end()); + std::list::const_iterator it(test_list.begin()); + BOOST_TEST(*it++ == 1); + BOOST_TEST(*it++ == 3); + return; + } + + void remove_copy_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[2]; + BOOST_TEST(boost::phoenix::remove_copy(arg1, arg2, 2)(array, array2) == array2 + 2); + BOOST_TEST(array2[0] == 1); + BOOST_TEST(array2[1] == 3); + return; + } + + void remove_copy_if_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[2]; + BOOST_TEST(boost::phoenix::remove_copy_if(arg1, arg2, even())(array, array2) == array2 + 2); + BOOST_TEST(array2[0] == 1); + BOOST_TEST(array2[1] == 3); + return; + } + + void unique_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,2,3}; + std::list test_list(array, array + 4); + BOOST_TEST(unique(arg1)(array) == array + 3); + BOOST_TEST(array[0] == 1); + BOOST_TEST(array[1] == 2); + BOOST_TEST(array[2] == 3); + + BOOST_TEST(unique(arg1)(test_list) == test_list.end()); + std::list::const_iterator it(test_list.begin()); + BOOST_TEST(*it++ == 1); + BOOST_TEST(*it++ == 2); + BOOST_TEST(*it++ == 3); + + int array2[] = {1,3,2}; + std::list test_list2(array2, array2 + 3); + BOOST_TEST(unique(arg1, mod_2_comparison())(array2) == array2 + 2); + BOOST_TEST(array2[0] == 1); + BOOST_TEST(array2[1] == 2); + + BOOST_TEST(unique(arg1, mod_2_comparison())(test_list2) == test_list2.end()); + std::list::const_iterator jt(test_list2.begin()); + BOOST_TEST(*jt++ == 1); + BOOST_TEST(*jt++ == 2); + + return; + } + + void unique_copy_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,2,3}; + int out[3]; + BOOST_TEST(unique_copy(arg1, arg2)(array, out) == out + 3); + BOOST_TEST(out[0] == 1); + BOOST_TEST(out[1] == 2); + BOOST_TEST(out[2] == 3); + + int array2[] = {1,3,2}; + int out2[2]; + BOOST_TEST(unique_copy(arg1, arg2, mod_2_comparison())(array2, out2) == out2 + 2); + BOOST_TEST(out2[0] == 1); + BOOST_TEST(out2[1] == 2); + + return; + } + + void reverse_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + std::list test_list(array, array + 3); + reverse(arg1)(array); + BOOST_TEST(array[0] == 3); + BOOST_TEST(array[1] == 2); + BOOST_TEST(array[2] == 1); + + reverse(arg1)(test_list); + std::list::iterator it(test_list.begin()); + BOOST_TEST(*it++ == 3); + BOOST_TEST(*it++ == 2); + BOOST_TEST(*it++ == 1); + return; + } + + void reverse_copy_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[3]; + reverse_copy(arg1, arg2)(array, array2); + BOOST_TEST(array[0] == 1); + BOOST_TEST(array[1] == 2); + BOOST_TEST(array[2] == 3); + + BOOST_TEST(array2[0] == 3); + BOOST_TEST(array2[1] == 2); + BOOST_TEST(array2[2] == 1); + + return; + } +} + +int main() +{ + copy_test(); + copy_backward_test(); + transform_test(); + replace_test(); + replace_if_test(); + replace_copy_test(); + replace_copy_if_test(); + fill_test(); + fill_n_test(); + generate_test(); + generate_n_test(); + remove_test(); + remove_if_test(); + remove_copy_test(); + remove_copy_if_test(); + unique_test(); + unique_copy_test(); + reverse_test(); + reverse_copy_test(); + boost::report_errors(); +} diff --git a/phoenix/test/algorithm/transformation2.cpp b/phoenix/test/algorithm/transformation2.cpp new file mode 100644 index 000000000..a1a27c0aa --- /dev/null +++ b/phoenix/test/algorithm/transformation2.cpp @@ -0,0 +1,188 @@ +/*============================================================================= + Copyright (c) 2005-2007 Dan Marsden + Copyright (c) 2005-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#include +#include +#include + +#include + +namespace +{ + struct even + { + bool operator()(const int i) const + { + return i % 2 == 0; + } + }; + + void rotate_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + rotate(arg1, array + 1)(array); + std::cout << array[0] << array[1] << array[2] << std::endl; + BOOST_TEST(array[0] == 2); + BOOST_TEST(array[1] == 3); + BOOST_TEST(array[2] == 1); + + return; + } + + void rotate_copy_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[3]; + rotate_copy(arg1, array + 1, arg2)(array, array2); + BOOST_TEST(array2[0] == 2); + BOOST_TEST(array2[1] == 3); + BOOST_TEST(array2[2] == 1); + + return; + } + + void random_shuffle_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + random_shuffle(arg1)(array); + const int first = array[0]; + BOOST_TEST(first == 1 || first == 2 || first == 3); + const int second = array[1]; + BOOST_TEST(second == 1 || second == 2 || second == 3); + BOOST_TEST(first != second); + const int third = array[2]; + BOOST_TEST(third == 1 || third == 2 || third == 3); + BOOST_TEST(first != third && second != third); + return; + } + + void partition_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int* const end = partition(arg1, even())(array); + BOOST_TEST(end == array + 1); + BOOST_TEST(array[0] % 2 == 0); + BOOST_TEST(array[1] % 2 != 0); + BOOST_TEST(array[2] % 2 != 0); + return; + } + + void stable_partition_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int* const end = stable_partition(arg1, even())(array); + BOOST_TEST(end == array + 1); + BOOST_TEST(array[0] == 2); + BOOST_TEST(array[1] == 1); + BOOST_TEST(array[2] == 3); + return; + } + + void sort_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {3,1,2}; + std::list test_list(array, array + 3); + sort(arg1)(array); + BOOST_TEST(array[0] == 1); + BOOST_TEST(array[1] == 2); + BOOST_TEST(array[2] == 3); + + sort(arg1)(test_list); + std::list::const_iterator it(test_list.begin()); + BOOST_TEST(*it++ == 1); + BOOST_TEST(*it++ == 2); + BOOST_TEST(*it++ == 3); + + boost::phoenix::sort(arg1, std::greater())(array); + BOOST_TEST(array[0] == 3); + BOOST_TEST(array[1] == 2); + BOOST_TEST(array[2] == 1); + + boost::phoenix::sort(arg1, std::greater())(test_list); + std::list::const_iterator jt(test_list.begin()); + BOOST_TEST(*jt++ == 3); + BOOST_TEST(*jt++ == 2); + BOOST_TEST(*jt++ == 1); + + return; + } + + void stable_sort_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {3,1,2}; + stable_sort(arg1)(array); + BOOST_TEST(array[0] == 1); + BOOST_TEST(array[1] == 2); + BOOST_TEST(array[2] == 3); + + boost::phoenix::stable_sort(arg1, std::greater())(array); + BOOST_TEST(array[0] == 3); + BOOST_TEST(array[1] == 2); + BOOST_TEST(array[2] == 1); + + return; + } + + void partial_sort_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {2,4,1,3}; + partial_sort(arg1, array + 2)(array); + BOOST_TEST(array[0] == 1); + BOOST_TEST(array[1] == 2); + + boost::phoenix::partial_sort(arg1, array + 2, std::greater())(array); + BOOST_TEST(array[0] == 4); + BOOST_TEST(array[1] == 3); + return; + } + + void partial_sort_copy_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {2,4,1,3}; + int array2[2]; + partial_sort_copy(arg1, arg2)(array, array2); + BOOST_TEST(array2[0] == 1); + BOOST_TEST(array2[1] == 2); + + boost::phoenix::partial_sort(arg1, arg2, std::greater())(array, array2); + BOOST_TEST(array2[0] == 4); + BOOST_TEST(array2[1] == 3); + return; + } +} + +int main() +{ + rotate_test(); + rotate_copy_test(); + random_shuffle_test(); + partition_test(); + stable_partition_test(); + sort_test(); + stable_sort_test(); + partial_sort_test(); + return boost::report_errors(); +} diff --git a/phoenix/test/algorithm/transformation3.cpp b/phoenix/test/algorithm/transformation3.cpp new file mode 100644 index 000000000..9a0e22fb9 --- /dev/null +++ b/phoenix/test/algorithm/transformation3.cpp @@ -0,0 +1,176 @@ +/*============================================================================= + Copyright (c) 2005-2007 Dan Marsden + Copyright (c) 2005-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#include +#include +#include + +#include + +namespace +{ + void nth_element_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {5,1,4,3,2}; + nth_element(arg1, array + 2)(array); + BOOST_TEST(array[0] < 3); + BOOST_TEST(array[1] < 3); + BOOST_TEST(array[2] == 3); + BOOST_TEST(array[3] > 3); + BOOST_TEST(array[4] > 3); + + boost::phoenix::nth_element(arg1, array + 2, std::greater())(array); + BOOST_TEST(array[0] > 3); + BOOST_TEST(array[1] > 3); + BOOST_TEST(array[2] == 3); + BOOST_TEST(array[3] < 3); + BOOST_TEST(array[4] < 3); + + return; + } + + void merge_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[] = {2,3,4}; + int output[6]; + + BOOST_TEST(merge(arg1, arg2, arg3)(array, array2, output) == output + 6); + int expected_result[] = {1,2,2,3,3,4}; + BOOST_TEST(std::equal(output, output + 6, expected_result)); + + int array3[] = {5,4,3}; + int array4[] = {3,2,1}; + int output2[6]; + BOOST_TEST(boost::phoenix::merge(arg1, arg2, arg3, std::greater())(array3, array4, output2) == + output2 + 6); + int expected_result2[] = {5,4,3,3,2,1}; + BOOST_TEST(std::equal(output2, output2 + 6, expected_result2)); + return; + } + + void inplace_merge_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3,2,3,4}; + inplace_merge(arg1, array + 3)(array); + int expected_result[] = {1,2,2,3,3,4}; + BOOST_TEST(std::equal(array, array + 6, expected_result)); + + int array2[] = {5,4,3,4,3,2}; + boost::phoenix::inplace_merge(arg1, array2 + 3, std::greater())(array2); + int expected_result2[] = {5,4,4,3,3,2}; + BOOST_TEST(std::equal(array2, array2 + 6, expected_result2)); + return; + } + + void set_union_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[] = {2,3,4}; + int output[4]; + BOOST_TEST(set_union(arg1, arg2, arg3)(array, array2, output) == output + 4); + int expected_result[] = {1,2,3,4}; + BOOST_TEST(std::equal(output, output + 4, expected_result)); + + int array3[] = {3,2,1}; + int array4[] = {4,3,2}; + int output2[4]; + BOOST_TEST(boost::phoenix::set_union(arg1, arg2, arg3, std::greater()) + (array3, array4, output2) == + output2 + 4); + int expected_result2[] = {4,3,2,1}; + BOOST_TEST(std::equal(output2, output2 + 4, expected_result2)); + return; + } + + void set_intersection_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[] = {2,3,4}; + int output[2]; + BOOST_TEST(set_intersection(arg1, arg2, arg3)(array, array2, output) == output + 2); + int expected_result[] = {2,3}; + BOOST_TEST(std::equal(output, output + 2, expected_result)); + + int array3[] = {3,2,1}; + int array4[] = {4,3,2}; + int output2[2]; + BOOST_TEST(boost::phoenix::set_intersection(arg1, arg2, arg3, std::greater()) + (array3, array4, output2) == + output2 + 2); + int expected_result2[] = {3,2}; + BOOST_TEST(std::equal(output2, output2 + 2, expected_result2)); + return; + } + + void set_difference_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[] = {2,3,4}; + int output[1]; + BOOST_TEST(set_difference(arg1, arg2, arg3)(array, array2, output) == output + 1); + int expected_result[] = {1}; + BOOST_TEST(std::equal(output, output + 1, expected_result)); + + int array3[] = {3,2,1}; + int array4[] = {4,3,2}; + int output2[1]; + BOOST_TEST(boost::phoenix::set_difference(arg1, arg2, arg3, std::greater()) + (array3, array4, output2) == + output2 + 1); + int expected_result2[] = {1}; + BOOST_TEST(std::equal(output2, output2 + 1, expected_result2)); + return; + } + + void set_symmetric_difference_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int array2[] = {2,3,4}; + int output[2]; + BOOST_TEST(set_symmetric_difference(arg1, arg2, arg3)(array, array2, output) == output + 2); + int expected_result[] = {1,4}; + BOOST_TEST(std::equal(output, output + 2, expected_result)); + + int array3[] = {3,2,1}; + int array4[] = {4,3,2}; + int output2[2]; + BOOST_TEST(boost::phoenix::set_symmetric_difference(arg1, arg2, arg3, std::greater()) + (array3, array4, output2) == + output2 + 2); + int expected_result2[] = {4,1}; + BOOST_TEST(std::equal(output2, output2 + 2, expected_result2)); + return; + } +} + +int main() +{ + nth_element_test(); + merge_test(); + inplace_merge_test(); + set_union_test(); + set_intersection_test(); + set_difference_test(); + set_symmetric_difference_test(); + return boost::report_errors(); +} diff --git a/phoenix/test/algorithm/transformation4.cpp b/phoenix/test/algorithm/transformation4.cpp new file mode 100644 index 000000000..4dd157d9e --- /dev/null +++ b/phoenix/test/algorithm/transformation4.cpp @@ -0,0 +1,153 @@ +/*============================================================================= + Copyright (c) 2005-2007 Dan Marsden + Copyright (c) 2005-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#include +#include +#include + +#include +#include +#include + +namespace +{ + void heap_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + std::vector vec(array, array + 3); + boost::phoenix::make_heap(arg1)(vec); + vec.push_back(5); + boost::phoenix::push_heap(arg1)(vec); + vec.push_back(4); + boost::phoenix::push_heap(arg1)(vec); + boost::phoenix::pop_heap(arg1)(vec); + BOOST_TEST(vec.back() == 5); + vec.pop_back(); + boost::phoenix::sort_heap(arg1)(vec); + int expected_result[] = {1,2,3,4}; + BOOST_TEST(std::equal(vec.begin(), vec.end(), expected_result)); + + int array2[] = {3,2,1}; + std::vector vec2(array2, array2 + 3); + boost::phoenix::make_heap(arg1, std::greater())(vec2); + vec2.push_back(5); + boost::phoenix::push_heap(arg1, std::greater())(vec2); + vec2.push_back(4); + boost::phoenix::push_heap(arg1, std::greater())(vec2); + boost::phoenix::pop_heap(arg1, std::greater())(vec2); + BOOST_TEST(vec2.back() == 1); + vec2.pop_back(); + boost::phoenix::sort_heap(arg1, std::greater())(vec2); + int expected_result2[] = {5,4,3,2}; + BOOST_TEST(std::equal(vec2.begin(), vec2.end(), expected_result2)); + + return; + } + + void next_permutation_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2}; + int expected_result[] = {2,1}; + int expected_result2[] = {1,2}; + + BOOST_TEST(next_permutation(arg1)(array)); + BOOST_TEST(std::equal(array, array + 2, expected_result)); + BOOST_TEST(!next_permutation(arg1)(array)); + BOOST_TEST(std::equal(array, array + 2, expected_result2)); + + std::reverse(array, array + 2); + BOOST_TEST(boost::phoenix::next_permutation(arg1, std::greater())(array)); + BOOST_TEST(std::equal(array, array + 2, expected_result2)); + BOOST_TEST(!boost::phoenix::next_permutation(arg1, std::greater())(array)); + BOOST_TEST(std::equal(array, array + 2, expected_result)); + return; + } + + void prev_permutation_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {2,1}; + int expected_result[] = {1,2}; + int expected_result2[] = {2,1}; + + BOOST_TEST(prev_permutation(arg1)(array)); + BOOST_TEST(std::equal(array, array + 2, expected_result)); + BOOST_TEST(!prev_permutation(arg1)(array)); + BOOST_TEST(std::equal(array, array + 2, expected_result2)); + + std::reverse(array, array + 2); + BOOST_TEST(boost::phoenix::prev_permutation(arg1, std::greater())(array)); + BOOST_TEST(std::equal(array, array + 2, expected_result2)); + BOOST_TEST(!boost::phoenix::prev_permutation(arg1, std::greater())(array)); + BOOST_TEST(std::equal(array, array + 2, expected_result)); + return; + } + + void inner_product_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int lhs[] = {1,2,3}; + int rhs[] = {4,5,6}; + BOOST_TEST(inner_product(arg1, arg2, 0) + (lhs, rhs) == 1*4 + 2*5 + 3*6); + BOOST_TEST(boost::phoenix::inner_product(arg1, arg2, 1, std::multiplies(), std::minus()) + (lhs, rhs) == (1 - 4) * (2 - 5) * (3 - 6)); + return; + } + + void partial_sum_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int output[3]; + BOOST_TEST(partial_sum(arg1, arg2)(array, output) == output + 3); + int expected_result[] = {1, 3, 6}; + BOOST_TEST(std::equal(output, output + 3, expected_result)); + + BOOST_TEST(boost::phoenix::partial_sum(arg1, arg2, std::multiplies()) + (array, output) == output + 3); + int expected_result2[] = {1, 2, 6}; + BOOST_TEST(std::equal(output, output + 3, expected_result2)); + return; + } + + void adjacent_difference_test() + { + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + int array[] = {1,2,3}; + int output[3]; + BOOST_TEST(adjacent_difference(arg1, arg2)(array, output) == output + 3); + int expected_result[] = {1, 1, 1}; + BOOST_TEST(std::equal(output, output + 3, expected_result)); + BOOST_TEST(boost::phoenix::adjacent_difference(arg1, arg2, std::plus()) + (array, output) == output + 3); + int expected_result2[] = {1, 3, 5}; + BOOST_TEST(std::equal(output, output + 3, expected_result2)); + return; + } + +} + +int main() +{ + heap_test(); + next_permutation_test(); + prev_permutation_test(); + inner_product_test(); + partial_sum_test(); + adjacent_difference_test(); + return boost::report_errors(); +} diff --git a/phoenix/test/bind/bind_function_object_tests.cpp b/phoenix/test/bind/bind_function_object_tests.cpp new file mode 100644 index 000000000..6ae8a801f --- /dev/null +++ b/phoenix/test/bind/bind_function_object_tests.cpp @@ -0,0 +1,106 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + + struct test + { + typedef void result_type; + void operator()() const + { + cout << "Test lazy functions...\n"; + } + }; + + struct sqr + { + template + struct result + { + typedef Arg type; + }; + + template + Arg operator()(Arg n) const + { + return n * n; + } + }; + + struct fact + { + template + struct result + { + typedef Arg type; + }; + + template + Arg operator()(Arg n) const + { + return (n <= 0) ? 1 : n * (*this)(n-1); + } + }; + + struct power + { + template + struct result + { + typedef Arg1 type; + }; + + template + Arg1 operator()(Arg1 a, Arg2 b) const + { + return pow(a, b); + } + }; + + struct add + { + template + struct result + { + typedef Arg1 type; + }; + + template + Arg1 operator()(Arg1 a, Arg2 b, Arg3 c, Arg4 d) const + { + return a + b + c + d; + } + }; + +int +main() +{ + int i5 = 5; + double d5 = 5, d3 = 3; + + test()(); + BOOST_TEST(bind(sqr(), arg1)(i5) == (i5*i5)); + BOOST_TEST(bind(fact(), 4)() == 24); + BOOST_TEST(bind(fact(), arg1)(i5) == 120); + BOOST_TEST((int)bind(power(), arg1, arg2)(d5, d3) == (int)pow(d5, d3)); + BOOST_TEST((bind(sqr(), arg1) + 5)(i5) == ((i5*i5)+5)); + BOOST_TEST(bind(add(), arg1, arg1, arg1, arg1)(i5) == (5+5+5+5)); + + int const ic5 = 5; + // testing consts + BOOST_TEST(bind(sqr(), arg1)(ic5) == (ic5*ic5)); + + return boost::report_errors(); +} diff --git a/phoenix/test/bind/bind_function_tests.cpp b/phoenix/test/bind/bind_function_tests.cpp new file mode 100644 index 000000000..c55b51818 --- /dev/null +++ b/phoenix/test/bind/bind_function_tests.cpp @@ -0,0 +1,57 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +namespace test +{ + void + test() + { + cout << "Test binding functions...\n"; + } + + int + negate(int n) + { + return -n; + } + + int + plus(int a, int b) + { + return a + b; + } + + int + plus4(int a, int b, int c, int d) + { + return a + b + c + d; + } +} + +int +main() +{ + int a = 123; + int b = 256; + + bind(test::test)(); + BOOST_TEST(bind(test::negate, arg1)(a) == -a); + BOOST_TEST(bind(test::plus, arg1, arg2)(a, b) == a+b); + BOOST_TEST(bind(test::plus4, arg1, arg2, 3, 4)(a, b) == a+b+3+4); + + return boost::report_errors(); +} diff --git a/phoenix/test/bind/bind_member_function_tests.cpp b/phoenix/test/bind/bind_member_function_tests.cpp new file mode 100644 index 000000000..552a8305e --- /dev/null +++ b/phoenix/test/bind/bind_member_function_tests.cpp @@ -0,0 +1,76 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; +namespace phx = boost::phoenix; + +namespace test +{ + struct x : boost::noncopyable // test non-copyable (hold this by reference) + { + void + test() const + { + cout << "Test binding member functions...\n"; + } + }; + + struct y : boost::noncopyable // test non-copyable (hold this by reference) + { + int + negate(int n) + { + return -n; + } + }; + + struct z : boost::noncopyable // test non-copyable (hold this by reference) + { + int + plus(int a, int b) + { + return a + b; + } + }; + + struct zz : boost::noncopyable // test non-copyable (hold this by reference) + { + int + plus3(int a, int b, int c) + { + return a + b + c; + } + }; +} + +int +main() +{ + int a = 123; + int b = 256; + test::x x_; + test::y y_; + test::z z_; + test::zz zz_; + + bind(&test::x::test, x_)(); + BOOST_TEST(bind(&test::y::negate, y_, arg1)(a) == -a); + BOOST_TEST(bind(&test::z::plus, arg1, arg2, arg3)(z_, a, b) == a+b); + BOOST_TEST(bind(&test::zz::plus3, zz_, arg1, arg2, arg3)(a, b, a) == a+b+a); + BOOST_TEST(bind(&test::y::negate, &y_, 777)(a) == -777); + + return boost::report_errors(); +} diff --git a/phoenix/test/bind/bind_member_variable_tests.cpp b/phoenix/test/bind/bind_member_variable_tests.cpp new file mode 100644 index 000000000..29b11652b --- /dev/null +++ b/phoenix/test/bind/bind_member_variable_tests.cpp @@ -0,0 +1,37 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; +namespace phx = boost::phoenix; + +namespace test +{ + struct x : boost::noncopyable // test non-copyable (hold this by reference) + { + int m; + }; +} + +int +main() +{ + test::x x_; + bind(&test::x::m, x_)() = 123; + bind(&test::x::m, arg1)(x_) = 123; + BOOST_TEST(x_.m == 123); + + return boost::report_errors(); +} diff --git a/phoenix/test/container/container_tests.hpp b/phoenix/test/container/container_tests.hpp new file mode 100644 index 000000000..cf37f83ba --- /dev/null +++ b/phoenix/test/container/container_tests.hpp @@ -0,0 +1,814 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#if !defined(CONTAINER_TESTS_HPP) +#define CONTAINER_TESTS_HPP + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef BOOST_MSVC +#pragma warning(disable : 4800) +#endif + +using std::cerr; +namespace phx = boost::phoenix; + +std::deque const build_deque(); +std::list const build_list(); +std::map const build_map(); +std::multimap const build_multimap(); +std::vector const build_vector(); + +inline bool +test(bool fail) +{ + BOOST_TEST(!fail); + return fail; +} + +template +void test_assign(Container c) +{ + using phx::arg_names::arg1; + using phx::assign; + + typename Container::size_type count = 2; + typename Container::const_iterator first = c.begin(); + typename Container::const_iterator second = first; + typename Container::value_type value = *first; + + assign(arg1, count, value)(c); + + // iterators may be invalidated! + first = c.begin(); + second = first; + + std::advance(second, 1); + if (test(*first != *second)) { + cerr << "Failed " << typeid(Container).name() << " test_assign 1\n"; + return; + } +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + // Should not --- does not, Yay! --- compile. + Container const const_c = c; + assign(const_c, count, value); +#endif +} + +template +void test_assign2(Container c) +{ + using phx::arg_names::arg1; + using phx::arg_names::arg2; + using phx::arg_names::arg3; + using phx::assign; + + Container c2 = c; + typename Container::const_iterator first = c2.begin(); + typename Container::const_iterator last = c2.end(); + typename Container::size_type size = c2.size(); + + c.clear(); + assign(arg1, arg2, arg3)(c, first, last); + if (test(c.size() != size)) { + cerr << "Failed " << typeid(Container).name() + << " test_assign2 1\n" + << "size == " << c.size() << '\n'; + return; + } + +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + // Should not --- does not, Yay! --- compile. + Container const const_c = c; + assign(const_c, first, second); +#endif +} + +template +void test_at(Container c) +{ + using phx::arg_names::arg1; + using phx::at; + + typename Container::reference r1 = at(arg1, 0)(c); + if (test(r1 != c.at(0))) { + cerr << "Failed " << typeid(Container).name() << " test_at 1\n"; + return; + } + + typename Container::const_reference r2 = at(arg1, 0)(c); + if (test(r2 != c.at(0))) { + cerr << "Failed " << typeid(Container).name() << " test_at 2\n"; + return; + } + + Container const const_c = c; +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + // Should not --- does not, Yay! --- compile. + typename Container::reference r3 = at(arg1, 0)(const_c); +#endif + + typename Container::const_reference r4 = at(arg1, 0)(const_c); + if (test(r4 != c.at(0))) { + cerr << "Failed " << typeid(Container).name() << " test_at 4\n"; + return; + } +} + +template +void test_back(Container c) +{ + using phx::arg_names::arg1; + using phx::back; + + typename Container::reference r1 = back(arg1)(c); + if (test(r1 != c.back())) { + cerr << "Failed " << typeid(Container).name() << " test_back 1\n"; + return; + } + typename Container::const_reference r2 = back(arg1)(c); + if (test(r2 != c.back())) { + cerr << "Failed " << typeid(Container).name() << " test_back 2\n"; + return; + } + + Container const const_c = c; +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + // Should not --- does not, Yay! --- compile. + typename Container::reference r3 = back(arg1)(const_c); +#endif + + typename Container::const_reference r4 = back(arg1)(const_c); + if (test(r4 != c.back())) { + cerr << "Failed " << typeid(Container).name() << " test_back 4\n"; + return; + } +} + +template +void test_begin(Container c) +{ + using phx::arg_names::arg1; + using phx::begin; + + typename Container::iterator it1 = begin(arg1)(c); + if (test(it1 != c.begin())) { + cerr << "Failed " << typeid(Container).name() << " test_begin 1\n"; + return; + } + typename Container::const_iterator it2 = begin(arg1)(c); + if (test(it2 != c.begin())) { + cerr << "Failed " << typeid(Container).name() << " test_begin 2\n"; + return; + } + + Container const const_c = c; +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + // Should not --- does not, Yay! --- compile. + typename Container::iterator it3 = begin(arg1)(const_c); +#endif + + typename Container::const_iterator it4 = begin(arg1)(const_c); + if (test(it4 != const_c.begin())) { + cerr << "Failed " << typeid(Container).name() << " test_begin 4\n"; + return; + } +} + +template +void test_capacity(Container c) +{ + using phx::arg_names::arg1; + using phx::capacity; + + typename Container::size_type s1 = capacity(arg1)(c); + if (test(s1 != c.capacity())) { + cerr << "Failed " << typeid(Container).name() << " test_capacity 1\n"; + return; + } + + Container const const_c = c; + typename Container::size_type s2 = capacity(arg1)(const_c); + if (test(s2 != const_c.capacity())) { + cerr << "Failed " << typeid(Container).name() << " test_capacity 2\n"; + return; + } +} + +template +void test_clear(Container c) +{ + using phx::arg_names::arg1; + using phx::clear; + + clear(arg1)(c); + if (test(!c.empty())) { + cerr << "Failed " << typeid(Container).name() << " test_clear 1\n"; + return; + } + +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + Container const const_c = c; + clear(arg1)(const_c); +#endif +} + +template +void test_empty(Container c) +{ + using phx::arg_names::arg1; + using phx::empty; + + typename Container::size_type s1 = empty(arg1)(c); + if (test(bool(s1) != c.empty())) { + cerr << "Failed " << typeid(Container).name() << " test_empty 1\n"; + return; + } + + Container const const_c = c; + typename Container::size_type s2 = empty(arg1)(const_c); + if (test(bool(s2) != const_c.empty())) { + cerr << "Failed " << typeid(Container).name() << " test_empty 2\n"; + return; + } +} + +template +void test_end(Container c) +{ + using phx::arg_names::arg1; + using phx::end; + + typename Container::iterator it1 = end(arg1)(c); + if (test(it1 != c.end())) { + cerr << "Failed " << typeid(Container).name() << " test_end 1\n"; + return; + } + typename Container::const_iterator it2 = end(arg1)(c); + if (test(it2 != c.end())) { + cerr << "Failed " << typeid(Container).name() << " test_end 2\n"; + return; + } + + Container const const_c = c; +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + // Should not --- does not, Yay! --- compile. + typename Container::iterator it3 = end(arg1)(const_c); +#endif + + typename Container::const_iterator it4 = end(arg1)(const_c); + if (test(it4 != const_c.end())) { + cerr << "Failed " << typeid(Container).name() << " test_end 4\n"; + return; + } +} + +template +void test_erase(Container c) +{ + using phx::arg_names::arg1; + using phx::arg_names::arg2; + using phx::arg_names::arg3; + using phx::erase; + + Container const const_c = c; + + typename Container::size_type size = c.size(); + typename Container::iterator c_begin = c.begin(); + erase(arg1, arg2)(c, c_begin); + if (test(c.size() + 1 != size)) { + cerr << "Failed " << typeid(Container).name() << " test_erase 1\n"; + return; + } + + c_begin = c.begin(); + typename Container::iterator c_end = c.end(); + erase(arg1, arg2, arg3)(c, c_begin, c_end); + if (test(!c.empty())) { + cerr << "Failed " << typeid(Container).name() << " test_erase 2\n"; + return; + } + +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + erase(arg1, const_c.begin())(const_c); + erase(arg1, const_c.begin(), const_c.end())(const_c); +#endif +} + +template +void test_map_erase(Container c) +{ + test_erase(c); + if (boost::report_errors() != 0) + return; + + using phx::arg_names::arg1; + using phx::arg_names::arg2; + using phx::erase; + + typename Container::value_type const value = *c.begin(); + typename Container::key_type const key = value.first; + typename Container::size_type const removed = + erase(arg1, arg2)(c, key); + if (test(removed != 1)) { + cerr << "Failed " << typeid(Container).name() << " test_map_erase 1\n"; + return; + } +} + +template +void test_front(Container c) +{ + using phx::arg_names::arg1; + using phx::front; + + typename Container::reference r1 = front(arg1)(c); + if (test(r1 != c.front())) { + cerr << "Failed " << typeid(Container).name() << " test_front 1\n"; + return; + } + typename Container::const_reference r2 = front(arg1)(c); + if (test(r2 != c.front())) { + cerr << "Failed " << typeid(Container).name() << " test_front 2\n"; + return; + } + + Container const const_c = c; +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + // Should not --- does not, Yay! --- compile. + typename Container::reference r3 = front(arg1)(const_c); +#endif + + typename Container::const_reference r4 = front(arg1)(const_c); + if (test(r4 != c.front())) { + cerr << "Failed " << typeid(Container).name() << " test_front 4\n"; + return; + } +} + +template +void test_get_allocator(Container c) +{ + using phx::arg_names::arg1; + using phx::get_allocator; + + Container const const_c = c; + + typename Container::allocator_type a1 = get_allocator(arg1)(c); + if (test(a1 != c.get_allocator())) { + cerr << "Failed " << typeid(Container).name() << " test_get_allocator 1\n"; + return; + } + + typename Container::allocator_type a2 = get_allocator(arg1)(const_c); + if (test(a2 != const_c.get_allocator())) { + cerr << "Failed " << typeid(Container).name() << " test_get_allocator 2\n"; + return; + } +} + +template +void test_insert(Container c) +{ + using phx::arg_names::arg1; + using phx::insert; + + typename Container::value_type const value = *c.begin(); + typename Container::iterator it = insert(arg1, c.begin(), value)(c); + if (test(it != c.begin() || *it != *(++it))) { + cerr << "Failed " << typeid(Container).name() << " test_insert 1\n"; + return; + } + + typename Container::size_type size = c.size(); + insert(arg1, c.begin(), 3, value)(c); + if (test(c.size() != size + 3)) { + cerr << "Failed " << typeid(Container).name() << " test_insert 2\n"; + return; + } + + Container const const_c = c; + size = c.size(); + insert(arg1, c.begin(), const_c.begin(), const_c.end())(c); + if (test(c.size() != 2 * size)) { + cerr << "Failed " << typeid(Container).name() << " test_insert 3\n"; + return; + } +} + +inline void test_map_insert(std::map c) +{ + using phx::arg_names::arg1; + using phx::arg_names::arg2; + using phx::arg_names::arg3; + + typedef std::map Map; + + Map::value_type const value = *c.begin(); + Map::iterator c_begin = c.begin(); + // wrapper for + // iterator insert(iterator where, const value_type& val); + Map::iterator it = + phx::insert(arg1, arg2, arg3)(c, c_begin, value); + + if (test(it != c.begin() /*|| *it != *(++it)*/)) { + cerr << "Failed " << typeid(Map).name() << " test_map_insert 1\n"; + return; + } + + // wrapper for + // pair insert(const value_type& val); + Map::value_type const value2(1400, 2200); + std::pair result = + phx::insert(arg1, arg2)(c, value2); + if (test(!result.second)) { + cerr << "Failed " << typeid(Map).name() << " test_map_insert 2\n"; + return; + } + + // wrapper for + // template + // void insert(InIt first, InIt last); + Map const const_c = build_map(); + Map::size_type size = c.size(); + phx::insert(arg1, const_c.begin(), const_c.end())(c); + if (test(c.size() != size + const_c.size())) { + cerr << "Failed " << typeid(Map).name() << " test_map_insert 3\n"; + return; + } +} + +inline void test_multimap_insert(std::multimap c) +{ + using phx::arg_names::arg1; + using phx::arg_names::arg2; + using phx::arg_names::arg3; + + typedef std::multimap Multimap; + + Multimap::value_type const value = *c.begin(); + Multimap::iterator c_begin = c.begin(); + // wrapper for + // iterator insert(iterator where, const value_type& val); + Multimap::iterator it = + phx::insert(arg1, arg2, arg3)(c, c_begin, value); + + if (test(it != c.begin() || *it != *(++it))) { + cerr << "Failed " << typeid(Multimap).name() + << " test_multimap_insert 1\n"; + return; + } + + // wrapper for + // iterator insert(const value_type& val); + Multimap::value_type const value2(1400, 2200); + it = phx::insert(arg1, arg2)(c, value2); + if (test(it == c.end())) { + cerr << "Failed " << typeid(Multimap).name() + << " test_multimap_insert 2\n"; + return; + } + + // wrapper for + // template + // void insert(InIt first, InIt last); + Multimap const const_c = build_multimap(); + Multimap::size_type size = c.size(); + phx::insert(arg1, const_c.begin(), const_c.end())(c); + if (test(c.size() != size + const_c.size())) { + cerr << "Failed " << typeid(Multimap).name() + << " test_multimap_insert 3\n"; + return; + } +} + +template +void test_key_comp(Container c) +{ + using phx::arg_names::arg1; + using phx::key_comp; + + typename Container::key_compare comp = key_comp(arg1)(c); + + Container const const_c = c; + comp = key_comp(arg1)(const_c); +} + +template +void test_max_size(Container c) +{ + using phx::arg_names::arg1; + using phx::max_size; + + Container const const_c = c; + + typename Container::size_type s1 = max_size(arg1)(c); + if (test(s1 != c.max_size())) { + cerr << "Failed " << typeid(Container).name() << " test_max_size 1\n"; + return; + } + + typename Container::size_type s2 = max_size(arg1)(const_c); + if (test(s2 != const_c.max_size())) { + cerr << "Failed " << typeid(Container).name() << " test_max_size 2\n"; + return; + } +} + +template +void test_pop_back(Container c) +{ + using phx::arg_names::arg1; + using phx::pop_back; + + Container const const_c = c; + + typename Container::size_type size = c.size(); + + pop_back(arg1)(c); + if (test(c.size() + 1 != size)) { + cerr << "Failed " << typeid(Container).name() << " test_pop_back 1\n"; + return; + } + +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + pop_back(arg1)(const_c); +#endif +} + +template +void test_pop_front(Container c) +{ + using phx::arg_names::arg1; + using phx::pop_front; + + Container const const_c = c; + + typename Container::size_type size = c.size(); + + pop_front(arg1)(c); + if (test(c.size() + 1 != size)) { + cerr << "Failed " << typeid(Container).name() << " test_pop_front 1\n"; + return; + } +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + pop_front(arg1)(const_c); +#endif +} + +template +void test_push_back(Container c) +{ + using phx::arg_names::arg1; + using phx::arg_names::arg2; + using phx::push_back; + + Container const const_c = c; + + typename Container::value_type data = *c.begin(); + typename Container::size_type size = c.size(); + push_back(arg1, arg2)(c, data); + if (test(c.size() != size + 1)) { + cerr << "Failed " << typeid(Container).name() << " test_push_back 1\n"; + return; + } +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + push_back(arg1, arg2)(const_c, data); +#endif +} + +template +void test_push_front(Container c) +{ + using phx::arg_names::arg1; + using phx::arg_names::arg2; + using phx::push_front; + + Container const const_c = c; + + typename Container::value_type data = *c.begin(); + typename Container::size_type size = c.size(); + push_front(arg1, arg2)(c, data); + if (test(c.size() != size + 1)) { + cerr << "Failed " << typeid(Container).name() << " test_push_front 1\n"; + return; + } +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + push_front(arg1, arg2)(const_c, data); +#endif +} + +template +void test_rbegin(Container c) +{ + using phx::arg_names::arg1; + using phx::rbegin; + + typename Container::reverse_iterator it1 = rbegin(arg1)(c); + typename Container::reverse_iterator it1_test = c.rbegin(); + if (test(it1 != it1_test)) { + cerr << "Failed " << typeid(Container).name() << " test_rbegin 1\n"; + return; + } + typename Container::const_reverse_iterator it2 = rbegin(arg1)(c); + typename Container::const_reverse_iterator it2_test = c.rbegin(); + if (test(it2 != it2_test)) { + cerr << "Failed " << typeid(Container).name() << " test_rbegin 2\n"; + return; + } + + Container const const_c = c; +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + // Should not --- does not, Yay! --- compile. + typename Container::reverse_iterator it3 = rbegin(arg1)(const_c); +#endif + + typename Container::const_reverse_iterator it4 = rbegin(arg1)(const_c); + it2_test = const_c.rbegin(); + if (test(it4 != it2_test)) { + cerr << "Failed " << typeid(Container).name() << " test_rbegin 4\n"; + return; + } +} + +template +void test_rend(Container c) +{ + using phx::arg_names::arg1; + using phx::rend; + + typename Container::reverse_iterator it1 = rend(arg1)(c); + typename Container::reverse_iterator it1_test = c.rend(); + if (test(it1 != it1_test)) { + cerr << "Failed " << typeid(Container).name() << " test_rend 1\n"; + return; + } + typename Container::const_reverse_iterator it2 = rend(arg1)(c); + typename Container::const_reverse_iterator it2_test = c.rend(); + if (test(it2 != it2_test)) { + cerr << "Failed " << typeid(Container).name() << " test_rend 2\n"; + return; + } + + Container const const_c = c; +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + // Should not --- does not, Yay! --- compile. + typename Container::reverse_iterator it3 = rend(arg1)(const_c); +#endif + + typename Container::const_reverse_iterator it4 = rend(arg1)(const_c); + it2_test = const_c.rend(); + if (test(it4 != it2_test)) { + cerr << "Failed " << typeid(Container).name() << " test_rend 4\n"; + return; + } +} + +template +void test_reserve(Container c) +{ + using phx::arg_names::arg1; + using phx::reserve; + + Container const const_c = c; + + typename Container::size_type count = 2 * c.size(); + reserve(arg1, count)(c); + if (test(c.capacity() < count)) { + cerr << "Failed " << typeid(Container).name() << " test_reserve 1\n"; + return; + } +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + reserve(arg1, count)(const_c)(const_c); +#endif +} + +template +void test_resize(Container c) +{ + using phx::arg_names::arg1; + using phx::resize; + + Container const const_c = c; + + typename Container::size_type new_size = 2 * c.size(); + resize(arg1, new_size)(c); + if (test(c.size() != new_size)) { + cerr << "Failed " << typeid(Container).name() << " test_resize 1\n"; + return; + } + + new_size = 2 * c.size(); + typename Container::value_type value = *c.begin(); + resize(arg1, new_size, value)(c); + if (test(c.size() != new_size)) { + cerr << "Failed " << typeid(Container).name() << " test_resize 2\n"; + return; + } +#if defined(BOOST_PHOENIX_COMPILE_FAIL_TEST) + new_size = 2 * const_c.size(); + resize(arg1, new_size)(const_c); + + new_size = 2 * const_c.size(); + resize(arg1, new_size, value)(const_c); +#endif +} + +template +void test_size(Container c) +{ + using phx::arg_names::arg1; + using phx::size; + + Container const const_c = c; + + typename Container::size_type s1 = size(arg1)(c); + if (test(s1 != c.size())) { + cerr << "Failed " << typeid(Container).name() << " test_size 1\n"; + return; + } + + typename Container::size_type s2 = size(arg1)(const_c); + if (test(s2 != const_c.size())) { + cerr << "Failed " << typeid(Container).name() << " test_size 2\n"; + return; + } +} + +template +void test_splice(Container c) +{ + using phx::arg_names::arg1; + using phx::arg_names::arg2; + using phx::arg_names::arg3; + using phx::arg_names::arg4; + using phx::arg_names::arg5; + using phx::splice; + + typename Container::iterator c_end; + typename Container::iterator c2_begin; + typename Container::iterator c2_end; + typename Container::size_type size = c.size(); + + Container const copy = c; + Container const copy2 = build_list(); + Container c2 = copy2; + + size = c.size(); + c_end = c.end(); + splice(arg1, arg2, arg3)(c, c_end, c2); + if (test(c.size() != 2 * size)) { + cerr << "Failed " << typeid(Container).name() << " test_splice 1\n"; + return; + } + + c = copy; + c_end = c.end(); + c2 = copy2; + c2_begin = c2.begin(); + size = c.size() + 1; + splice(arg1, arg2, arg3, arg4)(c, c_end, c2, c2_begin); + if (test(c.size() != size)) { + cerr << "Failed " << typeid(Container).name() << " test_splice 2\n"; + return; + } + + c = copy; + c_end = c.end(); + c2 = copy2; + c2_begin = c2.begin(); + c2_end = c2.end(); + size = c.size() + c2.size(); + splice(arg1, arg2, arg3, arg4, arg5)(c, c_end, c2, c2_begin, c2_end); + if (test(c.size() != size)) { + cerr << "Failed " << typeid(Container).name() << " test_splice 3\n"; + return; + } +} + +template +void test_value_comp(Container c) +{ + using phx::arg_names::arg1; + using phx::value_comp; + + typename Container::value_compare comp = value_comp(arg1)(c); + + Container const const_c = c; + comp = value_comp(arg1)(const_c); +} + +#endif diff --git a/phoenix/test/container/container_tests1a.cpp b/phoenix/test/container/container_tests1a.cpp new file mode 100644 index 000000000..9d6d3eb51 --- /dev/null +++ b/phoenix/test/container/container_tests1a.cpp @@ -0,0 +1,46 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" + +std::list const build_list() +{ + std::vector const data = build_vector(); + return std::list(data.begin(), data.end()); +} + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + std::list const data = build_list(); + test_assign(data); + test_assign2(data); + test_back(data); + test_begin(data); + test_clear(data); + return boost::report_errors(); +} + diff --git a/phoenix/test/container/container_tests1b.cpp b/phoenix/test/container/container_tests1b.cpp new file mode 100644 index 000000000..ed54395fa --- /dev/null +++ b/phoenix/test/container/container_tests1b.cpp @@ -0,0 +1,48 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" + +std::list const build_list() +{ + std::vector const data = build_vector(); + return std::list(data.begin(), data.end()); +} + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + std::list const data = build_list(); + test_empty(data); + test_end(data); + test_erase(data); + test_front(data); + test_get_allocator(data); + test_insert(data); + test_max_size(data); + return boost::report_errors(); +} + diff --git a/phoenix/test/container/container_tests2a.cpp b/phoenix/test/container/container_tests2a.cpp new file mode 100644 index 000000000..e8fc8a3e3 --- /dev/null +++ b/phoenix/test/container/container_tests2a.cpp @@ -0,0 +1,46 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" + +std::list const build_list() +{ + std::vector const data = build_vector(); + return std::list(data.begin(), data.end()); +} + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + std::list const data = build_list(); + test_pop_back(data); + test_pop_front(data); + test_push_back(data); + test_push_front(data); + return boost::report_errors(); +} + + diff --git a/phoenix/test/container/container_tests2b.cpp b/phoenix/test/container/container_tests2b.cpp new file mode 100644 index 000000000..c666dd07b --- /dev/null +++ b/phoenix/test/container/container_tests2b.cpp @@ -0,0 +1,47 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" + +std::list const build_list() +{ + std::vector const data = build_vector(); + return std::list(data.begin(), data.end()); +} + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + std::list const data = build_list(); + test_rbegin(data); + test_rend(data); + test_resize(data); + test_size(data); + test_splice(data); + return boost::report_errors(); +} + + diff --git a/phoenix/test/container/container_tests3a.cpp b/phoenix/test/container/container_tests3a.cpp new file mode 100644 index 000000000..aad9ad8bb --- /dev/null +++ b/phoenix/test/container/container_tests3a.cpp @@ -0,0 +1,60 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" +#include + +std::map const build_map() +{ + typedef std::map int_map; + typedef std::vector int_vector; + + int_map result; + int_vector const data = build_vector(); + int_vector::const_iterator it = data.begin(); + int_vector::const_iterator const end = data.end(); + for (; it != end; ++it) { + int const value = *it; + result[value] = 100 * value; + } + return result; +} + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + BOOST_STATIC_ASSERT((phx::stl::has_mapped_type >::value)); + + std::map const data = build_map(); + test_begin(data); + test_clear(data); + test_empty(data); + test_end(data); + test_map_erase(data); + test_get_allocator(data); + return boost::report_errors(); +} + diff --git a/phoenix/test/container/container_tests3b.cpp b/phoenix/test/container/container_tests3b.cpp new file mode 100644 index 000000000..4add4541a --- /dev/null +++ b/phoenix/test/container/container_tests3b.cpp @@ -0,0 +1,61 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" +#include + +std::map const build_map() +{ + typedef std::map int_map; + typedef std::vector int_vector; + + int_map result; + int_vector const data = build_vector(); + int_vector::const_iterator it = data.begin(); + int_vector::const_iterator const end = data.end(); + for (; it != end; ++it) { + int const value = *it; + result[value] = 100 * value; + } + return result; +} + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + BOOST_STATIC_ASSERT((phx::stl::has_mapped_type >::value)); + + std::map const data = build_map(); + test_map_insert(data); + test_key_comp(data); + test_max_size(data); + test_rbegin(data); + test_rend(data); + test_size(data); + test_value_comp(data); + return boost::report_errors(); +} + diff --git a/phoenix/test/container/container_tests4a.cpp b/phoenix/test/container/container_tests4a.cpp new file mode 100644 index 000000000..cd42ae8b7 --- /dev/null +++ b/phoenix/test/container/container_tests4a.cpp @@ -0,0 +1,47 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" +#include + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + std::vector const data = build_vector(); + test_assign(data); + test_assign2(data); + test_at(data); + test_back(data); + test_begin(data); + test_capacity(data); + test_clear(data); + test_end(data); + test_empty(data); + test_erase(data); + test_front(data); + return boost::report_errors(); +} + diff --git a/phoenix/test/container/container_tests4b.cpp b/phoenix/test/container/container_tests4b.cpp new file mode 100644 index 000000000..2967d4ba1 --- /dev/null +++ b/phoenix/test/container/container_tests4b.cpp @@ -0,0 +1,46 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" +#include + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + std::vector const data = build_vector(); + test_get_allocator(data); + test_insert(data); + test_max_size(data); + test_pop_back(data); + test_push_back(data); + test_rbegin(data); + test_rend(data); + test_reserve(data); + test_resize(data); + test_size(data); + return boost::report_errors(); +} + diff --git a/phoenix/test/container/container_tests5a.cpp b/phoenix/test/container/container_tests5a.cpp new file mode 100644 index 000000000..6c6f1d83c --- /dev/null +++ b/phoenix/test/container/container_tests5a.cpp @@ -0,0 +1,53 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" + +std::deque const build_deque() +{ + std::vector const data = build_vector(); + return std::deque(data.begin(), data.end()); +} + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + std::deque const data = build_deque(); + test_assign(data); + test_assign2(data); + test_at(data); + test_back(data); + test_begin(data); + test_clear(data); + test_front(data); + test_empty(data); + test_end(data); + test_erase(data); + test_get_allocator(data); + return boost::report_errors(); +} + + diff --git a/phoenix/test/container/container_tests5b.cpp b/phoenix/test/container/container_tests5b.cpp new file mode 100644 index 000000000..26f5a04ca --- /dev/null +++ b/phoenix/test/container/container_tests5b.cpp @@ -0,0 +1,52 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" + +std::deque const build_deque() +{ + std::vector const data = build_vector(); + return std::deque(data.begin(), data.end()); +} + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + std::deque const data = build_deque(); + test_insert(data); + test_max_size(data); + test_pop_back(data); + test_pop_front(data); + test_push_back(data); + test_push_front(data); + test_rbegin(data); + test_rend(data); + test_resize(data); + test_size(data); + return boost::report_errors(); +} + + diff --git a/phoenix/test/container/container_tests6a.cpp b/phoenix/test/container/container_tests6a.cpp new file mode 100644 index 000000000..14392769c --- /dev/null +++ b/phoenix/test/container/container_tests6a.cpp @@ -0,0 +1,69 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" +#include + +std::map const build_map() +{ + typedef std::map int_map; + typedef std::vector int_vector; + + int_map result; + int_vector const data = build_vector(); + int_vector::const_iterator it = data.begin(); + int_vector::const_iterator const end = data.end(); + for (; it != end; ++it) { + int const value = *it; + result[value] = 100 * value; + } + return result; +} + +std::multimap const build_multimap() +{ + typedef std::map int_map; + typedef std::multimap int_multimap; + int_map const data = build_map(); + return int_multimap(data.begin(), data.end()); +} + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + std::multimap const data = build_multimap(); + test_begin(data); + test_clear(data); + test_empty(data); + test_end(data); + test_map_erase(data); + test_get_allocator(data); + return boost::report_errors(); +} + + + + diff --git a/phoenix/test/container/container_tests6b.cpp b/phoenix/test/container/container_tests6b.cpp new file mode 100644 index 000000000..4c0b2127b --- /dev/null +++ b/phoenix/test/container/container_tests6b.cpp @@ -0,0 +1,70 @@ +/*============================================================================= + Copyright (c) 2004 Angus Leeming + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include "container_tests.hpp" +#include + +std::map const build_map() +{ + typedef std::map int_map; + typedef std::vector int_vector; + + int_map result; + int_vector const data = build_vector(); + int_vector::const_iterator it = data.begin(); + int_vector::const_iterator const end = data.end(); + for (; it != end; ++it) { + int const value = *it; + result[value] = 100 * value; + } + return result; +} + +std::multimap const build_multimap() +{ + typedef std::map int_map; + typedef std::multimap int_multimap; + int_map const data = build_map(); + return int_multimap(data.begin(), data.end()); +} + +std::vector const init_vector() +{ + typedef std::vector int_vector; + int const data[] = { -4, -3, -2, -1, 0 }; + int_vector::size_type const data_size = sizeof(data) / sizeof(data[0]); + return int_vector(data, data + data_size); +} + +std::vector const build_vector() +{ + typedef std::vector int_vector; + static int_vector data = init_vector(); + int_vector::size_type const size = data.size(); + int_vector::iterator it = data.begin(); + int_vector::iterator const end = data.end(); + for (; it != end; ++it) + *it += size; + return data; +} + +int +main() +{ + std::multimap const data = build_multimap(); + test_multimap_insert(data); + test_key_comp(data); + test_max_size(data); + test_rbegin(data); + test_rend(data); + test_size(data); + test_value_comp(data); + return boost::report_errors(); +} + + + + diff --git a/phoenix/test/core/compose_tests.cpp b/phoenix/test/core/compose_tests.cpp new file mode 100644 index 000000000..60968a4ae --- /dev/null +++ b/phoenix/test/core/compose_tests.cpp @@ -0,0 +1,84 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include + +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +struct X +{ + template < + typename Env + , typename A0 = void_ + , typename A1 = void_ + , typename A2 = void_ + > + struct result + { + typedef int type; + }; + + template + static RT + eval(Env const& env, A0& a0, A1& a1, A2& a2) + { + return a0.eval(env) + a1.eval(env) + a2.eval(env); + } +}; + +int +main() +{ + using boost::fusion::at_c; + { + // testing as_actor + BOOST_STATIC_ASSERT((boost::is_same< + as_actor > >::type, actor > >::value)); + BOOST_STATIC_ASSERT((boost::is_same< + as_actor::type, actor > >::value)); + } + + { + // testing compose + char const* s = "Hi"; + int x = 123; + + BOOST_TEST(at_c<0>(compose(1, arg1, val(1))) + .eval(basic_environment<>()) == 1); + BOOST_TEST(at_c<1>(compose(1, arg1, val(456))) + .eval(basic_environment(s)) == s); + BOOST_TEST(at_c<2>(compose(1, arg1, val(456))) + .eval(basic_environment<>()) == 456); + BOOST_TEST(compose(9876, arg1, val(456)) + .eval(basic_environment(x)) == 10455); + + // testing composite sizes + cout << "sizeof(arg1) is: " + << sizeof(arg1) << endl; + cout << "sizeof(compose(arg1)) is: " + << sizeof(compose(arg1)) << endl; + cout << "sizeof(compose(1, arg1, val(456))) is: " + << sizeof(compose(1, arg1, val(456))) << endl; + cout << "sizeof(compose()) is: " + << sizeof(compose()) << endl; + cout << "sizeof(compose('x')) is: " + << sizeof(compose('x')) << endl; + cout << "sizeof(compose('x', 3)) is: " + << sizeof(compose('x', 3)) << endl; + cout << "sizeof(compose('x', 'y', 3)) is: " + << sizeof(compose('x', 'y', 3)) << endl; + } + + return boost::report_errors(); +} diff --git a/phoenix/test/core/primitives_tests.cpp b/phoenix/test/core/primitives_tests.cpp new file mode 100644 index 000000000..5224edd6b --- /dev/null +++ b/phoenix/test/core/primitives_tests.cpp @@ -0,0 +1,69 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include + +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + char c1 = '1'; + int i1 = 1, i2 = 2, i = 4; + const char* s2 = "2"; + + /////////////////////////////////////////////////////////////////////////// + // + // Values, references and arguments + // + /////////////////////////////////////////////////////////////////////////// + + // argument + BOOST_TEST(arg1(c1) == c1); + BOOST_TEST(arg1(i1, i2) == i1); + BOOST_TEST(arg2(i1, s2) == s2); + BOOST_TEST(&(arg1(c1)) == &c1); // must be an lvalue + + // value + cout << val("Hello,")() << val(' ')() << val("World")() << endl; + BOOST_TEST(val(3)() == 3); + BOOST_TEST(val("Hello, world")() == std::string("Hello, world")); + BOOST_TEST(val(_1)(i1) == i1); + + // should not compile: +#ifdef PHOENIX_SHOULD_NOT_COMPILE_TEST + &val(_1)(i1); // should return an rvalue +#endif + + // reference + BOOST_TEST(cref(i)() == ref(i)()); + BOOST_TEST(cref(i)() == 4); + BOOST_TEST(i == 4); + BOOST_TEST(ref(++i)() == 5); + BOOST_TEST(i == 5); + + // should not compile: +#ifdef PHOENIX_SHOULD_NOT_COMPILE_TEST + ref(arg1); +#endif + + // testing consts + int const ic = 123; + BOOST_TEST(arg1(ic) == 123); + + // should not compile: +#ifdef PHOENIX_SHOULD_NOT_COMPILE_TEST + arg1(); +#endif + + return boost::report_errors(); +} diff --git a/phoenix/test/detail/type_deduction_tests.cpp b/phoenix/test/detail/type_deduction_tests.cpp new file mode 100644 index 000000000..9d4dcb678 --- /dev/null +++ b/phoenix/test/detail/type_deduction_tests.cpp @@ -0,0 +1,374 @@ +/*============================================================================= + Copyright (c) 2001-2003 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost +{ + BOOST_UNARY_RESULT_OF(-x, result_of_negate); + BOOST_UNARY_RESULT_OF(+x, result_of_posit); + BOOST_UNARY_RESULT_OF(!x, result_of_logical_not); + BOOST_UNARY_RESULT_OF(~x, result_of_invert); + BOOST_UNARY_RESULT_OF(&x, result_of_reference); + BOOST_UNARY_RESULT_OF(*x, result_of_dereference); + + BOOST_UNARY_RESULT_OF(++x, result_of_pre_increment); + BOOST_UNARY_RESULT_OF(--x, result_of_pre_decrement); + BOOST_UNARY_RESULT_OF(x++, result_of_post_increment); + BOOST_UNARY_RESULT_OF(x--, result_of_post_decrement); + + BOOST_BINARY_RESULT_OF(x = y, result_of_assign); + BOOST_ASYMMETRIC_BINARY_RESULT_OF(x[y], result_of_index); + + BOOST_BINARY_RESULT_OF(x += y, result_of_plus_assign); + BOOST_BINARY_RESULT_OF(x -= y, result_of_minus_assign); + BOOST_BINARY_RESULT_OF(x *= y, result_of_multiplies_assign); + BOOST_BINARY_RESULT_OF(x /= y, result_of_divides_assign); + BOOST_BINARY_RESULT_OF(x %= y, result_of_modulus_assign); + + BOOST_BINARY_RESULT_OF(x &= y, result_of_and_assign); + BOOST_BINARY_RESULT_OF(x |= y, result_of_or_assign); + BOOST_BINARY_RESULT_OF(x ^= y, result_of_xor_assign); + BOOST_BINARY_RESULT_OF(x <<= y, result_of_shift_left_assign); + BOOST_BINARY_RESULT_OF(x >>= y, result_of_shift_right_assign); + + BOOST_BINARY_RESULT_OF(x + y, result_of_plus); + BOOST_BINARY_RESULT_OF(x - y, result_of_minus); + BOOST_BINARY_RESULT_OF(x * y, result_of_multiplies); + BOOST_BINARY_RESULT_OF(x / y, result_of_divides); + BOOST_BINARY_RESULT_OF(x % y, result_of_modulus); + + BOOST_BINARY_RESULT_OF(x & y, result_of_and); + BOOST_BINARY_RESULT_OF(x | y, result_of_or); + BOOST_BINARY_RESULT_OF(x ^ y, result_of_xor); + BOOST_BINARY_RESULT_OF(x << y, result_of_shift_left); + BOOST_BINARY_RESULT_OF(x >> y, result_of_shift_right); + + BOOST_BINARY_RESULT_OF(x == y, result_of_equal_to); + BOOST_BINARY_RESULT_OF(x != y, result_of_not_equal_to); + BOOST_BINARY_RESULT_OF(x < y, result_of_less); + BOOST_BINARY_RESULT_OF(x <= y, result_of_less_equal); + BOOST_BINARY_RESULT_OF(x > y, result_of_greater); + BOOST_BINARY_RESULT_OF(x >= y, result_of_greater_equal); + + BOOST_BINARY_RESULT_OF(x && y, result_of_logical_and); + BOOST_BINARY_RESULT_OF(x || y, result_of_logical_or); + BOOST_BINARY_RESULT_OF(true ? x : y, result_of_if_else); +} + +using namespace boost; +using namespace std; + +struct X {}; +X operator+(X, int); + +struct Y {}; +Y* operator+(Y, int); + +struct Z {}; +Z const* operator+(Z const&, int); +Z& operator+(Z&, int); +bool operator==(Z, Z); +bool operator==(Z, int); + +struct W {}; +Z operator+(W, int); +bool operator==(W, Z); + +int +main() +{ + // ASSIGN + { + typedef result_of_assign::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + { + typedef result_of_assign::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // PLUS + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus, double>::type result; + BOOST_STATIC_ASSERT((is_same >::value)); + } + { + typedef result_of_plus >::type result; + BOOST_STATIC_ASSERT((is_same >::value)); + } + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // INDEX + { + typedef result_of_index::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index, int>::type result; + BOOST_STATIC_ASSERT((is_same::reference>::value)); + } + { + typedef result_of_index const, int>::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index const, int>::type result; + BOOST_STATIC_ASSERT((is_same::const_reference>::value)); + } + { + typedef result_of_index, int>::type result; + BOOST_STATIC_ASSERT((is_same::reference>::value)); + } + { + typedef result_of_index::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index::iterator, int>::type result; + BOOST_STATIC_ASSERT((is_same::iterator::reference>::value)); + } + { + typedef result_of_index::const_iterator, int>::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_index::const_iterator, int>::type result; + BOOST_STATIC_ASSERT((is_same::const_iterator::reference>::value)); + } + { + typedef result_of_index, char>::type result; + BOOST_STATIC_ASSERT((is_same::mapped_type>::value)); + } + + // PLUS ASSIGN + { + typedef result_of_plus_assign::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus_assign::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_plus_assign, double>::type result; + BOOST_STATIC_ASSERT((is_same&>::value)); + } + + // SHIFT LEFT + { + typedef result_of_shift_left::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_shift_left::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_shift_left::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // EQUAL + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_equal_to::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // MINUS (pointers) + { + typedef result_of_minus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // DEREFERENCE + { + typedef result_of_dereference::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_dereference::iterator>::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_dereference >::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // ADDRESS OF + { + typedef result_of_reference::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_reference::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // PRE INCREMENT + { + typedef result_of_pre_increment::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // POST INCREMENT + { + typedef result_of_post_increment::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // IF-ELSE-EXPRESSION ( c ? a : b ) + { + typedef result_of_if_else::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_if_else::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_if_else::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_if_else::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + { + typedef result_of_if_else::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // DEDUCTION FAILURE + { + typedef result_of_plus::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // local_reference + { + using phoenix::detail::local_reference; + typedef result_of_assign, int>::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // local_reference + { + using phoenix::detail::local_reference; + typedef result_of_pre_increment >::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + // local_reference + { + using phoenix::detail::local_reference; + typedef result_of_if_else, local_reference >::type result; + BOOST_STATIC_ASSERT((is_same::value)); + } + + return boost::report_errors(); +} diff --git a/phoenix/test/function/function_tests.cpp b/phoenix/test/function/function_tests.cpp new file mode 100644 index 000000000..b567b7aba --- /dev/null +++ b/phoenix/test/function/function_tests.cpp @@ -0,0 +1,116 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + + struct test_impl + { + typedef void result_type; + void operator()() const + { + cout << "Test lazy functions...\n"; + } + }; + + function test; + + struct sqr_impl + { + template + struct result + { + typedef Arg type; + }; + + template + Arg operator()(Arg n) const + { + return n * n; + } + }; + + function sqr; + + struct fact_impl + { + template + struct result + { + typedef Arg type; + }; + + template + Arg operator()(Arg n) const + { + return (n <= 0) ? 1 : n * (*this)(n-1); + } + }; + + function fact; + + struct pow_impl + { + template + struct result + { + typedef Arg1 type; + }; + + template + Arg1 operator()(Arg1 a, Arg2 b) const + { + return pow(a, b); + } + }; + + function power; + + struct add_impl + { + template + struct result + { + typedef Arg1 type; + }; + + template + Arg1 operator()(Arg1 a, Arg2 b, Arg3 c, Arg4 d) const + { + return a + b + c + d; + } + }; + + function add; + +int +main() +{ + int i5 = 5; + double d5 = 5, d3 = 3; + + test()(); + BOOST_TEST(sqr(arg1)(i5) == (i5*i5)); + BOOST_TEST(fact(4)() == 24); + BOOST_TEST(fact(arg1)(i5) == 120); + BOOST_TEST((int)power(arg1, arg2)(d5, d3) == (int)pow(d5, d3)); + BOOST_TEST((sqr(arg1) + 5)(i5) == ((i5*i5)+5)); + BOOST_TEST(add(arg1, arg1, arg1, arg1)(i5) == (5+5+5+5)); + + int const ic5 = 5; + // testing consts + BOOST_TEST(sqr(arg1)(ic5) == (ic5*ic5)); + + return boost::report_errors(); +} diff --git a/phoenix/test/object/cast_tests.cpp b/phoenix/test/object/cast_tests.cpp new file mode 100644 index 000000000..338ed7e48 --- /dev/null +++ b/phoenix/test/object/cast_tests.cpp @@ -0,0 +1,63 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +struct T +{ + string foo() { return "T"; } +}; + +struct U : T +{ + string foo() { return "U"; } +}; + +struct VT +{ + virtual string foo() { return "T"; } +}; + +struct VU : VT +{ + virtual string foo() { return "U"; } +}; + +int +main() +{ + { + U u; + BOOST_TEST(arg1(u).foo() == "U"); + BOOST_TEST(static_cast_(arg1)(u).foo() == "T"); + } + + { + U const u = U(); + BOOST_TEST(const_cast_(arg1)(u).foo() == "U"); + } + + { + VU u; + VT* tp = &u; + BOOST_TEST(arg1(tp)->foo() == "U"); + BOOST_TEST(dynamic_cast_(arg1)(tp) != 0); + } + + { + void* p = 0; + reinterpret_cast_(arg1)(p); // compile test only + } + + return boost::report_errors(); +} diff --git a/phoenix/test/object/new_delete_tests.cpp b/phoenix/test/object/new_delete_tests.cpp new file mode 100644 index 000000000..d9018c7a9 --- /dev/null +++ b/phoenix/test/object/new_delete_tests.cpp @@ -0,0 +1,52 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int n = 0; + +struct X +{ + X(int, int, int) { cout << "new X(int, int, int)" << endl; ++n; } + X() { cout << "new X" << endl; ++n; } + ~X() { cout << "delete X" << endl; --n; } +}; + +int +main() +{ + { + vector v(10); + + for_each(v.begin(), v.end(), arg1 = new_()); + for_each(v.begin(), v.end(), delete_(arg1)); + + for_each(v.begin(), v.end(), arg1 = new_(1, 2, 3)); + for_each(v.begin(), v.end(), delete_(arg1)); + } + + { + using boost::shared_ptr; + vector > v(10); + for_each(v.begin(), v.end(), + arg1 = construct >(new_()) + ); + } + + BOOST_TEST(n == 0); + return boost::report_errors(); +} diff --git a/phoenix/test/operator/arithmetic_tests.cpp b/phoenix/test/operator/arithmetic_tests.cpp new file mode 100644 index 000000000..33f77d58c --- /dev/null +++ b/phoenix/test/operator/arithmetic_tests.cpp @@ -0,0 +1,54 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include + +using namespace boost::phoenix; +using namespace std; + +int +main() +{ + { + int x = 123; + + BOOST_TEST((ref(x) += 456)() == 123 + 456); + BOOST_TEST(x == 123 + 456); + BOOST_TEST((ref(x) -= 456)() == 123); + BOOST_TEST(x == 123); + BOOST_TEST((ref(x) *= 456)() == 123 * 456); + BOOST_TEST(x == 123 * 456); + BOOST_TEST((ref(x) /= 456)() == 123); + BOOST_TEST(x == 123); + + int& r1 = (ref(x) += 456)(); // should be an lvalue + int& r2 = (ref(x) -= 456)(); // should be an lvalue + int& r3 = (ref(x) *= 456)(); // should be an lvalue + int& r4 = (ref(x) /= 456)(); // should be an lvalue + BOOST_TEST(r1 == 123 && r2 == 123 && r3 == 123 && r4 == 123); + + BOOST_TEST((ref(x) %= 456)() == 123 % 456); + BOOST_TEST(x == 123 % 456); + } + + { + BOOST_TEST((val(123) + 456)() == 123 + 456); + BOOST_TEST((val(123) - 456)() == 123 - 456); + BOOST_TEST((val(123) * 456)() == 123 * 456); + BOOST_TEST((val(123) / 456)() == 123 / 456); + BOOST_TEST((val(123) % 456)() == 123 % 456); + + BOOST_TEST((123 + val(456))() == 123 + 456); + BOOST_TEST((123 - val(456))() == 123 - 456); + BOOST_TEST((123 * val(456))() == 123 * 456); + BOOST_TEST((123 / val(456))() == 123 / 456); + BOOST_TEST((123 % val(456))() == 123 % 456); + } + + return boost::report_errors(); +} diff --git a/phoenix/test/operator/bitwise_tests.cpp b/phoenix/test/operator/bitwise_tests.cpp new file mode 100644 index 000000000..f21b94a76 --- /dev/null +++ b/phoenix/test/operator/bitwise_tests.cpp @@ -0,0 +1,73 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + { + int x; + int y; + + x = 123; + y = 123; + (ref(x) &= 456)(); + y &= 456; + BOOST_TEST(x == y); + + x = 123; + y = 123; + (ref(x) |= 456)(); + y |= 456; + BOOST_TEST(x == y); + + x = 123; + y = 123; + (ref(x) ^= 456)(); + y ^= 456; + BOOST_TEST(x == y); + + x = 123; + y = 123; + (ref(x) <<= 4)(); + y <<= 4; + BOOST_TEST(x == y); + + x = 1230000; + y = 1230000; + (ref(x) >>= 4)(); + y >>= 4; + BOOST_TEST(x == y); + + int& r1 = (ref(x) &= 456)(); // should be an lvalue + int& r2 = (ref(x) |= 456)(); // should be an lvalue + int& r3 = (ref(x) ^= 456)(); // should be an lvalue + int& r4 = (ref(x) <<= 4)(); // should be an lvalue + int& r5 = (ref(x) >>= 4)(); // should be an lvalue + BOOST_TEST(&r1 == &r2 && &r2 == &r3 && &r3 == &r4 && &r4 == &r5); + } + + { + BOOST_TEST((val(123) & 456)() == (123 & 456)); + BOOST_TEST((val(123) | 456)() == (123 | 456)); + BOOST_TEST((val(123) ^ 456)() == (123 ^ 456)); + BOOST_TEST((val(123) << 4)() == (123 << 4)); + BOOST_TEST((val(1230000) >> 4)() == (1230000 >> 4)); + + char const* s = "Yabadabadoo!!!\n"; + (cout << arg1)(s); + } + + return boost::report_errors(); +} diff --git a/phoenix/test/operator/comparison_tests.cpp b/phoenix/test/operator/comparison_tests.cpp new file mode 100644 index 000000000..c1be505d1 --- /dev/null +++ b/phoenix/test/operator/comparison_tests.cpp @@ -0,0 +1,28 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include + +using namespace boost::phoenix; +using namespace std; + +int +main() +{ + { + BOOST_TEST(!(val(123) == 456)()); + BOOST_TEST((val(123) != 456)()); + BOOST_TEST(!(val(123) > 456)()); + BOOST_TEST((val(123) < 456)()); + BOOST_TEST(!(val(123) > 456)()); + BOOST_TEST((val(123) <= 123)()); + BOOST_TEST((val(123) >= 123)()); + } + + return boost::report_errors(); +} diff --git a/phoenix/test/operator/if_else_tests.cpp b/phoenix/test/operator/if_else_tests.cpp new file mode 100644 index 000000000..6ae4c150d --- /dev/null +++ b/phoenix/test/operator/if_else_tests.cpp @@ -0,0 +1,34 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + { + int x = 0; + int y = 0; + bool c = false; + + BOOST_TEST(if_else(arg1, 1234, 5678)(c) == 5678); + BOOST_TEST(if_else(arg1, 1234, 'x')(c) == 'x'); + + int& r = if_else(arg1, ref(x), ref(y))(c); // should be an lvalue + BOOST_TEST(&y == &r); + + (if_else(arg1, ref(x), ref(y)) = 986754321)(c); + BOOST_TEST(y == 986754321); + } + + return boost::report_errors(); +} diff --git a/phoenix/test/operator/io_tests.cpp b/phoenix/test/operator/io_tests.cpp new file mode 100644 index 000000000..e7125b031 --- /dev/null +++ b/phoenix/test/operator/io_tests.cpp @@ -0,0 +1,51 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + int i100 = 100; + string hello = "hello"; + const char* world = " world"; + + int init[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + vector v(init, init+10); + + char const* msg = "cout assert\n"; + (cout << arg1)(msg); + (cout << arg1 << endl)(hello); + (arg1 << hex)(cout); + (cout << val(hello))(); + + (cout << val(hello) << world << ", you da man!\n")(); + for_each(v.begin(), v.end(), cout << arg1 << ','); + + (cout << arg1 << "this is it, shukz:" << hex << arg2 << endl << endl)(msg, i100); + + int in; + int out = 12345; + stringstream sstr; + (sstr << arg1)(out); + (sstr >> arg1)(in); + BOOST_TEST(in == out); + + return boost::report_errors(); +} diff --git a/phoenix/test/operator/logical_tests.cpp b/phoenix/test/operator/logical_tests.cpp new file mode 100644 index 000000000..6a49e5887 --- /dev/null +++ b/phoenix/test/operator/logical_tests.cpp @@ -0,0 +1,36 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + { + bool x = false; + bool y = true; + + BOOST_TEST((!arg1)(x) == true); + BOOST_TEST((!arg1)(y) == false); + BOOST_TEST((arg1 || arg2)(x, y) == true); + BOOST_TEST((arg1 && arg2)(x, y) == false); + + // short circuiting: + int i = 1234; + (arg1 || (arg2 = 4567))(y, i); + BOOST_TEST(i == 1234); + (arg1 && (arg2 = 4567))(y, i); + BOOST_TEST(i == 4567); + } + + return boost::report_errors(); +} diff --git a/phoenix/test/operator/member.cpp b/phoenix/test/operator/member.cpp new file mode 100644 index 000000000..b13204459 --- /dev/null +++ b/phoenix/test/operator/member.cpp @@ -0,0 +1,83 @@ +/*============================================================================= + Copyright (c) 2005-2007 Dan Marsden + Copyright (c) 2005-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#include +#include +#include + +#include + +#include +#include + +namespace +{ + struct Test + { + int value; + + int func(int n) const { return n; } + int dunc() { return 10; } + }; +} + +int main() +{ + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + using namespace boost; + + Test test = {1}; + const Test* cptr = &test; + Test* ptr = &test; + + BOOST_TEST((val(ptr)->*&Test::value)() == 1); + BOOST_TEST((val(cptr)->*&Test::value)() == 1); + BOOST_TEST((arg1->*&Test::value)(cptr) == 1); + + ((val(ptr)->*&Test::value) = 2)(); + BOOST_TEST(test.value == 2); + + BOOST_TEST((val(ptr)->*&Test::func)(3)() == 3); + BOOST_TEST((val(cptr)->*&Test::func)(4)() == 4); + BOOST_TEST((val(ptr)->*&Test::dunc)()() == 10); + + BOOST_TEST((arg1->*&Test::func)(5)(ptr) == 5); + + shared_ptr sptr(new Test(test)); + + BOOST_TEST((arg1->*&Test::value)(sptr) == 2); + BOOST_TEST((arg1->*&Test::func)(6)(sptr) == 6); + + scoped_ptr scptr(new Test(test)); + + BOOST_TEST((arg1->*&Test::value)(scptr) == 2); + BOOST_TEST((arg1->*&Test::func)(7)(scptr) == 7); + + shared_ptr csptr(new Test(test)); + + BOOST_TEST((arg1->*&Test::value)(csptr) == 2); + BOOST_TEST((arg1->*&Test::func)(8)(csptr) == 8); + + scoped_ptr cscptr(new Test(test)); + + BOOST_TEST((arg1->*&Test::value)(cscptr) == 2); + BOOST_TEST((arg1->*&Test::func)(9)(cscptr) == 9); + + std::auto_ptr aptr(new Test(test)); + + BOOST_TEST((arg1->*&Test::value)(aptr) == 2); + BOOST_TEST((arg1->*&Test::func)(10)(aptr) == 10); + + std::auto_ptr captr(new Test(test)); + + BOOST_TEST((arg1->*&Test::value)(captr) == 2); + BOOST_TEST((arg1->*&Test::func)(11)(captr) == 11); + + return 0; +} diff --git a/phoenix/test/operator/misc_binary_tests.cpp b/phoenix/test/operator/misc_binary_tests.cpp new file mode 100644 index 000000000..b538225db --- /dev/null +++ b/phoenix/test/operator/misc_binary_tests.cpp @@ -0,0 +1,97 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + { // From Phoenix 1.1 binary tests + + int i2 = 2, i3 = 3, i = 5; + const char* world = " world"; + + BOOST_TEST((ref(i) = ref(i))() == 5); + BOOST_TEST((ref(i) = 3)() == 3); + BOOST_TEST(i == 3); + i = 5; + int x, y, z; + (ref(x) = ref(y) = ref(z) = 10)(); + BOOST_TEST(x == 10 && y == 10 && z == 10); + BOOST_TEST((val(world)[3])() == world[3]); + + BOOST_TEST((ref(i) += 5)() == 10); + BOOST_TEST((ref(i) -= 5)() == 5); + BOOST_TEST((ref(i) *= 5)() == 25); + BOOST_TEST((ref(i) /= 5)() == 5); + BOOST_TEST((ref(i) %= 2)() == 1); + + BOOST_TEST((ref(i) <<= 3)() == 8); + BOOST_TEST((ref(i) >>= 1)() == 4); + BOOST_TEST((ref(i) |= 0xFF)() == 0xFF); + BOOST_TEST((ref(i) &= 0xF0)() == 0xF0); + BOOST_TEST((ref(i) ^= 0xFFFFFFFF)() == int(0xFFFFFF0F)); + + BOOST_TEST((val(5) == val(5))()); + BOOST_TEST((val(5) == 5)()); + + BOOST_TEST((arg1 + arg2)(i2, i3) == i2 + i3); + BOOST_TEST((arg1 - arg2)(i2, i3) == i2 - i3); + BOOST_TEST((arg1 * arg2)(i2, i3) == i2 * i3); + BOOST_TEST((arg1 / arg2)(i2, i3) == i2 / i3); + BOOST_TEST((arg1 % arg2)(i2, i3) == i2 % i3); + BOOST_TEST((arg1 & arg2)(i2, i3) == (i2 & i3)); + BOOST_TEST((arg1 | arg2)(i2, i3) == (i2 | i3)); + BOOST_TEST((arg1 ^ arg2)(i2, i3) == (i2 ^ i3)); + BOOST_TEST((arg1 << arg2)(i2, i3) == i2 << i3); + BOOST_TEST((arg1 >> arg2)(i2, i3) == i2 >> i3); + + BOOST_TEST((val(5) != val(6))()); + BOOST_TEST((val(5) < val(6))()); + BOOST_TEST(!(val(5) > val(6))()); + BOOST_TEST((val(5) < val(6))()); + BOOST_TEST((val(5) <= val(6))()); + BOOST_TEST((val(5) <= val(5))()); + BOOST_TEST((val(7) >= val(6))()); + BOOST_TEST((val(7) >= val(7))()); + + BOOST_TEST((val(false) && val(false))() == false); + BOOST_TEST((val(true) && val(false))() == false); + BOOST_TEST((val(false) && val(true))() == false); + BOOST_TEST((val(true) && val(true))() == true); + + BOOST_TEST((val(false) || val(false))() == false); + BOOST_TEST((val(true) || val(false))() == true); + BOOST_TEST((val(false) || val(true))() == true); + BOOST_TEST((val(true) || val(true))() == true); + } + + { // From Phoenix 1.1 mixed_binary tests + + int i1 = 1, i2 = 2, i50 = 50, i100 = 100; + double d2_5 = 2.5; + string hello = "hello"; + const char* world = " world"; + + BOOST_TEST((arg1 + arg2)(i100, i50) == (i100 + i50)); + BOOST_TEST((arg1 + 3)(i100) == (3 + i100)); + BOOST_TEST((arg1 + arg2)(hello, world) == "hello world"); + BOOST_TEST((arg1 + arg2)(i1, d2_5) == (i1 + d2_5)); + + BOOST_TEST((*(arg1 + arg2))(world, i2) == *(world + i2)); + BOOST_TEST((*(arg1 + arg2))(i2, world) == *(i2 + world)); + BOOST_TEST((*(val(world+i2) - arg1))(i2) == *world); + } + + return boost::report_errors(); +} diff --git a/phoenix/test/operator/self_tests.cpp b/phoenix/test/operator/self_tests.cpp new file mode 100644 index 000000000..b723e0f71 --- /dev/null +++ b/phoenix/test/operator/self_tests.cpp @@ -0,0 +1,55 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + { + int x = 123; + BOOST_TEST((&arg1)(x) == &x); + BOOST_TEST((*&arg1)(x) == 123); + + int y = 968; + (ref(x) = arg1)(y); + BOOST_TEST(x == y); + + (arg1 = 456)(x); + BOOST_TEST(x == 456); + int& r = (arg1 = 456)(x); // must be an lvalue + BOOST_TEST(&r == &x); + + int c[] = { 1, 2, 3, 4, 5 }; + BOOST_TEST((arg1[3])(c) == 4); + + int& r2 = (arg1[3])(c); // must be an lvalue + BOOST_TEST(&r2 == &c[3]); + + vector v; + v.push_back("a"); + v.push_back("b"); + v.push_back("c"); + v.push_back("d"); + + BOOST_TEST((arg1[3])(v) == "d"); + + map m; + (arg1["Kimpo"] = arg2)(m, x); + BOOST_TEST(m["Kimpo"] == x); + } + + return boost::report_errors(); +} diff --git a/phoenix/test/operator/unary_tests.cpp b/phoenix/test/operator/unary_tests.cpp new file mode 100644 index 000000000..2a546ad5a --- /dev/null +++ b/phoenix/test/operator/unary_tests.cpp @@ -0,0 +1,64 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + { + BOOST_TEST((-val(123))() == -123); + BOOST_TEST((- -val(123))() == 123); + BOOST_TEST((+val(123))() == 123); + } + + { + int x = 123; + + BOOST_TEST((++ref(x))() == 124); + BOOST_TEST(x == 124); + BOOST_TEST((--ref(x))() == 123); + BOOST_TEST(x == 123); + + BOOST_TEST((ref(x)++)() == 123); + BOOST_TEST(x == 124); + BOOST_TEST((ref(x)--)() == 124); + BOOST_TEST(x == 123); + + int& r1 = (++ref(x))(); // should be an lvalue + int& r2 = (--ref(x))(); // should be an lvalue + BOOST_TEST(r1 == 123 && r2 == 123); + } + + { // From Phoenix 1.1 unary tests + + int i1 = 1, i = 5; + + BOOST_TEST((!val(true))() == false); + BOOST_TEST((-val(1))() == -1); + BOOST_TEST((+val(1))() == +1); + BOOST_TEST((~val(1))() == ~1); + BOOST_TEST(*(&arg1)(i1) == *(&i1)); + BOOST_TEST((&arg1)(i1) == &i1); + + BOOST_TEST((*val(&i1))() == *(&i1)); + BOOST_TEST((*&arg1)(i1) == *(&i1)); + BOOST_TEST((++ref(i))() == 6); + BOOST_TEST((--ref(i))() == 5); + BOOST_TEST((ref(i)++)() == 5); + BOOST_TEST(i == 6); + BOOST_TEST((ref(i)--)() == 6); + BOOST_TEST(i == 5); + } + + return boost::report_errors(); +} diff --git a/phoenix/test/scope/bug_000008.cpp b/phoenix/test/scope/bug_000008.cpp new file mode 100644 index 000000000..32334ce59 --- /dev/null +++ b/phoenix/test/scope/bug_000008.cpp @@ -0,0 +1,96 @@ +/*============================================================================= + Copyright (c) 2003 Martin Wille + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + + // see http://article.gmane.org/gmane.comp.parsers.spirit.general/4575 + // or https://sf.net/mailarchive/forum.php?thread_id=2692308&forum_id=1595 + // for a description of the bug being tested for by this program + // + // This code is borrowed from Spirit's bug_000008.cpp test for multithreads. +#include +#include +#include +#include + +#if defined(DONT_HAVE_BOOST) \ + || !defined(BOOST_HAS_THREADS) \ + || defined(BOOST_DISABLE_THREADS) \ + || (defined(__GNUC__) && defined(__WIN32__)) // MinGW +#define SKIP_TEST +#endif + + +#if defined(SKIP_TEST) +// we end here if we can't do multithreading +static void skipped() +{ + std::cout << "skipped\n"; +} + +int +main() +{ + skipped(); + return boost::report_errors(); +} + +#else +// the real MT stuff + +#include +#include +#include + +static const int number_of_calls_per_thread=20000; + +struct test_dynamic : boost::phoenix::dynamic +{ + test_dynamic() : b(*this) {} + member1 b; +}; + +void +in_thread(void) +{ + test_dynamic s; // should now be a local + + for (int i = 0; i < number_of_calls_per_thread; ++i) + { + boost::phoenix::dynamic_frame frame(s); + (s.b = 123)(); + { + boost::phoenix::dynamic_frame frame(s); + (s.b = 456)(); + BOOST_ASSERT((s.b == 456)()); + } + BOOST_ASSERT((s.b == 123)()); + } +} + +void +bug_000008() +{ + boost::thread t1(in_thread); + boost::thread t2(in_thread); + boost::thread t3(in_thread); + boost::thread t4(in_thread); + + t1.join(); + t2.join(); + t3.join(); + t4.join(); +} + +int +main() +{ + bug_000008(); + return boost::report_errors(); +} + +#endif + diff --git a/phoenix/test/scope/dynamic_tests.cpp b/phoenix/test/scope/dynamic_tests.cpp new file mode 100644 index 000000000..4dd9b9766 --- /dev/null +++ b/phoenix/test/scope/dynamic_tests.cpp @@ -0,0 +1,79 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include + +#define PHOENIX_LIMIT 6 + +#include +#include +#include +#include + +struct my_dynamic : ::boost::phoenix::dynamic +{ + my_dynamic() : num(*this), message(*this), real(*this) {} + + member1 num; + member2 message; + member3 real; +}; + +// You may also use the macro below to achieve the same as above: +// +// PHOENIX_DYNAMIC( +// my_dynamic, +// (int, num) +// (std::string, message) +// (double, real) +// ); + +int +main() +{ + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + + my_dynamic clos; + + { // First stack frame + dynamic_frame frame(clos); + (clos.num = 123)(); + (clos.num += 456)(); + (clos.real = clos.num / 56.5)(); + (clos.message = "Hello " + std::string("World "))(); + + { // Second stack frame + dynamic_frame frame(clos); + (clos.num = 987)(); + (clos.message = "Abracadabra ")(); + (clos.real = clos.num * 1e30)(); + + { // Third stack frame + boost::fusion::vector init(-1, "Direct Init ", 3.14); + dynamic_frame frame(clos, init); + + (std::cout << clos.message << clos.num << ", " << clos.real << '\n')(); + BOOST_TEST(clos.num() == -1); + BOOST_TEST(clos.real() == 3.14); + BOOST_TEST(clos.message() == "Direct Init "); + } + + (std::cout << clos.message << clos.num << ", " << clos.real << '\n')(); + BOOST_TEST(clos.num() == 987); + BOOST_TEST(clos.real() == clos.num() * 1e30); + BOOST_TEST(clos.message() == "Abracadabra "); + } + + (std::cout << clos.message << clos.num << ", " << clos.real << '\n')(); + BOOST_TEST(clos.num() == 123+456); + BOOST_TEST(clos.real() == clos.num() / 56.5); + BOOST_TEST(clos.message() == "Hello " + std::string("World ")); + } + + return 0; +} diff --git a/phoenix/test/scope/lambda_tests.cpp b/phoenix/test/scope/lambda_tests.cpp new file mode 100644 index 000000000..570cc7cd3 --- /dev/null +++ b/phoenix/test/scope/lambda_tests.cpp @@ -0,0 +1,179 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include + +#define PHOENIX_LIMIT 5 + +#include +#include +#include +#include +#include + +namespace boost { namespace phoenix +{ + struct for_each_impl + { + template + struct result + { + typedef void type; + }; + + template + void operator()(C& c, F f) const + { + std::for_each(c.begin(), c.end(), f); + } + }; + + function const for_each = for_each_impl(); + + struct push_back_impl + { + template + struct result + { + typedef void type; + }; + + template + void operator()(C& c, T& x) const + { + c.push_back(x); + } + }; + + function const push_back = push_back_impl(); +}} + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace boost::phoenix::local_names; +using namespace std; + +struct zzz {}; + +int +main() +{ + { + int x = 1; + int y = lambda[_1]()(x); + BOOST_TEST(x == y); + } + + { + int x = 1, y = 10; + BOOST_TEST( + (_1 + lambda[_1 + 2])(x)(y) == 1+10+2 + ); + BOOST_TEST( + (_1 + lambda[-_1])(x)(y) == 1+-10 + ); + } + + { + int x = 1, y = 10, z = 13; + BOOST_TEST( + lambda(_a = _1, _b = _2) + [ + _1 + _a + _b + ] + (x, z)(y) == x + y + z + ); + } + + { + int x = 4; + int y = 5; + lambda(_a = _1)[_a = 555](x)(); + BOOST_TEST(x == 555); + (void)y; + } + + { + int x = 1; + long x2 = 2; + short x3 = 3; + char const* y = "hello"; + zzz z; + + BOOST_TEST(lambda[_1](x)(y) == y); + BOOST_TEST(lambda(_a = _1)[_a](x)(y) == x); + BOOST_TEST(lambda(_a = _1)[lambda[_a]](x)(y)(z) == x); + BOOST_TEST(lambda(_a = _1)[lambda[_a + _1]](x)(y)(x) == 2); + BOOST_TEST(lambda(_a = _1)[lambda(_b = _1)[_a + _b + _1]](x)(x2)(x3) == 6); + } + + { + int x = 1, y = 10; + BOOST_TEST( + (_1 + lambda(_a = _1)[_a + _1 + 2])(x)(y) == 1+1+10+2 + ); + } + + { + int x = 1, y = 10; + BOOST_TEST( + (_1 + + lambda(_a = _1) + [ + _a + lambda[_a + 2] + ] + ) + (x)(y)(y) == 1+1+1+2 + ); + } + + { + using boost::phoenix::for_each; + + int init[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + std::vector v(init, init+10); + + int x = 0; + for_each(_1, lambda(_a = _2)[_a += _1])(v, x); + BOOST_TEST(x == 55); + } + + { + using boost::phoenix::for_each; + using boost::phoenix::push_back; + + int x = 10; + std::vector > v(10); + + for_each(_1, lambda(_a = _2)[push_back(_1, _a)])(v, x); + + int y = 0; + for_each(arg1, lambda[ref(y) += _1[0]])(v); + BOOST_TEST(y == 100); + } + + { + int x = 1, y = 10, z = 13; + BOOST_TEST( + lambda(_a = _1, _b = _2) + [ + _1 + _a + _b + ] + (x, z)(y) == x + y + z + ); + } + + { + int x = (let(_a = lambda[val(1)])[_a])()(); + BOOST_TEST(x == 1); + } + + return boost::report_errors(); +} + diff --git a/phoenix/test/scope/let_tests.cpp b/phoenix/test/scope/let_tests.cpp new file mode 100644 index 000000000..a4d3804e6 --- /dev/null +++ b/phoenix/test/scope/let_tests.cpp @@ -0,0 +1,146 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include + +#define PHOENIX_LIMIT 6 + +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace boost::phoenix::local_names; +using namespace std; + +int +main() +{ + { + int x = 1; + BOOST_TEST( + let(_a = _1) + [ + _a + ] + (x) == x + ); + } + + { + int x = 1, y = 10; + BOOST_TEST( + let(_a = _1, _b = _2) + [ + _a + _b + ] + (x, y) == x + y + ); + } + + { + int x = 1, y = 10, z = 13; + BOOST_TEST( + let(_x = _1, _y = _2) + [ + let(_z = _3) + [ + _x + _y + _z + ] + ] + (x, y, z) == x + y + z + ); + } + + { + int x = 1, y = 10; + BOOST_TEST( + let(_x = _1) + [ + _x + + let(_x = _2) + [ + -_x + ] + ] + (x, y) == x + -y + ); + } + + { + int x = 999; + BOOST_TEST( + let(_x = _1) // _x is a reference to _x + [ + _x += 888 + ] + (x) == 999 + 888 + ); + + BOOST_TEST(x == 888 + 999); + } + + { + int x = 999; + BOOST_TEST( + let(_x = val(_1)) // _x holds x by value + [ + val(_x += 888) + ] + (x) == x + 888 + ); + + BOOST_TEST(x == 999); + } + + { + BOOST_TEST( + let(_a = 1, _b = 2, _c = 3, _d = 4, _e = 5) + [ + _a + _b + _c + _d + _e + ] + () == 1 + 2 + 3 + 4 + 5 + ); + } + +#ifdef PHOENIX_SHOULD_NOT_COMPILE_TEST + { + // disallow this: + int i; + (_a + _b)(i); + } +#endif + + { + // show that we can return a local from an outer scope + int y = 0; + int x = (let(_a = 1)[let(_b = _1)[ _a ]])(y); + BOOST_TEST(x == 1); + } + + { + // show that this code returns an lvalue + int i = 1; + let(_a = arg1)[ _a ](i)++; + BOOST_TEST(i == 2); + } + + { + // show that what you put in is what you get out + int i = 1; + int& j = let(_a = arg1)[ _a ](i); + BOOST_TEST(&i == &j); + } + + return boost::report_errors(); +} + diff --git a/phoenix/test/statement/exceptions.cpp b/phoenix/test/statement/exceptions.cpp new file mode 100644 index 000000000..2c58c3bb0 --- /dev/null +++ b/phoenix/test/statement/exceptions.cpp @@ -0,0 +1,100 @@ +/*============================================================================= + Copyright (c) 2005-2007 Dan Marsden + Copyright (c) 2005-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ + +#include +#include + +#include +#include +#include + +#include + +int main() +{ + using namespace boost::phoenix; + using namespace boost::phoenix::arg_names; + using namespace std; + + { + try + { + throw_(runtime_error("error"))(); + BOOST_ERROR("exception should have been thrown"); + } + catch(runtime_error& err) + { + BOOST_TEST(err.what() == string("error")); + } + } + + { + try + { + try + { + throw runtime_error("error"); + } + catch(exception&) + { + throw_()(); + BOOST_ERROR("exception should have been rethrown"); + } + } + catch(exception& err) + { + BOOST_TEST(err.what() == string("error")); + } + } + + { + bool caught_exception = false; + + try_ + [ throw_(runtime_error("error")) ] + .catch_() + [ ref(caught_exception) = true ](); + + BOOST_TEST(caught_exception); + } + + { + bool caught_exception = false; + try_ + [ throw_(runtime_error("error")) ] + .catch_all + [ ref(caught_exception) = true ](); + BOOST_TEST(caught_exception); + } + + { + bool caught_correct_exception = false; + try_ + [ throw_(runtime_error("error")) ] + .catch_() + [ ref(caught_correct_exception) = false ] + .catch_() + [ ref(caught_correct_exception) = true](); + + BOOST_TEST(caught_correct_exception); + } + + { + bool caught_correct_exception = false; + try_ + [ throw_(runtime_error("error")) ] + .catch_() + [ ref(caught_correct_exception) = false ] + .catch_all + [ ref(caught_correct_exception) = true](); + + BOOST_TEST(caught_correct_exception); + } + + return boost::report_errors(); +} diff --git a/phoenix/test/statement/if_tests.cpp b/phoenix/test/statement/if_tests.cpp new file mode 100644 index 000000000..7e986bc74 --- /dev/null +++ b/phoenix/test/statement/if_tests.cpp @@ -0,0 +1,70 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + int init[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + vector v(init, init+10); + + cout << dec; + int x = 0; + + for_each(v.begin(), v.end(), + if_(arg1 > 3 && arg1 <= 8) + [ + cout << arg1 << ", ", + ref(x) += arg1 + ] + ); + + cout << endl; + BOOST_TEST(x == 4+5+6+7+8); + + x = 0; + int y = 0; + int z = 0; + + for_each(v.begin(), v.end(), + if_(arg1 > 5) + [ + cout << arg1 << " > 5\n", + ref(x) += arg1 + ] + .else_ + [ + if_(arg1 == 5) + [ + cout << arg1 << " == 5\n", + ref(z) += arg1 + ] + .else_ + [ + cout << arg1 << " < 5\n", + ref(y) += arg1 + ] + ] + ); + + cout << endl; + BOOST_TEST(x == 6+7+8+9+10); + BOOST_TEST(y == 1+2+3+4); + BOOST_TEST(z == 5); + + return boost::report_errors(); +} diff --git a/phoenix/test/statement/loops_tests.cpp b/phoenix/test/statement/loops_tests.cpp new file mode 100644 index 000000000..f3ad4b2e8 --- /dev/null +++ b/phoenix/test/statement/loops_tests.cpp @@ -0,0 +1,79 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + int init[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + vector v(init, init+10); + vector t = v; + cout << endl; + int x = 0; + + for_each(v.begin(), v.end(), + ( + while_(arg1--) + [ + cout << arg1 << ", ", + ++ref(x) + ], + cout << val("\n") + ) + ); + + BOOST_TEST(x == 1+2+3+4+5+6+7+8+9+10); + cout << endl; + v = t; + x = 0; + + for_each(v.begin(), v.end(), + ( + do_ + [ + cout << arg1 << ", ", + ++ref(x) + ] + .while_(arg1--), + cout << val("\n") + ) + ); + + BOOST_TEST(x == 2+3+4+5+6+7+8+9+10+11); + cout << endl; + v = t; + x = 0; + + int iii; + for_each(v.begin(), v.end(), + ( + for_(ref(iii) = 0, ref(iii) < arg1, ++ref(iii)) + [ + cout << arg1 << ", ", + ++ref(x) + ], + cout << val("\n") + ) + ); + + BOOST_TEST(x == 1+2+3+4+5+6+7+8+9+10); + cout << endl; + v = t; + x = 0; + + return boost::report_errors(); +} diff --git a/phoenix/test/statement/switch_tests.cpp b/phoenix/test/statement/switch_tests.cpp new file mode 100644 index 000000000..5116639bb --- /dev/null +++ b/phoenix/test/statement/switch_tests.cpp @@ -0,0 +1,69 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include +#include + +using namespace boost::phoenix; +using namespace boost::phoenix::arg_names; +using namespace std; + +int +main() +{ + int init[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + vector v(init, init+10); + + for_each(v.begin(), v.end(), + switch_(_1) + [ + // wierd case, why not just use if(...), but valid, nonetheless + case_<4>(cout << val("<4>") << endl) + ] + ); + + cout << endl; + + for_each(v.begin(), v.end(), + switch_(_1) + [ + // wierd case, but valid, nonetheless + default_(cout << val("") << endl) + ] + ); + + cout << endl; + + for_each(v.begin(), v.end(), + switch_(_1) + [ + case_<1>(cout << val("<1>") << endl), + case_<2>(cout << val("<2>") << endl), + case_<3>(cout << val("<3>") << endl), + case_<4>(cout << val("<4>") << endl) + ] + ); + + cout << endl; + + for_each(v.begin(), v.end(), + switch_(_1) + [ + case_<1>(cout << val("<1>") << endl), + case_<2>(cout << val("<2>") << endl), + case_<3>(cout << val("<3>") << endl), + case_<4>(cout << val("<4>") << endl), + default_(cout << val("") << endl) + ] + ); + + return boost::report_errors(); +} diff --git a/test/Jamfile b/test/Jamfile new file mode 100644 index 000000000..5caa9cbbc --- /dev/null +++ b/test/Jamfile @@ -0,0 +1,98 @@ +#============================================================================== +# Copyright (c) 2001-2007 Joel de Guzman +# Copyright (c) 2001-2008 Hartmut Kaiser +# +# Use, modification and distribution is subject to the Boost Software +# License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +#============================================================================== + +# bring in rules for testing +import testing ; + +{ + test-suite spirit_v2 : + + # run Qi tests + [ run qi/char.cpp : : : : ] + [ run qi/char_class.cpp : : : : ] + [ run qi/lit.cpp : : : : ] + [ run qi/int.cpp : : : : ] + [ run qi/uint.cpp : : : : ] + [ run qi/real.cpp : : : : ] + [ run qi/eps.cpp : : : : ] + [ run qi/none.cpp : : : : ] + [ run qi/lazy.cpp : : : : ] + [ run qi/tst.cpp : : : : ] + [ run qi/symbols.cpp : : : : ] + [ run qi/range_run.cpp : : : : ] + [ run qi/no_case.cpp : : : : ] + [ run qi/lexeme.cpp : : : : ] + [ run qi/raw.cpp : : : : ] + [ run qi/sequence.cpp : : : : ] + [ run qi/alternative.cpp : : : : ] + [ run qi/sequential_or.cpp : : : : ] + [ run qi/permutation.cpp : : : : ] + [ run qi/difference.cpp : : : : ] + [ run qi/list.cpp : : : : ] + [ run qi/optional.cpp : : : : ] + [ run qi/kleene.cpp : : : : ] + [ run qi/plus.cpp : : : : ] + [ run qi/and_predicate.cpp : : : : ] + [ run qi/not_predicate.cpp : : : : ] + [ run qi/expect.cpp : : : : ] + [ run qi/rule.cpp : : : : ] + [ run qi/grammar.cpp : : : : ] + [ run qi/functor.cpp : : : : ] + [ run qi/match_manip.cpp : : : : ] + [ run qi/binary.cpp : : : : ] + [ run qi/debug.cpp : : : : ] + + [ compile-fail qi/grammar_fail.cpp : : ] + [ compile-fail qi/rule_fail.cpp : : ] + + # run Karma tests + [ run karma/alternative.cpp : : : : karma_alternative ] + [ run karma/binary.cpp : : : : karma_binary ] + [ run karma/case_handling.cpp : : : : ] + [ run karma/center_alignment.cpp : : : : ] + [ run karma/char.cpp : : : : karma_char ] + [ run karma/delimiter.cpp : : : : ] + [ run karma/eps.cpp : : : : karma_eps ] + [ run karma/format_manip.cpp : : : : ] + [ run karma/functor.cpp : : : : karma_functor ] + [ run karma/grammar.cpp : : : : karma_grammar ] + [ run karma/int_numerics.cpp : : : : ] + [ run karma/kleene.cpp : : : : karma_kleene ] + [ run karma/lazy.cpp : : : : karma_lazy ] + [ run karma/left_alignment.cpp : : : : ] + [ run karma/list.cpp : : : : karma_list ] + [ run karma/lit.cpp : : : : karma_lit ] + [ run karma/none.cpp : : : : karma_none ] + [ run karma/optional.cpp : : : : karma_optional ] + [ run karma/pattern.cpp : : : : ] + [ run karma/real_numerics.cpp : : : : ] + [ run karma/right_alignment.cpp : : : : ] + [ run karma/sequence.cpp : : : : karma_sequence ] + + [ compile-fail karma/grammar_fail.cpp : : karma_grammar_fail ] + [ compile-fail karma/rule_fail.cpp : : karma_rule_fail ] + + # run support tests + [ run support/hold_any.cpp : : : : ] + [ run support/multi_pass_compile.cpp : : : : ] + [ run support/multi_pass.cpp : : : : ] + + # run lexer tests + [ run lex/lexertl1.cpp : : : : ] + [ run lex/lexertl2.cpp : : : : ] + [ run lex/lexertl3.cpp : : : : ] + [ run lex/lexertl4.cpp : : : : ] + [ run lex/lexertl5.cpp : : : : ] + [ run lex/state_switcher_test.cpp : : : : ] + + ; + +} + + diff --git a/test/karma/alternative.cpp b/test/karma/alternative.cpp new file mode 100644 index 000000000..39e179336 --- /dev/null +++ b/test/karma/alternative.cpp @@ -0,0 +1,88 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// #define KARMA_TEST_COMPILE_FAIL + +#include + +#include +#include +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost; + using namespace boost::spirit; + + { + { + BOOST_TEST(test("x", char_('x') | char_('i'))); + BOOST_TEST(test("xi", char_('x') << char_('i') | char_('i'))); + + variant v (10); + BOOST_TEST(test("10", char_ | int_, v)); + BOOST_TEST(test("a", char_('a') | char_ | int_, v)); + + v = 'c'; + BOOST_TEST(test("c", char_ | int_, v)); + BOOST_TEST(test("a", char_('a') | char_ | int_, v)); + } + + { + // test if alternatives with all components having unused + // parameter return attributes them self + fusion::vector v('a', 'b'); + BOOST_TEST(test("axb", char_ << (char_('x') | char_('i')) << char_, v)); + BOOST_TEST(test("axib", + char_ << (char_('x') << char_('i') | char_('i')) << char_, v)); + } + + { + BOOST_TEST(test_delimited("x ", char_('x') | char_('i'), char_(' '))); + BOOST_TEST(test_delimited("x i ", + char_('x') << char_('i') | char_('i'), char_(' '))); + + variant v (10); + BOOST_TEST(test_delimited("10 ", char_ | int_, v, char_(' '))); + + v = 'c'; + BOOST_TEST(test_delimited("c ", char_ | int_, v, char_(' '))); + } + + { + // if nothing matches, the first explicit alternative will be chosen + variant v (10.0); + BOOST_TEST(test("11", char_ | int_(11), v)); + BOOST_TEST(test("10.0", double_ | int_(11), v)); + BOOST_TEST(!test("", char_ | int_, v)); + + v = "c"; + BOOST_TEST(test("11", char_ | int_(11), v)); + BOOST_TEST(test("11", double_ | int_(11), v)); + BOOST_TEST(!test("", char_ | int_, v)); + } + + { + // if nothing matches, the first explicit alternative will be chosen + variant v (10.0); + BOOST_TEST(test_delimited("11 ", char_ | int_(11), v, char_(' '))); + + v = "c"; + BOOST_TEST(test_delimited("11 ", char_ | int_(11), v, char_(' '))); + } + } + + return boost::report_errors(); +} + diff --git a/test/karma/binary.cpp b/test/karma/binary.cpp new file mode 100644 index 000000000..2c650711e --- /dev/null +++ b/test/karma/binary.cpp @@ -0,0 +1,101 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + + { // test native endian binaries +#ifdef BOOST_LITTLE_ENDIAN + BOOST_TEST(binary_test("\x01", 1, byte, 0x01)); + BOOST_TEST(binary_test("\x01\x02", 2, word, 0x0201)); + BOOST_TEST(binary_test("\x01\x02\x03\x04", 4, dword, 0x04030201)); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(binary_test("\x01\x02\x03\x04\x05\x06\x07\x08", 8, qword, + 0x0807060504030201LL)); +#endif +#else + BOOST_TEST(binary_test("\x01", 1, byte, 0x01)); + BOOST_TEST(binary_test("\x01\x02", 2, word, 0x0102)); + BOOST_TEST(binary_test("\x01\x02\x03\x04", 4, dword, 0x01020304)); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(binary_test("\x01\x02\x03\x04\x05\x06\x07\x08", 8, qword, + 0x0102030405060708LL)); +#endif +#endif + } + + { // test native endian binaries +#ifdef BOOST_LITTLE_ENDIAN + BOOST_TEST(binary_test("\x01", 1, byte(0x01))); + BOOST_TEST(binary_test("\x01\x02", 2, word(0x0201))); + BOOST_TEST(binary_test("\x01\x02\x03\x04", 4, dword(0x04030201))); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(binary_test("\x01\x02\x03\x04\x05\x06\x07\x08", 8, + qword(0x0807060504030201LL))); +#endif +#else + BOOST_TEST(binary_test("\x01", 1, byte(0x01))); + BOOST_TEST(binary_test("\x01\x02", 2, word(0x0102))); + BOOST_TEST(binary_test("\x01\x02\x03\x04", 4, dword(0x01020304))); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(binary_test("\x01\x02\x03\x04\x05\x06\x07\x08", 8, + qword(0x0102030405060708LL))); +#endif +#endif + } + + { // test big endian binaries + BOOST_TEST(binary_test("\x01\x02", 2, big_word, 0x0102)); + BOOST_TEST(binary_test("\x01\x02\x03\x04", 4, big_dword, 0x01020304)); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(binary_test("\x01\x02\x03\x04\x05\x06\x07\x08", 8, big_qword, + 0x0102030405060708LL)); +#endif + } + + { + BOOST_TEST(binary_test("\x01\x02", 2, big_word(0x0102))); + BOOST_TEST(binary_test("\x01\x02\x03\x04", 4, big_dword(0x01020304))); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(binary_test("\x01\x02\x03\x04\x05\x06\x07\x08", 8, + big_qword(0x0102030405060708LL))); +#endif + } + + { // test little endian binaries + BOOST_TEST(binary_test("\x01\x02", 2, little_word, 0x0201)); + BOOST_TEST(binary_test("\x01\x02\x03\x04", 4, little_dword, 0x04030201)); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(binary_test("\x01\x02\x03\x04\x05\x06\x07\x08", 8, little_qword, + 0x0807060504030201LL)); +#endif + } + + { + BOOST_TEST(binary_test("\x01\x02", 2, little_word(0x0201))); + BOOST_TEST(binary_test("\x01\x02\x03\x04", 4, little_dword(0x04030201))); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(binary_test("\x01\x02\x03\x04\x05\x06\x07\x08", 8, + little_qword(0x0807060504030201LL))); +#endif + } + + return boost::report_errors(); +} diff --git a/test/karma/case_handling.cpp b/test/karma/case_handling.cpp new file mode 100644 index 000000000..6d7c53c21 --- /dev/null +++ b/test/karma/case_handling.cpp @@ -0,0 +1,176 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + + { + using namespace boost::spirit::ascii; + + BOOST_TEST(test("x", lower['X'])); + BOOST_TEST(test("x", lower['x'])); + BOOST_TEST(test(L"x", lower[L'X'])); + BOOST_TEST(test(L"x", lower[L'x'])); + + BOOST_TEST(test("x", lower[char_], 'X')); + BOOST_TEST(test("x", lower[char_], 'x')); + BOOST_TEST(test("x", lower[char_('X')])); + BOOST_TEST(test("x", lower[char_('x')])); + + BOOST_TEST(test(" ", lower[space])); + BOOST_TEST(test("\t", lower[space], '\t')); + BOOST_TEST(test("\t", lower[space('\t')])); + + BOOST_TEST(test("x", lower[lower['X']])); + BOOST_TEST(test("x", lower[lower['x']])); + BOOST_TEST(test(L"x", lower[lower[L'X']])); + BOOST_TEST(test(L"x", lower[lower[L'x']])); + + BOOST_TEST(test("x", lower[lower[char_]], 'X')); + BOOST_TEST(test("x", lower[lower[char_]], 'x')); + BOOST_TEST(test("x", lower[lower[char_('X')]])); + BOOST_TEST(test("x", lower[lower[char_('x')]])); + + BOOST_TEST(test(" ", lower[lower[space]])); + BOOST_TEST(test("\t", lower[lower[space]], '\t')); + BOOST_TEST(test("\t", lower[lower[space('\t')]])); + + BOOST_TEST(test("X", upper[lower['X']])); + BOOST_TEST(test("X", upper[lower['x']])); + BOOST_TEST(test(L"X", upper[lower[L'X']])); + BOOST_TEST(test(L"X", upper[lower[L'x']])); + + BOOST_TEST(test("X", upper[lower[char_]], 'X')); + BOOST_TEST(test("X", upper[lower[char_]], 'x')); + BOOST_TEST(test("X", upper[lower[char_('X')]])); + BOOST_TEST(test("X", upper[lower[char_('x')]])); + + BOOST_TEST(test(" ", upper[lower[space]])); + BOOST_TEST(test("\t", upper[lower[space]], '\t')); + BOOST_TEST(test("\t", upper[lower[space('\t')]])); + + BOOST_TEST(test("X", upper['X'])); + BOOST_TEST(test("X", upper['x'])); + BOOST_TEST(test(L"X", upper[L'X'])); + BOOST_TEST(test(L"X", upper[L'x'])); + + BOOST_TEST(test("X", upper[char_], 'X')); + BOOST_TEST(test("X", upper[char_], 'x')); + BOOST_TEST(test("X", upper[char_('X')])); + BOOST_TEST(test("X", upper[char_('x')])); + + BOOST_TEST(test(" ", upper[space])); + BOOST_TEST(test("\t", upper[space], '\t')); + BOOST_TEST(test("\t", upper[space('\t')])); + + BOOST_TEST(test("x", lower[upper['X']])); + BOOST_TEST(test("x", lower[upper['x']])); + BOOST_TEST(test(L"x", lower[upper[L'X']])); + BOOST_TEST(test(L"x", lower[upper[L'x']])); + + BOOST_TEST(test("x", lower[upper[char_]], 'X')); + BOOST_TEST(test("x", lower[upper[char_]], 'x')); + BOOST_TEST(test("x", lower[upper[char_('X')]])); + BOOST_TEST(test("x", lower[upper[char_('x')]])); + + BOOST_TEST(test(" ", lower[upper[space]])); + BOOST_TEST(test("\t", lower[upper[space]], '\t')); + BOOST_TEST(test("\t", lower[upper[space('\t')]])); + + BOOST_TEST(test("X", upper[upper['X']])); + BOOST_TEST(test("X", upper[upper['x']])); + BOOST_TEST(test(L"X", upper[upper[L'X']])); + BOOST_TEST(test(L"X", upper[upper[L'x']])); + + BOOST_TEST(test("X", upper[upper[char_]], 'X')); + BOOST_TEST(test("X", upper[upper[char_]], 'x')); + BOOST_TEST(test("X", upper[upper[char_('X')]])); + BOOST_TEST(test("X", upper[upper[char_('x')]])); + + BOOST_TEST(test(" ", upper[upper[space]])); + BOOST_TEST(test("\t", upper[upper[space]], '\t')); + BOOST_TEST(test("\t", upper[upper[space('\t')]])); + } + + { + using namespace boost::spirit::ascii; + + BOOST_TEST(test("a1- ", lower["a1- "])); + BOOST_TEST(test("a1- ", lower["a1- "])); + BOOST_TEST(test("a1- ", lower["a1- "])); + BOOST_TEST(test("a1- ", lower["A1- "])); + + BOOST_TEST(test("a1- ", lower[lit], "a1- ")); + BOOST_TEST(test("a1- ", lower[lit], "A1- ")); + BOOST_TEST(test("a1- ", lower[lit("a1- ")])); + BOOST_TEST(test("a1- ", lower[lit("A1- ")])); + + BOOST_TEST(test("a1- ", lower[lower["a1- "]])); + BOOST_TEST(test("a1- ", lower[lower["a1- "]])); + BOOST_TEST(test("a1- ", lower[lower["a1- "]])); + BOOST_TEST(test("a1- ", lower[lower["A1- "]])); + + BOOST_TEST(test("a1- ", lower[lower[lit]], "a1- ")); + BOOST_TEST(test("a1- ", lower[lower[lit]], "A1- ")); + BOOST_TEST(test("a1- ", lower[lower[lit("a1- ")]])); + BOOST_TEST(test("a1- ", lower[lower[lit("A1- ")]])); + + BOOST_TEST(test("A1- ", upper[lower["a1- "]])); + BOOST_TEST(test("A1- ", upper[lower["a1- "]])); + BOOST_TEST(test("A1- ", upper[lower["a1- "]])); + BOOST_TEST(test("A1- ", upper[lower["A1- "]])); + + BOOST_TEST(test("A1- ", upper[lower[lit]], "a1- ")); + BOOST_TEST(test("A1- ", upper[lower[lit]], "A1- ")); + BOOST_TEST(test("A1- ", upper[lower[lit("a1- ")]])); + BOOST_TEST(test("A1- ", upper[lower[lit("A1- ")]])); + + BOOST_TEST(test("A1- ", upper["a1- "])); + BOOST_TEST(test("A1- ", upper["a1- "])); + BOOST_TEST(test("A1- ", upper["a1- "])); + BOOST_TEST(test("A1- ", upper["A1- "])); + + BOOST_TEST(test("A1- ", upper[lit], "a1- ")); + BOOST_TEST(test("A1- ", upper[lit], "A1- ")); + BOOST_TEST(test("A1- ", upper[lit("a1- ")])); + BOOST_TEST(test("A1- ", upper[lit("A1- ")])); + + BOOST_TEST(test("a1- ", lower[upper["a1- "]])); + BOOST_TEST(test("a1- ", lower[upper["a1- "]])); + BOOST_TEST(test("a1- ", lower[upper["a1- "]])); + BOOST_TEST(test("a1- ", lower[upper["A1- "]])); + + BOOST_TEST(test("a1- ", lower[upper[lit]], "a1- ")); + BOOST_TEST(test("a1- ", lower[upper[lit]], "A1- ")); + BOOST_TEST(test("a1- ", lower[upper[lit("a1- ")]])); + BOOST_TEST(test("a1- ", lower[upper[lit("A1- ")]])); + + BOOST_TEST(test("A1- ", upper[upper["a1- "]])); + BOOST_TEST(test("A1- ", upper[upper["a1- "]])); + BOOST_TEST(test("A1- ", upper[upper["a1- "]])); + BOOST_TEST(test("A1- ", upper[upper["A1- "]])); + + BOOST_TEST(test("A1- ", upper[upper[lit]], "a1- ")); + BOOST_TEST(test("A1- ", upper[upper[lit]], "A1- ")); + BOOST_TEST(test("A1- ", upper[upper[lit("a1- ")]])); + BOOST_TEST(test("A1- ", upper[upper[lit("A1- ")]])); + } + + return boost::report_errors(); +} diff --git a/test/karma/center_alignment.cpp b/test/karma/center_alignment.cpp new file mode 100644 index 000000000..2cdf5bd47 --- /dev/null +++ b/test/karma/center_alignment.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + + { + BOOST_TEST(test(" x ", center[char_('x')])); + BOOST_TEST(test(" x ", center[char_], 'x')); + BOOST_TEST(test(" x ", center['x'])); + + BOOST_TEST(test(" x ", center(10)[char_('x')])); + BOOST_TEST(test(" x ", center(10)[char_], 'x')); + BOOST_TEST(test(" x ", center(10)['x'])); + + BOOST_TEST(test("*****x****", center(10, char_('*'))[char_('x')])); + BOOST_TEST(test("*****x****", center(10, '*')[char_], 'x')); + BOOST_TEST(test("*****x****", center(10, '*')['x'])); + + BOOST_TEST(test("*****x****", center(char_('*'))[char_('x')])); + BOOST_TEST(test("*****x****", center(char_('*'))[char_], 'x')); + BOOST_TEST(test("*****x****", center(char_('*'))['x'])); + + BOOST_TEST(test(" x ", center(11)[char_('x')])); + + BOOST_TEST(test(" abc ", center[lit("abc")])); + BOOST_TEST(test(" abc ", center[lit], "abc")); + + BOOST_TEST(test(" abc ", center(10)[lit("abc")])); + BOOST_TEST(test(" abc ", center(10)[lit], "abc")); + BOOST_TEST(test(" abc ", center(10)["abc"])); + + BOOST_TEST(test("****abc***", center(10, char_('*'))[lit("abc")])); + BOOST_TEST(test("****abc***", center(10, '*')[lit], "abc")); + BOOST_TEST(test("****abc***", center(10, '*')["abc"])); + + BOOST_TEST(test("****abc***", center(char_('*'))[lit("abc")])); + BOOST_TEST(test("****abc***", center(char_('*'))[lit], "abc")); + BOOST_TEST(test("****abc***", center(char_('*'))["abc"])); + + BOOST_TEST(test(" abc ", center(11)[lit("abc")])); + + BOOST_TEST(test(" 100 ", center[int_(100)])); + BOOST_TEST(test(" 100 ", center[int_], 100)); + + BOOST_TEST(test(" 100 ", center(10)[int_(100)])); + BOOST_TEST(test(" 100 ", center(10)[int_], 100)); + + BOOST_TEST(test("****100***", center(10, char_('*'))[int_(100)])); + BOOST_TEST(test("****100***", center(10, '*')[int_], 100)); + + BOOST_TEST(test(" 100 ", center(11)[int_(100)])); + + BOOST_TEST(test("****100***", center(char_('*'))[int_(100)])); + BOOST_TEST(test("****100***", center(char_('*'))[int_], 100)); + } + + return boost::report_errors(); +} diff --git a/test/karma/char.cpp b/test/karma/char.cpp new file mode 100644 index 000000000..372fc32b5 --- /dev/null +++ b/test/karma/char.cpp @@ -0,0 +1,151 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +//#define KARMA_FAIL_COMPILATION + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + + { + using namespace boost::spirit::ascii; + + BOOST_TEST(test(" ", space)); + BOOST_TEST(test(L" ", space)); + BOOST_TEST(!test("\t", space)); + BOOST_TEST(!test(L"\t", space)); + + BOOST_TEST(test(" ", space(' '))); + BOOST_TEST(test(L" ", space(L' '))); + BOOST_TEST(test("\t", space('\t'))); + BOOST_TEST(test(L"\t", space(L'\t'))); + + BOOST_TEST(test(" ", space(' '), '\t')); + BOOST_TEST(test(L" ", space(' '), L'\t')); + BOOST_TEST(test("\t", space('\t'), ' ')); + BOOST_TEST(test(L"\t", space('\t'), L' ')); + + BOOST_TEST(test(" ", space, ' ')); + BOOST_TEST(test(L" ", space, L' ')); + BOOST_TEST(test("\t", space, '\t')); + BOOST_TEST(test(L"\t", space, L'\t')); + } + + { + BOOST_TEST(test("x", 'x')); + BOOST_TEST(test(L"x", L'x')); + BOOST_TEST(!test("x", 'y')); + BOOST_TEST(!test(L"x", L'y')); + + BOOST_TEST(test("x", char_, 'x')); + BOOST_TEST(test(L"x", char_, L'x')); + BOOST_TEST(!test("x", char_, 'y')); + BOOST_TEST(!test(L"x", char_, L'y')); + + BOOST_TEST(test("x", char_('x'))); + BOOST_TEST(!test("x", char_('y'))); + BOOST_TEST(test(L"x", char_(L'x'))); + BOOST_TEST(!test(L"x", char_(L'y'))); + +// BOOST_TEST(test("x", char_("x"))); +// BOOST_TEST(test(L"x", char_(L"x"))); + +#if defined(KARMA_FAIL_COMPILATION) + BOOST_TEST(test("x", char_)); // anychar without a parameter doesn't make any sense +#endif + } + + { + BOOST_TEST(test(L"x", L'x')); + BOOST_TEST(test(L"x", 'x')); + + BOOST_TEST(test(L"x", wchar, L'x')); + BOOST_TEST(test(L"x", wchar, 'x')); + + BOOST_TEST(test(L"x", wchar(L'x'))); + BOOST_TEST(test(L"x", wchar('x'))); + } + + { + using namespace boost::spirit::ascii; + + BOOST_TEST(test_delimited("x ", 'x', ' ')); + BOOST_TEST(test_delimited(L"x ", L'x', L' ')); + BOOST_TEST(!test_delimited("x ", 'y', ' ')); + BOOST_TEST(!test_delimited(L"x ", L'y', L' ')); + + BOOST_TEST(test_delimited("x ", 'x', space)); + BOOST_TEST(test_delimited(L"x ", L'x', space(L' '))); + BOOST_TEST(!test_delimited("x ", 'y', space)); + BOOST_TEST(!test_delimited(L"x ", L'y', space(L' '))); + + BOOST_TEST(test_delimited("x ", char_, 'x', space)); + BOOST_TEST(test_delimited(L"x ", char_, L'x', space(L' '))); + BOOST_TEST(!test_delimited("x ", char_, 'y', space)); + BOOST_TEST(!test_delimited(L"x ", char_, L'y', space(L' '))); + + BOOST_TEST(test_delimited("x ", char_('x'), space)); + BOOST_TEST(!test_delimited("x ", char_('y'), space(L' '))); + BOOST_TEST(test_delimited(L"x ", char_(L'x'), space)); + BOOST_TEST(!test_delimited(L"x ", char_(L'y'), space(L' '))); + +// BOOST_TEST(test_delimited("x ", char_("x"), space)); + +#if defined(KARMA_FAIL_COMPILATION) + BOOST_TEST(test_delimited("x ", char_, space)); // anychar without a parameter doesn't make any sense +#endif + } + + { + BOOST_TEST(test_delimited(L"x ", L'x', wchar(' '))); + BOOST_TEST(test_delimited(L"x ", 'x', wchar(' '))); + + BOOST_TEST(test_delimited(L"x ", wchar, L'x', wchar(' '))); + BOOST_TEST(test_delimited(L"x ", wchar, 'x', wchar(' '))); + + BOOST_TEST(test_delimited(L"x ", wchar(L'x'), wchar(' '))); + BOOST_TEST(test_delimited(L"x ", wchar('x'), wchar(' '))); + +#if defined(KARMA_FAIL_COMPILATION) + BOOST_TEST(test_delimited("x ", char_, space)); // anychar without a parameter doesn't make any sense +#endif + } + + // action tests + { + BOOST_TEST(test("x", char_[_1 = val('x')])); + BOOST_TEST(test(L"x", char_[_1 = val(L'x')])); + BOOST_TEST(!test("x", char_[_1 = val('y')])); + BOOST_TEST(!test(L"x", char_[_1 = val(L'y')])); + } + + { // lazy chars + + using namespace boost::phoenix; + BOOST_TEST((test("x", char_(val('x'))))); + BOOST_TEST((test(L"x", char_(val(L'x'))))); + } + + return boost::report_errors(); +} diff --git a/test/karma/delimiter.cpp b/test/karma/delimiter.cpp new file mode 100644 index 000000000..e1a60707a --- /dev/null +++ b/test/karma/delimiter.cpp @@ -0,0 +1,55 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +//#define KARMA_FAIL_COMPILATION + +#include + +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + + { + BOOST_TEST(test("a b ", delimit[char_('a') << 'b'])); + BOOST_TEST(test("a*b*", delimit('*')[char_('a') << 'b'])); + + BOOST_TEST(test("ab c d", + char_('a') << delimit[char_('b') << 'c'] << 'd')); + BOOST_TEST(test("ab*c*d", + char_('a') << delimit('*')[char_('b') << 'c'] << 'd')); + + BOOST_TEST(test_delimited("a b ", delimit[char_('a') << 'b'], char_(' '))); + BOOST_TEST(test_delimited("a*b*", delimit('*')[char_('a') << 'b'], char_(' '))); + + BOOST_TEST(test_delimited("a b c d ", + char_('a') << delimit[char_('b') << 'c'] << 'd', char_(' '))); + BOOST_TEST(test_delimited("a b*c*d ", + char_('a') << delimit('*')[char_('b') << 'c'] << 'd', char_(' '))); + } + + { + BOOST_TEST(test("ab", verbatim[char_('a') << 'b'])); + BOOST_TEST(test("abcd", + char_('a') << verbatim[char_('b') << 'c'] << 'd')); + + BOOST_TEST(test_delimited("ab ", + verbatim[char_('a') << 'b'], char_(' '))); + BOOST_TEST(test_delimited("a bc d ", + char_('a') << verbatim[char_('b') << 'c'] << 'd', char_(' '))); + } + + return boost::report_errors(); +} diff --git a/test/karma/eps.cpp b/test/karma/eps.cpp new file mode 100644 index 000000000..a2ee9150c --- /dev/null +++ b/test/karma/eps.cpp @@ -0,0 +1,31 @@ +// Copyright (c) 2001-2007 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using namespace boost::spirit; + + { + BOOST_TEST(test("", eps)); + } + + { // test action + + using namespace boost::phoenix; + BOOST_TEST(test("", eps(val(true)))); + BOOST_TEST(!test("", eps(val(false)))); + } + + return boost::report_errors(); +} diff --git a/test/karma/format_manip.cpp b/test/karma/format_manip.cpp new file mode 100644 index 000000000..0f291753e --- /dev/null +++ b/test/karma/format_manip.cpp @@ -0,0 +1,200 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////////// +template +bool test(Char const *expected, Expr const& xpr) +{ + namespace spirit = boost::spirit; + typedef + spirit::traits::is_component + is_component; + + // report invalid expression error as early as possible + BOOST_MPL_ASSERT_MSG(is_component::value, + xpr_is_not_convertible_to_a_generator, ()); + + typedef + typename spirit::result_of::as_component::type + component; + typedef typename component::director director; + + component c = spirit::as_component(spirit::karma::domain(), xpr); + + std::ostringstream ostrm; + ostrm << c; + return ostrm.good() && ostrm.str() == expected; +} + +template +bool test(Char const *expected, + boost::spirit::karma::detail::format_manip const& fm) +{ + std::ostringstream ostrm; + ostrm << fm; + return ostrm.good() && ostrm.str() == expected; +} + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::ascii; + using namespace boost::spirit::arg_names; + using namespace boost::spirit::karma; + + namespace fusion = boost::fusion; + using namespace boost::phoenix; + + { + BOOST_TEST(test( "a", + char_[_1 = val('a')] + )); + BOOST_TEST(test( "a", + format(char_[_1 = val('a')]) + )); + BOOST_TEST(test( "a ", + format_delimited(char_[_1 = val('a')], space) + )); + BOOST_TEST(test( "a", + format(char_, 'a') + )); + BOOST_TEST(test( "a ", + format_delimited(char_, 'a', space) + )); + } + + { + BOOST_TEST(test( "ab", + char_[_1 = val('a')] << char_[_1 = val('b')] + )); + BOOST_TEST(test( "ab", + format(char_[_1 = val('a')] << char_[_1 = val('b')]) + )); + BOOST_TEST(test( "a b ", + format_delimited(char_[_1 = val('a')] << char_[_1 = val('b')], space) + )); + + fusion::vector t('a', 'b'); + + BOOST_TEST(test( "ab", + format(char_ << char_, t) + )); + BOOST_TEST(test( "a b ", + format_delimited(char_ << char_, t, space) + )); + } + + { + BOOST_TEST(test( "abc", + char_[_1 = 'a'] << char_[_1 = 'b'] << char_[_1 = 'c'] + )); + BOOST_TEST(test( "abc", + format(char_('a') << char_('b') << char_('c')) + )); + BOOST_TEST(test( "a b c ", + format_delimited(char_('a') << char_('b') << char_('c'), space) + )); + + fusion::vector t('a', 'b', 'c'); + + BOOST_TEST(test( "abc", + format(char_ << char_ << char_, t) + )); + BOOST_TEST(test( "a b c ", + format_delimited(char_ << char_ << char_, t, space) + )); + } + + { + BOOST_TEST(test( "a2", + (char_ << int_)[_1 = 'a', _2 = 2] + )); + + fusion::vector t('a', 2); + + BOOST_TEST(test( "a2", + format(char_ << int_, t) + )); + BOOST_TEST(test( "a 2 ", + format_delimited(char_ << int_, t, space) + )); + } + + using namespace boost::assign; + + { + // output all elements of a vector + std::vector v; + v += 'a', 'b', 'c'; + + BOOST_TEST(test( "abc", + (*char_)[_1 = v] + )); + BOOST_TEST(test( "abc", + format(*char_, v) + )); + BOOST_TEST(test( "a b c ", + format_delimited(*char_, v, space) + )); + + // output a comma separated list of vector elements + BOOST_TEST(test( "a, b, c", + (char_ % lit(", "))[_0 = fusion::make_single_view(v)] + )); + BOOST_TEST(test( "a, b, c", + format((char_ % lit(", "))[_0 = fusion::make_single_view(v)]) + )); + BOOST_TEST(test( "a , b , c ", + format_delimited((char_ % ',')[_0 = fusion::make_single_view(v)], space) + )); + BOOST_TEST(test( "a,b,c", + format(char_ % ',', v) + )); + BOOST_TEST(test( "a , b , c ", + format_delimited(char_ % ',', v, space) + )); + + // output all elements of a list + std::list l; + l += 'a', 'b', 'c'; + +// BOOST_TEST(test( "abc", +// (*char_)[_1 = l] +// )); +// BOOST_TEST(test( "abc", +// format((*char_)[_1 = l]) +// )); +// BOOST_TEST(test( "a b c ", +// format_delimited((*char_)[_1 = l], space) +// )); + BOOST_TEST(test( "abc", + format(*char_, l) + )); + BOOST_TEST(test( "a b c ", + format_delimited(*char_, l, space) + )); + } + + return boost::report_errors(); +} + diff --git a/test/karma/functor.cpp b/test/karma/functor.cpp new file mode 100644 index 000000000..c4e83eb4e --- /dev/null +++ b/test/karma/functor.cpp @@ -0,0 +1,58 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include + +#include +#include "test.hpp" + +/////////////////////////////////////////////////////////////////////////////// +struct number_generator : public boost::spirit::karma::functor_base +{ + template + struct apply + { + typedef int type; + }; + + template + bool operator()(Parameter v, Context& ctx, OutputIterator& sink) const + { + char ch = v % 10 + '0'; + v /= 10; + + if (0 != v) + (*this)(v, ctx, sink); + + *sink = ch; + ++sink; + return true; + } +}; + +boost::spirit::karma::functor_generator number; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + using spirit_test::test; + using namespace boost::spirit; + using namespace boost::spirit::karma; + + { + BOOST_TEST(test("0", number)); + BOOST_TEST(test("1234", number, 1234)); + } + + return boost::report_errors(); +} diff --git a/test/karma/grammar.cpp b/test/karma/grammar.cpp new file mode 100644 index 000000000..59be46e0f --- /dev/null +++ b/test/karma/grammar.cpp @@ -0,0 +1,63 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +// include this first to make rules have placeholders (e.g. r._1) +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "test.hpp" + +using namespace spirit_test; +using namespace boost::spirit::karma; +using namespace boost::spirit::ascii; + +typedef spirit_test::output_iterator::type outiter_type; + +struct num_list : grammar_def +{ + num_list() + { + using boost::spirit::int_; + num1 = int_(123); + num2 = int_(456); + num3 = int_(789); + start = num1 << ',' << num2 << ',' << num3; + } + + rule start, num1, num2, num3; +}; + +int +main() +{ + { // simple grammar test + num_list def; + grammar nlist(def); + BOOST_TEST(test_delimited("123 , 456 , 789 ", nlist, space)); + } + + { // direct access to the rules + + num_list def; + BOOST_TEST(test_delimited("123 ", def.num1, space)); + BOOST_TEST(test_delimited("123 , 456 , 789 ", def.start, space)); + } + + return boost::report_errors(); +} + diff --git a/test/karma/grammar_fail.cpp b/test/karma/grammar_fail.cpp new file mode 100644 index 000000000..22f837083 --- /dev/null +++ b/test/karma/grammar_fail.cpp @@ -0,0 +1,46 @@ +/*============================================================================= + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#include +#include +#include +#include +#include +#include + +#include "test.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::karma; +using namespace boost::spirit::ascii; + +struct num_list : grammar_def > +{ + num_list() + { + using boost::spirit::int_; + start = int_(1) << ',' << int_(0); + } + + rule > start; +}; + +// this test must fail compiling +int main() +{ + using boost::make_function_output_iterator; + using spirit_test::make_string_appender; + + std::string generated; + + num_list def; + bool r = generate_delimited( + make_function_output_iterator(make_string_appender(generated)), + make_generator(def), char_('%') << '\n'); + + return 0; +} diff --git a/test/karma/int_numerics.cpp b/test/karma/int_numerics.cpp new file mode 100644 index 000000000..0ac68e175 --- /dev/null +++ b/test/karma/int_numerics.cpp @@ -0,0 +1,306 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +//#define KARMA_FAIL_COMPILATION + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +struct test_minmax +{ + template + void operator()(T) const + { + using namespace boost::spirit; + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + + T minval = (std::numeric_limits::min)(); + T maxval = (std::numeric_limits::max)(); + + std::string expected_minval = boost::lexical_cast(minval); + std::string expected_maxval = boost::lexical_cast(maxval); + + // create a correct generator type from the given integer type + typedef typename + boost::mpl::if_< + boost::mpl::bool_::is_signed>, + karma::int_spec, + karma::uint_spec + >::type + int_spec_type; + + int_spec_type const gen = int_spec_type(); + + BOOST_TEST(test(expected_maxval, gen, maxval)); + BOOST_TEST(test(expected_minval, gen, minval)); + BOOST_TEST(test(expected_maxval, gen(maxval))); + BOOST_TEST(test(expected_minval, gen(minval))); + + BOOST_TEST(test_delimited(expected_maxval + " ", gen, maxval, char(' '))); + BOOST_TEST(test_delimited(expected_minval + " ", gen, minval, char(' '))); + BOOST_TEST(test_delimited(expected_maxval + " ", gen(maxval), char(' '))); + BOOST_TEST(test_delimited(expected_minval + " ", gen(minval), char(' '))); + + // action tests + BOOST_TEST(test(expected_maxval, gen[_1 = val(maxval)])); + BOOST_TEST(test(expected_minval, gen[_1 = val(minval)])); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + + { + using namespace boost::spirit::ascii; + + /////////////////////////////////////////////////////////////////////// + // this is currently ambiguous with character literals +// BOOST_TEST(test("0", 0)); +// BOOST_TEST(test("123", 123)); +// BOOST_TEST(test("-123", -123)); + + BOOST_TEST(test("0", int_, 0)); + BOOST_TEST(test("123", int_, 123)); + BOOST_TEST(test("-123", int_, -123)); + + BOOST_TEST(test_delimited("0 ", int_, 0, char_(' '))); + BOOST_TEST(test_delimited("123 ", int_, 123, char_(' '))); + BOOST_TEST(test_delimited("-123 ", int_, -123, char_(' '))); + + BOOST_TEST(test("0", lower[int_], 0)); + BOOST_TEST(test("123", lower[int_], 123)); + BOOST_TEST(test("-123", lower[int_], -123)); + + BOOST_TEST(test_delimited("0 ", lower[int_], 0, char_(' '))); + BOOST_TEST(test_delimited("123 ", lower[int_], 123, char_(' '))); + BOOST_TEST(test_delimited("-123 ", lower[int_], -123, char_(' '))); + + BOOST_TEST(test("0", upper[int_], 0)); + BOOST_TEST(test("123", upper[int_], 123)); + BOOST_TEST(test("-123", upper[int_], -123)); + + BOOST_TEST(test_delimited("0 ", upper[int_], 0, char_(' '))); + BOOST_TEST(test_delimited("123 ", upper[int_], 123, char_(' '))); + BOOST_TEST(test_delimited("-123 ", upper[int_], -123, char_(' '))); + + /////////////////////////////////////////////////////////////////////// + BOOST_TEST(test("0", int_(0))); + BOOST_TEST(test("123", int_(123))); + BOOST_TEST(test("-123", int_(-123))); + + BOOST_TEST(test_delimited("0 ", int_(0), char_(' '))); + BOOST_TEST(test_delimited("123 ", int_(123), char_(' '))); + BOOST_TEST(test_delimited("-123 ", int_(-123), char_(' '))); + + BOOST_TEST(test("0", lower[int_(0)])); + BOOST_TEST(test("123", lower[int_(123)])); + BOOST_TEST(test("-123", lower[int_(-123)])); + + BOOST_TEST(test_delimited("0 ", lower[int_(0)], char_(' '))); + BOOST_TEST(test_delimited("123 ", lower[int_(123)], char_(' '))); + BOOST_TEST(test_delimited("-123 ", lower[int_(-123)], char_(' '))); + + BOOST_TEST(test("0", upper[int_(0)])); + BOOST_TEST(test("123", upper[int_(123)])); + BOOST_TEST(test("-123", upper[int_(-123)])); + + BOOST_TEST(test_delimited("0 ", upper[int_(0)], char_(' '))); + BOOST_TEST(test_delimited("123 ", upper[int_(123)], char_(' '))); + BOOST_TEST(test_delimited("-123 ", upper[int_(-123)], char_(' '))); + } + + { + using namespace boost::spirit::ascii; + + karma::int_spec const signed_int = + karma::int_spec(); + + /////////////////////////////////////////////////////////////////////// + BOOST_TEST(test(" 0", signed_int, 0)); + BOOST_TEST(test("+123", signed_int, 123)); + BOOST_TEST(test("-123", signed_int, -123)); + + BOOST_TEST(test_delimited(" 0 ", signed_int, 0, char_(' '))); + BOOST_TEST(test_delimited("+123 ", signed_int, 123, char_(' '))); + BOOST_TEST(test_delimited("-123 ", signed_int, -123, char_(' '))); + + BOOST_TEST(test(" 0", lower[signed_int], 0)); + BOOST_TEST(test("+123", lower[signed_int], 123)); + BOOST_TEST(test("-123", lower[signed_int], -123)); + + BOOST_TEST(test_delimited(" 0 ", lower[signed_int], 0, char_(' '))); + BOOST_TEST(test_delimited("+123 ", lower[signed_int], 123, char_(' '))); + BOOST_TEST(test_delimited("-123 ", lower[signed_int], -123, char_(' '))); + + BOOST_TEST(test(" 0", upper[signed_int], 0)); + BOOST_TEST(test("+123", upper[signed_int], 123)); + BOOST_TEST(test("-123", upper[signed_int], -123)); + + BOOST_TEST(test_delimited(" 0 ", upper[signed_int], 0, char_(' '))); + BOOST_TEST(test_delimited("+123 ", upper[signed_int], 123, char_(' '))); + BOOST_TEST(test_delimited("-123 ", upper[signed_int], -123, char_(' '))); + + /////////////////////////////////////////////////////////////////////// + BOOST_TEST(test(" 0", signed_int(0))); + BOOST_TEST(test("+123", signed_int(123))); + BOOST_TEST(test("-123", signed_int(-123))); + + BOOST_TEST(test_delimited(" 0 ", signed_int(0), char_(' '))); + BOOST_TEST(test_delimited("+123 ", signed_int(123), char_(' '))); + BOOST_TEST(test_delimited("-123 ", signed_int(-123), char_(' '))); + + BOOST_TEST(test(" 0", lower[signed_int(0)])); + BOOST_TEST(test("+123", lower[signed_int(123)])); + BOOST_TEST(test("-123", lower[signed_int(-123)])); + + BOOST_TEST(test_delimited(" 0 ", lower[signed_int(0)], char_(' '))); + BOOST_TEST(test_delimited("+123 ", lower[signed_int(123)], char_(' '))); + BOOST_TEST(test_delimited("-123 ", lower[signed_int(-123)], char_(' '))); + + BOOST_TEST(test(" 0", upper[signed_int(0)])); + BOOST_TEST(test("+123", upper[signed_int(123)])); + BOOST_TEST(test("-123", upper[signed_int(-123)])); + + BOOST_TEST(test_delimited(" 0 ", upper[signed_int(0)], char_(' '))); + BOOST_TEST(test_delimited("+123 ", upper[signed_int(123)], char_(' '))); + BOOST_TEST(test_delimited("-123 ", upper[signed_int(-123)], char_(' '))); + } + + { + /////////////////////////////////////////////////////////////////////// + using boost::spirit::uint_; + using namespace boost::spirit::ascii; + + BOOST_TEST(test("1234", uint_, 1234)); + BOOST_TEST(test("ff", hex, 0xff)); + BOOST_TEST(test("1234", oct, 01234)); + BOOST_TEST(test("11110000", bin, 0xf0)); + + BOOST_TEST(test_delimited("1234 ", uint_, 1234, char_(' '))); + BOOST_TEST(test_delimited("ff ", hex, 0xff, char_(' '))); + BOOST_TEST(test_delimited("1234 ", oct, 01234, char_(' '))); + BOOST_TEST(test_delimited("11110000 ", bin, 0xf0, char_(' '))); + + BOOST_TEST(test("1234", lower[uint_], 1234)); + BOOST_TEST(test("ff", lower[hex], 0xff)); + BOOST_TEST(test("1234", lower[oct], 01234)); + BOOST_TEST(test("11110000", lower[bin], 0xf0)); + + BOOST_TEST(test_delimited("1234 ", lower[uint_], 1234, char_(' '))); + BOOST_TEST(test_delimited("ff ", lower[hex], 0xff, char_(' '))); + BOOST_TEST(test_delimited("1234 ", lower[oct], 01234, char_(' '))); + BOOST_TEST(test_delimited("11110000 ", lower[bin], 0xf0, char_(' '))); + + BOOST_TEST(test("1234", upper[uint_], 1234)); + BOOST_TEST(test("FF", upper[hex], 0xff)); + BOOST_TEST(test("1234", upper[oct], 01234)); + BOOST_TEST(test("11110000", upper[bin], 0xf0)); + + BOOST_TEST(test_delimited("1234 ", upper[uint_], 1234, char_(' '))); + BOOST_TEST(test_delimited("FF ", upper[hex], 0xff, char_(' '))); + BOOST_TEST(test_delimited("1234 ", upper[oct], 01234, char_(' '))); + BOOST_TEST(test_delimited("11110000 ", upper[bin], 0xf0, char_(' '))); + + // no generator transformation should occur for uint_'s + BOOST_TEST(test("1234", upper[upper[uint_]], 1234)); + BOOST_TEST(test("1234", upper[lower[uint_]], 1234)); + BOOST_TEST(test("1234", lower[upper[uint_]], 1234)); + BOOST_TEST(test("1234", lower[lower[uint_]], 1234)); + + BOOST_TEST(test_delimited("1234 ", upper[upper[uint_]], 1234, char_(' '))); + BOOST_TEST(test_delimited("1234 ", upper[lower[uint_]], 1234, char_(' '))); + BOOST_TEST(test_delimited("1234 ", lower[upper[uint_]], 1234, char_(' '))); + BOOST_TEST(test_delimited("1234 ", lower[lower[uint_]], 1234, char_(' '))); + + BOOST_TEST(test("FF", upper[upper[hex]], 0xff)); + BOOST_TEST(test("FF", upper[lower[hex]], 0xff)); + BOOST_TEST(test("ff", lower[upper[hex]], 0xff)); + BOOST_TEST(test("ff", lower[lower[hex]], 0xff)); + + BOOST_TEST(test_delimited("FF ", upper[upper[hex]], 0xff, char_(' '))); + BOOST_TEST(test_delimited("FF ", upper[lower[hex]], 0xff, char_(' '))); + BOOST_TEST(test_delimited("ff ", lower[upper[hex]], 0xff, char_(' '))); + BOOST_TEST(test_delimited("ff ", lower[lower[hex]], 0xff, char_(' '))); + + /////////////////////////////////////////////////////////////////////// + BOOST_TEST(test("1234", uint_(1234))); + BOOST_TEST(test("ff", hex(0xff))); + BOOST_TEST(test("1234", oct(01234))); + BOOST_TEST(test("11110000", bin(0xf0))); + + BOOST_TEST(test_delimited("1234 ", uint_(1234), char_(' '))); + BOOST_TEST(test_delimited("ff ", hex(0xff), char_(' '))); + BOOST_TEST(test_delimited("1234 ", oct(01234), char_(' '))); + BOOST_TEST(test_delimited("11110000 ", bin(0xf0), char_(' '))); + + BOOST_TEST(test("1234", lower[uint_(1234)])); + BOOST_TEST(test("ff", lower[hex(0xff)])); + BOOST_TEST(test("1234", lower[oct(01234)])); + BOOST_TEST(test("11110000", lower[bin(0xf0)])); + + BOOST_TEST(test_delimited("1234 ", lower[uint_(1234)], char_(' '))); + BOOST_TEST(test_delimited("ff ", lower[hex(0xff)], char_(' '))); + BOOST_TEST(test_delimited("1234 ", lower[oct(01234)], char_(' '))); + BOOST_TEST(test_delimited("11110000 ", lower[bin(0xf0)], char_(' '))); + + BOOST_TEST(test("1234", upper[uint_(1234)])); + BOOST_TEST(test("FF", upper[hex(0xff)])); + BOOST_TEST(test("1234", upper[oct(01234)])); + BOOST_TEST(test("11110000", upper[bin(0xf0)])); + + BOOST_TEST(test_delimited("1234 ", upper[uint_(1234)], char_(' '))); + BOOST_TEST(test_delimited("FF ", upper[hex(0xff)], char_(' '))); + BOOST_TEST(test_delimited("1234 ", upper[oct(01234)], char_(' '))); + BOOST_TEST(test_delimited("11110000 ", upper[bin(0xf0)], char_(' '))); + + BOOST_TEST(test("FF", upper[upper[hex(0xff)]])); + BOOST_TEST(test("FF", upper[lower[hex(0xff)]])); + BOOST_TEST(test("ff", lower[upper[hex(0xff)]])); + BOOST_TEST(test("ff", lower[lower[hex(0xff)]])); + + BOOST_TEST(test_delimited("FF ", upper[upper[hex(0xff)]], char_(' '))); + BOOST_TEST(test_delimited("FF ", upper[lower[hex(0xff)]], char_(' '))); + BOOST_TEST(test_delimited("ff ", lower[upper[hex(0xff)]], char_(' '))); + BOOST_TEST(test_delimited("ff ", lower[lower[hex(0xff)]], char_(' '))); + } + +// test boundary values + typedef boost::mpl::vector< +#ifdef BOOST_HAS_LONG_LONG + boost::long_long_type, boost::ulong_long_type, +#endif + short, unsigned short, + int, unsigned int, + long, unsigned long + > integer_types; + boost::mpl::for_each(test_minmax()); + + return boost::report_errors(); +} + diff --git a/test/karma/kleene.cpp b/test/karma/kleene.cpp new file mode 100644 index 000000000..7278c3f05 --- /dev/null +++ b/test/karma/kleene.cpp @@ -0,0 +1,113 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include + +#include +// #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + using namespace boost::spirit; + namespace fusion = boost::fusion; + + { + using namespace boost::assign; + + std::vector v; + v += 'a', 'b', 'c'; + + BOOST_TEST(test("abc", *char_, v)); + BOOST_TEST(test_delimited("a b c ", *char_, v, ' ')); + } + + { + using namespace boost::assign; + + std::vector v; + + // these need to fail, because the return value should be false + BOOST_TEST(!test("", +char_, v)); + BOOST_TEST(!test_delimited("", +char_, v, ' ')); + + v += 'a', 'b', 'c'; + + BOOST_TEST(test("abc", +char_, v)); + BOOST_TEST(test_delimited("a b c ", +char_, v, ' ')); + } + + { + using namespace boost::assign; + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + + std::vector v; + v += 10, 20, 30; + + BOOST_TEST(test("102030", *int_, v)); + BOOST_TEST(test_delimited("10, 20, 30, ", *int_, v, lit(", "))); + + typedef fusion::vector fvec; + std::vector sv; + sv += fvec(10), fvec(20), fvec(30); + + BOOST_TEST(test("10,20,30,", *(int_ << ','), sv)); + BOOST_TEST(test_delimited("10 , 20 , 30 , ", *(int_ << ','), sv, lit(" "))); + + fusion::vector cc ('a', 'c'); + BOOST_TEST(test("ac", char_ << *(char_(' ') << ',') << char_, cc)); + BOOST_TEST(test_delimited("a c ", + char_ << *(char_(' ') << ',') << char_, cc, " ")); + } + + { + using namespace boost::assign; + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + + std::vector v; + + BOOST_TEST(!test("", +int_, v)); + BOOST_TEST(!test_delimited("", +int_, v, lit(", "))); + + v += 10, 20, 30; + + BOOST_TEST(test("102030", +int_, v)); + BOOST_TEST(test_delimited("10, 20, 30, ", +int_, v, lit(", "))); + + typedef fusion::vector fvec; + std::vector sv; + sv += fvec(10), fvec(20), fvec(30); + + BOOST_TEST(test("10,20,30,", +(int_ << ','), sv)); + BOOST_TEST(test_delimited("10 , 20 , 30 , ", +(int_ << ','), sv, lit(" "))); + + fusion::vector cc ('a', 'c'); + BOOST_TEST(!test("", char_ << +(char_(' ') << ',') << char_, cc)); + BOOST_TEST(!test_delimited("", + char_ << +(char_(' ') << ',') << char_, cc, " ")); + } + + return boost::report_errors(); +} + diff --git a/test/karma/lazy.cpp b/test/karma/lazy.cpp new file mode 100644 index 000000000..16a5f29c1 --- /dev/null +++ b/test/karma/lazy.cpp @@ -0,0 +1,51 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using namespace boost::spirit; + using namespace boost::spirit::karma; + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + + { + BOOST_TEST(test("123", lazy(val(int_)), 123)); + } + + { + int result = 123; + BOOST_TEST(test("123", lazy(val(int_))[_1 = ref(result)])); + } + + { + typedef spirit_test::output_iterator::type outiter_type; + rule r; + + r = char_('<') << lazy(_r1) << '>' << "'; + + std::string tag("tag"), foo("foo"); + BOOST_TEST(test("", r (ref(tag)))); + BOOST_TEST(!test("", r (ref(foo)))); + } + + return boost::report_errors(); +} diff --git a/test/karma/left_alignment.cpp b/test/karma/left_alignment.cpp new file mode 100644 index 000000000..b6be0a0d9 --- /dev/null +++ b/test/karma/left_alignment.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + + { + BOOST_TEST(test("x ", left_align[char_('x')])); + BOOST_TEST(test("x ", left_align[char_], 'x')); + BOOST_TEST(test("x ", left_align['x'])); + + BOOST_TEST(test("x ", left_align(10)[char_('x')])); + BOOST_TEST(test("x ", left_align(10)[char_], 'x')); + BOOST_TEST(test("x ", left_align(10)['x'])); + + BOOST_TEST(test("x*********", left_align(10, char_('*'))[char_('x')])); + BOOST_TEST(test("x*********", left_align(10, '*')[char_], 'x')); + BOOST_TEST(test("x*********", left_align(10, '*')['x'])); + + BOOST_TEST(test("x*********", left_align(char_('*'))[char_('x')])); + BOOST_TEST(test("x*********", left_align(char_('*'))[char_], 'x')); + BOOST_TEST(test("x*********", left_align(char_('*'))['x'])); + + BOOST_TEST(test("abc ", left_align[lit("abc")])); + BOOST_TEST(test("abc ", left_align[lit], "abc")); + + BOOST_TEST(test("abc ", left_align(10)[lit("abc")])); + BOOST_TEST(test("abc ", left_align(10)[lit], "abc")); + BOOST_TEST(test("abc ", left_align(10)["abc"])); + + BOOST_TEST(test("abc*******", left_align(10, char_('*'))[lit("abc")])); + BOOST_TEST(test("abc*******", left_align(10, '*')[lit], "abc")); + BOOST_TEST(test("abc*******", left_align(10, '*')["abc"])); + + BOOST_TEST(test("abc*******", left_align(char_('*'))[lit("abc")])); + BOOST_TEST(test("abc*******", left_align(char_('*'))[lit], "abc")); + BOOST_TEST(test("abc*******", left_align(char_('*'))["abc"])); + + BOOST_TEST(test("100 ", left_align[int_(100)])); + BOOST_TEST(test("100 ", left_align[int_], 100)); + + BOOST_TEST(test("100 ", left_align(10)[int_(100)])); + BOOST_TEST(test("100 ", left_align(10)[int_], 100)); + + BOOST_TEST(test("100*******", left_align(10, char_('*'))[int_(100)])); + BOOST_TEST(test("100*******", left_align(10, '*')[int_], 100)); + + BOOST_TEST(test("100*******", left_align(char_('*'))[int_(100)])); + BOOST_TEST(test("100*******", left_align(char_('*'))[int_], 100)); + } + + return boost::report_errors(); +} diff --git a/test/karma/list.cpp b/test/karma/list.cpp new file mode 100644 index 000000000..c9f1d2023 --- /dev/null +++ b/test/karma/list.cpp @@ -0,0 +1,66 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +using namespace spirit_test; + +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::ascii; + + using namespace boost::assign; + + std::vector v; + v += 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'; + + { + BOOST_TEST(test("a,b,c,d,e,f,g,h", char_ % ',', v)); + BOOST_TEST(test_delimited("a , b , c , d , e , f , g , h ", + char_ % ',', v, space)); + } + + { + std::string s ("abcdefgh"); + BOOST_TEST(test("a,b,c,d,e,f,g,h", char_ % ',', s)); + BOOST_TEST(test_delimited("a , b , c , d , e , f , g , h ", + char_ % ',', s, space)); + } + + { // actions + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + + BOOST_TEST(test("a,b,c,d,e,f,g,h", (char_ % ',')[_1 = ref(v)])); + BOOST_TEST(test_delimited("a , b , c , d , e , f , g , h ", + (char_ % ',')[_1 = ref(v)], space)); + } + + return boost::report_errors(); +} + diff --git a/test/karma/lit.cpp b/test/karma/lit.cpp new file mode 100644 index 000000000..7b04cd5ed --- /dev/null +++ b/test/karma/lit.cpp @@ -0,0 +1,137 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + + { + BOOST_TEST(test("a", lit('a'))); + BOOST_TEST(!test("a", lit('b'))); + + BOOST_TEST(test("abc", "abc")); + BOOST_TEST(!test("abcd", "abc")); + + BOOST_TEST(test("abc", lit("abc"))); + BOOST_TEST(!test("abcd", lit("abc"))); + + BOOST_TEST(test("abc", lit, "abc")); + BOOST_TEST(!test("abcd", lit, "abc")); + } + + { + std::string str("abc"); + BOOST_TEST(test("abc", lit(str))); + BOOST_TEST(!test("abcd", lit(str))); + + BOOST_TEST(test("abc", lit, str)); + BOOST_TEST(!test("abcd", lit, str)); + + BOOST_TEST(test("abc", str)); + BOOST_TEST(!test("abcd", str)); + + std::basic_string wstr(L"abc"); + BOOST_TEST(test(L"abc", lit(wstr))); + BOOST_TEST(!test(L"abcd", lit(wstr))); + + BOOST_TEST(test(L"abc", lit, wstr)); + BOOST_TEST(!test(L"abcd", lit, wstr)); + + BOOST_TEST(test(L"abc", wstr)); + BOOST_TEST(!test(L"abcd", wstr)); + } + + { + BOOST_TEST(test(L"a", lit(L'a'))); + BOOST_TEST(!test(L"a", lit(L'b'))); + + BOOST_TEST(test(L"abc", L"abc")); + BOOST_TEST(test(L"abc", "abc")); + BOOST_TEST(!test(L"abcd", L"abc")); + + BOOST_TEST(test(L"abc", lit(L"abc"))); + BOOST_TEST(test(L"abc", wlit(L"abc"))); + BOOST_TEST(!test(L"abcd", lit(L"abc"))); + + BOOST_TEST(test(L"abc", lit, L"abc")); + BOOST_TEST(test(L"abc", wlit, L"abc")); + BOOST_TEST(!test(L"abcd", lit, L"abc")); + + BOOST_TEST(test(L"abc", lit, "abc")); + BOOST_TEST(test(L"abc", wlit, "abc")); + BOOST_TEST(!test(L"abcd", lit, "abc")); + } + + { + BOOST_TEST(test_delimited("a ", lit('a'), ' ')); + BOOST_TEST(!test_delimited("a ", lit('b'), ' ')); + + BOOST_TEST(test_delimited("abc ", "abc", ' ')); + BOOST_TEST(!test_delimited("abcd ", "abc", ' ')); + + BOOST_TEST(test_delimited("abc ", lit("abc"), ' ')); + BOOST_TEST(!test_delimited("abcd ", lit("abc"), ' ')); + + BOOST_TEST(test_delimited("abc ", lit, "abc", ' ')); + BOOST_TEST(!test_delimited("abcd ", lit, "abc", ' ')); + } + + { + BOOST_TEST(test_delimited(L"a ", lit(L'a'), ' ')); + BOOST_TEST(!test_delimited(L"a ", lit(L'b'), ' ')); + + BOOST_TEST(test_delimited(L"abc ", L"abc", ' ')); + BOOST_TEST(!test_delimited(L"abcd ", L"abc", ' ')); + + BOOST_TEST(test_delimited(L"abc ", lit(L"abc"), ' ')); + BOOST_TEST(test_delimited(L"abc ", wlit(L"abc"), ' ')); + BOOST_TEST(test_delimited(L"abc ", wlit("abc"), ' ')); + BOOST_TEST(!test_delimited(L"abcd ", lit(L"abc"), ' ')); + + BOOST_TEST(test_delimited(L"abc ", lit, L"abc", ' ')); + BOOST_TEST(test_delimited(L"abc ", wlit, L"abc", ' ')); + BOOST_TEST(test_delimited(L"abc ", wlit, "abc", ' ')); + BOOST_TEST(!test_delimited(L"abcd ", lit, L"abc", ' ')); + } + + { // test action + + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + using namespace boost::spirit::ascii; + + std::string str("abc"); + BOOST_TEST(test("abc", lit[_1 = ref(str)])); + BOOST_TEST(test_delimited("abc ", lit[_1 = ref(str)], space)); + } + + { // lazy strings + + using namespace boost::phoenix; + std::basic_string s("abc"); + BOOST_TEST((test("abc", lit(val(s))))); + + std::basic_string ws(L"abc"); + BOOST_TEST((test(L"abc", lit(ref(ws))))); + } + + return boost::report_errors(); +} diff --git a/test/karma/none.cpp b/test/karma/none.cpp new file mode 100644 index 000000000..8fde9474c --- /dev/null +++ b/test/karma/none.cpp @@ -0,0 +1,32 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using namespace spirit_test; + using namespace boost::spirit; + using namespace boost::spirit::ascii; + + { + BOOST_TEST((!test("", none, 1))); + BOOST_TEST((!test("", none, "test"))); + } + + { + BOOST_TEST((!test_delimited(" ", none, 1, space))); + BOOST_TEST((!test_delimited(" ", none, "test", space))); + } + + return boost::report_errors(); +} diff --git a/test/karma/optional.cpp b/test/karma/optional.cpp new file mode 100644 index 000000000..94dbceaae --- /dev/null +++ b/test/karma/optional.cpp @@ -0,0 +1,68 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "test.hpp" + +int main() +{ + using namespace spirit_test; + using namespace boost::spirit; + + { + boost::optional opt; + BOOST_TEST(test("", -int_, opt)); + + opt = 10; + BOOST_TEST(test("10", -int_, opt)); + } + + { + using namespace boost::spirit::ascii; + + boost::optional opt; + BOOST_TEST(test_delimited("", -int_, opt, space)); + + opt = 10; + BOOST_TEST(test_delimited("10 ", -int_, opt, space)); + } + + { // test action + using namespace boost::phoenix; + namespace phoenix = boost::phoenix; + using namespace boost::spirit::arg_names; + + boost::optional n ; + BOOST_TEST(test("", (-int_)[_1 = phoenix::ref(n)])); + + n = 1234; + BOOST_TEST(test("1234", (-int_)[_1 = phoenix::ref(n)])); + } + + { // test action + using namespace boost::phoenix; + namespace phoenix = boost::phoenix; + using namespace boost::spirit::arg_names; + using namespace boost::spirit::ascii; + + boost::optional n; + BOOST_TEST(test_delimited("", (-int_)[_1 = phoenix::ref(n)], space)); + + n = 1234; + BOOST_TEST(test_delimited("1234 ", (-int_)[_1 = phoenix::ref(n)], space)); + } + + return boost::report_errors(); +} diff --git a/test/karma/pattern.cpp b/test/karma/pattern.cpp new file mode 100644 index 000000000..92b851b6d --- /dev/null +++ b/test/karma/pattern.cpp @@ -0,0 +1,188 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +// include this first to make rules have placeholders (e.g. _r1) +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + using namespace boost; + using namespace boost::spirit; + using namespace boost::spirit::arg_names; + using namespace boost::spirit::karma; + using namespace boost::spirit::ascii; + + typedef spirit_test::output_iterator::type outiter_type; + + // basic tests + { + rule start; + + start = char_[_1 = 'a'] << int_[_1 = 10] << double_[_1 = 12.4]; + BOOST_TEST(test("a1012.4", start)); + + start = (char_ << int_ << double_)[_1 = 'a', _2 = 10, _3 = 12.4]; + BOOST_TEST(test("a1012.4", start)); + + rule a, b, c; + a = char_[_1 = 'a']; + b = int_[_1 = 10]; + c = double_[_1 = 12.4]; + + start = a << b << c; + BOOST_TEST(test("a1012.4", start)); + } + + // basic tests involving a direct parameter + { + typedef variant var_type; + fusion::vector v (unused, 'a'); + + rule start; + + start = (char_ | int_ | double_)[_1 = _r1]; + BOOST_TEST(test("a", start, v)); + + v = fusion::vector(unused, 10); + BOOST_TEST(test("10", start, v)); + v = fusion::vector(unused, 12.4); + BOOST_TEST(test("12.4", start, v)); + } + + { + rule start; + fusion::vector vec(unused, 'a', 10, 12.4); + + start = char_[_1 = _r1] << int_[_1 = _r2] << double_[_1 = _r3]; + BOOST_TEST(test("a1012.4", start, vec)); + BOOST_TEST(test("a1012.4", start('a', 10, 12.4))); + + start = (char_ << int_ << double_)[_1 = _r1, _2 = _r2, _3 = _r3]; + BOOST_TEST(test("a1012.4", start, vec)); + BOOST_TEST(test("a1012.4", start('a', 10, 12.4))); + + rule a; + rule b; + rule c; + + a = char_[_1 = _r1]; + b = int_[_1 = _r1]; + c = double_[_1 = _r1]; + start = a(_r1) << b(_r2) << c(_r3); + BOOST_TEST(test("a1012.4", start, vec)); + BOOST_TEST(test("a1012.4", start('a', 10, 12.4))); + } + + // test rule parameter propagation + { + rule start; + fusion::vector vec(unused, 'a', 10, 12.4); + + start %= char_ << int_ << double_; + BOOST_TEST(test("a1012.4", start, vec)); + BOOST_TEST(test("a1012.4", start('a', 10, 12.4))); + + rule a; + rule b; + rule c; + + a %= char_ << eps; + b %= int_; + c %= double_; + start = a(_r1) << b(_r2) << c(_r3); + BOOST_TEST(test("a1012.4", start, vec)); + BOOST_TEST(test("a1012.4", start('a', 10, 12.4))); + } + + // basic tests with delimiter + { + rule start; + + start = char_[_1 = 'a'] << int_[_1 = 10] << double_[_1 = 12.4]; + BOOST_TEST(test_delimited("a 10 12.4 ", start, space)); + + start = (char_ << int_ << double_)[_1 = 'a', _2 = 10, _3 = 12.4]; + BOOST_TEST(test_delimited("a 10 12.4 ", start, space)); + + rule a, b, c; + a = char_[_1 = 'a']; + b = int_[_1 = 10]; + c = double_[_1 = 12.4]; + + start = a << b << c; + BOOST_TEST(test_delimited("a 10 12.4 ", start, space)); + } + + // locals test + { + rule > start; + + start = lit[_1 = "abc", _a = _1] << int_[_1 = 10] << lit[_1 = _a]; + BOOST_TEST(test("abc10abc", start)); + } + + //~ { // alias tests + //~ typedef variant var_type; + //~ fusion::vector v (unused, 'a'); + + //~ rule d, start; + + //~ d = start.alias(); // d will always track start + + //~ start = (char_ | int_ | double_)[_1 = _r1]; + //~ BOOST_TEST(test("a", d, v)); + + //~ v = fusion::vector(unused, 10); + //~ BOOST_TEST(test("10", d, v)); + //~ v = fusion::vector(unused, 12.4); + //~ BOOST_TEST(test("12.4", d, v)); + //~ } + + //~ { // copy tests + + //~ rule a, b, c, start; + + //~ a = 'a'; + //~ b = 'b'; + //~ c = 'c'; + + //~ // The FF is the dynamic equivalent of start = *(a | b | c); + //~ start = a; + //~ start = start.copy() | b; + //~ start = start.copy() | c; + //~ start = *(start.copy()); + + //~ BOOST_TEST(test("abcabcacb", start)); + + //~ // The FF is the dynamic equivalent of start = (a | b) >> (start | b); + //~ start = b; + //~ start = a | copy(start); + //~ start = start.copy() >> (start | b); + + //~ BOOST_TEST(test("aaaabababaaabbb", start)); + //~ BOOST_TEST(test("aaaabababaaabba", start, false)); + //~ } + + return boost::report_errors(); +} + diff --git a/test/karma/real_numerics.cpp b/test/karma/real_numerics.cpp new file mode 100644 index 000000000..a8a9dc378 --- /dev/null +++ b/test/karma/real_numerics.cpp @@ -0,0 +1,448 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +//#define KARMA_FAIL_COMPILATION + +#include +#include +#include + +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +// policy for real_generator, which forces the scientific notation +template +struct scientific_policy : boost::spirit::karma::real_generator_policies +{ + // we want the numbers always to be in scientific format + typedef boost::spirit::karma::real_generator_policies base_type; + static int floatfield(T n) { return base_type::scientific; } +}; + +/////////////////////////////////////////////////////////////////////////////// +// policy for real_generator, which forces the fixed notation +template +struct fixed_policy : boost::spirit::karma::real_generator_policies +{ + typedef boost::spirit::karma::real_generator_policies base_type; + + // we want the numbers always to be in scientific format + static int floatfield(T n) { return base_type::fixed; } +}; + +/////////////////////////////////////////////////////////////////////////////// +// policy for real_generator, which forces to output trailing zeros in the +// fractional part +template +struct trailing_zeros_policy + : boost::spirit::karma::real_generator_policies // 4 digits +{ + // we want the numbers always to contain trailing zeros up to 4 digits in + // the fractional part + static bool const trailing_zeros = true; + + // we want to generate up to 4 fractional digits + static unsigned int precision(T n) { return 4; } +}; + +/////////////////////////////////////////////////////////////////////////////// +// policy for real_generator, which forces the sign to be generated +template +struct signed_policy + : boost::spirit::karma::real_generator_policies +{ + // we want to always have a sign generated + static bool const force_sign = true; +}; + +/////////////////////////////////////////////////////////////////////////////// +// this is a workaround for the missing lround for the real_concept type +long lround(boost::math::concepts::real_concept n) +{ + if (n >= 0) + return boost::math::tools::real_cast(n + 0.5); + return boost::math::tools::real_cast(n - 0.5); +} + +/////////////////////////////////////////////////////////////////////////////// +// We need to specialize is_real_lit_tag to allow to use a real_concept as a +// literal below +namespace boost { namespace spirit +{ + template + struct is_real_lit_tag + : boost::mpl::true_ {}; +}} + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + + { + /////////////////////////////////////////////////////////////////////// + // use the default real_policies + BOOST_TEST(test("0.0", double_, 0.0)); + BOOST_TEST(test("1.0", double_, 1.0)); + BOOST_TEST(test("1.0", double_, 1.0001)); + BOOST_TEST(test("1.001", double_, 1.001)); + BOOST_TEST(test("1.01", double_, 1.010)); + BOOST_TEST(test("1.1", double_, 1.100)); + + BOOST_TEST(test("1.234e-04", double_, 0.00012345)); + BOOST_TEST(test("0.001", double_, 0.0012345)); + BOOST_TEST(test("0.012", double_, 0.012345)); + BOOST_TEST(test("0.123", double_, 0.12345)); + BOOST_TEST(test("1.234", double_, 1.2345)); + BOOST_TEST(test("12.346", double_, 12.346)); + BOOST_TEST(test("123.46", double_, 123.46)); + BOOST_TEST(test("1234.5", double_, 1234.5)); + BOOST_TEST(test("12342.0", double_, 12342.)); + BOOST_TEST(test("1.234e05", double_, 123420.)); + + BOOST_TEST(test("-1.0", double_, -1.0)); + BOOST_TEST(test("-1.234", double_, -1.2345)); + BOOST_TEST(test("-1.235", double_, -1.2346)); + BOOST_TEST(test("-1234.2", double_, -1234.2)); + + BOOST_TEST(test("1.0", double_(1.0))); + BOOST_TEST(test("1.0", double_(1.0001))); + BOOST_TEST(test("1.001", double_(1.001))); + BOOST_TEST(test("1.01", double_(1.010))); + BOOST_TEST(test("1.1", double_(1.100))); + + BOOST_TEST(test("1.234e-04", double_(0.00012345))); + BOOST_TEST(test("0.001", double_(0.0012345))); + BOOST_TEST(test("0.012", double_(0.012345))); + BOOST_TEST(test("0.123", double_(0.12345))); + BOOST_TEST(test("1.234", double_(1.2345))); + BOOST_TEST(test("12.346", double_(12.346))); + BOOST_TEST(test("123.46", double_(123.46))); + BOOST_TEST(test("1234.5", double_(1234.5))); + BOOST_TEST(test("12342.0", double_(12342.))); + BOOST_TEST(test("1.234e05", double_(123420.))); + } + + { + /////////////////////////////////////////////////////////////////////// + // test NaN and Inf + BOOST_TEST(test("nan", double_, std::numeric_limits::quiet_NaN())); + BOOST_TEST(test("-nan", double_, -std::numeric_limits::quiet_NaN())); + BOOST_TEST(test("inf", double_, std::numeric_limits::infinity())); + BOOST_TEST(test("-inf", double_, -std::numeric_limits::infinity())); + + typedef karma::real_spec > signed_type; + signed_type const signed_ = signed_type(); + + BOOST_TEST(test("+nan", signed_, std::numeric_limits::quiet_NaN())); + BOOST_TEST(test("-nan", signed_, -std::numeric_limits::quiet_NaN())); + BOOST_TEST(test("+inf", signed_, std::numeric_limits::infinity())); + BOOST_TEST(test("-inf", signed_, -std::numeric_limits::infinity())); + BOOST_TEST(test(" 0.0", signed_, 0.0)); + + BOOST_TEST(test("+nan", signed_(std::numeric_limits::quiet_NaN()))); + BOOST_TEST(test("-nan", signed_(-std::numeric_limits::quiet_NaN()))); + BOOST_TEST(test("+inf", signed_(std::numeric_limits::infinity()))); + BOOST_TEST(test("-inf", signed_(-std::numeric_limits::infinity()))); + BOOST_TEST(test(" 0.0", signed_(0.0))); + } + + { + /////////////////////////////////////////////////////////////////////// + typedef karma::real_spec > + trailing_zeros_type; + trailing_zeros_type const trail_zeros = trailing_zeros_type(); + + BOOST_TEST(test("0.0000", trail_zeros, 0.0)); + BOOST_TEST(test("1.0000", trail_zeros, 1.0)); + BOOST_TEST(test("1.0001", trail_zeros, 1.0001)); + BOOST_TEST(test("1.0010", trail_zeros, 1.001)); + BOOST_TEST(test("1.0100", trail_zeros, 1.010)); + BOOST_TEST(test("1.1000", trail_zeros, 1.100)); + + BOOST_TEST(test("1.2345e-04", trail_zeros, 0.00012345)); + BOOST_TEST(test("0.0012", trail_zeros, 0.0012345)); + BOOST_TEST(test("0.0123", trail_zeros, 0.012345)); + BOOST_TEST(test("0.1235", trail_zeros, 0.12345)); + BOOST_TEST(test("1.2345", trail_zeros, 1.2345)); + BOOST_TEST(test("12.3460", trail_zeros, 12.346)); + BOOST_TEST(test("123.4600", trail_zeros, 123.46)); + BOOST_TEST(test("1234.5000", trail_zeros, 1234.5)); + BOOST_TEST(test("12342.0000", trail_zeros, 12342.)); + BOOST_TEST(test("1.2342e05", trail_zeros, 123420.)); + + BOOST_TEST(test("-1.0000", trail_zeros, -1.0)); + BOOST_TEST(test("-1.2345", trail_zeros, -1.2345)); + BOOST_TEST(test("-1.2346", trail_zeros, -1.2346)); + BOOST_TEST(test("-1234.2000", trail_zeros, -1234.2)); + + BOOST_TEST(test("1.0000", trail_zeros(1.0))); + BOOST_TEST(test("1.0001", trail_zeros(1.0001))); + BOOST_TEST(test("1.0010", trail_zeros(1.001))); + BOOST_TEST(test("1.0100", trail_zeros(1.010))); + BOOST_TEST(test("1.1000", trail_zeros(1.100))); + + BOOST_TEST(test("1.2345e-04", trail_zeros(0.00012345))); + BOOST_TEST(test("0.0012", trail_zeros(0.0012345))); + BOOST_TEST(test("0.0123", trail_zeros(0.012345))); + BOOST_TEST(test("0.1235", trail_zeros(0.12345))); + BOOST_TEST(test("1.2345", trail_zeros(1.2345))); + BOOST_TEST(test("12.3460", trail_zeros(12.346))); + BOOST_TEST(test("123.4600", trail_zeros(123.46))); + BOOST_TEST(test("1234.5000", trail_zeros(1234.5))); + BOOST_TEST(test("12342.0000", trail_zeros(12342.))); + BOOST_TEST(test("1.2342e05", trail_zeros(123420.))); + } + + { + /////////////////////////////////////////////////////////////////////// + BOOST_TEST(test_delimited("0.0 ", double_, 0.0, char_(' '))); + BOOST_TEST(test_delimited("1.0 ", double_, 1.0, char_(' '))); + BOOST_TEST(test_delimited("1.0 ", double_, 1.0001, char_(' '))); + BOOST_TEST(test_delimited("1.001 ", double_, 1.001, char_(' '))); + BOOST_TEST(test_delimited("1.01 ", double_, 1.010, char_(' '))); + BOOST_TEST(test_delimited("1.1 ", double_, 1.100, char_(' '))); + + BOOST_TEST(test_delimited("1.234e-04 ", double_, 0.00012345, char_(' '))); + BOOST_TEST(test_delimited("0.001 ", double_, 0.0012345, char_(' '))); + BOOST_TEST(test_delimited("0.012 ", double_, 0.012345, char_(' '))); + BOOST_TEST(test_delimited("0.123 ", double_, 0.12345, char_(' '))); + BOOST_TEST(test_delimited("1.234 ", double_, 1.2345, char_(' '))); + BOOST_TEST(test_delimited("12.346 ", double_, 12.346, char_(' '))); + BOOST_TEST(test_delimited("123.46 ", double_, 123.46, char_(' '))); + BOOST_TEST(test_delimited("1234.5 ", double_, 1234.5, char_(' '))); + BOOST_TEST(test_delimited("12342.0 ", double_, 12342., char_(' '))); + BOOST_TEST(test_delimited("1.234e05 ", double_, 123420., char_(' '))); + + BOOST_TEST(test_delimited("-1.0 ", double_, -1.0, char_(' '))); + BOOST_TEST(test_delimited("-1.234 ", double_, -1.2345, char_(' '))); + BOOST_TEST(test_delimited("-1.235 ", double_, -1.2346, char_(' '))); + BOOST_TEST(test_delimited("-1234.2 ", double_, -1234.2, char_(' '))); + + BOOST_TEST(test_delimited("1.0 ", double_(1.0), char_(' '))); + BOOST_TEST(test_delimited("1.0 ", double_(1.0001), char_(' '))); + BOOST_TEST(test_delimited("1.001 ", double_(1.001), char_(' '))); + BOOST_TEST(test_delimited("1.01 ", double_(1.010), char_(' '))); + BOOST_TEST(test_delimited("1.1 ", double_(1.100), char_(' '))); + + BOOST_TEST(test_delimited("1.234e-04 ", double_(0.00012345), char_(' '))); + BOOST_TEST(test_delimited("0.001 ", double_(0.0012345), char_(' '))); + BOOST_TEST(test_delimited("0.012 ", double_(0.012345), char_(' '))); + BOOST_TEST(test_delimited("0.123 ", double_(0.12345), char_(' '))); + BOOST_TEST(test_delimited("1.234 ", double_(1.2345), char_(' '))); + BOOST_TEST(test_delimited("12.346 ", double_(12.346), char_(' '))); + BOOST_TEST(test_delimited("123.46 ", double_(123.46), char_(' '))); + BOOST_TEST(test_delimited("1234.5 ", double_(1234.5), char_(' '))); + BOOST_TEST(test_delimited("12342.0 ", double_(12342.), char_(' '))); + BOOST_TEST(test_delimited("1.234e05 ", double_(123420.), char_(' '))); + } + + { + /////////////////////////////////////////////////////////////////////// + // test NaN and Inf + BOOST_TEST(test_delimited("nan ", double_, + std::numeric_limits::quiet_NaN(), char_(' '))); + BOOST_TEST(test_delimited("-nan ", double_, + -std::numeric_limits::quiet_NaN(), char_(' '))); + BOOST_TEST(test_delimited("inf ", double_, + std::numeric_limits::infinity(), char_(' '))); + BOOST_TEST(test_delimited("-inf ", double_, + -std::numeric_limits::infinity(), char_(' '))); + + typedef karma::real_spec > signed_type; + signed_type const signed_ = signed_type(); + + BOOST_TEST(test_delimited("+nan ", signed_, + std::numeric_limits::quiet_NaN(), char_(' '))); + BOOST_TEST(test_delimited("-nan ", signed_, + -std::numeric_limits::quiet_NaN(), char_(' '))); + BOOST_TEST(test_delimited("+inf ", signed_, + std::numeric_limits::infinity(), char_(' '))); + BOOST_TEST(test_delimited("-inf ", signed_, + -std::numeric_limits::infinity(), char_(' '))); + BOOST_TEST(test_delimited(" 0.0 ", signed_, 0.0, char_(' '))); + } + + { + using namespace boost::spirit::ascii; + + /////////////////////////////////////////////////////////////////////// + typedef karma::real_spec > + science_type; + science_type const science = science_type(); + + BOOST_TEST(test("0.0e00", science, 0.0)); + BOOST_TEST(test("1.0e00", science, 1.0)); + + BOOST_TEST(test("1.234e-05", science, 0.000012345)); + BOOST_TEST(test("1.234e-04", science, 0.00012345)); + BOOST_TEST(test("1.234e-03", science, 0.0012345)); + BOOST_TEST(test("1.234e-02", science, 0.012345)); + BOOST_TEST(test("1.235e-01", science, 0.12345)); // note the rounding error! + BOOST_TEST(test("1.234e00", science, 1.2345)); + BOOST_TEST(test("1.235e01", science, 12.346)); + BOOST_TEST(test("1.235e02", science, 123.46)); + BOOST_TEST(test("1.234e03", science, 1234.5)); + BOOST_TEST(test("1.234e04", science, 12342.)); + BOOST_TEST(test("1.234e05", science, 123420.)); + + BOOST_TEST(test("-1.234e-05", science, -0.000012345)); + BOOST_TEST(test("-1.234e-04", science, -0.00012345)); + BOOST_TEST(test("-1.234e-03", science, -0.0012345)); + BOOST_TEST(test("-1.234e-02", science, -0.012345)); + BOOST_TEST(test("-1.235e-01", science, -0.12345)); // note the rounding error! + BOOST_TEST(test("-1.234e00", science, -1.2345)); + BOOST_TEST(test("-1.235e01", science, -12.346)); + BOOST_TEST(test("-1.235e02", science, -123.46)); + BOOST_TEST(test("-1.234e03", science, -1234.5)); + BOOST_TEST(test("-1.234e04", science, -12342.)); + BOOST_TEST(test("-1.234e05", science, -123420.)); + + BOOST_TEST(test("1.234E-05", upper[science], 0.000012345)); + BOOST_TEST(test("1.234E-04", upper[science], 0.00012345)); + BOOST_TEST(test("1.234E-03", upper[science], 0.0012345)); + BOOST_TEST(test("1.234E-02", upper[science], 0.012345)); + BOOST_TEST(test("1.235E-01", upper[science], 0.12345)); // note the rounding error! + BOOST_TEST(test("1.234E00", upper[science], 1.2345)); + BOOST_TEST(test("1.235E01", upper[science], 12.346)); + BOOST_TEST(test("1.235E02", upper[science], 123.46)); + BOOST_TEST(test("1.234E03", upper[science], 1234.5)); + BOOST_TEST(test("1.234E04", upper[science], 12342.)); + BOOST_TEST(test("1.234E05", upper[science], 123420.)); + + BOOST_TEST(test("-1.234E-05", upper[science], -0.000012345)); + BOOST_TEST(test("-1.234E-04", upper[science], -0.00012345)); + BOOST_TEST(test("-1.234E-03", upper[science], -0.0012345)); + BOOST_TEST(test("-1.234E-02", upper[science], -0.012345)); + BOOST_TEST(test("-1.235E-01", upper[science], -0.12345)); // note the rounding error! + BOOST_TEST(test("-1.234E00", upper[science], -1.2345)); + BOOST_TEST(test("-1.235E01", upper[science], -12.346)); + BOOST_TEST(test("-1.235E02", upper[science], -123.46)); + BOOST_TEST(test("-1.234E03", upper[science], -1234.5)); + BOOST_TEST(test("-1.234E04", upper[science], -12342.)); + BOOST_TEST(test("-1.234E05", upper[science], -123420.)); + } + + { + using namespace boost::spirit::ascii; + + /////////////////////////////////////////////////////////////////////// + typedef karma::real_spec > fixed_type; + fixed_type const fixed = fixed_type(); + + BOOST_TEST(test("0.0", fixed, 0.0)); + BOOST_TEST(test("1.0", fixed, 1.0)); + + BOOST_TEST(test("0.0", fixed, 0.000012345)); + BOOST_TEST(test("0.0", fixed, 0.00012345)); + BOOST_TEST(test("0.001", fixed, 0.0012345)); + BOOST_TEST(test("0.012", fixed, 0.012345)); + BOOST_TEST(test("0.123", fixed, 0.12345)); + BOOST_TEST(test("1.234", fixed, 1.2345)); + BOOST_TEST(test("12.345", fixed, 12.345)); + BOOST_TEST(test("123.45", fixed, 123.45)); + BOOST_TEST(test("1234.5", fixed, 1234.5)); + BOOST_TEST(test("12342.0", fixed, 12342.)); + BOOST_TEST(test("123420.0", fixed, 123420.)); + BOOST_TEST(test("123420000000000000000.0", fixed, 1.23420e20)); + + BOOST_TEST(test("0.0", fixed, -0.000012345)); + BOOST_TEST(test("0.0", fixed, -0.00012345)); + BOOST_TEST(test("-0.001", fixed, -0.0012345)); + BOOST_TEST(test("-0.012", fixed, -0.012345)); + BOOST_TEST(test("-0.123", fixed, -0.12345)); + BOOST_TEST(test("-1.234", fixed, -1.2345)); + BOOST_TEST(test("-12.346", fixed, -12.346)); + BOOST_TEST(test("-123.46", fixed, -123.46)); + BOOST_TEST(test("-1234.5", fixed, -1234.5)); + BOOST_TEST(test("-12342.0", fixed, -12342.)); + BOOST_TEST(test("-123420.0", fixed, -123420.)); + BOOST_TEST(test("-123420000000000000000.0", fixed, -1.23420e20)); + } + + { + using namespace boost::spirit::ascii; + + /////////////////////////////////////////////////////////////////////// + // test NaN and Inf + BOOST_TEST(test("NAN", upper[double_], + std::numeric_limits::quiet_NaN())); + BOOST_TEST(test("-NAN", upper[double_], + -std::numeric_limits::quiet_NaN())); + BOOST_TEST(test("INF", upper[double_], + std::numeric_limits::infinity())); + BOOST_TEST(test("-INF", upper[double_], + -std::numeric_limits::infinity())); + + typedef karma::real_spec > signed_type; + signed_type const signed_ = signed_type(); + + BOOST_TEST(test("+NAN", upper[signed_], + std::numeric_limits::quiet_NaN())); + BOOST_TEST(test("-NAN", upper[signed_], + -std::numeric_limits::quiet_NaN())); + BOOST_TEST(test("+INF", upper[signed_], + std::numeric_limits::infinity())); + BOOST_TEST(test("-INF", upper[signed_], + -std::numeric_limits::infinity())); + BOOST_TEST(test(" 0.0", upper[signed_], 0.0)); + } + + { + using boost::math::concepts::real_concept; + typedef karma::real_spec custom_type; + custom_type const custom = custom_type(); + + BOOST_TEST(test("0.0", custom, real_concept(0.0))); + BOOST_TEST(test("1.0", custom, real_concept(1.0))); + BOOST_TEST(test("1.0", custom, real_concept(1.0001))); + BOOST_TEST(test("1.001", custom, real_concept(1.001))); + BOOST_TEST(test("1.01", custom, real_concept(1.010))); + BOOST_TEST(test("1.1", custom, real_concept(1.100))); + + BOOST_TEST(test("1.234e-04", custom, real_concept(0.00012345))); + BOOST_TEST(test("0.001", custom, real_concept(0.0012345))); + BOOST_TEST(test("0.012", custom, real_concept(0.012345))); + BOOST_TEST(test("0.123", custom, real_concept(0.12345))); + BOOST_TEST(test("1.234", custom, real_concept(1.2345))); + BOOST_TEST(test("12.346", custom, real_concept(12.346))); + BOOST_TEST(test("123.46", custom, real_concept(123.46))); + BOOST_TEST(test("1234.5", custom, real_concept(1234.5))); + BOOST_TEST(test("12342.0", custom, real_concept(12342.))); + BOOST_TEST(test("1.234e05", custom, real_concept(123420.))); + + BOOST_TEST(test("-1.0", custom, real_concept(-1.0))); + BOOST_TEST(test("-1.234", custom, real_concept(-1.2345))); + BOOST_TEST(test("-1.235", custom, real_concept(-1.2346))); + BOOST_TEST(test("-1234.2", custom, real_concept(-1234.2))); + + BOOST_TEST(test("1.0", custom(real_concept(1.0)))); + BOOST_TEST(test("1.0", custom(real_concept(1.0001)))); + BOOST_TEST(test("1.001", custom(real_concept(1.001)))); + BOOST_TEST(test("1.01", custom(real_concept(1.010)))); + BOOST_TEST(test("1.1", custom(real_concept(1.100)))); + + BOOST_TEST(test("1.234e-04", custom(real_concept(0.00012345)))); + BOOST_TEST(test("0.001", custom(real_concept(0.0012345)))); + BOOST_TEST(test("0.012", custom(real_concept(0.012345)))); + BOOST_TEST(test("0.123", custom(real_concept(0.12345)))); + BOOST_TEST(test("1.234", custom(real_concept(1.2345)))); + BOOST_TEST(test("12.346", custom(real_concept(12.346)))); + BOOST_TEST(test("123.46", custom(real_concept(123.46)))); + BOOST_TEST(test("1234.5", custom(real_concept(1234.5)))); + BOOST_TEST(test("12342.0", custom(real_concept(12342.)))); + BOOST_TEST(test("1.234e05", custom(real_concept(123420.)))); + } + + return boost::report_errors(); +} diff --git a/test/karma/right_alignment.cpp b/test/karma/right_alignment.cpp new file mode 100644 index 000000000..3a1343bdf --- /dev/null +++ b/test/karma/right_alignment.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + + { + BOOST_TEST(test(" x", right_align[char_('x')])); + BOOST_TEST(test(" x", right_align[char_], 'x')); + BOOST_TEST(test(" x", right_align['x'])); + + BOOST_TEST(test(" x", right_align(10)[char_('x')])); + BOOST_TEST(test(" x", right_align(10)[char_], 'x')); + BOOST_TEST(test(" x", right_align(10)['x'])); + + BOOST_TEST(test("*********x", right_align(10, char_('*'))[char_('x')])); + BOOST_TEST(test("*********x", right_align(10, '*')[char_], 'x')); + BOOST_TEST(test("*********x", right_align(10, '*')['x'])); + + BOOST_TEST(test("*********x", right_align(char_('*'))[char_('x')])); + BOOST_TEST(test("*********x", right_align(char_('*'))[char_], 'x')); + BOOST_TEST(test("*********x", right_align(char_('*'))['x'])); + + BOOST_TEST(test(" abc", right_align[lit("abc")])); + BOOST_TEST(test(" abc", right_align[lit], "abc")); + + BOOST_TEST(test(" abc", right_align(10)[lit("abc")])); + BOOST_TEST(test(" abc", right_align(10)[lit], "abc")); + BOOST_TEST(test(" abc", right_align(10)["abc"])); + + BOOST_TEST(test("*******abc", right_align(10, char_('*'))[lit("abc")])); + BOOST_TEST(test("*******abc", right_align(10, '*')[lit], "abc")); + BOOST_TEST(test("*******abc", right_align(10, '*')["abc"])); + + BOOST_TEST(test("*******abc", right_align(char_('*'))[lit("abc")])); + BOOST_TEST(test("*******abc", right_align(char_('*'))[lit], "abc")); + BOOST_TEST(test("*******abc", right_align(char_('*'))["abc"])); + + BOOST_TEST(test(" 100", right_align[int_(100)])); + BOOST_TEST(test(" 100", right_align[int_], 100)); + + BOOST_TEST(test(" 100", right_align(10)[int_(100)])); + BOOST_TEST(test(" 100", right_align(10)[int_], 100)); + + BOOST_TEST(test("*******100", right_align(10, char_('*'))[int_(100)])); + BOOST_TEST(test("*******100", right_align(10, '*')[int_], 100)); + + BOOST_TEST(test("*******100", right_align(char_('*'))[int_(100)])); + BOOST_TEST(test("*******100", right_align(char_('*'))[int_], 100)); + } + + return boost::report_errors(); +} diff --git a/test/karma/rule_fail.cpp b/test/karma/rule_fail.cpp new file mode 100644 index 000000000..c22a1c5d7 --- /dev/null +++ b/test/karma/rule_fail.cpp @@ -0,0 +1,36 @@ +/*============================================================================= + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#include +#include +#include +#include +#include + +#include "test.hpp" + +using namespace boost::spirit; +using namespace boost::spirit::karma; +using namespace boost::spirit::ascii; + +// this test must fail compiling +int main() +{ + using boost::make_function_output_iterator; + using spirit_test::make_string_appender; + + std::string generated; + + rule > def; + def = int_(1) << ',' << int_(0); + + bool r = generate_delimited( + make_function_output_iterator(make_string_appender(generated)), + def, char_('%') << '\n'); + + return 0; +} diff --git a/test/karma/sequence.cpp b/test/karma/sequence.cpp new file mode 100644 index 000000000..94461db27 --- /dev/null +++ b/test/karma/sequence.cpp @@ -0,0 +1,130 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// #define KARMA_TEST_COMPILE_FAIL + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + namespace fusion = boost::fusion; + + { + { + BOOST_TEST(test("xi", char_('x') << char_('i'))); + BOOST_TEST(!test("xi", char_('x') << char_('o'))); + } + + { + BOOST_TEST(test_delimited("x i ", char_('x') << 'i', char(' '))); + BOOST_TEST(!test_delimited("x i ", + char_('x') << char_('o'), char(' '))); + } + + { + BOOST_TEST(test_delimited("Hello , World ", + lit("Hello") << ',' << "World", char(' '))); + } + + { + fusion::vector p ('a', 'b', "cdefg"); + BOOST_TEST(test("abcdefg", char_ << char_ << lit, p)); + BOOST_TEST(test_delimited("a b cdefg ", + char_ << char_ << lit, p, char(' '))); + } + + { + fusion::vector p ('a', 12, 'c'); + BOOST_TEST(test("a12c", char_ << int_ << char_, p)); + BOOST_TEST(test_delimited("a 12 c ", + char_ << int_ << char_, p, char(' '))); + } + + { + // if all elements of a sequence have unused parameters, the whole + // sequence has an unused parameter as well + fusion::vector p ('a', 'e'); + BOOST_TEST(test("abcde", + char_ << (char_('b') << 'c' << 'd') << char_, p)); + BOOST_TEST(test_delimited("a b c d e ", + char_ << (char_('b') << 'c' << 'd') << char_, p, char(' '))); + } + + { + // literal generators do not need a parameter + fusion::vector p('a', 'c'); + BOOST_TEST(test("abc", char_ << 'b' << char_, p)); + BOOST_TEST(test_delimited("a b c ", + char_ << 'b' << char_, p, char(' '))); + } + + { + using namespace boost::spirit::ascii; + + BOOST_TEST(test("aa", lower[char_('A') << 'a'])); + BOOST_TEST(test_delimited("BEGIN END ", + upper[lit("begin") << "end"], char(' '))); + BOOST_TEST(!test_delimited("BEGIN END ", + upper[lit("begin") << "nend"], char(' '))); + + BOOST_TEST(test("Aa ", left_align[char_('A') << 'a'])); + BOOST_TEST(test(" Aa ", center[char_('A') << 'a'])); + BOOST_TEST(test(" Aa", right_align[char_('A') << 'a'])); + } + + // action tests + { + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + using namespace boost::spirit::ascii; + + BOOST_TEST(test("abcdefg", + (char_ << char_ << lit)[_1 = 'a', _2 = 'b', _3 = "cdefg"])); + BOOST_TEST(test_delimited("a b cdefg ", + (char_ << char_ << lit)[_1 = 'a', _2 = 'b', _3 = "cdefg"], + char(' '))); + + BOOST_TEST(test_delimited("a 12 c ", + (char_ << int_(12) << char_)[_1 = 'a', _2 = 'c'], char(' '))); + + char c = 'c'; + BOOST_TEST(test("abc", + (char_[_1 = 'a'] << 'b' << char_)[_1 = 'x', _2 = ref(c)])); + BOOST_TEST(test_delimited("a b c ", + (char_[_1 = 'a'] << 'b' << char_)[_2 = ref(c)], char(' '))); + + BOOST_TEST(test("aa", lower[char_ << 'A'][_1 = 'A'])); + BOOST_TEST(test("AA", upper[char_ << 'a'][_1 = 'a'])); + + BOOST_TEST(test("Aa ", left_align[char_ << 'a'][_1 = 'A'])); + BOOST_TEST(test(" Aa ", center[char_ << 'a'][_1 = 'A'])); + BOOST_TEST(test(" Aa", right_align[char_ << 'a'][_1 = 'A'])); + } + } + + return boost::report_errors(); +} + diff --git a/test/karma/test.hpp b/test/karma/test.hpp new file mode 100644 index 000000000..740e5d3f3 --- /dev/null +++ b/test/karma/test.hpp @@ -0,0 +1,290 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#if !defined(BOOST_SPIRIT_KARMA_TEST_FEB_23_2007_1221PM) +#define BOOST_SPIRIT_KARMA_TEST_FEB_23_2007_1221PM + +#include +#include +#include + +#include +#include +#include + +namespace spirit_test +{ + + /////////////////////////////////////////////////////////////////////////// + struct display_type + { + template + void operator()(T const &) const + { + std::cout << typeid(T).name() << std::endl; + } + + template + static void print() + { + std::cout << typeid(T).name() << std::endl; + } + }; + + display_type const display = {}; + + /////////////////////////////////////////////////////////////////////////// + template + struct string_appender + { + string_appender(String& s) + : str(s) + {} + + template + void operator()(T const &x) const + { + str += x; + } + + String& str; + }; + + template + inline string_appender + make_string_appender(String& str) + { + return string_appender(str); + } + + template + struct output_iterator + { + typedef std::basic_string string_type; + typedef string_appender appender_type; + typedef boost::function_output_iterator type; + }; + + /////////////////////////////////////////////////////////////////////////// + template + inline bool test(Char const *expected, Generator const& g) + { + // we don't care about the result of the "what" function. + // we only care that all generators have it: + boost::spirit::karma::what(g); + + using boost::make_function_output_iterator; + namespace karma = boost::spirit::karma; + + typedef std::basic_string string_type; + + string_type generated; + + bool result = karma::generate(make_function_output_iterator( + make_string_appender(generated)), g); + + return result && generated == expected; + } + + template + inline bool test(std::basic_string const& expected, Generator const& g) + { + // we don't care about the result of the "what" function. + // we only care that all generators have it: + boost::spirit::karma::what(g); + + using boost::make_function_output_iterator; + namespace karma = boost::spirit::karma; + + typedef std::basic_string string_type; + + string_type generated; + + bool result = karma::generate(make_function_output_iterator( + make_string_appender(generated)), g); + + return result && generated == expected; + } + + /////////////////////////////////////////////////////////////////////////// + template + inline bool test(Char const *expected, Generator const& g, + Parameter const ¶meter) + { + // we don't care about the result of the "what" function. + // we only care that all generators have it: + boost::spirit::karma::what(g); + + using boost::make_function_output_iterator; + namespace karma = boost::spirit::karma; + + typedef std::basic_string string_type; + + string_type generated; + + bool result = karma::generate(make_function_output_iterator( + make_string_appender(generated)), g, parameter); + + return result && generated == expected; + } + + template + inline bool test(std::basic_string const& expected, Generator const& g, + Parameter const ¶meter) + { + // we don't care about the result of the "what" function. + // we only care that all generators have it: + boost::spirit::karma::what(g); + + using boost::make_function_output_iterator; + namespace karma = boost::spirit::karma; + + typedef std::basic_string string_type; + + string_type generated; + + bool result = karma::generate(make_function_output_iterator( + make_string_appender(generated)), g, parameter); + + return result && generated == expected; + } + + /////////////////////////////////////////////////////////////////////////// + template + inline bool test_delimited(Char const *expected, Generator const& g, + Delimiter const& d) + { + // we don't care about the result of the "what" function. + // we only care that all generators have it: + boost::spirit::karma::what(g); + + using boost::make_function_output_iterator; + namespace karma = boost::spirit::karma; + + typedef std::basic_string string_type; + + string_type generated; + + bool result = karma::generate_delimited(make_function_output_iterator( + make_string_appender(generated)), g, d); + + return result && generated == expected; + } + + template + inline bool test_delimited(std::basic_string const& expected, + Generator const& g, Delimiter const& d) + { + // we don't care about the result of the "what" function. + // we only care that all generators have it: + boost::spirit::karma::what(g); + + using boost::make_function_output_iterator; + namespace karma = boost::spirit::karma; + + typedef std::basic_string string_type; + + string_type generated; + + bool result = karma::generate_delimited(make_function_output_iterator( + make_string_appender(generated)), g, d); + + return result && generated == expected; + } + + /////////////////////////////////////////////////////////////////////////// + template + inline bool test_delimited(Char const *expected, Generator const& g, + Parameter const ¶meter, Delimiter const& d) + { + // we don't care about the result of the "what" function. + // we only care that all generators have it: + boost::spirit::karma::what(g); + + using boost::make_function_output_iterator; + namespace karma = boost::spirit::karma; + + typedef std::basic_string string_type; + + string_type generated; + + bool result = karma::generate_delimited(make_function_output_iterator( + make_string_appender(generated)), g, parameter, d); + + return result && generated == expected; + } + + template + inline bool test_delimited(std::basic_string const& expected, + Generator const& g, Parameter const ¶meter, Delimiter const& d) + { + // we don't care about the result of the "what" function. + // we only care that all generators have it: + boost::spirit::karma::what(g); + + using boost::make_function_output_iterator; + namespace karma = boost::spirit::karma; + + typedef std::basic_string string_type; + + string_type generated; + + bool result = karma::generate_delimited(make_function_output_iterator( + make_string_appender(generated)), g, parameter, d); + + return result && generated == expected; + } + + /////////////////////////////////////////////////////////////////////////// + template + inline bool + binary_test(char const *expected, std::size_t size, + Generator const& g) + { + // we don't care about the result of the "what" function. + // we only care that all generators have it: + boost::spirit::karma::what(g); + + using boost::make_function_output_iterator; + namespace karma = boost::spirit::karma; + + typedef std::basic_string string_type; + + string_type generated; + + bool result = karma::generate(make_function_output_iterator( + make_string_appender(generated)), g); + + return result && !std::memcmp(generated.c_str(), expected, size); + } + + /////////////////////////////////////////////////////////////////////////// + template + inline bool + binary_test(char const *expected, std::size_t size, + Generator const& g, Parameter const ¶meter) + { + // we don't care about the result of the "what" function. + // we only care that all generators have it: + boost::spirit::karma::what(g); + + using boost::make_function_output_iterator; + namespace karma = boost::spirit::karma; + + typedef std::basic_string string_type; + + string_type generated; + + bool result = karma::generate(make_function_output_iterator( + make_string_appender(generated)), g, parameter); + + return result && !std::memcmp(generated.c_str(), expected, size); + } + +} // namespace spirit_test + +#endif // !BOOST_SPIRIT_KARMA_TEST_FEB_23_2007_1221PM diff --git a/test/lex/lexertl1.cpp b/test/lex/lexertl1.cpp new file mode 100644 index 000000000..feb8d16e2 --- /dev/null +++ b/test/lex/lexertl1.cpp @@ -0,0 +1,72 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "test.hpp" + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + using namespace spirit_test; + + // the following test aims at the low level lexer and token_def objects, + // normally not visible to/directly used by the user + + // initialize tokens + typedef lex::token_def token_def; + + std::size_t const CCOMMENT = 1; + std::size_t const CPPCOMMENT = 2; + token_def c_comment ("\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/", CCOMMENT); + token_def cpp_comment ("\\/\\/[^\\n\\r]*(\\n|\\r|\\r\\n)", CPPCOMMENT); + + typedef std::string::iterator base_iterator_type; + typedef lex::lexertl_token token_type; + typedef lex::lexertl_lexer lexer_type; + typedef lex::lexer_def lexer_def; + + { + // initialize lexer + lexer_def def; + def.self = c_comment; + def.self += cpp_comment; + + // test lexer for two different input strings + lex::lexer lex(def); + BOOST_TEST(test (lex, "/* this is a comment */", CCOMMENT)); + BOOST_TEST(test (lex, "// this is a comment as well\n", CPPCOMMENT)); + } + + { + // initialize lexer + lexer_def def; + def.self = c_comment | cpp_comment; + + // test lexer for two different input strings + lex::lexer lex(def); + BOOST_TEST(test (lex, "/* this is a comment */", CCOMMENT)); + BOOST_TEST(test (lex, "// this is a comment as well\n", CPPCOMMENT)); + } + + { + // initialize lexer + lexer_def def; + def.self = token_def('+') | '-' | c_comment; + def.self += '*' | cpp_comment; + + // test lexer for two different input strings + lex::lexer lex(def); + BOOST_TEST(test (lex, "/* this is a comment */", CCOMMENT)); + BOOST_TEST(test (lex, "// this is a comment as well\n", CPPCOMMENT)); + BOOST_TEST(test (lex, "+", '+')); + BOOST_TEST(test (lex, "-", '-')); + BOOST_TEST(test (lex, "*", '*')); + } + + return boost::report_errors(); +} diff --git a/test/lex/lexertl2.cpp b/test/lex/lexertl2.cpp new file mode 100644 index 000000000..c304f4db6 --- /dev/null +++ b/test/lex/lexertl2.cpp @@ -0,0 +1,96 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include + +#include "test.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// a simple lexer class +template +struct lexertl_test : boost::spirit::lex::lexer_def +{ + typedef boost::spirit::lex::token_def token_def; + + static std::size_t const CCOMMENT = 1; + static std::size_t const CPPCOMMENT = 2; + + template + void def(Self& self) + { + c_comment = token_def("\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/", CCOMMENT); + cpp_comment = token_def("\\/\\/[^\\n\\r]*(\\n|\\r|\\r\\n)", CPPCOMMENT); + + self = c_comment; + self += cpp_comment; + } + + token_def c_comment, cpp_comment; +}; + +template +struct wlexertl_test : boost::spirit::lex::lexer_def +{ + typedef + boost::spirit::lex::token_def, wchar_t> + token_def; + + static std::size_t const CCOMMENT = 1; + static std::size_t const CPPCOMMENT = 2; + + template + void def(Self& self) + { + c_comment = token_def(L"\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/", CCOMMENT); + cpp_comment = token_def(L"\\/\\/[^\\n\\r]*(\\n|\\r|\\r\\n)", CPPCOMMENT); + + self = c_comment; + self += cpp_comment; + } + + token_def c_comment, cpp_comment; +}; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + using namespace spirit_test; + + // the following test aims at the low level lexer_ and token_ objects, + // normally not visible/used by the user + { + // initialize lexer + typedef std::string::iterator base_iterator_type; + typedef lex::lexertl_token token_type; + typedef lex::lexertl_lexer lexer_type; + typedef lexertl_test lexer_def; + + // test lexer for two different input strings + lexer_def def; + lex::lexer lex(def); + BOOST_TEST(test (lex, "/* this is a comment */", lexer_def::CCOMMENT)); + BOOST_TEST(test (lex, "// this is a comment as well\n", lexer_def::CPPCOMMENT)); + } + + { + // initialize lexer + typedef std::basic_string::iterator base_iterator_type; + typedef lex::lexertl_token token_type; + typedef lex::lexertl_lexer lexer_type; + typedef wlexertl_test lexer_def; + + // test lexer for two different input strings + lexer_def def; + lex::lexer lex(def); + BOOST_TEST(test (lex, L"/* this is a comment */", lexer_def::CCOMMENT)); + BOOST_TEST(test (lex, L"// this is a comment as well\n", lexer_def::CPPCOMMENT)); + } + + return boost::report_errors(); +} diff --git a/test/lex/lexertl3.cpp b/test/lex/lexertl3.cpp new file mode 100644 index 000000000..616637781 --- /dev/null +++ b/test/lex/lexertl3.cpp @@ -0,0 +1,78 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "test.hpp" + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::lex; + using namespace spirit_test; + + // initialize tokens + typedef lex::token_def token_def; + + std::size_t const CCOMMENT = 1; + std::size_t const CPPCOMMENT = 2; + token_def c_comment ("\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/", CCOMMENT); + token_def cpp_comment ("\\/\\/[^\\n\\r]*(\\n|\\r|\\r\\n)", CPPCOMMENT); + token_def ws_tok ("[\\v\\f\\n\\r]*"); + + typedef std::string::iterator base_iterator_type; + typedef lex::lexertl_token token_type; + typedef lex::lexertl_lexer lexer_type; + + typedef lexer_type::token_set token_set; + typedef lex::lexer_def lexer_def; + + token_set s; + lexer_def def; + + { + // initialize lexer + std::string str("def"); + lexer_def def; + def.self = c_comment; + def.self += cpp_comment | '1' | '2' | '3' | "abc" | str; + def.self += token_def(' ') | '\t' | ws_tok; + + // test lexer for two different input strings + lex::lexer lex(def); + BOOST_TEST(test (lex, "/* this is a comment */", CCOMMENT)); + BOOST_TEST(test (lex, "// this is a comment as well\n", CPPCOMMENT)); + BOOST_TEST(test (lex, "\n\n\v\f\r", ws_tok.id())); + BOOST_TEST(test (lex, " ", ' ')); + BOOST_TEST(test (lex, "2", '2')); + BOOST_TEST(test (lex, "abc")); + BOOST_TEST(test (lex, "def")); + } + + { + // init a token set + token_set ws; + ws = token_def(' ') | '\t' | ws_tok; + + // initialize lexer + lexer_def def; + def.self = c_comment; + def.self += cpp_comment | '1' | '2' | '3'; + def.self("WHITESPACE") = ws; + + // test lexer for two different input strings + lex::lexer lex(def); + BOOST_TEST(test (lex, "/* this is a comment */", CCOMMENT)); + BOOST_TEST(test (lex, "// this is a comment as well\n", CPPCOMMENT)); + BOOST_TEST(test (lex, "2", '2')); + BOOST_TEST(!test (lex, "\n\n\v\f\r", ws_tok.id())); + BOOST_TEST(test (lex, " ", ' ', "WHITESPACE")); + BOOST_TEST(test (lex, "\n\n\v\f\r", ws_tok.id(), "WHITESPACE")); + } + + return boost::report_errors(); +} diff --git a/test/lex/lexertl4.cpp b/test/lex/lexertl4.cpp new file mode 100644 index 000000000..0ab9674e9 --- /dev/null +++ b/test/lex/lexertl4.cpp @@ -0,0 +1,96 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "test.hpp" + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::lex; + using namespace spirit_test; + + // initialize tokens + typedef lex::token_def token_def; + + std::size_t const CCOMMENT = 1; + std::size_t const CPPCOMMENT = 2; + std::size_t const TOKEN_ID_ABC = 1000; + std::size_t const TOKEN_ID_STR = 1001; + std::size_t const TOKEN_ID_WS = 1002; + + token_def c_comment ("\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/", CCOMMENT); + token_def cpp_comment ("\\/\\/[^\\n\\r]*(\\n|\\r|\\r\\n)", CPPCOMMENT); + token_def ws_tok ("[\\v\\f\\n\\r]*", TOKEN_ID_WS); + + typedef std::string::iterator base_iterator_type; + typedef lex::lexertl_token token_type; + typedef lex::lexertl_lexer lexer_type; + + typedef lexer_type::token_set token_set; + typedef lex::lexer_def lexer_def; + + token_set s; + std::string str("def"); + + { + // initialize lexer + lexer_def def; + def.self.add + (c_comment)(cpp_comment) + ('1')('2')('3') + ("abc", TOKEN_ID_ABC) + (str, TOKEN_ID_STR) + ; + def.self += token_def(' ') | '\t' | ws_tok; + + // test lexer for different input strings + lex::lexer lex(def); + BOOST_TEST(test (lex, "/* this is a comment */", CCOMMENT)); + BOOST_TEST(test (lex, "// this is a comment as well\n", CPPCOMMENT)); + BOOST_TEST(test (lex, "\n\n\v\f\r", ws_tok.id())); + BOOST_TEST(test (lex, " ", ' ')); + BOOST_TEST(test (lex, "2", '2')); + BOOST_TEST(test (lex, "abc", TOKEN_ID_ABC)); + BOOST_TEST(test (lex, "def", TOKEN_ID_STR)); + } + + { + // init a token set + token_set ws; + ws.add + (' ')('\t') + (ws_tok) + ; + + // initialize lexer + lexer_def def; + def.self.add + (c_comment)(cpp_comment) + ('1')('2')('3') + ("abc", TOKEN_ID_ABC) + (str, TOKEN_ID_STR) + ; + + def.self("WHITESPACE").add(ws); + + // test lexer for different input strings + lex::lexer lex(def); + BOOST_TEST(test (lex, "/* this is a comment */", CCOMMENT)); + BOOST_TEST(test (lex, "// this is a comment as well\n", CPPCOMMENT)); + BOOST_TEST(test (lex, "2", '2')); + BOOST_TEST(test (lex, "abc", TOKEN_ID_ABC)); + BOOST_TEST(test (lex, "def", TOKEN_ID_STR)); + + BOOST_TEST(!test (lex, "\n\n\v\f\r", TOKEN_ID_WS)); + BOOST_TEST(test (lex, " ", ' ', "WHITESPACE")); + BOOST_TEST(test (lex, "\n\n\v\f\r", TOKEN_ID_WS, "WHITESPACE")); + } + + return boost::report_errors(); +} diff --git a/test/lex/lexertl5.cpp b/test/lex/lexertl5.cpp new file mode 100644 index 000000000..5ac26487d --- /dev/null +++ b/test/lex/lexertl5.cpp @@ -0,0 +1,115 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include "test.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// test pattern definition capabilities +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::lex; + using namespace spirit_test; + + // initialize tokens + typedef lex::token_def token_def; + + std::size_t const CCOMMENT = 1; + std::size_t const CPPCOMMENT = 2; + std::size_t const TOKEN_ID_ABC = 1000; + std::size_t const TOKEN_ID_STR = 1001; + std::size_t const TOKEN_ID_WS = 1002; + + typedef std::string::iterator base_iterator_type; + typedef lex::lexertl_token token_type; + typedef lex::lexertl_lexer lexer_type; + + typedef lexer_type::token_set token_set; + typedef lex::lexer_def lexer_def; + + token_set s; + std::string str("def"); + + { + // initialize lexer + lexer_def def; + + def.self.add_pattern + ("CCOMMENT", "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/") + ("CPPCOMMENT", "\\/\\/[^\\n\\r]*(\\n|\\r|\\r\\n)") + ("WS", "[\\v\\f\\n\\r]*") + ; + + token_def c_comment ("{CCOMMENT}", CCOMMENT); + token_def cpp_comment ("{CPPCOMMENT}", CPPCOMMENT); + token_def ws_tok ("{WS}"); + + def.self.add + (c_comment)(cpp_comment) + ('1')('2')('3') + ("abc", TOKEN_ID_ABC) + (str, TOKEN_ID_STR) + ; + def.self += token_def(' ') | '\t' | ws_tok; + + // test lexer for different input strings + lex::lexer lex(def); + BOOST_TEST(test (lex, "/* this is a comment */", CCOMMENT)); + BOOST_TEST(test (lex, "// this is a comment as well\n", CPPCOMMENT)); + BOOST_TEST(test (lex, "\n\n\v\f\r", ws_tok.id())); + BOOST_TEST(test (lex, " ", ' ')); + BOOST_TEST(test (lex, "2", '2')); + BOOST_TEST(test (lex, "abc", TOKEN_ID_ABC)); + BOOST_TEST(test (lex, "def", TOKEN_ID_STR)); + } + + { + // initialize lexer + lexer_def def; + + def.self.add_pattern + ("CCOMMENT", "\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\/") + ("CPPCOMMENT", "\\/\\/[^\\n\\r]*(\\n|\\r|\\r\\n)") + ("WS", "[\\v\\f\\n\\r]*") + ; + + token_def c_comment ("{CCOMMENT}", CCOMMENT); + token_def cpp_comment ("{CPPCOMMENT}", CPPCOMMENT); + token_def ws_tok ("{WS}"); + + // init a token set + token_set ws; + ws.add + (' ')('\t') + (ws_tok, TOKEN_ID_WS) + ; + + def.self.add + (c_comment)(cpp_comment) + ('1')('2')('3') + ("abc", TOKEN_ID_ABC) + (str, TOKEN_ID_STR) + ; + + def.self("WHITESPACE").add(ws); + + // test lexer for different input strings + lex::lexer lex(def); + BOOST_TEST(test (lex, "/* this is a comment */", CCOMMENT)); + BOOST_TEST(test (lex, "// this is a comment as well\n", CPPCOMMENT)); + BOOST_TEST(test (lex, "2", '2')); + BOOST_TEST(test (lex, "abc", TOKEN_ID_ABC)); + BOOST_TEST(test (lex, "def", TOKEN_ID_STR)); + + BOOST_TEST(!test (lex, "\n\n\v\f\r", TOKEN_ID_WS)); + BOOST_TEST(test (lex, " ", ' ', "WHITESPACE")); + BOOST_TEST(test (lex, "\n\n\v\f\r", TOKEN_ID_WS, "WHITESPACE")); + } + + return boost::report_errors(); +} diff --git a/test/lex/state_switcher_test.cpp b/test/lex/state_switcher_test.cpp new file mode 100644 index 000000000..72bd9c181 --- /dev/null +++ b/test/lex/state_switcher_test.cpp @@ -0,0 +1,92 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include "test_parser.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// Token definition +/////////////////////////////////////////////////////////////////////////////// +template +struct switch_state_tokens : boost::spirit::lex::lexer_def +{ + template + void def (Self& self) + { + // define tokens and associate them with the lexer + identifier = "[a-zA-Z_][a-zA-Z0-9_]*"; + self = identifier; + + // any token definition to be used as the skip parser during parsing + // has to be associated with a separate lexer state (here 'WS') + white_space = "[ \\t\\n]+"; + self("WS") = white_space; + + separators = "[,;]"; + self("SEP") = separators; + } + + boost::spirit::lex::token_def<> identifier, white_space, separators; +}; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::qi; + using namespace boost::spirit::lex; + using namespace spirit_test; + + typedef std::string::iterator base_iterator_type; + typedef boost::spirit::lex::lexertl_token token_type; + typedef boost::spirit::lex::lexertl_lexer lexer_type; + + { + // the tokens class will be initialized inside the test_parser function + switch_state_tokens tokens; + + BOOST_TEST(test_parser("ident", tokens.identifier, tokens)); + BOOST_TEST(!test_parser("ident", set_state("WS") >> tokens.identifier, tokens)); + BOOST_TEST(!test_parser("ident", in_state("WS")[tokens.identifier], tokens)); + + BOOST_TEST(test_parser("\t \n", set_state("WS") >> tokens.white_space, tokens)); + BOOST_TEST(test_parser("\t \n", in_state("WS")[tokens.white_space], tokens)); + BOOST_TEST(!test_parser("\t \n", tokens.white_space, tokens)); + } + + { + // the tokens class will be initialized inside the test_parser function + switch_state_tokens tokens; + + BOOST_TEST(test_parser(",ident", tokens.identifier, tokens, + in_state("SEP")[tokens.separators])); + BOOST_TEST(!test_parser(";ident", set_state("WS") >> tokens.identifier, + tokens, in_state("SEP")[tokens.separators])); + BOOST_TEST(!test_parser(",ident", in_state("WS")[tokens.identifier], + tokens, in_state("SEP")[tokens.separators])); + + BOOST_TEST(test_parser(",\t \n", set_state("WS") >> tokens.white_space, + tokens, in_state("SEP")[tokens.separators])); + BOOST_TEST(test_parser(";\t \n", in_state("WS")[tokens.white_space], + tokens, in_state("SEP")[tokens.separators])); + BOOST_TEST(!test_parser(",\t \n", tokens.white_space, tokens, + in_state("SEP")[tokens.separators])); + } + + { + // the tokens class will be initialized inside the test_parser function + switch_state_tokens tokens; + + BOOST_TEST(test_parser("ident\t \n", + tokens.identifier >> set_state("WS") >> tokens.white_space, tokens)); + BOOST_TEST(test_parser("\t \nident", + in_state("WS")[tokens.white_space] >> tokens.identifier, tokens)); + } + + return boost::report_errors(); +} diff --git a/test/lex/test.hpp b/test/lex/test.hpp new file mode 100644 index 000000000..2a587e1b5 --- /dev/null +++ b/test/lex/test.hpp @@ -0,0 +1,89 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#if !defined(BOOST_SPIRIT_LEX_TEST_MAR_23_2007_0721PM) +#define BOOST_SPIRIT_LEX_TEST_MAR_23_2007_0721PM + +namespace spirit_test +{ + /////////////////////////////////////////////////////////////////////////// + struct display_type + { + template + void operator()(T const &) const + { + std::cout << typeid(T).name() << std::endl; + } + + template + static void print() + { + std::cout << typeid(T).name() << std::endl; + } + }; + + /////////////////////////////////////////////////////////////////////////// + display_type const display = {}; + + /////////////////////////////////////////////////////////////////////////// + template + inline boost::iterator_range const& + get_iterpair(boost::iterator_range const& itp) + { + return itp; + } + + template + inline boost::iterator_range const& + get_iterpair(boost::variant const& v) + { + return get >(v); + } + + /////////////////////////////////////////////////////////////////////////// + template + inline bool + test(Lexer& lex, Char const* input, std::size_t token_id = 0, + Char const* state = NULL) + { + typedef typename Lexer::iterator_type iterator_type; + typedef std::basic_string string_type; + + string_type str(input); + typename string_type::iterator it = str.begin(); + + iterator_type first = lex.begin(it, str.end()); + iterator_type last = lex.end(); + + bool r = true; + + if (NULL != state) { + std::size_t stateid = lex.map_state(state); + r = r && (static_cast(~0) != stateid); + first.set_state(stateid); + } + + r = r && lex; + r = r && first != last; + + if (token_id != 0) + r = r && (*first).id() == token_id; + else + r = r && (*first).id() != 0; + + using namespace boost; + + typedef typename Lexer::iterator_type::base_iterator_type iterator; + typedef iterator_range iterpair_type; + iterpair_type const& ip = get_iterpair((*first).value()); + + r = r && string_type(ip.begin(), ip.end()) == str; + return r && first != last && ++first == last; + } +} + +#endif + + diff --git a/test/lex/test_parser.hpp b/test/lex/test_parser.hpp new file mode 100644 index 000000000..ef00e8bc2 --- /dev/null +++ b/test/lex/test_parser.hpp @@ -0,0 +1,67 @@ +/*============================================================================= + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_TEST_PARSER_SEP_24_2007_0558PM) +#define BOOST_SPIRIT_TEST_PARSER_SEP_24_2007_0558PM + +#include +#include + +namespace spirit_test +{ + template + bool test_parser(Char const* in, Parser const& p, Tokens& tokens, + bool full_match = true) + { + boost::spirit::lex::lexer lex(tokens); + + // we don't care about the result of the "what" function. + // we only care that all parsers have it: + boost::spirit::qi::what(p); + + std::string str (in); + std::string::iterator it_in = str.begin(); + std::string::iterator end_in = str.end(); + + typedef typename + boost::spirit::lex::lexer::iterator_type + iterator_type; + + iterator_type iter = lex.begin(it_in, end_in); + iterator_type end = lex.end(); + + return boost::spirit::qi::parse(iter, end, p) + && (!full_match || (iter == end)); + } + + template + bool test_parser(Char const* in, Parser const& p, Tokens& tokens, + Skipper const& s, bool full_match = true) + { + boost::spirit::lex::lexer lex(tokens); + + // we don't care about the result of the "what" function. + // we only care that all parsers have it: + boost::spirit::qi::what(p); + + std::string str (in); + std::string::iterator it_in = str.begin(); + std::string::iterator end_in = str.end(); + + typedef typename + boost::spirit::lex::lexer::iterator_type + iterator_type; + + iterator_type iter = lex.begin(it_in, end_in); + iterator_type end = lex.end(); + + return boost::spirit::qi::phrase_parse(iter, end, p, s) + && (!full_match || (iter == end)); + } + +} + +#endif diff --git a/test/qi/alternative.cpp b/test/qi/alternative.cpp new file mode 100644 index 000000000..c67e9a8e8 --- /dev/null +++ b/test/qi/alternative.cpp @@ -0,0 +1,87 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +using namespace spirit_test; + +int +main() +{ + using namespace boost::spirit; + + { + BOOST_TEST((test("a", char_ | char_))); + BOOST_TEST((test("x", char_('x') | char_('i')))); + BOOST_TEST((test("i", char_('x') | char_('i')))); + BOOST_TEST((!test("z", char_('x') | char_('o')))); + BOOST_TEST((test("rock", lit("rock") | lit("roll")))); + BOOST_TEST((test("roll", lit("rock") | lit("roll")))); + BOOST_TEST((test("rock", lit("rock") | int_))); + BOOST_TEST((test("12345", lit("rock") | int_))); + } + + { + boost::variant v; + + BOOST_TEST((test_attr("12345", lit("rock") | int_ | char_, v))); + BOOST_TEST(boost::get(v) == 12345); + + BOOST_TEST((test_attr("rock", lit("rock") | int_ | char_, v))); +// BOOST_TEST(boost::get(&v) == 0); + BOOST_TEST(boost::get(&v) == 0); + BOOST_TEST(boost::get(&v) == 0); + + BOOST_TEST((test_attr("x", lit("rock") | int_ | char_, v))); + BOOST_TEST(boost::get(v) == 'x'); + } + + { // test action + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + namespace phx = boost::phoenix; + + boost::variant v; + + BOOST_TEST((test("12345", (lit("rock") | int_ | char_)[phx::ref(v) = _1]))); + BOOST_TEST(boost::get(v) == 12345); + } + + { + boost::variant v; + BOOST_TEST((test("rock", lit("rock") | char_('x')))); + } + + { + // test if alternatives with all components having unused + // attributes return attributes them self + using namespace boost::fusion; + + vector v; + BOOST_TEST((test_attr("abc", + char_ >> (omit[char_] | omit[char_]) >> char_, v))); + BOOST_TEST((at_c<0>(v) == 'a')); + BOOST_TEST((at_c<1>(v) == 'c')); + } + + return boost::report_errors(); +} + diff --git a/test/qi/and_predicate.cpp b/test/qi/and_predicate.cpp new file mode 100644 index 000000000..027bfc8b2 --- /dev/null +++ b/test/qi/and_predicate.cpp @@ -0,0 +1,27 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using namespace boost::spirit; + + { + BOOST_TEST((test("1234", &int_, false))); + BOOST_TEST((!test("abcd", &int_))); + } + + return boost::report_errors(); +} diff --git a/test/qi/binary.cpp b/test/qi/binary.cpp new file mode 100644 index 000000000..f871c3e31 --- /dev/null +++ b/test/qi/binary.cpp @@ -0,0 +1,110 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +#include +#include +#include + +#include + +#include "test.hpp" + +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + + boost::uint8_t uc; + boost::uint16_t us; + boost::uint32_t ui; +#ifdef BOOST_HAS_LONG_LONG + boost::uint64_t ul; +#endif + + { // test native endian binaries +#ifdef BOOST_LITTLE_ENDIAN + BOOST_TEST(test_attr("\x01", byte, uc) && uc == 0x01); + BOOST_TEST(test_attr("\x01\x02", word, us) && us == 0x0201); + BOOST_TEST(test_attr("\x01\x02\x03\x04", dword, ui) && ui == 0x04030201); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(test_attr("\x01\x02\x03\x04\x05\x06\x07\x08", qword, ul) && + ul == 0x0807060504030201LL); +#endif +#else + BOOST_TEST(test_attr("\x01", byte, uc) && uc == 0x01); + BOOST_TEST(test_attr("\x01\x02", word, us) && us == 0x0102); + BOOST_TEST(test_attr("\x01\x02\x03\x04", dword, ui) && ui == 0x01020304); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(test_attr("\x01\x02\x03\x04\x05\x06\x07\x08", qword, ul) && + ul == 0x0102030405060708LL); +#endif +#endif + } + + { // test native endian binaries +#ifdef BOOST_LITTLE_ENDIAN + BOOST_TEST(test("\x01", byte(0x01))); + BOOST_TEST(test("\x01\x02", word(0x0201))); + BOOST_TEST(test("\x01\x02\x03\x04", dword(0x04030201))); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(test("\x01\x02\x03\x04\x05\x06\x07\x08", + qword(0x0807060504030201LL))); +#endif +#else + BOOST_TEST(test("\x01", 1, byte(0x01))); + BOOST_TEST(test("\x01\x02", 2, word(0x0102))); + BOOST_TEST(test("\x01\x02\x03\x04", 4, dword(0x01020304))); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(test("\x01\x02\x03\x04\x05\x06\x07\x08", 8, + qword(0x0102030405060708LL))); +#endif +#endif + } + + { // test big endian binaries + BOOST_TEST(test_attr("\x01\x02", big_word, us) && us == 0x0102); + BOOST_TEST(test_attr("\x01\x02\x03\x04", big_dword, ui) && ui == 0x01020304); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(test_attr("\x01\x02\x03\x04\x05\x06\x07\x08", big_qword, ul) + && ul == 0x0102030405060708LL); +#endif + } + + { + BOOST_TEST(test("\x01\x02", big_word(0x0102))); + BOOST_TEST(test("\x01\x02\x03\x04", big_dword(0x01020304))); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(test("\x01\x02\x03\x04\x05\x06\x07\x08", + big_qword(0x0102030405060708LL))); +#endif + } + + { // test little endian binaries + BOOST_TEST(test_attr("\x01\x02", little_word, us) && us == 0x0201); + BOOST_TEST(test_attr("\x01\x02\x03\x04", little_dword, ui) && ui == 0x04030201); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(test_attr("\x01\x02\x03\x04\x05\x06\x07\x08", little_qword, ul) + && ul == 0x0807060504030201LL); +#endif + } + + { + BOOST_TEST(test("\x01\x02", little_word(0x0201))); + BOOST_TEST(test("\x01\x02\x03\x04", little_dword(0x04030201))); +#ifdef BOOST_HAS_LONG_LONG + BOOST_TEST(test("\x01\x02\x03\x04\x05\x06\x07\x08", + little_qword(0x0807060504030201LL))); +#endif + } + + return boost::report_errors(); +} diff --git a/test/qi/char.cpp b/test/qi/char.cpp new file mode 100644 index 000000000..b2c52fa4d --- /dev/null +++ b/test/qi/char.cpp @@ -0,0 +1,102 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using namespace boost::spirit::qi; + using namespace boost::spirit::ascii; + using boost::spirit::char_; + using boost::spirit::wchar; + + { + BOOST_TEST(test("x", 'x')); + BOOST_TEST(test(L"x", L'x')); + + BOOST_TEST(test("x", char_)); + BOOST_TEST(test("x", char_('x'))); + BOOST_TEST(!test("x", char_('y'))); + BOOST_TEST(test(L"x", char_(L'x'))); + BOOST_TEST(!test(L"x", char_(L'y'))); + BOOST_TEST(test("x", char_('a', 'z'))); + BOOST_TEST(!test("x", char_('0', '9'))); + + BOOST_TEST(!test("x", ~char_)); + BOOST_TEST(!test("x", ~char_('x'))); + BOOST_TEST(test(" ", ~char_('x'))); + BOOST_TEST(test("X", ~char_('x'))); + BOOST_TEST(!test("x", ~char_('b', 'y'))); + BOOST_TEST(test("a", ~char_('b', 'y'))); + BOOST_TEST(test("z", ~char_('b', 'y'))); + + BOOST_TEST(test("x", ~~char_)); + BOOST_TEST(test("x", ~~char_('x'))); + BOOST_TEST(!test(" ", ~~char_('x'))); + BOOST_TEST(!test("X", ~~char_('x'))); + BOOST_TEST(test("x", ~~char_('b', 'y'))); + BOOST_TEST(!test("a", ~~char_('b', 'y'))); + BOOST_TEST(!test("z", ~~char_('b', 'y'))); + } + + { + BOOST_TEST(test(" x", 'x', space)); + BOOST_TEST(test(L" x", L'x', space)); + + BOOST_TEST(test(" x", char_, space)); + BOOST_TEST(test(" x", char_('x'), space)); + BOOST_TEST(!test(" x", char_('y'), space)); + BOOST_TEST(test(L" x", char_(L'x'), space)); + BOOST_TEST(!test(L" x", char_(L'y'), space)); + BOOST_TEST(test(" x", char_('a', 'z'), space)); + BOOST_TEST(!test(" x", char_('0', '9'), space)); + + } + + { + BOOST_TEST(test(L"x", wchar)); + BOOST_TEST(test(L"x", wchar(L'x'))); + BOOST_TEST(!test(L"x", wchar(L'y'))); + BOOST_TEST(test(L"x", wchar('a', 'z'))); + BOOST_TEST(!test(L"x", wchar('0', '9'))); + + BOOST_TEST(!test(L"x", ~wchar)); + BOOST_TEST(!test(L"x", ~wchar('x'))); + BOOST_TEST(test(L" ", ~wchar('x'))); + BOOST_TEST(test(L"X", ~wchar('x'))); + BOOST_TEST(!test(L"x", ~wchar('b', 'y'))); + BOOST_TEST(test(L"a", ~wchar('b', 'y'))); + BOOST_TEST(test(L"z", ~wchar('b', 'y'))); + + BOOST_TEST(test(L"x", ~~wchar)); + BOOST_TEST(test(L"x", ~~wchar('x'))); + BOOST_TEST(!test(L" ", ~~wchar('x'))); + BOOST_TEST(!test(L"X", ~~wchar('x'))); + BOOST_TEST(test(L"x", ~~wchar('b', 'y'))); + BOOST_TEST(!test(L"a", ~~wchar('b', 'y'))); + BOOST_TEST(!test(L"z", ~~wchar('b', 'y'))); + } + + { // lazy chars + + using namespace boost::phoenix; + BOOST_TEST((test("x", char_(val('x'))))); + BOOST_TEST((test("h", char_(val('a'), val('n'))))); + } + + return boost::report_errors(); +} diff --git a/test/qi/char_class.cpp b/test/qi/char_class.cpp new file mode 100644 index 000000000..32dc38fb6 --- /dev/null +++ b/test/qi/char_class.cpp @@ -0,0 +1,170 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + Copyright (c) 2001-2007 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using spirit_test::test_attr; + + { + using namespace boost::spirit::ascii; + BOOST_TEST(test("1", alnum)); + BOOST_TEST(!test(" ", alnum)); + BOOST_TEST(!test("1", alpha)); + BOOST_TEST(test("x", alpha)); + BOOST_TEST(test(" ", blank)); + BOOST_TEST(!test("x", blank)); + BOOST_TEST(test("1", digit)); + BOOST_TEST(!test("x", digit)); + BOOST_TEST(test("a", lower)); + BOOST_TEST(!test("A", lower)); + BOOST_TEST(test("!", punct)); + BOOST_TEST(!test("x", punct)); + BOOST_TEST(test(" ", space)); + BOOST_TEST(test("\n", space)); + BOOST_TEST(test("\r", space)); + BOOST_TEST(test("\t", space)); + BOOST_TEST(test("A", upper)); + BOOST_TEST(!test("a", upper)); + BOOST_TEST(test("A", xdigit)); + BOOST_TEST(test("0", xdigit)); + BOOST_TEST(test("f", xdigit)); + BOOST_TEST(!test("g", xdigit)); + } + + { + using namespace boost::spirit::iso8859_1; + BOOST_TEST(test("1", alnum)); + BOOST_TEST(!test(" ", alnum)); + BOOST_TEST(!test("1", alpha)); + BOOST_TEST(test("x", alpha)); + BOOST_TEST(test(" ", blank)); + BOOST_TEST(!test("x", blank)); + BOOST_TEST(test("1", digit)); + BOOST_TEST(!test("x", digit)); + BOOST_TEST(test("a", lower)); + BOOST_TEST(!test("A", lower)); + BOOST_TEST(test("!", punct)); + BOOST_TEST(!test("x", punct)); + BOOST_TEST(test(" ", space)); + BOOST_TEST(test("\n", space)); + BOOST_TEST(test("\r", space)); + BOOST_TEST(test("\t", space)); + BOOST_TEST(test("A", upper)); + BOOST_TEST(!test("a", upper)); + BOOST_TEST(test("A", xdigit)); + BOOST_TEST(test("0", xdigit)); + BOOST_TEST(test("f", xdigit)); + BOOST_TEST(!test("g", xdigit)); + } + + { + using namespace boost::spirit::standard; + BOOST_TEST(test("1", alnum)); + BOOST_TEST(!test(" ", alnum)); + BOOST_TEST(!test("1", alpha)); + BOOST_TEST(test("x", alpha)); + BOOST_TEST(test(" ", blank)); + BOOST_TEST(!test("x", blank)); + BOOST_TEST(test("1", digit)); + BOOST_TEST(!test("x", digit)); + BOOST_TEST(test("a", lower)); + BOOST_TEST(!test("A", lower)); + BOOST_TEST(test("!", punct)); + BOOST_TEST(!test("x", punct)); + BOOST_TEST(test(" ", space)); + BOOST_TEST(test("\n", space)); + BOOST_TEST(test("\r", space)); + BOOST_TEST(test("\t", space)); + BOOST_TEST(test("A", upper)); + BOOST_TEST(!test("a", upper)); + BOOST_TEST(test("A", xdigit)); + BOOST_TEST(test("0", xdigit)); + BOOST_TEST(test("f", xdigit)); + BOOST_TEST(!test("g", xdigit)); + } + + { + using namespace boost::spirit::standard_wide; + BOOST_TEST(test(L"1", alnum)); + BOOST_TEST(!test(L" ", alnum)); + BOOST_TEST(!test(L"1", alpha)); + BOOST_TEST(test(L"x", alpha)); + BOOST_TEST(test(L" ", blank)); + BOOST_TEST(!test(L"x", blank)); + BOOST_TEST(test(L"1", digit)); + BOOST_TEST(!test(L"x", digit)); + BOOST_TEST(test(L"a", lower)); + BOOST_TEST(!test(L"A", lower)); + BOOST_TEST(test(L"!", punct)); + BOOST_TEST(!test(L"x", punct)); + BOOST_TEST(test(L" ", space)); + BOOST_TEST(test(L"\n", space)); + BOOST_TEST(test(L"\r", space)); + BOOST_TEST(test(L"\t", space)); + BOOST_TEST(test(L"A", upper)); + BOOST_TEST(!test(L"a", upper)); + BOOST_TEST(test(L"A", xdigit)); + BOOST_TEST(test(L"0", xdigit)); + BOOST_TEST(test(L"f", xdigit)); + BOOST_TEST(!test(L"g", xdigit)); + } + + { // test attribute extraction + using boost::spirit::qi::domain; + using boost::spirit::traits::attribute_of; + using boost::spirit::iso8859_1::alpha; + using boost::spirit::iso8859_1::alpha_type; + + BOOST_STATIC_ASSERT(( + boost::is_same< + attribute_of::type + , char>::value)); + + int attr = 0; + BOOST_TEST(test_attr("a", alpha, attr)); + BOOST_TEST(attr == 'a'); + } + + { // test attribute extraction + using boost::spirit::iso8859_1::alpha; + using boost::spirit::iso8859_1::space; + char attr = 0; + BOOST_TEST(test_attr(" a", alpha, attr, space)); + BOOST_TEST(attr == 'a'); + } + + { // test action + + using namespace boost::phoenix; + using namespace boost::spirit::ascii; + using boost::spirit::arg_names::_1; + char ch; + + BOOST_TEST(test("x", alnum[ref(ch) = _1])); + BOOST_TEST(ch == 'x'); + BOOST_TEST(test(" A", alnum[ref(ch) = _1], space)); + BOOST_TEST(ch == 'A'); + } + + return boost::report_errors(); +} diff --git a/test/qi/debug.cpp b/test/qi/debug.cpp new file mode 100644 index 000000000..b12747ecd --- /dev/null +++ b/test/qi/debug.cpp @@ -0,0 +1,241 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#define BOOST_SPIRIT_DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +using namespace spirit_test; + +int +main() +{ + using namespace boost::spirit::qi; + using namespace boost::spirit::ascii; + + { // basic tests + rule a, b, c, start; + + a = 'a'; + b = 'b'; + c = 'c'; + BOOST_SPIRIT_DEBUG_NODE(a); + BOOST_SPIRIT_DEBUG_NODE(b); + BOOST_SPIRIT_DEBUG_NODE(c); + + start = *(a | b | c); + BOOST_SPIRIT_DEBUG_NODE(start); + BOOST_TEST(test("abcabcacb", start)); + + start = (a | b) >> (start | b); + BOOST_SPIRIT_DEBUG_NODE(start); + BOOST_TEST(test("aaaabababaaabbb", start)); + BOOST_TEST(test("aaaabababaaabba", start, false)); + } + + { // basic tests w/ skipper + + rule a, b, c, start; + + a = 'a'; + b = 'b'; + c = 'c'; + BOOST_SPIRIT_DEBUG_NODE(a); + BOOST_SPIRIT_DEBUG_NODE(b); + BOOST_SPIRIT_DEBUG_NODE(c); + + start = *(a | b | c); + BOOST_TEST(test(" a b c a b c a c b", start, space)); + + // allow no skipping too: + BOOST_SPIRIT_DEBUG_NODE(start); + BOOST_TEST(test("abcabcacb", start)); + + start = (a | b) >> (start | b); + BOOST_SPIRIT_DEBUG_NODE(start); + BOOST_TEST(test(" a a a a b a b a b a a a b b b ", start, space)); + BOOST_TEST(test(" a a a a b a b a b a a a b b a ", start, space, false)); + } + +// { // alias tests +// +// rule a, b, c, d, start; +// +// a = 'a'; +// b = 'b'; +// c = 'c'; +// d = start.alias(); // d will always track start +// +// start = *(a | b | c); +// BOOST_TEST(test("abcabcacb", d)); +// +// start = (a | b) >> (start | b); +// BOOST_TEST(test("aaaabababaaabbb", d)); +// } +// +// { // copy tests +// +// rule a, b, c, start; +// +// a = 'a'; +// b = 'b'; +// c = 'c'; +// +// // The FF is the dynamic equivalent of start = *(a | b | c); +// start = a; +// start = start.copy() | b; +// start = start.copy() | c; +// start = *(start.copy()); +// +// BOOST_TEST(test("abcabcacb", start)); +// +// // The FF is the dynamic equivalent of start = (a | b) >> (start | b); +// start = b; +// start = a | start.copy(); +// start = start.copy() >> (start | b); +// +// BOOST_TEST(test("aaaabababaaabbb", start)); +// BOOST_TEST(test("aaaabababaaabba", start, false)); +// } +// +// { // context tests +// +// using namespace boost::phoenix; +// using namespace boost::spirit::ascii; +// using boost::spirit::arg_names::_1; +// using boost::spirit::arg_names::_val; +// +// char ch; +// rule a; +// a = alpha[_val = _1]; +// +// BOOST_TEST(test("x", a[ref(ch) = _1])); +// BOOST_TEST(ch == 'x'); +// +// BOOST_TEST(test_attr("z", a, ch)); // attribute is given. +// BOOST_TEST(ch == 'z'); +// } +// +// { // context (w/arg) tests +// +// using namespace boost::phoenix; +// using namespace boost::spirit::ascii; +// using boost::spirit::arg_names::_1; +// using boost::spirit::arg_names::_r1; +// using boost::spirit::arg_names::_r2; +// using boost::spirit::arg_names::_val; +// +// char ch; +// rule a; // 1 arg +// a = alpha[_val = _1 + _r1]; +// +// BOOST_TEST(test("x", a(val(1))[ref(ch) = _1])); +// BOOST_TEST(ch == 'x' + 1); +// +// BOOST_TEST(test_attr("a", a(1), ch)); // allow scalars as rule args too. +// BOOST_TEST(ch == 'a' + 1); +// +// rule b; // 2 args +// b = alpha[_val = _1 + _r1 + _r2]; +// BOOST_TEST(test_attr("a", b(1, 2), ch)); +// BOOST_TEST(ch == 'a' + 1 + 2); +// } +// +// { // context (w/locals) tests +// using namespace boost::phoenix; +// using namespace boost::spirit::ascii; +// using boost::spirit::arg_names::_1; +// using boost::spirit::arg_names::_a; +// using boost::spirit::char_; +// using boost::spirit::locals; +// +// rule > a; // 1 local +// a = alpha[_a = _1] >> char_(_a); +// BOOST_TEST(test("aa", a)); +// BOOST_TEST(!test("ax", a)); +// } +// +// { // context (w/args and locals) tests +// using namespace boost::phoenix; +// using namespace boost::spirit::ascii; +// using boost::spirit::arg_names::_1; +// using boost::spirit::arg_names::_r1; +// using boost::spirit::arg_names::_a; +// using boost::spirit::char_; +// using boost::spirit::locals; +// +// rule > a; // 1 arg + 1 local +// a = alpha[_a = _1 + _r1] >> char_(_a); +// BOOST_TEST(test("ab", a(val(1)))); +// BOOST_TEST(test("xy", a(val(1)))); +// BOOST_TEST(!test("ax", a(val(1)))); +// } +// +// { // bug: test that injected attributes are ok +// using namespace boost::phoenix; +// using namespace boost::spirit::ascii; +// using boost::spirit::arg_names::_1; +// using boost::spirit::arg_names::_r1; +// using boost::spirit::arg_names::_val; +// using boost::spirit::char_; +// +// rule r; +// +// // problem code: +// r = char_(_r1)[_val = _1]; +// } + + { // error handling + + using namespace boost::phoenix; + using namespace boost::spirit::ascii; + using boost::phoenix::val; + using boost::spirit::int_; + using boost::spirit::arg_names::_4; // what + using boost::spirit::arg_names::_3; // error pos + using boost::spirit::arg_names::_2; // end + using boost::spirit::qi::fail; + + rule r; + r = '(' > int_ > ',' > int_ > ')'; + + on_error + ( + r, std::cout + << val("Error! Expecting: ") + << _4 + << val(" Here: \"") + << construct(_3, _2) + << val("\"") + << std::endl + ); + + BOOST_SPIRIT_DEBUG_NODE(r); + BOOST_TEST(test("(123,456)", r)); + BOOST_TEST(!test("(abc,def)", r)); + BOOST_TEST(!test("(123,456]", r)); + BOOST_TEST(!test("(123;456)", r)); + BOOST_TEST(!test("[123,456]", r)); + } + + return boost::report_errors(); +} + diff --git a/test/qi/difference.cpp b/test/qi/difference.cpp new file mode 100644 index 000000000..f02b79b86 --- /dev/null +++ b/test/qi/difference.cpp @@ -0,0 +1,78 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +using namespace spirit_test; + +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::ascii; + + { + BOOST_TEST(test("b", char_ - 'a')); + BOOST_TEST(!test("a", char_ - 'a')); + BOOST_TEST(test("/* abcdefghijk */", "/*" >> *(char_ - "*/") >> "*/")); + } + + { + BOOST_TEST(test("b", char_ - no_case['a'])); + BOOST_TEST(!test("a", char_ - no_case['a'])); + BOOST_TEST(!test("A", char_ - no_case['a'])); + + BOOST_TEST(test("b", no_case[lower - 'a'])); + BOOST_TEST(test("B", no_case[lower - 'a'])); + BOOST_TEST(!test("a", no_case[lower - 'a'])); + BOOST_TEST(!test("A", no_case[lower - 'a'])); + } + + { + // $$$ See difference.hpp why these tests are not done anymore. $$$ + + //~ BOOST_TEST(test("switcher", lit("switcher") - "switch")); + //~ BOOST_TEST(test(" switcher ", lit("switcher") - "switch", space)); + + BOOST_TEST(!test("switch", lit("switch") - "switch")); + } + + { + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + std::string s; + + BOOST_TEST(test( + "/*abcdefghijk*/" + , "/*" >> *(char_ - "*/")[ref(s) += _1] >> "*/" + )); + BOOST_TEST(s == "abcdefghijk"); + s.clear(); + + BOOST_TEST(test( + " /*abcdefghijk*/ " + , "/*" >> *(char_ - "*/")[ref(s) += _1] >> "*/" + , space + )); + BOOST_TEST(s == "abcdefghijk"); + } + + return boost::report_errors(); +} + diff --git a/test/qi/eps.cpp b/test/qi/eps.cpp new file mode 100644 index 000000000..f65477253 --- /dev/null +++ b/test/qi/eps.cpp @@ -0,0 +1,33 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using namespace boost::spirit; + + { + BOOST_TEST((test("", eps))); + BOOST_TEST((test("xxx", eps, false))); + } + + { // test action + + using namespace boost::phoenix; + BOOST_TEST((test("", eps(val(true))))); + BOOST_TEST((!test("", eps(val(false))))); + } + + return boost::report_errors(); +} diff --git a/test/qi/expect.cpp b/test/qi/expect.cpp new file mode 100644 index 000000000..1b9da41c8 --- /dev/null +++ b/test/qi/expect.cpp @@ -0,0 +1,89 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::ascii; + using spirit_test::test; + using boost::spirit::qi::expectation_failure; + + { + try + { + BOOST_TEST((test("aa", char_ > char_))); + BOOST_TEST((test("aaa", char_ > char_ > char_('a')))); + BOOST_TEST((test("xi", char_('x') > char_('i')))); + BOOST_TEST((!test("xi", char_('y') > char_('o')))); // should not throw! + BOOST_TEST((test("xin", char_('x') > char_('i') > char_('n')))); + BOOST_TEST((!test("xi", char_('x') > char_('o')))); + } + catch (expectation_failure const& x) + { + std::cout << "expected: " << x.what << std::endl; + std::cout << "got: \"" << x.first << '"' << std::endl; + + BOOST_TEST(x.what == "'o'"); + BOOST_TEST(std::string(x.first, x.last) == "i"); + } + } + + { + try + { + BOOST_TEST((test(" a a ", char_ > char_, space))); + BOOST_TEST((test(" x i ", char_('x') > char_('i'), space))); + BOOST_TEST((!test(" x i ", char_('x') > char_('o'), space))); + } + catch (expectation_failure const& x) + { + std::cout << "expected: " << x.what << std::endl; + std::cout << "got: \"" << x.first << '"' << std::endl; + + BOOST_TEST(x.what == "'o'"); + BOOST_TEST(std::string(x.first, x.last) == "i "); + } + } + + { + try + { + BOOST_TEST((test("aA", no_case[char_('a') > 'a']))); + BOOST_TEST((test("BEGIN END", no_case[lit("begin") > "end"], space))); + BOOST_TEST((!test("BEGIN END", no_case[lit("begin") > "nend"], space))); + } + catch (expectation_failure const& x) + { + std::cout << "expected: " << x.what << std::endl; + std::cout << "got: \"" << x.first << '"' << std::endl; + + BOOST_TEST(x.what == "case-insensitive \"nend\""); + BOOST_TEST(std::string(x.first, x.last) == "END"); + } + } + + return boost::report_errors(); +} + diff --git a/test/qi/functor.cpp b/test/qi/functor.cpp new file mode 100644 index 000000000..ec7ee7a2e --- /dev/null +++ b/test/qi/functor.cpp @@ -0,0 +1,68 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include + +#include +#include "test.hpp" + +/////////////////////////////////////////////////////////////////////////////// +struct number_parser : public boost::spirit::qi::functor_base +{ + template + struct apply + { + typedef int type; + }; + + template + bool operator()(Attribute& attr, Context& ctx, + Iterator& first, Iterator const& last) const + { + if (first == last) + return false; + + char ch = *first; + if (ch < '0' || ch > '9') + return false; + + attr = 0; + do { + attr = attr * 10 + int(ch - '0'); + ++first; + } while (first != last && (ch = *first, ch >= '0' && ch <= '9')); + return true; + } +}; + +boost::spirit::qi::functor_parser number; + +/////////////////////////////////////////////////////////////////////////////// +int main() +{ + using spirit_test::test; + using namespace boost::spirit; + using namespace boost::spirit::qi; + + { + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + + int n = 0; + BOOST_TEST(test("1234", number)); + BOOST_TEST(test("1234", number[ref(n) = _1])); + BOOST_TEST(n == 1234); + } + + return boost::report_errors(); +} diff --git a/test/qi/grammar.cpp b/test/qi/grammar.cpp new file mode 100644 index 000000000..01841c5fc --- /dev/null +++ b/test/qi/grammar.cpp @@ -0,0 +1,109 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +using namespace spirit_test; +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; +using namespace boost::spirit::arg_names; + +struct num_list : grammar_def +{ + num_list() + { + using boost::spirit::int_; + num = int_; + start = num >> *(',' >> num); + } + + rule start, num; +}; + +struct inh_g : grammar_def +{ + inh_g() + { + start = lit("inherited")[_val = _r1]; + } + + rule start, num; +}; + +struct my_skipper : grammar_def +{ + my_skipper() + { + start = space; + } + + rule start, num; +}; + +struct num_list2 : grammar_def > +{ + num_list2() + { + using boost::spirit::int_; + num = int_; + start = num >> *(',' >> num); + } + + rule > start, num; +}; + +int +main() +{ + { // simple grammar test + + num_list def; + grammar nlist(def); + BOOST_TEST(test("123, 456, 789", nlist, space)); + } + + { // simple grammar test with user-skipper + + num_list2 def; + grammar nlist(def); + my_skipper skipdef; + grammar skip(skipdef); + BOOST_TEST(test("123, 456, 789", nlist, skip)); + } + + { // direct access to the rules + + num_list def; + BOOST_TEST(test("123", def.num)); + BOOST_TEST(test("123, 456, 789", def.start, space)); + } + + { // grammar with inherited attributes + + inh_g def; + grammar g(def); + int n = -1; + BOOST_TEST(test_attr("inherited", def.start(123), n, space)); // direct to the rule + BOOST_TEST(n == 123); + BOOST_TEST(test_attr("inherited", g(123), n, space)); // using the grammar + BOOST_TEST(n == 123); + } + return boost::report_errors(); +} + diff --git a/test/qi/grammar_fail.cpp b/test/qi/grammar_fail.cpp new file mode 100644 index 000000000..7696c66dd --- /dev/null +++ b/test/qi/grammar_fail.cpp @@ -0,0 +1,42 @@ +/*============================================================================= + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#include +#include +#include +#include +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; + +struct num_list : grammar_def > +{ + num_list() + { + using boost::spirit::int_; + num = int_; + start = num >> *(',' >> num); + } + + rule > start, num; +}; + +// this test must fail compiling +int main() +{ + char const* input = "some input, it doesn't matter"; + char const* end = &input[strlen(input)+1]; + + num_list def; + bool r = phrase_parse(input, end, make_parser(def), + space | ('%' >> *~char_('\n') >> '\n')); + + return 0; +} diff --git a/test/qi/int.cpp b/test/qi/int.cpp new file mode 100644 index 000000000..ad2705d2a --- /dev/null +++ b/test/qi/int.cpp @@ -0,0 +1,164 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// +// *** BEWARE PLATFORM DEPENDENT!!! *** +// *** The following assumes 32 bit integers and 64 bit long longs. +// *** Modify these constant strings when appropriate. +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef BOOST_HAS_LONG_LONG +// Some compilers have long long, but don't define the +// LONG_LONG_MIN and LONG_LONG_MAX macros in limits.h. This +// assumes that long long is 64 bits. +#if !defined(LONG_LONG_MIN) && !defined(LONG_LONG_MAX) +# define LONG_LONG_MAX 0x7fffffffffffffffLL +# define LONG_LONG_MIN (-LONG_LONG_MAX - 1) +#endif +#endif // BOOST_HAS_LONG_LONG + + char const* max_int = "2147483647"; + char const* int_overflow = "2147483648"; + char const* min_int = "-2147483648"; + char const* int_underflow = "-2147483649"; + +#ifdef BOOST_HAS_LONG_LONG + char const* max_long_long = "9223372036854775807"; + char const* long_long_overflow = "9223372036854775808"; + char const* min_long_long = "-9223372036854775808"; + char const* long_long_underflow = "-9223372036854775809"; +#endif + +int +main() +{ + using namespace spirit_test; + + /////////////////////////////////////////////////////////////////////////// + // signed integer tests + /////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::int_; + int i; + + BOOST_TEST(test("123456", int_)); + BOOST_TEST(test_attr("123456", int_, i)); + BOOST_TEST(i == 123456); + + BOOST_TEST(test("+123456", int_)); + BOOST_TEST(test_attr("+123456", int_, i)); + BOOST_TEST(i == 123456); + + BOOST_TEST(test("-123456", int_)); + BOOST_TEST(test_attr("-123456", int_, i)); + BOOST_TEST(i == -123456); + + BOOST_TEST(test(max_int, int_)); + BOOST_TEST(test_attr(max_int, int_, i)); + BOOST_TEST(i == INT_MAX); + + BOOST_TEST(test(min_int, int_)); + BOOST_TEST(test_attr(min_int, int_, i)); + BOOST_TEST(i == INT_MIN); + + BOOST_TEST(!test(int_overflow, int_)); + BOOST_TEST(!test_attr(int_overflow, int_, i)); + BOOST_TEST(!test(int_underflow, int_)); + BOOST_TEST(!test_attr(int_underflow, int_, i)); + + BOOST_TEST(!test("-", int_)); + BOOST_TEST(!test_attr("-", int_, i)); + + BOOST_TEST(!test("+", int_)); + BOOST_TEST(!test_attr("+", int_, i)); + + // Bug report from Steve Nutt + BOOST_TEST(test_attr("5368709120", int_, i, false)); + BOOST_TEST(i == 536870912); + + // with leading zeros + BOOST_TEST(test("0000000000123456", int_)); + BOOST_TEST(test_attr("0000000000123456", int_, i)); + BOOST_TEST(i == 123456); + } + + /////////////////////////////////////////////////////////////////////////// + // long long tests + /////////////////////////////////////////////////////////////////////////// +#ifdef BOOST_HAS_LONG_LONG + { + using boost::spirit::long_long; + boost::long_long_type ll; + + BOOST_TEST(test("1234567890123456789", long_long)); + BOOST_TEST(test_attr("1234567890123456789", long_long, ll)); + BOOST_TEST(ll == 1234567890123456789LL); + + BOOST_TEST(test("-1234567890123456789", long_long)); + BOOST_TEST(test_attr("-1234567890123456789", long_long, ll)); + BOOST_TEST(ll == -1234567890123456789LL); + + BOOST_TEST(test(max_long_long, long_long)); + BOOST_TEST(test_attr(max_long_long, long_long, ll)); + BOOST_TEST(ll == LONG_LONG_MAX); + + BOOST_TEST(test(min_long_long, long_long)); + BOOST_TEST(test_attr(min_long_long, long_long, ll)); + BOOST_TEST(ll == LONG_LONG_MIN); + + BOOST_TEST(!test(long_long_overflow, long_long)); + BOOST_TEST(!test_attr(long_long_overflow, long_long, ll)); + BOOST_TEST(!test(long_long_underflow, long_long)); + BOOST_TEST(!test_attr(long_long_underflow, long_long, ll)); + } +#endif + + /////////////////////////////////////////////////////////////////////////// + // int_spec tests + /////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::qi::int_spec; + using boost::spirit::unused_type; + int_spec any_int; + + BOOST_TEST(test("123456", any_int)); + BOOST_TEST(test("-123456", any_int)); + BOOST_TEST(test("-1234567890123456789", any_int)); + } + + /////////////////////////////////////////////////////////////////////////// + // action tests + /////////////////////////////////////////////////////////////////////////// + { + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + using boost::spirit::ascii::space; + using boost::spirit::int_; + int n, m; + + BOOST_TEST(test("123", int_[ref(n) = _1])); + BOOST_TEST(n == 123); + BOOST_TEST(test_attr("789", int_[ref(n) = _1], m)); + BOOST_TEST(n == 789 && m == 789); + BOOST_TEST(test(" 456", int_[ref(n) = _1], space)); + BOOST_TEST(n == 456); + } + + return boost::report_errors(); +} diff --git a/test/qi/kleene.cpp b/test/qi/kleene.cpp new file mode 100644 index 000000000..9f74b97e2 --- /dev/null +++ b/test/qi/kleene.cpp @@ -0,0 +1,113 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +using namespace spirit_test; + +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::ascii; + + { + BOOST_TEST(test("aaaaaaaa", *char_)); + BOOST_TEST(test("a", *char_)); + BOOST_TEST(test("", *char_)); + BOOST_TEST(test("aaaaaaaa", *alpha)); + BOOST_TEST(!test("aaaaaaaa", *upper)); + } + + { + BOOST_TEST(test(" a a aaa aa", *char_, space)); + BOOST_TEST(test("12345 678 9 ", *digit, space)); + } + + { + BOOST_TEST(test("aBcdeFGH", no_case[*char_])); + BOOST_TEST(test("a B cde FGH ", no_case[*char_], space)); + } + + { + using boost::spirit::uint; + BOOST_TEST(test("12345 678 955 987", *uint, space)); + BOOST_TEST(test("12345, 678, 955, 987", uint >> *(',' >> uint), space)); + } + + { + std::vector v; + BOOST_TEST(test_attr("bbbb", *char_, v) && 4 == v.size() && + v[0] == 'b' && v[1] == 'b' && v[2] == 'b' && v[3] == 'b'); + + v.clear(); + BOOST_TEST(test_attr("b b b b ", *char_, v, space) && 4 == v.size() && + v[0] == 'b' && v[1] == 'b' && v[2] == 'b' && v[3] == 'b'); + + v.clear(); + BOOST_TEST(test_attr("bbbb", *omit[char_('b')], v) && 0 == v.size()); + + v.clear(); + BOOST_TEST(test_attr("bbbb", omit[*char_('b')], v) && 0 == v.size()); + + v.clear(); + BOOST_TEST(test_attr("b b b b ", *omit[char_('b')], v, space) && 0 == v.size()); + + v.clear(); + BOOST_TEST(test_attr("b b b b ", omit[*char_('b')], v, space) && 0 == v.size()); + } + + { + std::vector v; + BOOST_TEST(test_attr("123 456 789 10", *int_, v, space) && 4 == v.size() && + v[0] == 123 && v[1] == 456 && v[2] == 789 && v[3] == 10); + } + + { + std::vector v; + BOOST_TEST(test_attr("123 456 789", *int_, v, space) && 3 == v.size() && + v[0] == 123 && v[1] == 456 && v[2] == 789); + } + + { // actions + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + + std::vector v; + BOOST_TEST(test("bbbb", (*char_)[ref(v) = _1]) && 4 == v.size() && + v[0] == 'b' && v[1] == 'b' && v[2] == 'b' && v[3] == 'b'); + } + + { // more actions + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + + std::vector v; + BOOST_TEST(test("123 456 789", (*int_)[ref(v) = _1], space) && 3 == v.size() && + v[0] == 123 && v[1] == 456 && v[2] == 789); + } + + return boost::report_errors(); +} + diff --git a/test/qi/lazy.cpp b/test/qi/lazy.cpp new file mode 100644 index 000000000..281b74736 --- /dev/null +++ b/test/qi/lazy.cpp @@ -0,0 +1,54 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using namespace boost::spirit; + using namespace boost::spirit::qi; + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + + { + BOOST_TEST(test("123", lazy(val(int_)))); + } + + { + int result; + BOOST_TEST(test("123", lazy(val(int_))[ref(result) = _1])); + BOOST_TEST((result == 123)); + } + + { + rule r; + + r = + '<' >> *(char_ - '>')[_val += _1] >> '>' + >> "> lazy(_val) >> '>' + ; + + BOOST_TEST(test("", r)); + BOOST_TEST(!test("", r)); + } + + return boost::report_errors(); +} diff --git a/test/qi/lexeme.cpp b/test/qi/lexeme.cpp new file mode 100644 index 000000000..0b1884232 --- /dev/null +++ b/test/qi/lexeme.cpp @@ -0,0 +1,38 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using namespace boost::spirit; + using namespace boost::spirit::ascii; + using boost::spirit::qi::rule; + + { + BOOST_TEST((test(" 1 2 3 4 5", +digit, space))); + BOOST_TEST((!test(" 1 2 3 4 5", lexeme[+digit], space))); + BOOST_TEST((test(" 12345", lexeme[+digit], space))); + BOOST_TEST((test(" 12345 ", lexeme[+digit], space, false))); + + rule r, rr; + r = +digit; + rr = lexeme[r]; + BOOST_TEST((!test(" 1 2 3 4 5", rr, space))); + BOOST_TEST((test(" 12345", rr, space))); + } + + return boost::report_errors(); +} diff --git a/test/qi/list.cpp b/test/qi/list.cpp new file mode 100644 index 000000000..0524d1e2c --- /dev/null +++ b/test/qi/list.cpp @@ -0,0 +1,64 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +using namespace spirit_test; + +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::ascii; + + { + BOOST_TEST(test("a,b,c,d,e,f,g,h", char_ % ',')); + BOOST_TEST(test("a,b,c,d,e,f,g,h,", char_ % ',', false)); + } + + { + BOOST_TEST(test("a, b, c, d, e, f, g, h", char_ % ',', space)); + BOOST_TEST(test("a, b, c, d, e, f, g, h,", char_ % ',', space, false)); + } + + { + std::string s; + BOOST_TEST(test_attr("a,b,c,d,e,f,g,h", char_ % ',', s)); + BOOST_TEST(s == "abcdefgh"); + } + + { // actions + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + + std::string s; + BOOST_TEST(test("a,b,c,d,e,f,g,h", (char_ % ',') + [ref(s) = construct(begin(_1), end(_1))])); + } + + return boost::report_errors(); +} + diff --git a/test/qi/lit.cpp b/test/qi/lit.cpp new file mode 100644 index 000000000..5d9a65087 --- /dev/null +++ b/test/qi/lit.cpp @@ -0,0 +1,79 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + http://spirit.sourceforge.net/ + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using spirit_test::test_attr; + using namespace boost::spirit; + + { + BOOST_TEST((test("kimpo", "kimpo"))); + BOOST_TEST((test("kimpo", lit("kimpo")))); + BOOST_TEST((test("x", lit("x")))); + BOOST_TEST((test("x", lit('x')))); + BOOST_TEST((test(L"x", lit(L'x')))); + } + + { + BOOST_TEST((test(L"kimpo", L"kimpo"))); + BOOST_TEST((test(L"kimpo", wlit(L"kimpo")))); + BOOST_TEST((test(L"x", wlit(L"x")))); + BOOST_TEST((test(L"x", wlit(L'x')))); + BOOST_TEST((test(L"x", wlit(L'x')))); + } + + { + std::basic_string s("kimpo"); + BOOST_TEST((test("kimpo", lit(s)))); + + std::basic_string ws(L"kimpo"); + BOOST_TEST((test(L"kimpo", lit(ws)))); + } + + { + using namespace boost::spirit::ascii; + BOOST_TEST((test(" kimpo", lit("kimpo"), space))); + BOOST_TEST((test(L" kimpo", lit(L"kimpo"), space))); + BOOST_TEST((test(" x", lit("x"), space))); + BOOST_TEST((test(" x", lit('x'), space))); + BOOST_TEST((test(L" x", lit(L'x'), space))); + } + + { + using namespace boost::spirit::ascii; + BOOST_TEST((test(" kimpo", wlit("kimpo"), space))); + BOOST_TEST((test(L" kimpo", wlit(L"kimpo"), space))); + BOOST_TEST((test(" x", wlit("x"), space))); + BOOST_TEST((test(" x", wlit('x'), space))); + BOOST_TEST((test(L" x", wlit(L'x'), space))); + } + + { // lazy strings + + using namespace boost::phoenix; + std::basic_string s("kimpo"); + BOOST_TEST((test("kimpo", lit(val(s))))); + + std::basic_string ws(L"kimpo"); + BOOST_TEST((test(L"kimpo", lit(ref(ws))))); + } + + return boost::report_errors(); +} diff --git a/test/qi/match_manip.cpp b/test/qi/match_manip.cpp new file mode 100644 index 000000000..8b64f85b1 --- /dev/null +++ b/test/qi/match_manip.cpp @@ -0,0 +1,293 @@ +// Copyright (c) 2001-2008 Hartmut Kaiser +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +/////////////////////////////////////////////////////////////////////////////// +template +bool test(Char const *toparse, Expr const& xpr) +{ + namespace spirit = boost::spirit; + typedef + spirit::traits::is_component + is_component; + + // report invalid expression error as early as possible + BOOST_MPL_ASSERT_MSG(is_component::value, + xpr_is_not_convertible_to_a_parser, ()); + + typedef + typename spirit::result_of::as_component::type + component; + typedef typename component::director director; + + component c = spirit::as_component(spirit::qi::domain(), xpr); + + std::istringstream istrm(toparse); + istrm >> c; + return istrm.good() || istrm.eof(); +} + +template +bool test(Char const *toparse, + boost::spirit::qi::detail::match_manip const& mm) +{ + std::istringstream istrm(toparse); + istrm >> mm; + return istrm.good() || istrm.eof(); +} + +/////////////////////////////////////////////////////////////////////////////// +bool is_list_ok(std::list const& l) +{ + std::list::const_iterator cit = l.begin(); + if (cit == l.end() || *cit != 'a') + return false; + if (++cit == l.end() || *cit != 'b') + return false; + + return ++cit != l.end() && *cit == 'c'; +} + +/////////////////////////////////////////////////////////////////////////////// +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::ascii; + using namespace boost::spirit::arg_names; + using namespace boost::spirit::qi; + + namespace fusion = boost::fusion; + using namespace boost::phoenix; + + { + char c = '\0'; + BOOST_TEST(test( "a", + char_[ref(c) = _1] + ) && c == 'a'); + + c = '\0'; + BOOST_TEST(test( "a", + match(char_[ref(c) = _1]) + ) && c == 'a'); + + c = '\0'; + BOOST_TEST(test( " a", + phrase_match(char_[ref(c) = _1], space) + ) && c == 'a'); + + c = '\0'; + BOOST_TEST(test( "a", + match(char_, c) + ) && c == 'a'); + + c = '\0'; + BOOST_TEST(test( " a", + phrase_match(char_, c, space) + ) && c == 'a'); + } + + { + /////////////////////////////////////////////////////////////////////// + typedef typed_stream char_stream_type; + char_stream_type const char_stream = char_stream_type(); + + typedef typed_stream int_stream_type; + int_stream_type const int_stream = int_stream_type(); + + /////////////////////////////////////////////////////////////////////// + char c = '\0'; + BOOST_TEST(test( "a", + char_stream[ref(c) = _1] + ) && c == 'a'); + + c = '\0'; + BOOST_TEST(test( "a", + match(char_stream[ref(c) = _1]) + ) && c == 'a'); + + c = '\0'; + BOOST_TEST(test( " a", + phrase_match(char_stream[ref(c) = _1], space) + ) && c == 'a'); + + int i = 0; + BOOST_TEST(test( "42", + int_stream[ref(i) = _1] + ) && i == 42); + + i = 0; + BOOST_TEST(test( "42", + match(int_stream[ref(i) = _1]) + ) && i == 42); + + i = 0; + BOOST_TEST(test( " 42", + phrase_match(int_stream[ref(i) = _1], space) + ) && i == 42); + + /////////////////////////////////////////////////////////////////////// + c = '\0'; + BOOST_TEST(test( "a", + match(stream, c) + ) && c == 'a'); + + c = '\0'; + BOOST_TEST(test( " a", + phrase_match(stream, c, space) + ) && c == 'a'); + + i = 0; + BOOST_TEST(test( "42", + match(stream, i) + ) && i == 42); + + i = 0; + BOOST_TEST(test( " 42", + phrase_match(stream, i, space) + ) && i == 42); + } + + { + char a = '\0', b = '\0'; + BOOST_TEST(test( "ab", + char_[ref(a) = _1] >> char_[ref(b) = _1] + ) && a == 'a' && b == 'b'); + + a = '\0', b = '\0'; + BOOST_TEST(test( "ab", + match(char_[ref(a) = _1] >> char_[ref(b) = _1]) + ) && a == 'a' && b == 'b'); + + a = '\0', b = '\0'; + BOOST_TEST(test( " a b", + phrase_match(char_[ref(a) = _1] >> char_[ref(b) = _1], space) + ) && a == 'a' && b == 'b'); + + fusion::vector t; + BOOST_TEST(test( "ab", + match(char_ >> char_, t) + ) && fusion::at_c<0>(t) == 'a' && fusion::at_c<1>(t) == 'b'); + + t = fusion::vector(); + BOOST_TEST(test( " a b", + phrase_match(char_ >> char_, t, space) + ) && fusion::at_c<0>(t) == 'a' && fusion::at_c<1>(t) == 'b'); + } + + { + char a = '\0', b = '\0', c = '\0'; + BOOST_TEST(test( "abc", + char_[ref(a) = _1] >> char_[ref(b) = _1] >> char_[ref(c) = _1] + ) && a == 'a' && b == 'b' && c == 'c'); + + BOOST_TEST(test( "abc", + match(char_('a') >> char_('b') >> char_('c')) + )); + + BOOST_TEST(test( " a b c", + phrase_match(char_('a') >> char_('b') >> char_('c'), space) + )); + + BOOST_TEST(!test( "abc", + match(char_('a') >> char_('b') >> char_('d')) + )); + + BOOST_TEST(!test( " a b c", + phrase_match(char_('a') >> char_('b') >> char_('d'), space) + )); + + fusion::vector t; + BOOST_TEST(test( "abc", + match(char_ >> char_ >> char_, t) + ) && fusion::at_c<0>(t) == 'a' && fusion::at_c<1>(t) == 'b' && fusion::at_c<2>(t) == 'c'); + + t = fusion::vector(); + BOOST_TEST(test( " a b c", + phrase_match(char_ >> char_ >> char_, t, space) + ) && fusion::at_c<0>(t) == 'a' && fusion::at_c<1>(t) == 'b' && fusion::at_c<2>(t) == 'c'); + } + + { + char a = '\0'; + int i = 0; + BOOST_TEST(test( "a2", + (char_ >> int_)[ref(a) = _1, ref(i) = _2] + ) && a == 'a' && i == 2); + + fusion::vector t; + BOOST_TEST(test( "a2", + match(char_ >> int_, t) + ) && fusion::at_c<0>(t) == 'a' && fusion::at_c<1>(t) == 2); + + t = fusion::vector(); + BOOST_TEST(test( " a 2", + phrase_match(char_ >> int_, t, space) + ) && fusion::at_c<0>(t) == 'a' && fusion::at_c<1>(t) == 2); + + BOOST_TEST(!test( "a2", + match(char_ >> alpha, t) + )); + BOOST_TEST(!test( " a 2", + phrase_match(char_ >> alpha, t, space) + )); + } + + { + // output all elements of a vector + std::vector v; + BOOST_TEST(test( "abc", + (*char_)[ref(v) = _1] + ) && 3 == v.size() && v[0] == 'a' && v[1] == 'b' && v[2] == 'c'); + + v.clear(); + BOOST_TEST(test( "abc", + match(*char_, v) + ) && 3 == v.size() && v[0] == 'a' && v[1] == 'b' && v[2] == 'c'); + + v.clear(); + BOOST_TEST(test( " a b c", + phrase_match(*char_, v, space) + ) && 3 == v.size() && v[0] == 'a' && v[1] == 'b' && v[2] == 'c'); + + // parse a comma separated list of vector elements + v.clear(); + BOOST_TEST(test( "a,b,c", + match(char_ % ',', v) + ) && 3 == v.size() && v[0] == 'a' && v[1] == 'b' && v[2] == 'c'); + + v.clear(); + BOOST_TEST(test( " a , b , c", + phrase_match(char_ % ',', v, space) + ) && 3 == v.size() && v[0] == 'a' && v[1] == 'b' && v[2] == 'c'); + + // output all elements of a list + std::list l; + BOOST_TEST(test( "abc", + match(*char_, l) + ) && 3 == l.size() && is_list_ok(l)); + + l.clear(); + BOOST_TEST(test( " a b c", + phrase_match(*char_, l, space) + ) && 3 == l.size() && is_list_ok(l)); + } + + return boost::report_errors(); +} + diff --git a/test/qi/no_case.cpp b/test/qi/no_case.cpp new file mode 100644 index 000000000..afd171e5a --- /dev/null +++ b/test/qi/no_case.cpp @@ -0,0 +1,95 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + http://spirit.sourceforge.net/ + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using namespace boost::spirit; + + { + using namespace boost::spirit::ascii; + BOOST_TEST(test("x", no_case[char_])); + BOOST_TEST(test("X", no_case[char_('x')])); + BOOST_TEST(test("X", no_case[char_('X')])); + BOOST_TEST(test("x", no_case[char_('X')])); + BOOST_TEST(test("x", no_case[char_('x')])); + BOOST_TEST(!test("z", no_case[char_('X')])); + BOOST_TEST(!test("z", no_case[char_('x')])); + BOOST_TEST(test("x", no_case[char_('a', 'z')])); + BOOST_TEST(test("X", no_case[char_('a', 'z')])); + BOOST_TEST(!test("a", no_case[char_('b', 'z')])); + BOOST_TEST(!test("z", no_case[char_('a', 'y')])); + } + + { + using namespace boost::spirit::ascii; + BOOST_TEST(test("Bochi Bochi", no_case[lit("bochi bochi")])); + BOOST_TEST(test("BOCHI BOCHI", no_case[lit("bochi bochi")])); + BOOST_TEST(!test("Vavoo", no_case[lit("bochi bochi")])); + } + + { + // should work! + using namespace boost::spirit::ascii; + BOOST_TEST(test("x", no_case[no_case[char_]])); + BOOST_TEST(test("x", no_case[no_case[char_('x')]])); + BOOST_TEST(test("yabadabadoo", no_case[no_case[lit("Yabadabadoo")]])); + } + + { + using namespace boost::spirit::ascii; + BOOST_TEST(test("X", no_case[alnum])); + BOOST_TEST(test("6", no_case[alnum])); + BOOST_TEST(!test(":", no_case[alnum])); + + BOOST_TEST(test("X", no_case[lower])); + BOOST_TEST(test("x", no_case[lower])); + BOOST_TEST(test("X", no_case[upper])); + BOOST_TEST(test("x", no_case[upper])); + BOOST_TEST(!test(":", no_case[lower])); + BOOST_TEST(!test(":", no_case[upper])); + } + + { + using namespace boost::spirit::iso8859_1; + BOOST_TEST(test("X", no_case[alnum])); + BOOST_TEST(test("6", no_case[alnum])); + BOOST_TEST(!test(":", no_case[alnum])); + + BOOST_TEST(test("X", no_case[lower])); + BOOST_TEST(test("x", no_case[lower])); + BOOST_TEST(test("X", no_case[upper])); + BOOST_TEST(test("x", no_case[upper])); + BOOST_TEST(!test(":", no_case[lower])); + BOOST_TEST(!test(":", no_case[upper])); + } + + { + using namespace boost::spirit::standard; + BOOST_TEST(test("X", no_case[alnum])); + BOOST_TEST(test("6", no_case[alnum])); + BOOST_TEST(!test(":", no_case[alnum])); + + BOOST_TEST(test("X", no_case[lower])); + BOOST_TEST(test("x", no_case[lower])); + BOOST_TEST(test("X", no_case[upper])); + BOOST_TEST(test("x", no_case[upper])); + BOOST_TEST(!test(":", no_case[lower])); + BOOST_TEST(!test(":", no_case[upper])); + } + + return boost::report_errors(); +} diff --git a/test/qi/none.cpp b/test/qi/none.cpp new file mode 100644 index 000000000..d083fbe4f --- /dev/null +++ b/test/qi/none.cpp @@ -0,0 +1,25 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using namespace boost::spirit; + + { + BOOST_TEST((!test("", none))); + BOOST_TEST((!test("xxx", none))); + } + + return boost::report_errors(); +} diff --git a/test/qi/not_predicate.cpp b/test/qi/not_predicate.cpp new file mode 100644 index 000000000..72a2288e8 --- /dev/null +++ b/test/qi/not_predicate.cpp @@ -0,0 +1,27 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using namespace boost::spirit; + + { + BOOST_TEST((!test("1234", !int_))); + BOOST_TEST((test("abcd", !int_, false))); + } + + return boost::report_errors(); +} diff --git a/test/qi/optional.cpp b/test/qi/optional.cpp new file mode 100644 index 000000000..a60ad3abb --- /dev/null +++ b/test/qi/optional.cpp @@ -0,0 +1,66 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using spirit_test::test_attr; + using namespace boost::spirit; + + { + BOOST_TEST((test("1234", -int_))); + BOOST_TEST((test("abcd", -int_, false))); + } + + { // test propagation of unused + using namespace boost::fusion; + + vector v; + BOOST_TEST((test_attr("a1234c", char_ >> -omit[int_] >> char_, v))); + BOOST_TEST((at_c<0>(v) == 'a')); + BOOST_TEST((at_c<1>(v) == 'c')); + + v = boost::fusion::vector(); + BOOST_TEST((test_attr("a1234c", char_ >> omit[-int_] >> char_, v))); + BOOST_TEST((at_c<0>(v) == 'a')); + BOOST_TEST((at_c<1>(v) == 'c')); + + //~ char ch; + //~ BOOST_TEST((test_attr(",c", -(',' >> char_), ch))); + //~ BOOST_TEST((ch == 'c')); + } + + { // test action + + using namespace boost::phoenix; + namespace phx = boost::phoenix; + using namespace boost::spirit::arg_names; + + boost::optional n = 0; + BOOST_TEST((test("1234", (-int_)[phx::ref(n) = _1]))); + BOOST_TEST(n.get() == 1234); + + n = boost::optional(); + BOOST_TEST((test("abcd", (-int_)[phx::ref(n) = _1], false))); + BOOST_TEST(!n); + } + + return boost::report_errors(); +} diff --git a/test/qi/permutation.cpp b/test/qi/permutation.cpp new file mode 100644 index 000000000..c38d4bea7 --- /dev/null +++ b/test/qi/permutation.cpp @@ -0,0 +1,121 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +using namespace spirit_test; + +int +main() +{ + using namespace boost::spirit; + using boost::spirit::ascii::alpha; + using boost::spirit::qi::rule; + using boost::fusion::vector; + using boost::fusion::at_c; + using boost::optional; + + { + BOOST_TEST((test("a", char_('a') ^ char_('b') ^ char_('c')))); + BOOST_TEST((test("b", char_('a') ^ char_('b') ^ char_('c')))); + BOOST_TEST((test("ab", char_('a') ^ char_('b') ^ char_('c')))); + BOOST_TEST((test("ba", char_('a') ^ char_('b') ^ char_('c')))); + BOOST_TEST((test("abc", char_('a') ^ char_('b') ^ char_('c')))); + BOOST_TEST((test("acb", char_('a') ^ char_('b') ^ char_('c')))); + BOOST_TEST((test("bca", char_('a') ^ char_('b') ^ char_('c')))); + BOOST_TEST((test("bac", char_('a') ^ char_('b') ^ char_('c')))); + BOOST_TEST((test("cab", char_('a') ^ char_('b') ^ char_('c')))); + BOOST_TEST((test("cba", char_('a') ^ char_('b') ^ char_('c')))); + + BOOST_TEST((!test("cca", char_('a') ^ char_('b') ^ char_('c')))); + } + + { + vector, optional > attr; + + BOOST_TEST((test_attr("a", int_ ^ alpha, attr))); + BOOST_TEST((!at_c<0>(attr))); + BOOST_TEST((at_c<1>(attr).get() == 'a')); + + at_c<1>(attr) = optional(); // clear the optional + BOOST_TEST((test_attr("123", int_ ^ alpha, attr))); + BOOST_TEST((at_c<0>(attr).get() == 123)); + BOOST_TEST((!at_c<1>(attr))); + + at_c<0>(attr) = optional(); // clear the optional + BOOST_TEST((test_attr("123a", int_ ^ alpha, attr))); + BOOST_TEST((at_c<0>(attr).get() == 123)); + BOOST_TEST((at_c<1>(attr).get() == 'a')); + + at_c<0>(attr) = optional(); // clear the optional + at_c<1>(attr) = optional(); // clear the optional + BOOST_TEST((test_attr("a123", int_ ^ alpha, attr))); + BOOST_TEST((at_c<0>(attr).get() == 123)); + BOOST_TEST((at_c<1>(attr).get() == 'a')); + } + + { // test action + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + namespace phx = boost::phoenix; + + optional i; + optional c; + + BOOST_TEST((test("123a", (int_ ^ alpha)[phx::ref(i) = _1, phx::ref(c) = _2]))); + BOOST_TEST((i.get() == 123)); + BOOST_TEST((c.get() == 'a')); + } + + { // test rule %= + + typedef vector, optional > attr_type; + attr_type attr; + + rule r; + r %= int_ ^ alpha; + + BOOST_TEST((test_attr("a", r, attr))); + BOOST_TEST((!at_c<0>(attr))); + BOOST_TEST((at_c<1>(attr).get() == 'a')); + + at_c<1>(attr) = optional(); // clear the optional + BOOST_TEST((test_attr("123", r, attr))); + BOOST_TEST((at_c<0>(attr).get() == 123)); + BOOST_TEST((!at_c<1>(attr))); + + at_c<0>(attr) = optional(); // clear the optional + BOOST_TEST((test_attr("123a", r, attr))); + BOOST_TEST((at_c<0>(attr).get() == 123)); + BOOST_TEST((at_c<1>(attr).get() == 'a')); + + at_c<0>(attr) = optional(); // clear the optional + at_c<1>(attr) = optional(); // clear the optional + BOOST_TEST((test_attr("a123", r, attr))); + BOOST_TEST((at_c<0>(attr).get() == 123)); + BOOST_TEST((at_c<1>(attr).get() == 'a')); + } + + return boost::report_errors(); +} + diff --git a/test/qi/plus.cpp b/test/qi/plus.cpp new file mode 100644 index 000000000..72d425f8b --- /dev/null +++ b/test/qi/plus.cpp @@ -0,0 +1,98 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +using namespace spirit_test; + +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::ascii; + + { + BOOST_TEST(test("aaaaaaaa", +char_)); + BOOST_TEST(test("a", +char_)); + BOOST_TEST(!test("", +char_)); + BOOST_TEST(test("aaaaaaaa", +alpha)); + BOOST_TEST(!test("aaaaaaaa", +upper)); + } + + { + BOOST_TEST(test(" a a aaa aa", +char_, space)); + BOOST_TEST(test("12345 678 9 ", +digit, space)); + } + + { + BOOST_TEST(test("aBcdeFGH", no_case[+char_])); + BOOST_TEST(test("a B cde FGH ", no_case[+char_], space)); + } + + { + std::vector v; + BOOST_TEST(test_attr("123 456 789 10", +int_, v, space) && 4 == v.size() && + v[0] == 123 && v[1] == 456 && v[2] == 789 && v[3] == 10); + } + + { + BOOST_TEST(test("Kim Kim Kim", +lit("Kim"), space)); + } + + { + std::vector v; + v.clear(); + BOOST_TEST(test_attr("bbbb", +omit[char_('b')], v) && 0 == v.size()); + + v.clear(); + BOOST_TEST(test_attr("bbbb", omit[+char_('b')], v) && 0 == v.size()); + + v.clear(); + BOOST_TEST(test_attr("b b b b ", +omit[char_('b')], v, space) && 0 == v.size()); + + v.clear(); + BOOST_TEST(test_attr("b b b b ", omit[+char_('b')], v, space) && 0 == v.size()); + } + + { // actions + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + + std::vector v; + BOOST_TEST(test("bbbb", (+char_)[ref(v) = _1]) && 4 == v.size() && + v[0] == 'b' && v[1] == 'b' && v[2] == 'b' && v[3] == 'b'); + } + + { // more actions + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + + std::vector v; + BOOST_TEST(test("1 2 3", (+int_)[ref(v) = _1], space) && 3 == v.size() && + v[0] == 1 && v[1] == 2 && v[2] == 3); + } + + return boost::report_errors(); +} + diff --git a/test/qi/range_run.cpp b/test/qi/range_run.cpp new file mode 100644 index 000000000..d9d2d4169 --- /dev/null +++ b/test/qi/range_run.cpp @@ -0,0 +1,167 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +==============================================================================*/ +#include +#include +#include +#include +#include +#include +#include + +#if defined(BOOST_MSVC) +# pragma warning(disable: 4800) // 'int' : forcing value to bool 'true' or 'false' warning +#endif + +template +void acid_test() +{ + using boost::spirit::qi::detail::range_run; + using boost::spirit::qi::detail::range; + + typedef boost::integer_traits integer_traits; + Char const const_min = integer_traits::const_min; + Char const const_max = integer_traits::const_max; + int const test_size = 1000; + + boost::mt19937 rng; + boost::uniform_int<> char_(const_min, const_max); + boost::variate_generator > + gen(rng, char_); + boost::uniform_int<> _1of10(1, 10); + boost::variate_generator > + on_or_off(rng, _1of10); + + range_run rr; + boost::dynamic_bitset<> bset(const_max-const_min+1); + + for (int i = 0; i < test_size; ++i) + { + range r = range(gen(), gen()); + if (r.first > r.last) + std::swap(r.first, r.last); + + bool set = on_or_off() != 1; + if (set) + rr.set(r); + else + rr.clear(r); + for (int j = r.first; j <= int(r.last); ++j) + bset[j-const_min] = set; + } + + for (int i = const_min; i <= int(const_max); ++i) + { + BOOST_TEST(rr.test(i) == bset[i-const_min]); + } +} + +int +main() +{ + using boost::spirit::qi::detail::range_run; + using boost::spirit::qi::detail::range; + + { + range_run rr; + rr.set(range('a', 'a')); + for (char c = 0; c < 127; ++c) + { + BOOST_TEST(c == 'a' == rr.test(c)); + } + } + { + range_run rr; + rr.set(range('a', 'z')); + rr.set(range('A', 'Z')); + rr.clear(range('A', 'Z')); + for (char c = 0; c < 127; ++c) + { + BOOST_TEST(bool(std::islower(c)) == rr.test(c)); + } + } + { + range_run rr; + rr.set(range(0, 0)); + for (char c = 0; c < 127; ++c) + { + BOOST_TEST(c == 0 == rr.test(c)); + } + rr.set(range(0, 50)); + for (char c = 0; c < 127; ++c) + { + BOOST_TEST(((c >= 0) && (c <= 50)) == rr.test(c)); + } + } + { + range_run rr; + rr.set(range(255, 255)); + for (unsigned char c = 0; c < 255; ++c) + { + BOOST_TEST(c == 255 == rr.test(c)); + } + rr.set(range(250, 255)); + for (unsigned char c = 0; c < 255; ++c) + { + BOOST_TEST((c >= 250) == rr.test(c)); + } + } + { + range_run rr; + rr.set(range('a', 'z')); + rr.set(range('A', 'Z')); + for (char c = 0; c < 127; ++c) + { + BOOST_TEST(bool(std::isalpha(c)) == rr.test(c)); + } + } + { + range_run rr; + rr.set(range('a', 'z')); + rr.set(range('A', 'Z')); + rr.clear(range('J', 'j')); + for (char c = 0; c < 127; ++c) + { + BOOST_TEST((bool(std::isalpha(c)) && (c < 'J' || c > 'j')) == rr.test(c)); + } + } + { + range_run rr; + rr.set(range(3, 3)); + rr.set(range(1, 5)); + BOOST_TEST(rr.test(5)); + } + { + range_run rr; + for (char c = 0; c < 127; ++c) + { + if (c & 1) + { + rr.set(range(c, c)); + } + } + for (char c = 0; c < 127; ++c) + { + BOOST_TEST(bool((c & 1)) == rr.test(c)); + } + rr.clear(range(90, 105)); + for (char c = 0; c < 127; ++c) + { + BOOST_TEST((bool((c & 1)) && (c < 90 || c > 105)) == rr.test(c)); + } + } + { + acid_test(); + acid_test(); + acid_test(); + acid_test(); + acid_test(); + acid_test(); + acid_test(); + } + + return boost::report_errors(); +} diff --git a/test/qi/raw.cpp b/test/qi/raw.cpp new file mode 100644 index 000000000..98c907b44 --- /dev/null +++ b/test/qi/raw.cpp @@ -0,0 +1,39 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test_attr; + using namespace boost::spirit; + using namespace boost::spirit::ascii; + using boost::spirit::qi::rule; + + { + boost::iterator_range range; + std::string str; + BOOST_TEST((test_attr("spirit_test_123", raw[alpha >> *(alnum | '_')], range))); + BOOST_TEST((std::string(range.begin(), range.end()) == "spirit_test_123")); + } + + { + std::string str; + BOOST_TEST((test_attr("spirit_test_123", raw[alpha >> *(alnum | '_')], str))); + BOOST_TEST((str == "spirit_test_123")); + } + + return boost::report_errors(); +} diff --git a/test/qi/real.cpp b/test/qi/real.cpp new file mode 100644 index 000000000..5ec2c7c7f --- /dev/null +++ b/test/qi/real.cpp @@ -0,0 +1,374 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + Copyright (c) 2001-2008 Hartmut Kaiser + + Use, modification and distribution is subject to the Boost Software + License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.hpp" +using namespace spirit_test; + +/////////////////////////////////////////////////////////////////////////////// +// These policies can be used to parse thousand separated +// numbers with at most 2 decimal digits after the decimal +// point. e.g. 123,456,789.01 +/////////////////////////////////////////////////////////////////////////////// +template +struct ts_real_policies : boost::spirit::qi::ureal_policies +{ + // 2 decimal places Max + template + static bool + parse_frac_n(Iterator& first, Iterator const& last, Attribute& attr) + { + return boost::spirit::qi:: + extract_uint::call(first, last, attr); + } + + // No exponent + template + static bool + parse_exp(Iterator& first, Iterator const& last) + { + return false; + } + + // No exponent + template + static bool + parse_exp_n(Iterator& first, Iterator const& last, Attribute& attr) + { + return false; + } + + // Thousands separated numbers + template + static bool + parse_n(Iterator& first, Iterator const& last, Attribute& attr) + { + using namespace boost::spirit::qi; + using namespace boost::spirit; + + uint_spec uint3; + uint_spec uint3_3; + + T result = 0; + if (parse(first, last, uint3, result)) + { + bool hit = false; + T n; + Iterator save = first; + + while (parse(first, last, ',') && parse(first, last, uint3_3, n)) + { + result = result * 1000 + n; + save = first; + hit = true; + } + + first = save; + if (hit) + attr = result; + return hit; + } + return false; + } +}; + +template +struct no_trailing_dot_policy : boost::spirit::qi::real_policies +{ + static bool const allow_trailing_dot = false; +}; + +template +struct no_leading_dot_policy : boost::spirit::qi::real_policies +{ + static bool const allow_leading_dot = false; +}; + +template +bool +compare(T n, double expected) +{ + double const eps = 0.00001; + T delta = n - expected; + return (delta >= -eps) && (delta <= eps); +} + +int +main() +{ + /////////////////////////////////////////////////////////////////////////////// + // thousand separated numbers + /////////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::qi::uint_spec; + using boost::spirit::qi::parse; + + uint_spec uint3; + uint_spec uint3_3; + + #define r (uint3 >> *(',' >> uint3_3)) + + BOOST_TEST(test("1,234,567,890", r)); + BOOST_TEST(test("12,345,678,900", r)); + BOOST_TEST(test("123,456,789,000", r)); + BOOST_TEST(!test("1000,234,567,890", r)); + BOOST_TEST(!test("1,234,56,890", r)); + BOOST_TEST(!test("1,66", r)); + } + + /////////////////////////////////////////////////////////////////////////////// + // unsigned real number tests + /////////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::qi::real_spec; + using boost::spirit::qi::parse; + using boost::spirit::qi::ureal_policies; + + real_spec > udouble; + double d; + + BOOST_TEST(test("1234", udouble)); + BOOST_TEST(test_attr("1234", udouble, d) && compare(d, 1234)); + + BOOST_TEST(test("1.2e3", udouble)); + BOOST_TEST(test_attr("1.2e3", udouble, d) && compare(d, 1.2e3)); + + BOOST_TEST(test("1.2e-3", udouble)); + BOOST_TEST(test_attr("1.2e-3", udouble, d) && compare(d, 1.2e-3)); + + BOOST_TEST(test("1.e2", udouble)); + BOOST_TEST(test_attr("1.e2", udouble, d) && compare(d, 1.e2)); + + BOOST_TEST(test("1.", udouble)); + BOOST_TEST(test_attr("1.", udouble, d) && compare(d, 1.)); + + BOOST_TEST(test(".2e3", udouble)); + BOOST_TEST(test_attr(".2e3", udouble, d) && compare(d, .2e3)); + + BOOST_TEST(test("2e3", udouble)); + BOOST_TEST(test_attr("2e3", udouble, d) && compare(d, 2e3)); + + BOOST_TEST(test("2", udouble)); + BOOST_TEST(test_attr("2", udouble, d) && compare(d, 2)); + + using boost::math::fpclassify; + BOOST_TEST(test("inf", udouble)); + BOOST_TEST(test("infinity", udouble)); + BOOST_TEST(test("INF", udouble)); + BOOST_TEST(test("INFINITY", udouble)); + BOOST_TEST(test_attr("inf", udouble, d) && FP_INFINITE == fpclassify(d)); + BOOST_TEST(test_attr("INF", udouble, d) && FP_INFINITE == fpclassify(d)); + BOOST_TEST(test_attr("infinity", udouble, d) && FP_INFINITE == fpclassify(d)); + BOOST_TEST(test_attr("INFINITY", udouble, d) && FP_INFINITE == fpclassify(d)); + + BOOST_TEST(test("nan", udouble)); + BOOST_TEST(test_attr("nan", udouble, d) && FP_NAN == fpclassify(d)); + BOOST_TEST(test("NAN", udouble)); + BOOST_TEST(test_attr("NAN", udouble, d) && FP_NAN == fpclassify(d)); + + BOOST_TEST(test("nan(...)", udouble)); + BOOST_TEST(test_attr("nan(...)", udouble, d) && FP_NAN == fpclassify(d)); + BOOST_TEST(test("NAN(...)", udouble)); + BOOST_TEST(test_attr("NAN(...)", udouble, d) && FP_NAN == fpclassify(d)); + + BOOST_TEST(!test("e3", udouble)); + BOOST_TEST(!test_attr("e3", udouble, d)); + + BOOST_TEST(!test("-1.2e3", udouble)); + BOOST_TEST(!test_attr("-1.2e3", udouble, d)); + + BOOST_TEST(!test("+1.2e3", udouble)); + BOOST_TEST(!test_attr("+1.2e3", udouble, d)); + + BOOST_TEST(!test("1.2e", udouble)); + BOOST_TEST(!test_attr("1.2e", udouble, d)); + + BOOST_TEST(!test("-.3", udouble)); + BOOST_TEST(!test_attr("-.3", udouble, d)); + } + +/////////////////////////////////////////////////////////////////////////////// +// signed real number tests +/////////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::double_; + using boost::spirit::qi::parse; + double d; + + BOOST_TEST(test("-1234", double_)); + BOOST_TEST(test_attr("-1234", double_, d) && compare(d, -1234)); + + BOOST_TEST(test("-1.2e3", double_)); + BOOST_TEST(test_attr("-1.2e3", double_, d) && compare(d, -1.2e3)); + + BOOST_TEST(test("+1.2e3", double_)); + BOOST_TEST(test_attr("+1.2e3", double_, d) && compare(d, 1.2e3)); + + BOOST_TEST(test("-0.1", double_)); + BOOST_TEST(test_attr("-0.1", double_, d) && compare(d, -0.1)); + + BOOST_TEST(test("-1.2e-3", double_)); + BOOST_TEST(test_attr("-1.2e-3", double_, d) && compare(d, -1.2e-3)); + + BOOST_TEST(test("-1.e2", double_)); + BOOST_TEST(test_attr("-1.e2", double_, d) && compare(d, -1.e2)); + + BOOST_TEST(test("-.2e3", double_)); + BOOST_TEST(test_attr("-.2e3", double_, d) && compare(d, -.2e3)); + + BOOST_TEST(test("-2e3", double_)); + BOOST_TEST(test_attr("-2e3", double_, d) && compare(d, -2e3)); + + BOOST_TEST(!test("-e3", double_)); + BOOST_TEST(!test_attr("-e3", double_, d)); + + BOOST_TEST(!test("-1.2e", double_)); + BOOST_TEST(!test_attr("-1.2e", double_, d)); + + using boost::math::fpclassify; + using boost::math::signbit; + BOOST_TEST(test("-inf", double_)); + BOOST_TEST(test("-infinity", double_)); + BOOST_TEST(test_attr("-inf", double_, d) && + FP_INFINITE == fpclassify(d) && signbit(d)); + BOOST_TEST(test_attr("-infinity", double_, d) && + FP_INFINITE == fpclassify(d) && signbit(d)); + BOOST_TEST(test("-INF", double_)); + BOOST_TEST(test("-INFINITY", double_)); + BOOST_TEST(test_attr("-INF", double_, d) && + FP_INFINITE == fpclassify(d) && signbit(d)); + BOOST_TEST(test_attr("-INFINITY", double_, d) && + FP_INFINITE == fpclassify(d) && signbit(d)); + + BOOST_TEST(test("-nan", double_)); + BOOST_TEST(test_attr("-nan", double_, d) && + FP_NAN == fpclassify(d) && signbit(d)); + BOOST_TEST(test("-NAN", double_)); + BOOST_TEST(test_attr("-NAN", double_, d) && + FP_NAN == fpclassify(d) && signbit(d)); + + BOOST_TEST(test("-nan(...)", double_)); + BOOST_TEST(test_attr("-nan(...)", double_, d) && + FP_NAN == fpclassify(d) && signbit(d)); + BOOST_TEST(test("-NAN(...)", double_)); + BOOST_TEST(test_attr("-NAN(...)", double_, d) && + FP_NAN == fpclassify(d) && signbit(d)); + } + + /////////////////////////////////////////////////////////////////////////////// + // strict real number tests + /////////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::qi::real_spec; + using boost::spirit::qi::parse; + using boost::spirit::qi::strict_ureal_policies; + using boost::spirit::qi::strict_real_policies; + + real_spec > strict_udouble; + real_spec > strict_double; + double d; + + BOOST_TEST(!test("1234", strict_udouble)); + BOOST_TEST(!test_attr("1234", strict_udouble, d)); + + BOOST_TEST(test("1.2", strict_udouble)); + BOOST_TEST(test_attr("1.2", strict_udouble, d) && compare(d, 1.2)); + + BOOST_TEST(!test("-1234", strict_double)); + BOOST_TEST(!test_attr("-1234", strict_double, d)); + + BOOST_TEST(test("123.", strict_double)); + BOOST_TEST(test_attr("123.", strict_double, d) && compare(d, 123)); + + BOOST_TEST(test("3.E6", strict_double)); + BOOST_TEST(test_attr("3.E6", strict_double, d) && compare(d, 3e6)); + + real_spec > notrdot_real; + real_spec > nolddot_real; + + BOOST_TEST(!test("1234.", notrdot_real)); // Bad trailing dot + BOOST_TEST(!test(".1234", nolddot_real)); // Bad leading dot + } + + /////////////////////////////////////////////////////////////////////////// + // Special thousands separated numbers + /////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::qi::real_spec; + using boost::spirit::qi::parse; + real_spec > ts_real; + double d; + + BOOST_TEST(test("123,456,789.01", ts_real)); + BOOST_TEST(test_attr("123,456,789.01", ts_real, d) && compare(d, 123456789.01)); + + BOOST_TEST(test("12,345,678.90", ts_real)); + BOOST_TEST(test_attr("12,345,678.90", ts_real, d) && compare(d, 12345678.90)); + + BOOST_TEST(test("1,234,567.89", ts_real)); + BOOST_TEST(test_attr("1,234,567.89", ts_real, d) && compare(d, 1234567.89)); + + BOOST_TEST(!test("1234,567,890", ts_real)); + BOOST_TEST(!test("1,234,5678,9", ts_real)); + BOOST_TEST(!test("1,234,567.89e6", ts_real)); + BOOST_TEST(!test("1,66", ts_real)); + } + + /////////////////////////////////////////////////////////////////////////// + // Custom data type + /////////////////////////////////////////////////////////////////////////// + { + using boost::math::concepts::real_concept; + using boost::spirit::qi::real_spec; + using boost::spirit::qi::real_policies; + using boost::spirit::qi::parse; + + real_spec > custom_real; + real_concept d; + + BOOST_TEST(test("-1234", custom_real)); + BOOST_TEST(test_attr("-1234", custom_real, d) && compare(d, -1234)); + + BOOST_TEST(test("-1.2e3", custom_real)); + BOOST_TEST(test_attr("-1.2e3", custom_real, d) && compare(d, -1.2e3)); + + BOOST_TEST(test("+1.2e3", custom_real)); + BOOST_TEST(test_attr("+1.2e3", custom_real, d) && compare(d, 1.2e3)); + + BOOST_TEST(test("-0.1", custom_real)); + BOOST_TEST(test_attr("-0.1", custom_real, d) && compare(d, -0.1)); + + BOOST_TEST(test("-1.2e-3", custom_real)); + BOOST_TEST(test_attr("-1.2e-3", custom_real, d) && compare(d, -1.2e-3)); + + BOOST_TEST(test("-1.e2", custom_real)); + BOOST_TEST(test_attr("-1.e2", custom_real, d) && compare(d, -1.e2)); + + BOOST_TEST(test("-.2e3", custom_real)); + BOOST_TEST(test_attr("-.2e3", custom_real, d) && compare(d, -.2e3)); + + BOOST_TEST(test("-2e3", custom_real)); + BOOST_TEST(test_attr("-2e3", custom_real, d) && compare(d, -2e3)); + + BOOST_TEST(!test("-e3", custom_real)); + BOOST_TEST(!test_attr("-e3", custom_real, d)); + + BOOST_TEST(!test("-1.2e", custom_real)); + BOOST_TEST(!test_attr("-1.2e", custom_real, d)); + } + + return boost::report_errors(); +} diff --git a/test/qi/rule.cpp b/test/qi/rule.cpp new file mode 100644 index 000000000..0eea493c5 --- /dev/null +++ b/test/qi/rule.cpp @@ -0,0 +1,227 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +using namespace spirit_test; + +int +main() +{ + using namespace boost::spirit::qi; + using namespace boost::spirit::ascii; + + { // basic tests + + rule a, b, c, start; + + a = 'a'; + b = 'b'; + c = 'c'; + + start = *(a | b | c); + BOOST_TEST(test("abcabcacb", start)); + + start = (a | b) >> (start | b); + BOOST_TEST(test("aaaabababaaabbb", start)); + BOOST_TEST(test("aaaabababaaabba", start, false)); + } + + { // basic tests w/ skipper + + rule a, b, c, start; + + a = 'a'; + b = 'b'; + c = 'c'; + + start = *(a | b | c); + BOOST_TEST(test(" a b c a b c a c b", start, space)); + + // allow no skipping too: + BOOST_TEST(test("abcabcacb", start)); + + start = (a | b) >> (start | b); + BOOST_TEST(test(" a a a a b a b a b a a a b b b ", start, space)); + BOOST_TEST(test(" a a a a b a b a b a a a b b a ", start, space, false)); + } + + { // alias tests + + rule a, b, c, d, start; + + a = 'a'; + b = 'b'; + c = 'c'; + d = start.alias(); // d will always track start + + start = *(a | b | c); + BOOST_TEST(test("abcabcacb", d)); + + start = (a | b) >> (start | b); + BOOST_TEST(test("aaaabababaaabbb", d)); + } + + { // copy tests + + rule a, b, c, start; + + a = 'a'; + b = 'b'; + c = 'c'; + + // The FF is the dynamic equivalent of start = *(a | b | c); + start = a; + start = start.copy() | b; + start = start.copy() | c; + start = *(start.copy()); + + BOOST_TEST(test("abcabcacb", start)); + + // The FF is the dynamic equivalent of start = (a | b) >> (start | b); + start = b; + start = a | start.copy(); + start = start.copy() >> (start | b); + + BOOST_TEST(test("aaaabababaaabbb", start)); + BOOST_TEST(test("aaaabababaaabba", start, false)); + } + + { // context tests + + using namespace boost::phoenix; + using namespace boost::spirit::ascii; + using boost::spirit::arg_names::_1; + using boost::spirit::arg_names::_val; + + char ch; + rule a; + a = alpha[_val = _1]; + + BOOST_TEST(test("x", a[ref(ch) = _1])); + BOOST_TEST(ch == 'x'); + + BOOST_TEST(test_attr("z", a, ch)); // attribute is given. + BOOST_TEST(ch == 'z'); + } + + { // context (w/arg) tests + + using namespace boost::phoenix; + using namespace boost::spirit::ascii; + using boost::spirit::arg_names::_1; + using boost::spirit::arg_names::_r1; + using boost::spirit::arg_names::_r2; + using boost::spirit::arg_names::_val; + + char ch; + rule a; // 1 arg + a = alpha[_val = _1 + _r1]; + + BOOST_TEST(test("x", a(val(1))[ref(ch) = _1])); + BOOST_TEST(ch == 'x' + 1); + + BOOST_TEST(test_attr("a", a(1), ch)); // allow scalars as rule args too. + BOOST_TEST(ch == 'a' + 1); + + rule b; // 2 args + b = alpha[_val = _1 + _r1 + _r2]; + BOOST_TEST(test_attr("a", b(1, 2), ch)); + BOOST_TEST(ch == 'a' + 1 + 2); + } + + { // context (w/locals) tests + using namespace boost::phoenix; + using namespace boost::spirit::ascii; + using boost::spirit::arg_names::_1; + using boost::spirit::arg_names::_a; + using boost::spirit::char_; + using boost::spirit::locals; + + rule > a; // 1 local + a = alpha[_a = _1] >> char_(_a); + BOOST_TEST(test("aa", a)); + BOOST_TEST(!test("ax", a)); + } + + { // context (w/args and locals) tests + using namespace boost::phoenix; + using namespace boost::spirit::ascii; + using boost::spirit::arg_names::_1; + using boost::spirit::arg_names::_r1; + using boost::spirit::arg_names::_a; + using boost::spirit::char_; + using boost::spirit::locals; + + rule > a; // 1 arg + 1 local + a = alpha[_a = _1 + _r1] >> char_(_a); + BOOST_TEST(test("ab", a(val(1)))); + BOOST_TEST(test("xy", a(val(1)))); + BOOST_TEST(!test("ax", a(val(1)))); + } + + { // bug: test that injected attributes are ok + using namespace boost::phoenix; + using namespace boost::spirit::ascii; + using boost::spirit::arg_names::_1; + using boost::spirit::arg_names::_r1; + using boost::spirit::arg_names::_val; + using boost::spirit::char_; + + rule r; + + // problem code: + r = char_(_r1)[_val = _1]; + } + + { // error handling + + using namespace boost::phoenix; + using namespace boost::spirit::ascii; + using boost::phoenix::val; + using boost::spirit::int_; + using boost::spirit::arg_names::_4; // what + using boost::spirit::arg_names::_3; // error pos + using boost::spirit::arg_names::_2; // end + using boost::spirit::qi::fail; + + rule r; + r = '(' > int_ > ',' > int_ > ')'; + + on_error + ( + r, std::cout + << val("Error! Expecting: ") + << _4 + << val(" Here: \"") + << construct(_3, _2) + << val("\"") + << std::endl + ); + + BOOST_TEST(test("(123,456)", r)); + BOOST_TEST(!test("(abc,def)", r)); + BOOST_TEST(!test("(123,456]", r)); + BOOST_TEST(!test("(123;456)", r)); + BOOST_TEST(!test("[123,456]", r)); + } + + return boost::report_errors(); +} + diff --git a/test/qi/rule_fail.cpp b/test/qi/rule_fail.cpp new file mode 100644 index 000000000..7ac5c4a0d --- /dev/null +++ b/test/qi/rule_fail.cpp @@ -0,0 +1,31 @@ +/*============================================================================= + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#include +#include +#include +#include +#include + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace boost::spirit::ascii; + +// this test must fail compiling +int main() +{ + char const* input = "some input, it doesn't matter"; + char const* end = &input[strlen(input)+1]; + + rule > def; + def = int_ >> *(',' >> int_); + + bool r = phrase_parse(input, end, def, + space | ('%' >> *~char_('\n') >> '\n')); + + return 0; +} diff --git a/test/qi/sequence.cpp b/test/qi/sequence.cpp new file mode 100644 index 000000000..001f7f29b --- /dev/null +++ b/test/qi/sequence.cpp @@ -0,0 +1,204 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +int +main() +{ + using namespace boost::spirit; + using namespace boost::spirit::ascii; + using boost::fusion::vector; + using boost::fusion::at_c; + using spirit_test::test; + using spirit_test::test_attr; + + { + BOOST_TEST((test("aa", char_ >> char_))); + BOOST_TEST((test("aaa", char_ >> char_ >> char_('a')))); + BOOST_TEST((test("xi", char_('x') >> char_('i')))); + BOOST_TEST((!test("xi", char_('x') >> char_('o')))); + BOOST_TEST((test("xin", char_('x') >> char_('i') >> char_('n')))); + + } + + { + BOOST_TEST((test(" a a ", char_ >> char_, space))); + BOOST_TEST((test(" x i ", char_('x') >> char_('i'), space))); + BOOST_TEST((!test(" x i ", char_('x') >> char_('o'), space))); + } + + { + BOOST_TEST((test(" Hello, World", lit("Hello") >> ',' >> "World", space))); + } + + { + vector attr; + BOOST_TEST((test_attr("abcdefg", char_ >> char_ >> "cdefg", attr))); + BOOST_TEST((at_c<0>(attr) == 'a')); + BOOST_TEST((at_c<1>(attr) == 'b')); + } + + { + vector attr; + BOOST_TEST((test_attr(" a\n b\n c\n ", char_ >> char_ >> char_, attr, space))); + BOOST_TEST((at_c<0>(attr) == 'a')); + BOOST_TEST((at_c<1>(attr) == 'b')); + BOOST_TEST((at_c<2>(attr) == 'c')); + } + + { + // unused_type means we don't care about the attribute + vector attr; + BOOST_TEST((test_attr("abc", char_ >> 'b' >> char_, attr))); + BOOST_TEST((at_c<0>(attr) == 'a')); + BOOST_TEST((at_c<1>(attr) == 'c')); + } + + { + // omit[] means we don't receive the attribute + vector attr; + BOOST_TEST((test_attr("abc", omit[char_] >> omit['b'] >> char_, attr))); + BOOST_TEST((at_c<0>(attr) == 'c')); + } + + { + // If all elements except 1 is omitted, the attribute is + // a single-element sequence. For this case alone, we allow + // naked attributes (unwrapped in a fusion sequence). + char attr; + BOOST_TEST((test_attr("abc", omit[char_] >> 'b' >> char_, attr))); + BOOST_TEST((attr == 'c')); + } + + { + // omit[] means we don't receive the attribute + vector<> attr; + BOOST_TEST((test_attr("abc", omit[char_] >> omit['b'] >> omit[char_], attr))); + } + + { + // omit[] means we don't receive the attribute + // this test is merely a compile test, because using a unused as the + // explicit attribute doesn't make any sense + unused_type attr; + BOOST_TEST((test_attr("abc", omit[char_ >> 'b' >> char_], attr))); + } + + { + // omit[] means we don't receive the attribute, if all elements of a + // sequence have unused attributes, the whole sequence has an unused + // attribute as well + vector attr; + BOOST_TEST((test_attr("abcde", + char_ >> (omit[char_] >> omit['c'] >> omit[char_]) >> char_, attr))); + BOOST_TEST((at_c<0>(attr) == 'a')); + BOOST_TEST((at_c<1>(attr) == 'e')); + } + + { + // "hello" has an unused_type. unused attrubutes are not part of the sequence + vector attr; + BOOST_TEST((test_attr("a hello c", char_ >> "hello" >> char_, attr, space))); + BOOST_TEST((at_c<0>(attr) == 'a')); + BOOST_TEST((at_c<1>(attr) == 'c')); + } + + { + // omit[] means we don't receive the attribute + vector attr; + BOOST_TEST((test_attr("a hello c", char_ >> "hello" >> omit[char_], attr, space))); + BOOST_TEST((at_c<0>(attr) == 'a')); + } + + { + // if only one node in a sequence is left (all the others are omitted), + // then we should also allow "naked" attributes (unwraped in a tuple) + int attr; + BOOST_TEST((test_attr("a 123 c", omit['a'] >> int_ >> omit['c'], attr, space))); + BOOST_TEST((attr == 123)); + } + + { + // unused means we don't care about the attribute + BOOST_TEST((test_attr("abc", char_ >> 'b' >> char_, unused))); + } + + { + BOOST_TEST((test("aA", no_case[char_('a') >> 'a']))); + BOOST_TEST((test("BEGIN END", no_case[lit("begin") >> "end"], space))); + BOOST_TEST((!test("BEGIN END", no_case[lit("begin") >> "nend"], space))); + } + + { +#ifdef SPIRIT_TEST_COMPILE_FAIL // $$$ + char_ >> char_ = char_ >> char_; // disallow this! +#endif + } + + { // test action + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + char c = 0; + int n = 0; + + BOOST_TEST(test("x123\"a string\"", (char_ >> int_ >> "\"a string\"") + [ref(c) = _1, ref(n) = _2])); + BOOST_TEST(c == 'x'); + BOOST_TEST(n == 123); + } + + { // test action with omitted attribute + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + char c = 0; + + BOOST_TEST(test("x123\"a string\"", (char_ >> omit[int_] >> "\"a string\"") + [ref(c) = _1])); + BOOST_TEST(c == 'x'); + } + + { // test action + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + char c = 0; + int n = 0; + + BOOST_TEST(test("x 123 \"a string\"", (char_ >> int_ >> "\"a string\"") + [ref(c) = _1, ref(n) = _2], space)); + BOOST_TEST(c == 'x'); + BOOST_TEST(n == 123); + } + + { // test action with omitted attribute + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + int n = 0; + + BOOST_TEST(test("x 123 \"a string\"", + (omit[char_] >> int_ >> "\"a string\"")[ref(n) = _1], space)); + BOOST_TEST(n == 123); + } + + return boost::report_errors(); +} + diff --git a/test/qi/sequential_or.cpp b/test/qi/sequential_or.cpp new file mode 100644 index 000000000..b0418c44a --- /dev/null +++ b/test/qi/sequential_or.cpp @@ -0,0 +1,77 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "test.hpp" + +using namespace spirit_test; + +int +main() +{ + using namespace boost::spirit; + using boost::spirit::ascii::alpha; + using boost::fusion::vector; + using boost::fusion::at_c; + using boost::optional; + + { + BOOST_TEST((test("a", char_('a') || char_('b')))); + BOOST_TEST((test("b", char_('a') || char_('b')))); + BOOST_TEST((test("ab", char_('a') || char_('b')))); + } + + { + vector, optional > attr; + + BOOST_TEST((test_attr("a", int_ || alpha, attr))); + BOOST_TEST((!at_c<0>(attr))); + BOOST_TEST((at_c<1>(attr).get() == 'a')); + + at_c<1>(attr) = optional(); // clear the optional + BOOST_TEST((test_attr("123", int_ || alpha, attr))); + BOOST_TEST((at_c<0>(attr).get() == 123)); + BOOST_TEST((!at_c<1>(attr))); + + at_c<0>(attr) = optional(); // clear the optional + BOOST_TEST((test_attr("123a", int_ || alpha, attr))); + BOOST_TEST((at_c<0>(attr).get() == 123)); + BOOST_TEST((at_c<1>(attr).get() == 'a')); + + BOOST_TEST((!test("a123", int_ || alpha))); + } + + { // test action + using namespace boost::phoenix; + using namespace boost::spirit::arg_names; + namespace phx = boost::phoenix; + + optional i; + optional c; + + BOOST_TEST((test("123a", (int_ || alpha)[phx::ref(i) = _1, phx::ref(c) = _2]))); + BOOST_TEST((i.get() == 123)); + BOOST_TEST((c.get() == 'a')); + } + + return boost::report_errors(); +} + diff --git a/test/qi/symbols.cpp b/test/qi/symbols.cpp new file mode 100644 index 000000000..499e95131 --- /dev/null +++ b/test/qi/symbols.cpp @@ -0,0 +1,189 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "test.hpp" + +int +main() +{ + using spirit_test::test; + using spirit_test::test_attr; + using namespace boost::spirit::qi; + + { // basics + symbols sym; + + sym.add + ("Joel") + ("Ruby") + ("Tenji") + ("Tutit") + ("Kim") + ("Joey") + ; + + BOOST_TEST((test("Joel", sym))); + BOOST_TEST((test("Ruby", sym))); + BOOST_TEST((test("Tenji", sym))); + BOOST_TEST((test("Tutit", sym))); + BOOST_TEST((test("Kim", sym))); + BOOST_TEST((test("Joey", sym))); + BOOST_TEST((!test("XXX", sym))); + + sym.remove + ("Joel") + ("Ruby") + ; + + BOOST_TEST((!test("Joel", sym))); + BOOST_TEST((!test("Ruby", sym))); + } + + { // comma syntax + symbols sym; + sym += "Joel", "Ruby", "Tenji", "Tutit", "Kim", "Joey"; + + BOOST_TEST((test("Joel", sym))); + BOOST_TEST((test("Ruby", sym))); + BOOST_TEST((test("Tenji", sym))); + BOOST_TEST((test("Tutit", sym))); + BOOST_TEST((test("Kim", sym))); + BOOST_TEST((test("Joey", sym))); + BOOST_TEST((!test("XXX", sym))); + + sym -= "Joel", "Ruby"; + + BOOST_TEST((!test("Joel", sym))); + BOOST_TEST((!test("Ruby", sym))); + } + + { // no-case handling + using namespace boost::spirit::ascii; + + symbols sym; + // NOTE: make sure all entries are in lower-case!!! + sym = "joel", "ruby", "tenji", "tutit", "kim", "joey"; + + BOOST_TEST((test("joel", no_case[sym]))); + BOOST_TEST((test("ruby", no_case[sym]))); + BOOST_TEST((test("tenji", no_case[sym]))); + BOOST_TEST((test("tutit", no_case[sym]))); + BOOST_TEST((test("kim", no_case[sym]))); + BOOST_TEST((test("joey", no_case[sym]))); + + BOOST_TEST((test("JOEL", no_case[sym]))); + BOOST_TEST((test("RUBY", no_case[sym]))); + BOOST_TEST((test("TENJI", no_case[sym]))); + BOOST_TEST((test("TUTIT", no_case[sym]))); + BOOST_TEST((test("KIM", no_case[sym]))); + BOOST_TEST((test("JOEY", no_case[sym]))); + } + + { // attributes + symbols sym; + + sym.add + ("Joel", 1) + ("Ruby", 2) + ("Tenji", 3) + ("Tutit", 4) + ("Kim", 5) + ("Joey", 6) + ; + + int i; + BOOST_TEST((test_attr("Joel", sym, i))); + BOOST_TEST(i == 1); + BOOST_TEST((test_attr("Ruby", sym, i))); + BOOST_TEST(i == 2); + BOOST_TEST((test_attr("Tenji", sym, i))); + BOOST_TEST(i == 3); + BOOST_TEST((test_attr("Tutit", sym, i))); + BOOST_TEST(i == 4); + BOOST_TEST((test_attr("Kim", sym, i))); + BOOST_TEST(i == 5); + BOOST_TEST((test_attr("Joey", sym, i))); + BOOST_TEST(i == 6); + BOOST_TEST((!test_attr("XXX", sym, i))); + } + + { // actions + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + + symbols sym; + sym.add + ("Joel", 1) + ("Ruby", 2) + ("Tenji", 3) + ("Tutit", 4) + ("Kim", 5) + ("Joey", 6) + ; + + int i; + BOOST_TEST((test("Joel", sym[ref(i) = _1]))); + BOOST_TEST(i == 1); + BOOST_TEST((test("Ruby", sym[ref(i) = _1]))); + BOOST_TEST(i == 2); + BOOST_TEST((test("Tenji", sym[ref(i) = _1]))); + BOOST_TEST(i == 3); + BOOST_TEST((test("Tutit", sym[ref(i) = _1]))); + BOOST_TEST(i == 4); + BOOST_TEST((test("Kim", sym[ref(i) = _1]))); + BOOST_TEST(i == 5); + BOOST_TEST((test("Joey", sym[ref(i) = _1]))); + BOOST_TEST(i == 6); + BOOST_TEST((!test("XXX", sym[ref(i) = _1]))); + } + + { // construction from symbol array + char const* syms[] = {"Joel","Ruby","Tenji","Tutit","Kim","Joey"}; + symbols sym(syms); + + BOOST_TEST((test("Joel", sym))); + BOOST_TEST((test("Ruby", sym))); + BOOST_TEST((test("Tenji", sym))); + BOOST_TEST((test("Tutit", sym))); + BOOST_TEST((test("Kim", sym))); + BOOST_TEST((test("Joey", sym))); + BOOST_TEST((!test("XXX", sym))); + } + + { // construction from 2 arrays + + char const* syms[] = {"Joel","Ruby","Tenji","Tutit","Kim","Joey"}; + int data[] = {1,2,3,4,5,6}; + symbols sym(syms, data); + + int i; + BOOST_TEST((test_attr("Joel", sym, i))); + BOOST_TEST(i == 1); + BOOST_TEST((test_attr("Ruby", sym, i))); + BOOST_TEST(i == 2); + BOOST_TEST((test_attr("Tenji", sym, i))); + BOOST_TEST(i == 3); + BOOST_TEST((test_attr("Tutit", sym, i))); + BOOST_TEST(i == 4); + BOOST_TEST((test_attr("Kim", sym, i))); + BOOST_TEST(i == 5); + BOOST_TEST((test_attr("Joey", sym, i))); + BOOST_TEST(i == 6); + BOOST_TEST((!test_attr("XXX", sym, i))); + } + + return boost::report_errors(); +} diff --git a/test/qi/test.hpp b/test/qi/test.hpp new file mode 100644 index 000000000..4f78eaaef --- /dev/null +++ b/test/qi/test.hpp @@ -0,0 +1,75 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#if !defined(BOOST_SPIRIT_TEST_FEB_01_2007_0605PM) +#define BOOST_SPIRIT_TEST_FEB_01_2007_0605PM + +#include +#include + +namespace spirit_test +{ + template + bool test(Char const* in, Parser const& p, bool full_match = true) + { + // we don't care about the result of the "what" function. + // we only care that all parsers have it: + boost::spirit::qi::what(p); + + Char const* last = in; + while (*last) + last++; + return boost::spirit::qi::parse(in, last, p) + && (!full_match || (in == last)); + } + + template + bool test(Char const* in, Parser const& p + , Skipper const& s, bool full_match = true) + { + // we don't care about the result of the "what" function. + // we only care that all parsers have it: + boost::spirit::qi::what(p); + + Char const* last = in; + while (*last) + last++; + return boost::spirit::qi::phrase_parse(in, last, p, s) + && (!full_match || (in == last)); + } + + template + bool test_attr(Char const* in, Parser const& p + , Attr& attr, bool full_match = true) + { + // we don't care about the result of the "what" function. + // we only care that all parsers have it: + boost::spirit::qi::what(p); + + Char const* last = in; + while (*last) + last++; + return boost::spirit::qi::parse(in, last, p, attr) + && (!full_match || (in == last)); + } + + template + bool test_attr(Char const* in, Parser const& p + , Attr& attr, Skipper const& s, bool full_match = true) + { + // we don't care about the result of the "what" function. + // we only care that all parsers have it: + boost::spirit::qi::what(p); + + Char const* last = in; + while (*last) + last++; + return boost::spirit::qi::phrase_parse(in, last, p, attr, s) + && (!full_match || (in == last)); + } +} + +#endif diff --git a/test/qi/tst.cpp b/test/qi/tst.cpp new file mode 100644 index 000000000..0387de263 --- /dev/null +++ b/test/qi/tst.cpp @@ -0,0 +1,354 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include + +#include +#include +#include + +namespace +{ + template + void add(TST& tst, Char const* s, int data) + { + Char const* last = s; + while (*last) + last++; + tst.add(s, last, data); + } + + template + void remove(TST& tst, Char const* s) + { + Char const* last = s; + while (*last) + last++; + tst.remove(s, last); + } + + template + void check(TST const& tst, Char const* s, bool expected, int N = 0, int val = -1) + { + Char const* first = s; + Char const* last = s; + while (*last) + last++; + int* r = tst.find(s, last); + BOOST_TEST((r != 0) == expected); + if (r != 0) + BOOST_TEST((s-first) == N); + if (r) + BOOST_TEST(*r == val); + } + + struct printer + { + template + void operator()(String const& s, Data const& data) + { + std::cout << " " << s << ": " << data << std::endl; + } + }; + + template + void print(TST const& tst) + { + std::cout << '[' << std::endl; + tst.for_each(printer()); + std::cout << ']' << std::endl; + } + + struct no_case_filter + { + template + Char operator()(Char ch) const + { + return std::tolower(ch); + } + }; + + template + void nc_check(TST const& tst, Char const* s, bool expected, int N = 0, int val = -1) + { + Char const* first = s; + Char const* last = s; + while (*last) + last++; + int* r = tst.find(s, last, no_case_filter()); + BOOST_TEST((r != 0) == expected); + if (r != 0) + BOOST_TEST((s-first) == N); + if (r) + BOOST_TEST(*r == val); + } +} + +template +void tests() +{ + { // basic tests + Lookup lookup; + + check(lookup, "not-yet-there", false); + check(lookup, "", false); + + add(lookup, "apple", 123); + check(lookup, "apple", true, 5, 123); // full match + check(lookup, "banana", false); // no-match + check(lookup, "applexxx", true, 5, 123); // partial match + + add(lookup, "applepie", 456); + check(lookup, "applepie", true, 8, 456); // full match + check(lookup, "banana", false); // no-match + check(lookup, "applepiexxx", true, 8, 456); // partial match + check(lookup, "apple", true, 5, 123); // full match + check(lookup, "applexxx", true, 5, 123); // partial match + } + + { // variation of above + Lookup lookup; + + add(lookup, "applepie", 456); + add(lookup, "apple", 123); + + check(lookup, "applepie", true, 8, 456); // full match + check(lookup, "banana", false); // no-match + check(lookup, "applepiexxx", true, 8, 456); // partial match + check(lookup, "apple", true, 5, 123); // full match + check(lookup, "applexxx", true, 5, 123); // partial match + } + { // variation of above + Lookup lookup; + + add(lookup, "applepie", 456); + add(lookup, "apple", 123); + + check(lookup, "applepie", true, 8, 456); // full match + check(lookup, "banana", false); // no-match + check(lookup, "applepiexxx", true, 8, 456); // partial match + check(lookup, "apple", true, 5, 123); // full match + check(lookup, "applexxx", true, 5, 123); // partial match + } + + { // narrow char tests + Lookup lookup; + add(lookup, "pineapple", 1); + add(lookup, "orange", 2); + add(lookup, "banana", 3); + add(lookup, "applepie", 4); + add(lookup, "apple", 5); + + check(lookup, "pineapple", true, 9, 1); + check(lookup, "orange", true, 6, 2); + check(lookup, "banana", true, 6, 3); + check(lookup, "apple", true, 5, 5); + check(lookup, "pizza", false); + check(lookup, "steak", false); + check(lookup, "applepie", true, 8, 4); + check(lookup, "bananarama", true, 6, 3); + check(lookup, "applet", true, 5, 5); + check(lookup, "applepi", true, 5, 5); + check(lookup, "appl", false); + + check(lookup, "pineapplez", true, 9, 1); + check(lookup, "orangez", true, 6, 2); + check(lookup, "bananaz", true, 6, 3); + check(lookup, "applez", true, 5, 5); + check(lookup, "pizzaz", false); + check(lookup, "steakz", false); + check(lookup, "applepiez", true, 8, 4); + check(lookup, "bananaramaz", true, 6, 3); + check(lookup, "appletz", true, 5, 5); + check(lookup, "applepix", true, 5, 5); + } + + { // wide char tests + WideLookup lookup; + add(lookup, L"pineapple", 1); + add(lookup, L"orange", 2); + add(lookup, L"banana", 3); + add(lookup, L"applepie", 4); + add(lookup, L"apple", 5); + + check(lookup, L"pineapple", true, 9, 1); + check(lookup, L"orange", true, 6, 2); + check(lookup, L"banana", true, 6, 3); + check(lookup, L"apple", true, 5, 5); + check(lookup, L"pizza", false); + check(lookup, L"steak", false); + check(lookup, L"applepie", true, 8, 4); + check(lookup, L"bananarama", true, 6, 3); + check(lookup, L"applet", true, 5, 5); + check(lookup, L"applepi", true, 5, 5); + check(lookup, L"appl", false); + + check(lookup, L"pineapplez", true, 9, 1); + check(lookup, L"orangez", true, 6, 2); + check(lookup, L"bananaz", true, 6, 3); + check(lookup, L"applez", true, 5, 5); + check(lookup, L"pizzaz", false); + check(lookup, L"steakz", false); + check(lookup, L"applepiez", true, 8, 4); + check(lookup, L"bananaramaz", true, 6, 3); + check(lookup, L"appletz", true, 5, 5); + check(lookup, L"applepix", true, 5, 5); + } + + { // test remove + Lookup lookup; + add(lookup, "pineapple", 1); + add(lookup, "orange", 2); + add(lookup, "banana", 3); + add(lookup, "applepie", 4); + add(lookup, "apple", 5); + + check(lookup, "pineapple", true, 9, 1); + check(lookup, "orange", true, 6, 2); + check(lookup, "banana", true, 6, 3); + check(lookup, "apple", true, 5, 5); + check(lookup, "applepie", true, 8, 4); + check(lookup, "bananarama", true, 6, 3); + check(lookup, "applet", true, 5, 5); + check(lookup, "applepi", true, 5, 5); + check(lookup, "appl", false); + + remove(lookup, "banana"); + check(lookup, "pineapple", true, 9, 1); + check(lookup, "orange", true, 6, 2); + check(lookup, "banana", false); + check(lookup, "apple", true, 5, 5); + check(lookup, "applepie", true, 8, 4); + check(lookup, "bananarama", false); + check(lookup, "applet", true, 5, 5); + check(lookup, "applepi", true, 5, 5); + check(lookup, "appl", false); + + remove(lookup, "apple"); + check(lookup, "pineapple", true, 9, 1); + check(lookup, "orange", true, 6, 2); + check(lookup, "apple", false); + check(lookup, "applepie", true, 8, 4); + check(lookup, "applet", false); + check(lookup, "applepi", false); + check(lookup, "appl", false); + + remove(lookup, "orange"); + check(lookup, "pineapple", true, 9, 1); + check(lookup, "orange", false); + check(lookup, "applepie", true, 8, 4); + + remove(lookup, "pineapple"); + check(lookup, "pineapple", false); + check(lookup, "orange", false); + check(lookup, "applepie", true, 8, 4); + + remove(lookup, "applepie"); + check(lookup, "applepie", false); + } + + { // copy/assign/clear test + Lookup lookupa; + add(lookupa, "pineapple", 1); + add(lookupa, "orange", 2); + add(lookupa, "banana", 3); + add(lookupa, "applepie", 4); + add(lookupa, "apple", 5); + + Lookup lookupb(lookupa); // copy ctor + check(lookupb, "pineapple", true, 9, 1); + check(lookupb, "orange", true, 6, 2); + check(lookupb, "banana", true, 6, 3); + check(lookupb, "apple", true, 5, 5); + check(lookupb, "pizza", false); + check(lookupb, "steak", false); + check(lookupb, "applepie", true, 8, 4); + check(lookupb, "bananarama", true, 6, 3); + check(lookupb, "applet", true, 5, 5); + check(lookupb, "applepi", true, 5, 5); + check(lookupb, "appl", false); + + lookupb.clear(); // clear + check(lookupb, "pineapple", false); + check(lookupb, "orange", false); + check(lookupb, "banana", false); + check(lookupb, "apple", false); + check(lookupb, "applepie", false); + check(lookupb, "bananarama", false); + check(lookupb, "applet", false); + check(lookupb, "applepi", false); + check(lookupb, "appl", false); + + lookupb = lookupa; // assign + check(lookupb, "pineapple", true, 9, 1); + check(lookupb, "orange", true, 6, 2); + check(lookupb, "banana", true, 6, 3); + check(lookupb, "apple", true, 5, 5); + check(lookupb, "pizza", false); + check(lookupb, "steak", false); + check(lookupb, "applepie", true, 8, 4); + check(lookupb, "bananarama", true, 6, 3); + check(lookupb, "applet", true, 5, 5); + check(lookupb, "applepi", true, 5, 5); + check(lookupb, "appl", false); + } + + { // test for_each + Lookup lookup; + add(lookup, "pineapple", 1); + add(lookup, "orange", 2); + add(lookup, "banana", 3); + add(lookup, "applepie", 4); + add(lookup, "apple", 5); + + print(lookup); + } + + { // case insensitive tests + Lookup lookup; + + // NOTE: make sure all entries are in lower-case!!! + add(lookup, "pineapple", 1); + add(lookup, "orange", 2); + add(lookup, "banana", 3); + add(lookup, "applepie", 4); + add(lookup, "apple", 5); + + nc_check(lookup, "pineapple", true, 9, 1); + nc_check(lookup, "orange", true, 6, 2); + nc_check(lookup, "banana", true, 6, 3); + nc_check(lookup, "apple", true, 5, 5); + nc_check(lookup, "applepie", true, 8, 4); + + nc_check(lookup, "PINEAPPLE", true, 9, 1); + nc_check(lookup, "ORANGE", true, 6, 2); + nc_check(lookup, "BANANA", true, 6, 3); + nc_check(lookup, "APPLE", true, 5, 5); + nc_check(lookup, "APPLEPIE", true, 8, 4); + + nc_check(lookup, "pineApple", true, 9, 1); + nc_check(lookup, "orangE", true, 6, 2); + nc_check(lookup, "Banana", true, 6, 3); + nc_check(lookup, "aPPLe", true, 5, 5); + nc_check(lookup, "ApplePie", true, 8, 4); + + print(lookup); + } +} + +int +main() +{ + using namespace boost::spirit::qi; + + tests, tst >(); + tests, tst_map >(); + + return boost::report_errors(); +} + diff --git a/test/qi/uint.cpp b/test/qi/uint.cpp new file mode 100644 index 000000000..7645e66ac --- /dev/null +++ b/test/qi/uint.cpp @@ -0,0 +1,175 @@ +/*============================================================================= + Copyright (c) 2001-2007 Joel de Guzman + Copyright (c) 2001-2007 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test.hpp" + +/////////////////////////////////////////////////////////////////////////////// +// +// *** BEWARE PLATFORM DEPENDENT!!! *** +// *** The following assumes 32 bit integers and 64 bit long longs. +// *** Modify these constant strings when appropriate. +// +/////////////////////////////////////////////////////////////////////////////// + + char const* max_unsigned = "4294967295"; + char const* unsigned_overflow = "4294967296"; + char const* max_int = "2147483647"; + char const* int_overflow = "2147483648"; + char const* min_int = "-2147483648"; + char const* int_underflow = "-2147483649"; + char const* max_binary = "11111111111111111111111111111111"; + char const* binary_overflow = "100000000000000000000000000000000"; + char const* max_octal = "37777777777"; + char const* octal_overflow = "100000000000"; + char const* max_hex = "FFFFFFFF"; + char const* hex_overflow = "100000000"; + +int +main() +{ + using namespace spirit_test; + + /////////////////////////////////////////////////////////////////////////// + // unsigned tests + /////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::uint; + unsigned u; + + BOOST_TEST(test("123456", uint)); + BOOST_TEST(test_attr("123456", uint, u)); + BOOST_TEST(u == 123456); + + BOOST_TEST(test(max_unsigned, uint)); + BOOST_TEST(test_attr(max_unsigned, uint, u)); + BOOST_TEST(u == UINT_MAX); + + BOOST_TEST(!test(unsigned_overflow, uint)); + BOOST_TEST(!test_attr(unsigned_overflow, uint, u)); + } + + /////////////////////////////////////////////////////////////////////////// + // binary tests + /////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::bin; + unsigned u; + + BOOST_TEST(test("11111110", bin)); + BOOST_TEST(test_attr("11111110", bin, u)); + BOOST_TEST(u == 0xFE); + + BOOST_TEST(test(max_binary, bin)); + BOOST_TEST(test_attr(max_binary, bin, u)); + BOOST_TEST(u == UINT_MAX); + + BOOST_TEST(!test(binary_overflow, bin)); + BOOST_TEST(!test_attr(binary_overflow, bin, u)); + } + + /////////////////////////////////////////////////////////////////////////// + // octal tests + /////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::oct; + unsigned u; + + BOOST_TEST(test("12545674515", oct)); + BOOST_TEST(test_attr("12545674515", oct, u)); + BOOST_TEST(u == 012545674515); + + BOOST_TEST(test(max_octal, oct)); + BOOST_TEST(test_attr(max_octal, oct, u)); + BOOST_TEST(u == UINT_MAX); + + BOOST_TEST(!test(octal_overflow, oct)); + BOOST_TEST(!test_attr(octal_overflow, oct, u)); + } + + /////////////////////////////////////////////////////////////////////////// + // hex tests + /////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::hex; + unsigned u; + + BOOST_TEST(test("95BC8DF", hex)); + BOOST_TEST(test_attr("95BC8DF", hex, u)); + BOOST_TEST(u == 0x95BC8DF); + + BOOST_TEST(test("abcdef12", hex)); + BOOST_TEST(test_attr("abcdef12", hex, u)); + BOOST_TEST(u == 0xabcdef12); + + BOOST_TEST(test(max_hex, hex)); + BOOST_TEST(test_attr(max_hex, hex, u)); + BOOST_TEST(u == UINT_MAX); + + BOOST_TEST(!test(hex_overflow, hex)); + BOOST_TEST(!test_attr(hex_overflow, hex, u)); + } + + /////////////////////////////////////////////////////////////////////////// + // limited fieldwidth + /////////////////////////////////////////////////////////////////////////// + { + unsigned u; + using boost::spirit::qi::uint_spec; + + uint_spec uint3; + BOOST_TEST(test("123456", uint3, false)); + BOOST_TEST(test_attr("123456", uint3, u, false)); + BOOST_TEST(u == 123); + + uint_spec uint4; + BOOST_TEST(test("123456", uint4, false)); + BOOST_TEST(test_attr("123456", uint4, u, false)); + BOOST_TEST(u == 1234); + + BOOST_TEST(!test("1", uint4)); + BOOST_TEST(!test_attr("1", uint4, u)); + } + + /////////////////////////////////////////////////////////////////////////// + // uint_spec tests + /////////////////////////////////////////////////////////////////////////// + { + using boost::spirit::qi::uint_spec; + using boost::spirit::unused_type; + uint_spec any_int; + + BOOST_TEST(test("123456", any_int)); + BOOST_TEST(test("1234567890123456789", any_int)); + } + + /////////////////////////////////////////////////////////////////////////// + // action tests + /////////////////////////////////////////////////////////////////////////// + { + using namespace boost::phoenix; + using boost::spirit::arg_names::_1; + using boost::spirit::ascii::space; + using boost::spirit::uint; + int n; + + BOOST_TEST(test("123", uint[ref(n) = _1])); + BOOST_TEST(n == 123); + BOOST_TEST(test(" 456", uint[ref(n) = _1], space)); + BOOST_TEST(n == 456); + } + + return boost::report_errors(); +} diff --git a/test/support/detail/sstream.hpp b/test/support/detail/sstream.hpp new file mode 100644 index 000000000..bb94cb9c9 --- /dev/null +++ b/test/support/detail/sstream.hpp @@ -0,0 +1,46 @@ +/*============================================================================= + Copyright (c) 2003 Martin Wille + http://spirit.sourceforge.net/ + + Use, modification and distribution is subject to the Boost Software + License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at + http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ +#include + +/////////////////////////////////////////////////////////////////////////// +// workaround for prestandard support of stringstreams +// +// * defines sstream_t for the string stream type +// * defines std::string getstring(sstream_t &); +// + +#ifdef BOOST_NO_STRINGSTREAM +# include + typedef strstream sstream_t; + std::string + getstring(std::strstream& ss) + { + ss << ends; + std::string rval = ss.str(); + ss.freeze(false); + return rval; + } +#else +# include + typedef std::stringstream sstream_t; + std::string + getstring(std::stringstream &ss) + { + return ss.str(); + } +#endif + +void use_getstring_to_avoid_compiler_warnings_about_unused_functions() +{ + sstream_t ss; + getstring(ss); + if(!ss) { // to be not recursive on all control paths + use_getstring_to_avoid_compiler_warnings_about_unused_functions(); + } +} diff --git a/test/support/hold_any.cpp b/test/support/hold_any.cpp new file mode 100644 index 000000000..988ef321c --- /dev/null +++ b/test/support/hold_any.cpp @@ -0,0 +1,201 @@ +// Copyright (c) 2007-2008 Hartmut Kaiser +// Copyright (c) Christopher Diggins 2005 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// #define BOOST_SPIRIT_ANY_IMPLICIT_CASTING + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace boost::spirit; + +/////////////////////////////////////////////////////////////////////////////// +bool output_any (hold_any const& a, std::string expected) +{ + std::ostringstream o; + if (a.type() == typeid(int)) + { + o << any_cast(a); + } + else if (a.type() == typeid(char)) + { + o << any_cast(a); + } + else if (a.type() == typeid(double)) + { + o << any_cast(a); + } + else if (a.type() == typeid(complex)) + { + o << any_cast >(a); + } + else + { + o << "unexpected type: " << a.type().name(); + return false; + } + return o.str() == expected; +} + +template +bool output_any_direct (T const& v, std::string expected) +{ + std::ostringstream o; + o << v; + return o.str() == expected; +} + +void simple_any_test() +{ + BOOST_TEST(output_any(42, "42")); + BOOST_TEST(output_any('q', "q")); + BOOST_TEST(output_any(3.14, "3.14")); + BOOST_TEST(output_any(complex(1, 2), "(1,2)")); + + int n = 42; BOOST_TEST(output_any(n, "42")); + char c = 'q'; BOOST_TEST(output_any(c, "q")); + double d = 3.14; BOOST_TEST(output_any(d, "3.14")); + complex x(1, 2); BOOST_TEST(output_any(x, "(1,2)")); + + hold_any a; + BOOST_TEST(output_any(a = n, "42")); + BOOST_TEST(output_any(a = c, "q")); + BOOST_TEST(output_any(a = d, "3.14")); + BOOST_TEST(output_any(a = x, "(1,2)")); + BOOST_TEST(output_any(a = 13, "13")); + +#ifdef BOOST_SPIRIT_ANY_IMPLICIT_CASTING + BOOST_TEST(output_any_direct(n = hold_any(42), "42")); + BOOST_TEST(output_any_direct(c = hold_any('q'), "q")); + BOOST_TEST(output_any_direct(d = hold_any(3.14), "3.14")); + BOOST_TEST(output_any_direct(x = complex(hold_any(complex(1, 2))), "(1,2)")); +#endif + + BOOST_TEST(output_any_direct(hold_any(42), "42")); + BOOST_TEST(output_any_direct(hold_any('q'), "q")); + BOOST_TEST(output_any_direct(hold_any(3.14), "3.14")); + BOOST_TEST(output_any_direct(hold_any(complex(1, 2)), "(1,2)")); + + BOOST_TEST(!a.empty()); + a = 0; + BOOST_TEST(!a.empty()); + a.reset(); + BOOST_TEST(a.empty()); + + try { + any_cast(a); + BOOST_TEST(false); + } + catch (boost::spirit::bad_any_cast const&) { + BOOST_TEST(true); + } + catch (...) { + BOOST_TEST(false); + } +} + +void test2(hold_any const& x, hold_any const& y) +{ + BOOST_TEST(x.type() != y.type()); + BOOST_TEST(x.type().name() != y.type().name()); +} + +/////////////////////////////////////////////////////////////////////////////// +int state; + +/////////////////////////////////////////////////////////////////////////////// +struct small_object +{ + small_object() {} + small_object(small_object const& fb) { state = 1; } + ~small_object() { state = 2; } +}; + +inline std::istream& +operator>> (std::istream& i, small_object&) +{ + return i; +} + +inline std::ostream& +operator<< (std::ostream& o, small_object const&) +{ + return o; +} + +/////////////////////////////////////////////////////////////////////////////// +struct large_object +{ + large_object() {} + large_object(large_object const& fb) { state = 3; } + ~large_object() { state = 4; } + + int data0; + int data1; + int data2; + int data3; +}; + +inline std::istream& +operator>> (std::istream& i, large_object&) +{ + return i; +} + +inline std::ostream& +operator<< (std::ostream& o, large_object const&) +{ + return o; +} + +void constructor_test() +{ + small_object lfb; + large_object bfb; + hold_any a; + state = 0; + + a = lfb; BOOST_TEST(1 == state); state = 0; + a = 42; BOOST_TEST(2 == state); state = 0; + a = bfb; BOOST_TEST(3 == state); state = 0; + a = 42; BOOST_TEST(4 == state); state = 0; + + // test assignment of large objects + a = bfb; + a = bfb; +} + +int main() +{ + test2(std::string("10"), std::complex(20)); + + constructor_test(); + simple_any_test(); + + hold_any non_const(10); + BOOST_TEST(any_cast(non_const) == 10); + *any_cast(&non_const) = 15; + BOOST_TEST(any_cast(non_const) == 15); + + hold_any const const_(10); + BOOST_TEST(any_cast(const_) == *any_cast(&const_)); + + hold_any a = 42, b = 'q'; + swap(a, b); + BOOST_TEST(any_cast(b) == 42); + BOOST_TEST(any_cast(a) == 'q'); + + return boost::report_errors(); +} + diff --git a/test/support/multi_pass.cpp b/test/support/multi_pass.cpp new file mode 100644 index 000000000..e3df135c0 --- /dev/null +++ b/test/support/multi_pass.cpp @@ -0,0 +1,770 @@ +/*============================================================================= + Copyright (c) 2001-2003 Daniel Nuffer + Copyright (c) 2001-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "detail/sstream.hpp" + +using namespace std; +using namespace boost::spirit; + +sstream_t res; + +typedef boost::spirit::multi_pass > default_multi_pass_t; + +typedef look_ahead, 6> fixed_multi_pass_t; + +typedef multi_pass< + istream_iterator, + multi_pass_policies::input_iterator, + multi_pass_policies::first_owner, + multi_pass_policies::buf_id_check, + multi_pass_policies::std_deque +> first_owner_multi_pass_t; + + +// a functor to test out the functor_multi_pass +class my_functor +{ + public: + typedef char result_type; + my_functor() + : c('A') + {} + + char operator()() + { + if (c == 'M') + return eof; + else + return c++; + } + + static result_type eof; + private: + char c; +}; + +my_functor::result_type my_functor::eof = '\0'; + +typedef multi_pass< + my_functor, + multi_pass_policies::functor_input, + multi_pass_policies::first_owner, + multi_pass_policies::no_check, + multi_pass_policies::std_deque +> functor_multi_pass_t; + +void test_default_multi_pass() +{ + res << "-*= test_default_multi_pass =*-\n"; + istream_iterator end; + boost::scoped_ptr mpend(new default_multi_pass_t(end)); + + { + sstream_t ss; + ss << "test string"; + + istream_iterator a(ss); + boost::scoped_ptr mp1(new default_multi_pass_t(a)); + + while (*mp1 != *mpend) + { + res << *((*mp1)++); + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator b(ss); + boost::scoped_ptr mp2(new default_multi_pass_t(b)); + boost::scoped_ptr mp3(new default_multi_pass_t(b)); + *mp3 = *mp2; + + for (int i = 0; i < 4; ++i) + { + res << **mp2; + ++*mp2; + } + + mp3.reset(); + + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator a(ss); + boost::scoped_ptr mp1(new default_multi_pass_t(a)); + boost::scoped_ptr mp2(new default_multi_pass_t(*mp1)); + + for (int i = 0; i < 4; ++i) + { + res << **mp1; + ++*mp1; + } + + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + while (*mp1 != *mpend) + { + res << **mp1; + ++*mp1; + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator b(ss); + boost::scoped_ptr mp2(new default_multi_pass_t(b)); + boost::scoped_ptr mp3(new default_multi_pass_t(b)); + *mp3 = *mp2; + + for (int i = 0; i < 4; ++i) + { + res << **mp2; + ++*mp2; + } + + mp3.reset(); + ++*mp2; + + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator a(ss); + boost::scoped_ptr mp1(new default_multi_pass_t(a)); + boost::scoped_ptr mp2(new default_multi_pass_t(*mp1)); + + BOOST_TEST(*mp1 == *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp1 <= *mp2); + for (int i = 0; i < 4; ++i) + { + res << **mp1; + ++*mp1; + } + + BOOST_TEST(*mp1 != *mp2); + BOOST_TEST(*mp1 > *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp2 < *mp1); + BOOST_TEST(*mp2 <= *mp1); + while (*mp2 != *mp1) + { + res << **mp2; + ++*mp2; + } + + BOOST_TEST(*mp1 == *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp1 <= *mp2); + while (*mp1 != *mpend) + { + res << **mp1; + ++*mp1; + } + + BOOST_TEST(*mp1 != *mp2); + BOOST_TEST(*mp1 > *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp2 < *mp1); + BOOST_TEST(*mp2 <= *mp1); + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + BOOST_TEST(*mp1 == *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp1 <= *mp2); + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator a(ss); + boost::scoped_ptr mp1(new default_multi_pass_t(a)); + boost::scoped_ptr mp2(new default_multi_pass_t(a)); + BOOST_TEST(*mp1 != *mp2); + ++*mp1; + BOOST_TEST(*mp1 != *mp2); + + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator b(ss); + boost::scoped_ptr mp2(new default_multi_pass_t(b)); + boost::scoped_ptr mp3(new default_multi_pass_t(b)); + *mp3 = *mp2; + + for (int i = 0; i < 4; ++i) + { + res << **mp2; + ++*mp2; + } + + mp2->clear_queue(); + + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + try + { + res << **mp3; // this should throw illegal_backtracking + BOOST_TEST(0); + } + catch (const boost::spirit::multi_pass_policies::illegal_backtracking& /*e*/) + { + } + res << endl; + } + + +} + +void test_fixed_multi_pass() +{ + res << "-*= test_fixed_multi_pass =*-\n"; + istream_iterator end; + boost::scoped_ptr mpend(new fixed_multi_pass_t(end)); + + { + sstream_t ss; + ss << "test string"; + + istream_iterator a(ss); + boost::scoped_ptr mp1(new fixed_multi_pass_t(a)); + + while (*mp1 != *mpend) + { + res << *((*mp1)++); + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator b(ss); + boost::scoped_ptr mp2(new fixed_multi_pass_t(b)); + boost::scoped_ptr mp3(new fixed_multi_pass_t(*mp2)); + + for (int i = 0; i < 4; ++i) + { + res << **mp2; + ++*mp2; + } + + mp3.reset(); + + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator a(ss); + boost::scoped_ptr mp1(new fixed_multi_pass_t(a)); + boost::scoped_ptr mp2(new fixed_multi_pass_t(*mp1)); + + for (int i = 0; i < 4; ++i) + { + res << **mp1; + ++*mp1; + } + + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + while (*mp1 != *mpend) + { + res << **mp1; + ++*mp1; + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator b(ss); + boost::scoped_ptr mp2(new fixed_multi_pass_t(b)); + boost::scoped_ptr mp3(new fixed_multi_pass_t(*mp2)); + + for (int i = 0; i < 4; ++i) + { + res << **mp2; + ++*mp2; + } + + mp3.reset(); + ++*mp2; + + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator a(ss); + boost::scoped_ptr mp1(new fixed_multi_pass_t(a)); + boost::scoped_ptr mp2(new fixed_multi_pass_t(*mp1)); + + BOOST_TEST(*mp1 == *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp1 <= *mp2); + for (int i = 0; i < 4; ++i) + { + res << **mp1; + ++*mp1; + } + + BOOST_TEST(*mp1 != *mp2); + BOOST_TEST(*mp1 > *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp2 < *mp1); + BOOST_TEST(*mp2 <= *mp1); + while (*mp2 != *mp1) + { + res << **mp2; + ++*mp2; + } + + BOOST_TEST(*mp1 == *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp1 <= *mp2); + while (*mp1 != *mpend) + { + res << **mp1; + ++*mp1; + } + + BOOST_TEST(*mp1 != *mp2); + BOOST_TEST(*mp1 > *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp2 < *mp1); + BOOST_TEST(*mp2 <= *mp1); + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + BOOST_TEST(*mp1 == *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp1 <= *mp2); + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator a(ss); + boost::scoped_ptr mp1(new fixed_multi_pass_t(a)); + boost::scoped_ptr mp2(new fixed_multi_pass_t(a)); + BOOST_TEST(*mp1 != *mp2); + ++*mp1; + BOOST_TEST(*mp1 != *mp2); + + } + +} + +void test_first_owner_multi_pass() +{ + res << "-*= test_first_owner_multi_pass =*-\n"; + istream_iterator end; + boost::scoped_ptr mpend(new first_owner_multi_pass_t(end)); + + { + sstream_t ss; + ss << "test string"; + + istream_iterator a(ss); + boost::scoped_ptr mp1(new first_owner_multi_pass_t(a)); + + while (*mp1 != *mpend) + { + res << *((*mp1)++); + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator b(ss); + boost::scoped_ptr mp2(new first_owner_multi_pass_t(b)); + boost::scoped_ptr mp3(new first_owner_multi_pass_t(*mp2)); + + for (int i = 0; i < 4; ++i) + { + res << **mp2; + ++*mp2; + } + + mp3.reset(); + + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator a(ss); + boost::scoped_ptr mp1(new first_owner_multi_pass_t(a)); + boost::scoped_ptr mp2(new first_owner_multi_pass_t(*mp1)); + + for (int i = 0; i < 4; ++i) + { + res << **mp1; + ++*mp1; + } + + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + while (*mp1 != *mpend) + { + res << **mp1; + ++*mp1; + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator b(ss); + boost::scoped_ptr mp2(new first_owner_multi_pass_t(b)); + boost::scoped_ptr mp3(new first_owner_multi_pass_t(*mp2)); + + for (int i = 0; i < 4; ++i) + { + res << **mp2; + ++*mp2; + } + + mp3.reset(); + ++*mp2; + + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator a(ss); + boost::scoped_ptr mp1(new first_owner_multi_pass_t(a)); + boost::scoped_ptr mp2(new first_owner_multi_pass_t(*mp1)); + + BOOST_TEST(*mp1 == *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp1 <= *mp2); + for (int i = 0; i < 4; ++i) + { + res << **mp1; + ++*mp1; + } + + BOOST_TEST(*mp1 != *mp2); + BOOST_TEST(*mp1 > *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp2 < *mp1); + BOOST_TEST(*mp2 <= *mp1); + while (*mp2 != *mp1) + { + res << **mp2; + ++*mp2; + } + + BOOST_TEST(*mp1 == *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp1 <= *mp2); + while (*mp1 != *mpend) + { + res << **mp1; + ++*mp1; + } + + BOOST_TEST(*mp1 != *mp2); + BOOST_TEST(*mp1 > *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp2 < *mp1); + BOOST_TEST(*mp2 <= *mp1); + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + BOOST_TEST(*mp1 == *mp2); + BOOST_TEST(*mp1 >= *mp2); + BOOST_TEST(*mp1 <= *mp2); + res << endl; + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator a(ss); + boost::scoped_ptr mp1(new first_owner_multi_pass_t(a)); + boost::scoped_ptr mp2(new first_owner_multi_pass_t(a)); + BOOST_TEST(*mp1 != *mp2); + ++*mp1; + BOOST_TEST(*mp1 != *mp2); + + } + + { + sstream_t ss; + ss << "test string"; + istream_iterator b(ss); + boost::scoped_ptr mp2(new first_owner_multi_pass_t(b)); + boost::scoped_ptr mp3(new first_owner_multi_pass_t(*mp2)); + + for (int i = 0; i < 4; ++i) + { + res << **mp2; + ++*mp2; + } + + mp2->clear_queue(); + + while (*mp2 != *mpend) + { + res << **mp2; + ++*mp2; + } + + try + { + res << **mp3; // this should throw illegal_backtracking + BOOST_TEST(0); + } + catch (const boost::spirit::multi_pass_policies::illegal_backtracking& /*e*/) + { + } + res << endl; + } + +} + + +void test_functor_multi_pass() +{ + res << "-*= test_functor_multi_pass =*-\n"; + functor_multi_pass_t mpend; + + { + functor_multi_pass_t mp1 = functor_multi_pass_t(my_functor()); + + while (mp1 != mpend) + { + res << *(mp1++); + } + + res << endl; + } + + { + functor_multi_pass_t mp1 = functor_multi_pass_t(my_functor()); + functor_multi_pass_t mp2 = functor_multi_pass_t(mp1); + + for (int i = 0; i < 4; ++i) + { + res << *mp1; + ++mp1; + } + + while (mp2 != mpend) + { + res << *mp2; + ++mp2; + } + + while (mp1 != mpend) + { + res << *mp1; + ++mp1; + } + + res << endl; + } + + { + functor_multi_pass_t mp1 = functor_multi_pass_t(my_functor()); + functor_multi_pass_t mp2 = functor_multi_pass_t(mp1); + + BOOST_TEST(mp1 == mp2); + BOOST_TEST(mp1 >= mp2); + BOOST_TEST(mp1 <= mp2); + for (int i = 0; i < 4; ++i) + { + res << *mp1; + ++mp1; + } + + BOOST_TEST(mp1 != mp2); + BOOST_TEST(mp1 > mp2); + BOOST_TEST(mp1 >= mp2); + BOOST_TEST(mp2 < mp1); + BOOST_TEST(mp2 <= mp1); + while (mp2 != mp1) + { + res << *mp2; + ++mp2; + } + + BOOST_TEST(mp1 == mp2); + BOOST_TEST(mp1 >= mp2); + BOOST_TEST(mp1 <= mp2); + while (mp1 != mpend) + { + res << *mp1; + ++mp1; + } + + BOOST_TEST(mp1 != mp2); + BOOST_TEST(mp1 > mp2); + BOOST_TEST(mp1 >= mp2); + BOOST_TEST(mp2 < mp1); + BOOST_TEST(mp2 <= mp1); + while (mp2 != mpend) + { + res << *mp2; + ++mp2; + } + + BOOST_TEST(mp1 == mp2); + BOOST_TEST(mp1 >= mp2); + BOOST_TEST(mp1 <= mp2); + res << endl; + } + + { + functor_multi_pass_t mp1 = functor_multi_pass_t(my_functor()); + functor_multi_pass_t mp2 = functor_multi_pass_t(my_functor()); + BOOST_TEST(mp1 != mp2); + ++mp1; + BOOST_TEST(mp1 != mp2); + + } +} + +int main(int, char**) +{ + + test_default_multi_pass(); + test_fixed_multi_pass(); + test_first_owner_multi_pass(); + test_functor_multi_pass(); + + BOOST_TEST(getstring(res) == "-*= test_default_multi_pass =*-\n" + "teststring\n" + "teststring\n" + "testteststringstring\n" + "testtring\n" + "testteststringstring\n" + "teststring\n" + "-*= test_fixed_multi_pass =*-\n" + "teststring\n" + "teststring\n" + "testteststringstring\n" + "testtring\n" + "testteststringstring\n" + "-*= test_first_owner_multi_pass =*-\n" + "teststring\n" + "teststring\n" + "testteststringstring\n" + "testtring\n" + "testteststringstring\n" + "teststring\n" + "-*= test_functor_multi_pass =*-\n" + "ABCDEFGHIJKL\n" + "ABCDABCDEFGHIJKLEFGHIJKL\n" + "ABCDABCDEFGHIJKLEFGHIJKL\n"); + + return boost::report_errors(); +} diff --git a/test/support/multi_pass_compile.cpp b/test/support/multi_pass_compile.cpp new file mode 100644 index 000000000..860053577 --- /dev/null +++ b/test/support/multi_pass_compile.cpp @@ -0,0 +1,63 @@ +/*============================================================================= + Copyright (c) 2004-2008 Hartmut Kaiser + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +=============================================================================*/ + +// This is a compile only test for verifying, whether the multi_pass<> +// iterator works ok with an input iterator, which returns a value_type and not +// a reference from its dereferencing operator. + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#if defined(BOOST_HAS_UNISTD_H) +#include // unlink() +#endif + +#if defined(__MINGW32__) +#include // unlink() +#endif + +using namespace boost::spirit; +using namespace boost::spirit::qi; +using namespace std; + +int main () +{ + // create a sample file + { + ofstream out("./input_file.txt"); + out << 1.0 << "," << 2.0; + } + + // read in the values from the sample file + { + ifstream in("./input_file.txt"); // we get our input from this file + + typedef multi_pass > iterator_type; + + iterator_type first(make_multi_pass(istreambuf_iterator(in))); + iterator_type last(make_multi_pass(istreambuf_iterator())); + + rule n_list; + n_list = double_ >> *(char_(',') >> double_); + BOOST_TEST(parse(first, last, n_list)); + } + +#if !defined(__COMO_VERSION__) + unlink("./input_file.txt"); +#endif + + return boost::report_errors(); +}