x
Yes
No
Do you want to visit DriveHQ English website?
首页
产品服务
价格
免费试用
下载客户端
关于我们
云文件服务
|
云备份服务
|
FTP服务
|
企业邮箱服务
|
网站托管
|
客户端软件
云文件服务
云备份服务
FTP服务
企业级邮箱服务
网站托管
客户端软件
cpp_expression_grammar.hpp - Hosted on DriveHQ Cloud IT Platform
返回上层目录
上传
下载
共享
发布
新建文件夹
新建文件
复制
剪切
删除
粘贴
评论
升级服务
路径: \\game3dprogramming\materials\GameFactory\GameFactoryDemo\references\boost_1_35_0\boost\wave\grammars\cpp_expression_grammar.hpp
旋转
特效
属性
历史版本
/*============================================================================= Boost.Wave: A Standard compliant C++ preprocessor library http://www.boost.org/ 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(CPP_EXPRESSION_GRAMMAR_HPP_099CD1A4_A6C0_44BE_8F24_0B00F5BE5674_INCLUDED) #define CPP_EXPRESSION_GRAMMAR_HPP_099CD1A4_A6C0_44BE_8F24_0B00F5BE5674_INCLUDED #include
#include
#include
#include
#include
#if SPIRIT_VERSION >= 0x1700 #include
#include
#endif // SPIRIT_VERSION >= 0x1700 #include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if !defined(spirit_append_actor) #if SPIRIT_VERSION >= 0x1700 #define spirit_append_actor(actor) boost::spirit::push_back_a(actor) #define spirit_assign_actor(actor) boost::spirit::assign_a(actor) #else #define spirit_append_actor(actor) boost::spirit::append(actor) #define spirit_assign_actor(actor) boost::spirit::assign(actor) #endif // SPIRIT_VERSION >= 0x1700 #endif // !defined(spirit_append_actor) // this must occur after all of the includes and before any code appears #ifdef BOOST_HAS_ABI_HEADERS #include BOOST_ABI_PREFIX #endif /////////////////////////////////////////////////////////////////////////////// // // Encapsulation of the grammar for evaluation of constant preprocessor // expressions // /////////////////////////////////////////////////////////////////////////////// namespace boost { namespace wave { namespace grammars { namespace closures { /////////////////////////////////////////////////////////////////////////////// // // define the closure type used throughout the C++ expression grammar // // Throughout this grammar all literal tokens are stored into a // closure_value variables, which converts the types appropriately, where // required. // /////////////////////////////////////////////////////////////////////////////// struct cpp_expr_closure : boost::spirit::closure
{ member1 val; }; } // namespace closures namespace impl { /////////////////////////////////////////////////////////////////////////////// // // convert the given token value (integer literal) to a unsigned long // /////////////////////////////////////////////////////////////////////////////// struct convert_intlit { template
struct result { typedef boost::wave::grammars::closures::closure_value type; }; template
boost::wave::grammars::closures::closure_value operator()(TokenT const &token) const { typedef boost::wave::grammars::closures::closure_value return_type; bool is_unsigned = false; uint_literal_type ul = intlit_grammar_gen
::evaluate(token, is_unsigned); return is_unsigned ? return_type(ul) : return_type(static_cast
(ul)); } }; phoenix::function
const as_intlit; /////////////////////////////////////////////////////////////////////////////// // // convert the given token value (character literal) to a unsigned int // /////////////////////////////////////////////////////////////////////////////// struct convert_chlit { template
struct result { typedef boost::wave::grammars::closures::closure_value type; }; template
boost::wave::grammars::closures::closure_value operator()(TokenT const &token) const { typedef boost::wave::grammars::closures::closure_value return_type; value_error status = error_noerror; unsigned int value = chlit_grammar_gen
::evaluate(token, status); return return_type(value, status); } }; phoenix::function
const as_chlit; //////////////////////////////////////////////////////////////////////////////// // // Handle the ?: operator with correct type and error propagation // //////////////////////////////////////////////////////////////////////////////// struct operator_questionmark { template
struct result { typedef boost::wave::grammars::closures::closure_value type; }; template
boost::wave::grammars::closures::closure_value operator()(CondT const &cond, Arg1T &val1, Arg2T const &val2) const { return val1.handle_questionmark(cond, val2); } }; phoenix::function
const questionmark; /////////////////////////////////////////////////////////////////////////////// // // Handle type conversion conserving error conditions // /////////////////////////////////////////////////////////////////////////////// struct operator_to_bool { template
struct result { typedef boost::wave::grammars::closures::closure_value type; }; template
boost::wave::grammars::closures::closure_value operator()(ArgT &val) const { typedef boost::wave::grammars::closures::closure_value return_type; return return_type( boost::wave::grammars::closures::as_bool(val), val.is_valid()); } }; phoenix::function
const to_bool; /////////////////////////////////////////////////////////////////////////////// // // Handle explicit type conversion // /////////////////////////////////////////////////////////////////////////////// struct operator_as_bool { template
struct result { typedef bool type; }; template
bool operator()(ArgT &val) const { return boost::wave::grammars::closures::as_bool(val); } }; phoenix::function
const as_bool; /////////////////////////////////////////////////////////////////////////////// // // Handle closure value operators with proper error propagation // /////////////////////////////////////////////////////////////////////////////// #define BOOST_WAVE_BINARYOP(op, optok) \ struct operator_binary_ ## op { \ \ template
\ struct result { \ \ typedef boost::wave::grammars::closures::closure_value type; \ }; \ \ template
\ boost::wave::grammars::closures::closure_value \ operator()(Arg1T &val1, Arg2T &val2) const \ { \ return val1 optok val2; \ } \ }; \ phoenix::function
const binary_ ## op \ /**/ BOOST_WAVE_BINARYOP(and, &&); BOOST_WAVE_BINARYOP(or, ||); BOOST_WAVE_BINARYOP(bitand, &); BOOST_WAVE_BINARYOP(bitor, |); BOOST_WAVE_BINARYOP(bitxor, ^); BOOST_WAVE_BINARYOP(lesseq, <=); BOOST_WAVE_BINARYOP(less, <); BOOST_WAVE_BINARYOP(greater, >); BOOST_WAVE_BINARYOP(greateq, >=); BOOST_WAVE_BINARYOP(eq, ==); BOOST_WAVE_BINARYOP(ne, !=); #undef BOOST_WAVE_BINARYOP /////////////////////////////////////////////////////////////////////////////// #define BOOST_WAVE_UNARYOP(op, optok) \ struct operator_unary_ ## op { \ \ template
\ struct result { \ \ typedef boost::wave::grammars::closures::closure_value type; \ }; \ \ template
\ boost::wave::grammars::closures::closure_value \ operator()(ArgT &val) const \ { \ return optok val; \ } \ }; \ phoenix::function
const unary_ ## op \ /**/ BOOST_WAVE_UNARYOP(neg, !); #undef BOOST_WAVE_UNARYOP } // namespace impl /////////////////////////////////////////////////////////////////////////////// // define, whether the rule's should generate some debug output #define TRACE_CPP_EXPR_GRAMMAR \ bool(BOOST_SPIRIT_DEBUG_FLAGS_CPP & BOOST_SPIRIT_DEBUG_FLAGS_CPP_EXPR_GRAMMAR) \ /**/ struct expression_grammar : public boost::spirit::grammar< expression_grammar, closures::cpp_expr_closure::context_t > { expression_grammar() { BOOST_SPIRIT_DEBUG_TRACE_GRAMMAR_NAME(*this, "expression_grammar", TRACE_CPP_EXPR_GRAMMAR); } // no need for copy constructor/assignment operator expression_grammar(expression_grammar const&); expression_grammar& operator= (expression_grammar const&); template
struct definition { typedef closures::cpp_expr_closure closure_type; typedef boost::spirit::rule
rule_t; typedef boost::spirit::rule
simple_rule_t; simple_rule_t pp_expression; rule_t const_exp; rule_t logical_or_exp, logical_and_exp; rule_t inclusive_or_exp, exclusive_or_exp, and_exp; rule_t cmp_equality, cmp_relational; rule_t shift_exp; rule_t add_exp, multiply_exp; rule_t unary_exp, primary_exp, constant; rule_t const_exp_nocalc; rule_t logical_or_exp_nocalc, logical_and_exp_nocalc; rule_t inclusive_or_exp_nocalc, exclusive_or_exp_nocalc, and_exp_nocalc; rule_t cmp_equality_nocalc, cmp_relational_nocalc; rule_t shift_exp_nocalc; rule_t add_exp_nocalc, multiply_exp_nocalc; rule_t unary_exp_nocalc, primary_exp_nocalc, constant_nocalc; boost::spirit::subrule<0, closure_type::context_t> const_exp_subrule; definition(expression_grammar const &self) { using namespace boost::spirit; using namespace phoenix; using namespace boost::wave; using boost::wave::util::pattern_p; pp_expression = const_exp[self.val = arg1] ; const_exp = logical_or_exp[const_exp.val = arg1] >> !(const_exp_subrule = ch_p(T_QUESTION_MARK) >> const_exp [ const_exp_subrule.val = arg1 ] >> ch_p(T_COLON) >> const_exp [ const_exp_subrule.val = impl::questionmark(const_exp.val, const_exp_subrule.val, arg1) ] )[const_exp.val = arg1] ; logical_or_exp = logical_and_exp[logical_or_exp.val = arg1] >> *( if_p(impl::as_bool(logical_or_exp.val)) [ // if one of the || operators is true, no more // evaluation is required pattern_p(T_OROR, MainTokenMask) >> logical_and_exp_nocalc [ logical_or_exp.val = impl::to_bool(logical_or_exp.val) ] ] .else_p [ pattern_p(T_OROR, MainTokenMask) >> logical_and_exp [ logical_or_exp.val = impl::binary_or(logical_or_exp.val, arg1) ] ] ) ; logical_and_exp = inclusive_or_exp[logical_and_exp.val = arg1] >> *( if_p(impl::as_bool(logical_and_exp.val)) [ pattern_p(T_ANDAND, MainTokenMask) >> inclusive_or_exp [ logical_and_exp.val = impl::binary_and(logical_and_exp.val, arg1) ] ] .else_p [ // if one of the && operators is false, no more // evaluation is required pattern_p(T_ANDAND, MainTokenMask) >> inclusive_or_exp_nocalc [ logical_and_exp.val = impl::to_bool(logical_and_exp.val) ] ] ) ; inclusive_or_exp = exclusive_or_exp[inclusive_or_exp.val = arg1] >> *( pattern_p(T_OR, MainTokenMask) >> exclusive_or_exp [ inclusive_or_exp.val = impl::binary_bitor(inclusive_or_exp.val, arg1) ] ) ; exclusive_or_exp = and_exp[exclusive_or_exp.val = arg1] >> *( pattern_p(T_XOR, MainTokenMask) >> and_exp [ exclusive_or_exp.val = impl::binary_bitxor(exclusive_or_exp.val, arg1) ] ) ; and_exp = cmp_equality[and_exp.val = arg1] >> *( pattern_p(T_AND, MainTokenMask) >> cmp_equality [ and_exp.val = impl::binary_bitand(and_exp.val, arg1) ] ) ; cmp_equality = cmp_relational[cmp_equality.val = arg1] >> *( ch_p(T_EQUAL) >> cmp_relational [ cmp_equality.val = impl::binary_eq(cmp_equality.val, arg1) ] | pattern_p(T_NOTEQUAL, MainTokenMask) >> cmp_relational [ cmp_equality.val = impl::binary_ne(cmp_equality.val, arg1) ] ) ; cmp_relational = shift_exp[cmp_relational.val = arg1] >> *( ch_p(T_LESSEQUAL) >> shift_exp [ cmp_relational.val = impl::binary_lesseq(cmp_relational.val, arg1) ] | ch_p(T_GREATEREQUAL) >> shift_exp [ cmp_relational.val = impl::binary_greateq(cmp_relational.val, arg1) ] | ch_p(T_LESS) >> shift_exp [ cmp_relational.val = impl::binary_less(cmp_relational.val, arg1) ] | ch_p(T_GREATER) >> shift_exp [ cmp_relational.val = impl::binary_greater(cmp_relational.val, arg1) ] ) ; shift_exp = add_exp[shift_exp.val = arg1] >> *( ch_p(T_SHIFTLEFT) >> add_exp [ shift_exp.val <<= arg1 ] | ch_p(T_SHIFTRIGHT) >> add_exp [ shift_exp.val >>= arg1 ] ) ; add_exp = multiply_exp[add_exp.val = arg1] >> *( ch_p(T_PLUS) >> multiply_exp [ add_exp.val += arg1 ] | ch_p(T_MINUS) >> multiply_exp [ add_exp.val -= arg1 ] ) ; multiply_exp = unary_exp[multiply_exp.val = arg1] >> *( ch_p(T_STAR) >> unary_exp [ multiply_exp.val *= arg1 ] | ch_p(T_DIVIDE) >> unary_exp [ multiply_exp.val /= arg1 ] | ch_p(T_PERCENT) >> unary_exp [ multiply_exp.val %= arg1 ] ) ; unary_exp = primary_exp[unary_exp.val = arg1] | ch_p(T_PLUS) >> unary_exp [ unary_exp.val = arg1 ] | ch_p(T_MINUS) >> unary_exp [ unary_exp.val = -arg1 ] | pattern_p(T_COMPL, MainTokenMask) >> unary_exp [ unary_exp.val = ~arg1 ] | pattern_p(T_NOT, MainTokenMask) >> unary_exp [ unary_exp.val = impl::unary_neg(arg1) ] ; primary_exp = constant[primary_exp.val = arg1] | ch_p(T_LEFTPAREN) >> const_exp[primary_exp.val = arg1] >> ch_p(T_RIGHTPAREN) ; constant = ch_p(T_PP_NUMBER) [ constant.val = impl::as_intlit(arg1) ] | ch_p(T_INTLIT) [ constant.val = impl::as_intlit(arg1) ] | ch_p(T_CHARLIT) [ constant.val = impl::as_chlit(arg1) ] ; // here follows the same grammar, but without any embedded // calculations const_exp_nocalc = logical_or_exp_nocalc >> !( ch_p(T_QUESTION_MARK) >> const_exp_nocalc >> ch_p(T_COLON) >> const_exp_nocalc ) ; logical_or_exp_nocalc = logical_and_exp_nocalc >> *( pattern_p(T_OROR, MainTokenMask) >> logical_and_exp_nocalc ) ; logical_and_exp_nocalc = inclusive_or_exp_nocalc >> *( pattern_p(T_ANDAND, MainTokenMask) >> inclusive_or_exp_nocalc ) ; inclusive_or_exp_nocalc = exclusive_or_exp_nocalc >> *( pattern_p(T_OR, MainTokenMask) >> exclusive_or_exp_nocalc ) ; exclusive_or_exp_nocalc = and_exp_nocalc >> *( pattern_p(T_XOR, MainTokenMask) >> and_exp_nocalc ) ; and_exp_nocalc = cmp_equality_nocalc >> *( pattern_p(T_AND, MainTokenMask) >> cmp_equality_nocalc ) ; cmp_equality_nocalc = cmp_relational_nocalc >> *( ch_p(T_EQUAL) >> cmp_relational_nocalc | pattern_p(T_NOTEQUAL, MainTokenMask) >> cmp_relational_nocalc ) ; cmp_relational_nocalc = shift_exp_nocalc >> *( ch_p(T_LESSEQUAL) >> shift_exp_nocalc | ch_p(T_GREATEREQUAL) >> shift_exp_nocalc | ch_p(T_LESS) >> shift_exp_nocalc | ch_p(T_GREATER) >> shift_exp_nocalc ) ; shift_exp_nocalc = add_exp_nocalc >> *( ch_p(T_SHIFTLEFT) >> add_exp_nocalc | ch_p(T_SHIFTRIGHT) >> add_exp_nocalc ) ; add_exp_nocalc = multiply_exp_nocalc >> *( ch_p(T_PLUS) >> multiply_exp_nocalc | ch_p(T_MINUS) >> multiply_exp_nocalc ) ; multiply_exp_nocalc = unary_exp_nocalc >> *( ch_p(T_STAR) >> unary_exp_nocalc | ch_p(T_DIVIDE) >> unary_exp_nocalc | ch_p(T_PERCENT) >> unary_exp_nocalc ) ; unary_exp_nocalc = primary_exp_nocalc | ch_p(T_PLUS) >> unary_exp_nocalc | ch_p(T_MINUS) >> unary_exp_nocalc | pattern_p(T_COMPL, MainTokenMask) >> unary_exp_nocalc | pattern_p(T_NOT, MainTokenMask) >> unary_exp_nocalc ; primary_exp_nocalc = constant_nocalc | ch_p(T_LEFTPAREN) >> const_exp_nocalc >> ch_p(T_RIGHTPAREN) ; constant_nocalc = ch_p(T_PP_NUMBER) | ch_p(T_INTLIT) | ch_p(T_CHARLIT) ; BOOST_SPIRIT_DEBUG_TRACE_RULE(pp_expression, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(const_exp, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(logical_or_exp, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(logical_and_exp, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(inclusive_or_exp, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(exclusive_or_exp, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(and_exp, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(cmp_equality, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(cmp_relational, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(shift_exp, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(add_exp, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(multiply_exp, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(unary_exp, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(primary_exp, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(constant, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(const_exp_subrule, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(const_exp_nocalc, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(logical_or_exp_nocalc, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(logical_and_exp_nocalc, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(inclusive_or_exp_nocalc, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(exclusive_or_exp_nocalc, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(and_exp_nocalc, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(cmp_equality_nocalc, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(cmp_relational_nocalc, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(shift_exp_nocalc, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(add_exp_nocalc, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(multiply_exp_nocalc, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(unary_exp_nocalc, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(primary_exp_nocalc, TRACE_CPP_EXPR_GRAMMAR); BOOST_SPIRIT_DEBUG_TRACE_RULE(constant_nocalc, TRACE_CPP_EXPR_GRAMMAR); } // start rule of this grammar simple_rule_t const& start() const { return pp_expression; } }; }; /////////////////////////////////////////////////////////////////////////////// #undef TRACE_CPP_EXPR_GRAMMAR /////////////////////////////////////////////////////////////////////////////// // // The following function is defined here, to allow the separation of // the compilation of the expression_grammar from the function using it. // /////////////////////////////////////////////////////////////////////////////// #if BOOST_WAVE_SEPARATE_GRAMMAR_INSTANTIATION != 0 #define BOOST_WAVE_EXPRGRAMMAR_GEN_INLINE #else #define BOOST_WAVE_EXPRGRAMMAR_GEN_INLINE inline #endif template
BOOST_WAVE_EXPRGRAMMAR_GEN_INLINE bool expression_grammar_gen
::evaluate( typename token_sequence_type::const_iterator const &first, typename token_sequence_type::const_iterator const &last, typename token_type::position_type const &act_pos, bool if_block_status, value_error &status) { using namespace boost::spirit; using namespace boost::wave; using namespace boost::wave::grammars::closures; using boost::wave::util::impl::as_string; typedef typename token_sequence_type::const_iterator iterator_type; typedef typename token_sequence_type::value_type::string_type string_type; parse_info
hit(first); closure_value result; // expression result try { expression_grammar g; // expression grammar hit = parse (first, last, g[spirit_assign_actor(result)], ch_p(T_SPACE) | ch_p(T_CCOMMENT) | ch_p(T_CPPCOMMENT)); if (!hit.hit) { // expression is illformed if (if_block_status) { string_type expression = as_string
(first, last); if (0 == expression.size()) expression = "
"; BOOST_WAVE_THROW(preprocess_exception, ill_formed_expression, expression.c_str(), act_pos); } else { // as the if_block_status is false no errors will be reported return false; } } } catch (boost::wave::preprocess_exception const& e) { // expression is illformed if (if_block_status) { boost::throw_exception(e); } else { // as the if_block_status is false no errors will be reported return false; } } if (!hit.full) { // The token list starts with a valid expression, but there remains // something. If the remainder consists out of whitespace only, the // expression is still valid. iterator_type next = hit.stop; while (next != last) { switch (static_cast
(token_id(*next))) { case T_SPACE: case T_SPACE2: case T_CCOMMENT: break; // ok continue case T_NEWLINE: case T_EOF: case T_CPPCOMMENT: // contains newline return as_bool(result); // expression is valid default: // expression is illformed if (if_block_status) { string_type expression = as_string
(first, last); if (0 == expression.size()) expression = "
"; BOOST_WAVE_THROW(preprocess_exception, ill_formed_expression, expression.c_str(), act_pos); } else { // as the if_block_status is false no errors will be reported return false; } } ++next; } } if (error_noerror != result.is_valid()) // division or other error by zero occurred status = result.is_valid(); // token sequence is a valid expression return as_bool(result); } #undef BOOST_WAVE_EXPRGRAMMAR_GEN_INLINE /////////////////////////////////////////////////////////////////////////////// } // namespace grammars } // namespace wave } // namespace boost // the suffix header occurs after all of the code #ifdef BOOST_HAS_ABI_HEADERS #include BOOST_ABI_SUFFIX #endif #endif // !defined(CPP_EXPRESSION_GRAMMAR_HPP_099CD1A4_A6C0_44BE_8F24_0B00F5BE5674_INCLUDED)
cpp_expression_grammar.hpp
网页地址
文件地址
上一页
4/13
下一页
下载
( 33 KB )
Comments
Total ratings:
0
Average rating:
无评论
of 10
Would you like to comment?
Join now
, or
Logon
if you are already a member.