diff --git a/CMakeLists.txt b/CMakeLists.txt index c3c0b82adfbf348fc5ab92839a7c3059dba6f1b0..6e134b18e3e901f7045b66a385678a50472e5a6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,4 +50,3 @@ find_package(Boost 1.41 REQUIRED COMPONENTS program_options filesystem regex) add_subdirectory(libwccl) add_subdirectory(tests) - diff --git a/libwccl/CMakeLists.txt b/libwccl/CMakeLists.txt index 2543ebe6f6a1dace4b5842b0c11b3e319d8a78fe..b3bb20e198ff38a710036e46cb381c6d4820fd7b 100644 --- a/libwccl/CMakeLists.txt +++ b/libwccl/CMakeLists.txt @@ -15,13 +15,14 @@ set(LIBS ${LIBS} ${Boost_LIBRARIES}) SET(libwccl_STAT_SRC exception.cpp - main.cpp ops/and.cpp ops/formatters.cpp ops/logicalpredicate.cpp ops/nor.cpp ops/or.cpp ops/predicate.cpp + parser/Parser.cpp + parser/ParserException.cpp sentencecontext.cpp values/bool.cpp values/position.cpp @@ -32,10 +33,28 @@ SET(libwccl_STAT_SRC variables.cpp ) +SET(libwccl_STAT_SRC ${libwccl_STAT_SRC} + ${PROJECT_BINARY_DIR}/parser/ANTLRLexer.cpp + ${PROJECT_BINARY_DIR}/parser/ANTLRParser.cpp +) + +include_directories(${PROJECT_BINARY_DIR}/parser) + +ADD_CUSTOM_COMMAND( + OUTPUT ${PROJECT_BINARY_DIR}/parser/ANTLRLexer.cpp + ${PROJECT_BINARY_DIR}/parser/ANTLRLexer.hpp + ${PROJECT_BINARY_DIR}/parser/ANTLRParser.cpp + ${PROJECT_BINARY_DIR}/parser/ANTLRParser.hpp + ${PROJECT_BINARY_DIR}/parser/ANTLRExprTokenTypes.hpp + ${PROJECT_BINARY_DIR}/parser/ANTLRExprTokenTypes.txt + COMMAND runantlr -o parser ${PROJECT_SOURCE_DIR}/parser/grammar.g + DEPENDS ${PROJECT_SOURCE_DIR}/parser/grammar.g + COMMENT "-- Generating ANTLR parser cpp/hpp/txt files") + file(GLOB_RECURSE INCS "*.h") add_library(wccl SHARED ${libwccl_STAT_SRC} ${INCS}) -target_link_libraries ( wccl ${LIBS} ) +target_link_libraries ( wccl ${LIBS}) set_target_properties(wccl PROPERTIES VERSION "${ver_major}.${ver_minor}" diff --git a/libwccl/main.cpp b/libwccl/main.cpp deleted file mode 100644 index 1f8701d8822b33010d14c6d3e0436df4e495ab6f..0000000000000000000000000000000000000000 --- a/libwccl/main.cpp +++ /dev/null @@ -1,4 +0,0 @@ - -namespace Wccl { - -} /* end ns Wccl */ diff --git a/libwccl/parser/Parser.cpp b/libwccl/parser/Parser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..067aeb0d07822dea41d93e3e564abcdf0992d0ad --- /dev/null +++ b/libwccl/parser/Parser.cpp @@ -0,0 +1,84 @@ +#include <libwccl/parser/Parser.h> + +/** + * @desc Parser constructor. Default tagset is NULL + */ +Parser::Parser(const Corpus2::Tagset& t) : tagset(t) +{ +} + +/** + * + */ +Parser::~Parser() +{ + // +} + +// ---------------------------------------------------------------------------- + +/** + * @desc Overloaded parsing operator writed in string. Calls base method + * @arg str operator as string + * @return call method @see parserOperator(const std::istream&) + */ +/* +std::string Parser::parseOperator(const std::string& str) const +{ + std::stringstream ss (std::stringstream::in | std::stringstream::out); + ss << str; + + return this->parseOperator(ss); +} +*/ +/** + * @desc Base method for parsing operator in stream + * @arg istr input stream with operator + * @return Operator + */ +/* +std::string Parser::parseOperator(std::istream& istr) const +{ + ANTLRLexer lexer(istr); + ANTLRParser parser(lexer); + + return "Ala ma kota"; +} +*/ + +// ----------------------------------------------------------------------------- + +/** + * @desc Overloaded parsing expression writed in string. Calls base method + * @arg str variables in string + */ +void Parser::parseExpression(const std::string& str) const +{ + std::stringstream ss (std::stringstream::in | std::stringstream::out); + ss << str; + + return this->parseExpression(ss); +} + +/** + * @desc Base method for parsing expression in stream + * @arg istr input stream with variable + * @return Operator + */ +void Parser::parseExpression(std::istream& istr) const +{ + ANTLRLexer lexer(istr); + ANTLRParser parser(lexer); + + try { + parser.start_rules(); + + std::cerr << "Syntax ok!" << std::endl; + } + catch (ParserException e) { + std::cerr << e.info() << std::endl; + } + catch (...) { + std::cerr << "Syntax error!" << std::endl; + } +} diff --git a/libwccl/parser/Parser.h b/libwccl/parser/Parser.h new file mode 100644 index 0000000000000000000000000000000000000000..a8b218bcdb0f137d1de95c7b789de7355b317649 --- /dev/null +++ b/libwccl/parser/Parser.h @@ -0,0 +1,28 @@ +#ifndef PARSER_H +#define PARSER_H + +#include <sstream> +#include <libcorpus2/tagset.h> + +#include "ANTLRLexer.hpp" +#include "ANTLRParser.hpp" + +#include <libwccl/parser/ParserException.h> + +// <libwccl> + +class Parser { +public: + Parser(const Corpus2::Tagset&); + ~Parser(); + + // -------------------------------------------------------------------------- + // FIXME + void parseExpression(const std::string&) const; + void parseExpression(std::istream& ) const; + +private: + const Corpus2::Tagset &tagset; +}; + +#endif // PARSER_H diff --git a/libwccl/parser/ParserException.cpp b/libwccl/parser/ParserException.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b2ca3661cf5f7c10b52ba1ae210f8baa3a61766a --- /dev/null +++ b/libwccl/parser/ParserException.cpp @@ -0,0 +1,6 @@ +#include <libwccl/parser/ParserException.h> + +ParserException::ParserException(std::string msg) +{ + this->msg = msg; +} diff --git a/libwccl/parser/ParserException.h b/libwccl/parser/ParserException.h new file mode 100644 index 0000000000000000000000000000000000000000..b74923295ed48349a08f343a7734e5e173694bc8 --- /dev/null +++ b/libwccl/parser/ParserException.h @@ -0,0 +1,18 @@ +#ifndef PARSEREXCEPTION_H +#define PARSEREXCEPTION_H + +#include <string> + +class ParserException +{ +public: + ParserException(const std::string); + + // + const std::string info() const { return this->msg; } + +private: + std::string msg; +}; + +#endif // PARSEREXCEPTION_H diff --git a/libwccl/parser/grammar.g b/libwccl/parser/grammar.g new file mode 100644 index 0000000000000000000000000000000000000000..cbbe0bd9426ed7023303a22b5738f3f647fb64dc --- /dev/null +++ b/libwccl/parser/grammar.g @@ -0,0 +1,498 @@ +header { + #include <libwccl/parser/ParserException.h> + + // libwccl + + #include <cstdio> + #include <antlr/Token.hpp> + #include <boost/lexical_cast.hpp> +} + +options { + language = "Cpp"; +} + +// ---------------------------------------------------------------------------------- +// ANTLR PARSER +// ---------------------------------------------------------------------------------- +class ANTLRParser extends Parser; +options { + k = 6; + exportVocab = ANTLRExpr; + buildAST = false; + defaultErrorHandler = false; +} +{ +private: + // + const std::string token_ref_to_std_string(antlr::RefToken& rstr) { + return (((antlr::Token*)rstr)->getText()); + } + const int token_ref_to_int(antlr::RefToken& rstr) { + return atoi(this->token_ref_to_std_string(rstr).c_str()); + } +} +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------------- +// Start all rules +start_rules +{ + std::string name = ""; +} + : values_ref [name] { fprintf(stderr, "%s\n", name.c_str()); } + | position_op [name] { fprintf(stderr, "%s\n", name.c_str()); } + | filters_op [name] { fprintf(stderr, "%s\n", name.c_str()); } + | setvar_op [name] { fprintf(stderr, "%s\n", name.c_str()); } + | string_operators [name] { fprintf(stderr, "%s\n", name.c_str()); } + | boolean_op [name] { fprintf(stderr, "%s\n", name.c_str()); } + ; + +// --------------------------------------------------------------------------------- +// values: +// Walues can be use for setvar(...,..) +values [std::string& name]: + position [name] + | str_set [name] + | sym_set [name] + | boolean [name] + ; +// +// Values reference => values + position_ref +// !! Cannot use for setvar(...,...) !! +values_ref [std::string& name]: + values [name] + | position_ref [name] + | boolean_ref [name] + ; +///////////////////////////////////////////////////////////////////////////////////// +// Position: $0name +position [std::string& name]: + DOLLAR "0" n: SYMBOL { name = token_ref_to_std_string(n); } + ; +// +// Position reference: $(0-9)+name +// !! Cannot use for setvar(...,...) !! +position_ref [std::string& name]: + DOLLAR INT n: SYMBOL { name = token_ref_to_std_string(n); } + ; +///////////////////////////////////////////////////////////////////////////////////// +// String set: $name +str_set [std::string& name]: + DOLLAR n: SYMBOL { name = token_ref_to_std_string(n); } + ; +///////////////////////////////////////////////////////////////////////////////////// +// Sym set: $$name +sym_set [std::string& name]: + DOLLAR DOLLAR n: SYMBOL { name = token_ref_to_std_string(n); } + ; +///////////////////////////////////////////////////////////////////////////////////// +// Bool: $?name +boolean [std::string& name]: + DOLLAR Q_MARK n1: SYMBOL { name = token_ref_to_std_string(n1); } + ; +// Boolean $!name +boolean_ref [std::string& name]: + DOLLAR E_MARK n1: SYMBOL { name = token_ref_to_std_string(n1); } + ; + +///////////////////////////////////////////////////////////////////////////////////// +// OPERATORS +///////////////////////////////////////////////////////////////////////////////////// +// ---------------------------------------------------------------------------------- +// Positions operator +// TODO range przyjmuje postion_ref. ?? Zmienic na position ?? +position_op [std::string& name] +{ + std::string r1, r2; +} + : "flex" LBRACKET position_ref [name] RBRACKET + | "range" LPAREN s: SYMBOL COMMA position_ref [r1] COMMA position_ref [r2] RPAREN + { name = ("Range opertator from " + token_ref_to_std_string(s) + " [" + r1 + ":" + r2 + "]!"); } + ; +// ---------------------------------------------------------------------------------- +// Filtering operator +filters_op [std::string& name] +{ + std::string p, p2, e1, e2; +} + : "catflt" LPAREN position_ref [p] COMMA es_any [e1] COMMA es_any [e2] RPAREN + { name = ( "Catflt operator in position " + p + " for sets " + e1 + " " + e2); } + | "agrflt" LPAREN position_ref [p] COMMA position_ref [p2] COMMA es_any [e1] COMMA i: INT RPAREN + { name = ( "Agrflt operator p1 " + p + " p2 " + p2 + " for set " + e1 + " aggr_attrs " + token_ref_to_std_string(i)); } + ; + +// ---------------------------------------------------------------------------------- +// Setvar operator +setvar_op [std::string& value] + : setvar_pos [value] + | setvar_bool [value] + | setvar_sset [value] + | setvar_tset [value] + ; + +// setvar dla position przyjmuje position_ref -> TODO sprawdzic dlaczego +// gramatyka nie pokrywa "setvar" LPAREN position COMMA position_v RPAREN +setvar_pos [std::string& value] + : "setvar" LPAREN position_ref [value] COMMA position_v [value] RPAREN +// : "setvar" LPAREN position [value] COMMA position_v [value] RPAREN + ; + +setvar_bool [std::string& value] + : "setvar" LPAREN boolean [value] COMMA boolean_v [value] RPAREN + ; + +setvar_sset [std::string& value] + : "setvar" LPAREN str_set [value] COMMA str_set_v [value] RPAREN + ; + +setvar_tset [std::string& value] + : "setvar" LPAREN sym_set [value] COMMA sym_set_v [value] RPAREN + ; + +// ---------------------------------------------------------------------------------- +// Values +///////////////////////////////////////////////////////////////////////////////////// +// boolean: +boolean_v [std::string& value] + : "True" { value = "True"; } + | "False" { value = "False"; } + | boolean[value] + ; +///////////////////////////////////////////////////////////////////////////////////// +// position value: +position_v [std::string& value] + : i: INT { value = token_ref_to_std_string(i); } + | "begin" { value = "begin"; } + | "end" { value = "end"; } + | "nowhere" { value = "nowhere"; } + | position [value] + ; +///////////////////////////////////////////////////////////////////////////////////// +// string set in +str_set_in [std::string& value] + : v1: STRING { value += token_ref_to_std_string(v1); } + | v2: STRING COMMA str_set_in [value] { value += (", " + token_ref_to_std_string(v2)); } + ; +// string set [] ['a'] ['a', 'b'] ["a"] ["a", "b"] ['a', "b"] +str_set_v [std::string& value] + : LBRACKET RBRACKET + | LBRACKET str_set_in[value] RBRACKET + ; +///////////////////////////////////////////////////////////////////////////////////// +// element of sym set +sym_set_elem_s [std::string& value] + : s1: SYMBOL { value += token_ref_to_std_string(s1); } + | s2: SYMBOL COMMA sym_set_elem_s [value] { value += token_ref_to_std_string(s2); } + | s3: SYMBOL COMMA sym_set_elem_g [value] { value += token_ref_to_std_string(s3); } + ; +// element of sym set +sym_set_elem_g [std::string& value] + : G_MARK s1: SYMBOL G_MARK { value += token_ref_to_std_string(s1); } + | G_MARK s2: SYMBOL G_MARK COMMA sym_set_elem_g [value] { value += token_ref_to_std_string(s2); } + | G_MARK s3: SYMBOL G_MARK COMMA sym_set_elem_s [value] { value += token_ref_to_std_string(s3); } + ; +// sym set in +sym_set_in [std::string& value] + : sym_set_elem_s [value] + | sym_set_elem_g [value] + ; +// sym set {} {a} {a, b} +sym_set_v [std::string& value] + : LCURLY RCURLY + | LCURLY sym_set_in [value] RCURLY + ; + +///////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////// +// internal values: +v_literal [std::string& value] + : s1: STRING { value = token_ref_to_std_string(s1); } + | s2: SYMBOL { value = token_ref_to_std_string(s2); } + ; + +///////////////////////////////////////////////////////////////////////////////////// +// constants +// set of values +s_literal [std::string& v] + : LBRACKET ((v_literal[v]) (COMMA v_literal [v])*)? RBRACKET + ; + +// comma-separated predicates +seq_et [std::string& v]: + et_any [v] (COMMA et_any [v])* + ; + +es_any [std::string& v]: + s_literal [v] + | es_op [v] + ; + +es_op [std::string& v]: + position_ref [v] + | filters_op [v] + ; + +et_bool [std::string& v]: + boolean [v] + | boolean_op [v] + ; + +// set relations +et_set [std::string& v] +{ + std::string v1, v2; +} + : "in" LPAREN es_any [v1] COMMA es_any [v2] RPAREN { v = ("name " + v1 + " " + v2); } + | "inter" LPAREN es_any [v1] COMMA es_any [v2] RPAREN { v = ("inter " + v1 + " " + v2); } + | "equal" LPAREN es_any [v1] COMMA es_any [v2] RPAREN { v = ("equal " + v1 + " " + v2); } + ; + +et_string [std::string& v] + : "isbig" LPAREN es_any [v] RPAREN + | "hasnum" LPAREN es_any [v] RPAREN + ; + +et_action [std::string& v] +{ + std::string v1, v2; +} + : "delete" LPAREN et_any [v] RPAREN + | "select" LPAREN et_any [v] RPAREN + | "relabel" LPAREN es_any [v1] COMMA et_any [v2] RPAREN { v = ("relabel " + v1 + " " + v2); } + | "unify" LPAREN es_any [v1] COMMA i: INT RPAREN { v = ("relabel " + v1 + " on position " + token_ref_to_std_string(i)); } + | "mark" LPAREN s1: SYMBOL RPAREN { v = ("mark " + token_ref_to_std_string(s1)); } + | "unmark" LPAREN s2: SYMBOL RPAREN { v = ("unmark " + token_ref_to_std_string(s2)); } + | "startnew" LPAREN s3: SYMBOL RPAREN { v = ("startnew " + token_ref_to_std_string(s3)); } + | "lextend" LPAREN s4: SYMBOL RPAREN { v = ("lextend " + token_ref_to_std_string(s4)); } + | "rextend" LPAREN s5: SYMBOL RPAREN { v = ("rextend " + token_ref_to_std_string(s5)); } + ; + +et_iter [std::string& v] +{ + std::string v1, v2, v3, v4; +} + : "only" LPAREN position_ref [v1] COMMA position_ref [v2] COMMA position_ref [v3] COMMA et_any [v4] RPAREN + | "atleast" LPAREN position_ref [v1] COMMA position_ref [v2] COMMA position_ref [v3] COMMA et_any [v4] COMMA i:INT RPAREN + | "llook" LPAREN position_ref [v1] COMMA position_ref [v2] COMMA position_ref [v3] COMMA et_any [v4] RPAREN + | "rlook" LPAREN position_ref [v1] COMMA position_ref [v2] COMMA position_ref [v3] COMMA et_any [v4] RPAREN + | "setvar" LPAREN position_ref [v1] COMMA position_ref [v2] RPAREN + | "lskip" LPAREN position_ref [v1] COMMA SYMBOL COMMA position_ref [v2] COMMA et_any [v3] RPAREN + | "lphrase" LPAREN position_ref [v1] COMMA SYMBOL COMMA position_ref [v2] RPAREN + | "rphrase" LPAREN position_ref [v1] COMMA SYMBOL COMMA position_ref [v2] RPAREN + | "accept" LPAREN seq_et [v1] RPAREN + ; + +// predicates checking agreement +et_agr [std::string& name] +{ + std::string p1, p2, v; +} + : "agr" LPAREN position_ref [p1] COMMA position_ref [p2] COMMA es_any [v] COMMA i1: INT RPAREN + | "agrpp" LPAREN position_ref [p1] COMMA position_ref [p2] COMMA es_any [v] COMMA i2: INT RPAREN + | "wagr" LPAREN position_ref [p1] COMMA position_ref [p2] COMMA es_any [v] COMMA i3: INT RPAREN + ; + +// annotation checking predicates +et_annot [std::string& v] + : "phrase" LPAREN position_ref [v] COMMA s: SYMBOL RPAREN + ; + +// constraints +et_any [std::string& v] + : et_bool [v] + | et_set [v] + | et_string [v] + | et_action [v] + | et_iter [v] + | et_agr [v] + | et_annot [v] + ; + +///////////////////////////////////////////////////////////////////////////////////// +// OERATORS +///////////////////////////////////////////////////////////////////////////////////// +// Operators returns str_set: orth[$-2P] +string_operators [std::string& name] + : op_orth [name] + | op_base [name] + | op_lower [name] + | op_upper [name] + | op_affix [name] +; + +op_orth [std::string& name] + : "orth" LBRACKET position_ref [name] RBRACKET { name = "Orth operator!"; } + ; + +op_base [std::string& name] + : "base" LBRACKET position_ref [name] RBRACKET { name = "Base operator!"; } + ; + +op_lower [std::string& name] + : "lower" LPAREN str_set [name] RPAREN { name = "Lower operator!"; } + | "lower" LPAREN str_set_v [name] RPAREN { name = "Lower operator!"; } + ; + +op_upper [std::string& name] + : "upper" LPAREN str_set [name] RPAREN { name = "Upper operator!"; } + | "upper" LPAREN str_set_v [name] RPAREN { name = "Upper operator!"; } + ; + +op_affix [std::string& name] + : "affix" LPAREN str_set [name] COMMA n1: INT RPAREN { name = "Affix operator " + token_ref_to_std_string(n1) + "!"; } + | "affix" LPAREN str_set_v [name] COMMA n2: INT RPAREN { name = "Affix operator " + token_ref_to_std_string(n2) + "!"; } + ; + +boolean_op [std::string& name] + : "and" LPAREN seq_et [name] RPAREN + | "not" LPAREN seq_et [name] RPAREN + | "or" LPAREN seq_et [name] RPAREN + ; + +// ---------------------------------------------------------------------------------- +// ANTLR LEXER +// ---------------------------------------------------------------------------------- +class ANTLRLexer extends Lexer; +options { + k = 2; + exportVocab = ANTLRExpr; + charVocabulary = '\3'..'\377'; + testLiterals = false; +} + +STRING +options { + paraphrase = "a string"; +} + : '"' (~'"')* '"' + | '\'' (~'\'')* '\'' + ; + +INT +options { + paraphrase = "Integer"; +} + : ('-'|'+')?('0'..'9')+ + ; + +Q_MARK +options { + paraphrase = "Query mark"; +} + : '?' + ; + +E_MARK +options { + paraphrase = "Exclamanation mark"; +} + : '!' + ; + +G_MARK +options { + paraphrase = "Gravis mark"; +} + : '`' + ; + +LBRACKET +options { + paraphrase = "'['"; +} + : '[' + ; + +RBRACKET +options { + paraphrase = "']'"; +} + : ']' + ; + +LPAREN +options { + paraphrase = "'('"; +} + : '(' + ; + +RPAREN +options { + paraphrase = "')'"; +} + : ')' + ; + +LCURLY +options { + paraphrase = "'{'"; +} + : '{' + ; + +RCURLY +options { + paraphrase = "'}'"; +} + : '}' + ; + +DOLLAR +options { + paraphrase = "'$'"; +} + : '$' + ; + +AT_MARK +options { + paraphrase = "'@'"; +} + : '@' + ; + +COMMA +options { + paraphrase = "','"; +} + : ',' + ; + +SYMBOL +options { + paraphrase = "symbol"; + testLiterals = true; +} + : ( 'a'..'z' | 'A'..'Z' | '_' ) ( 'a'..'z' | 'A'..'Z' | '_' | '0'..'9' )* + ; + +WS + : + ( ' ' + | '\t' + | '\r' '\n' {newline(); } + | '\n' {newline(); } ) { $setType(antlr::Token::SKIP); } + ; + +COMMENT +options { + paraphrase = "Comment"; +} + : "//" (~'\n')* '\n'{ $setType(antlr::Token::SKIP); newline(); } + ; + +HASH +options { + paraphrase = "'#'"; +} + : '#' + ; + +DSEPARATOR +options { + paraphrase = "':-'"; +} + : ":-" + ; diff --git a/libwccl/variables.h b/libwccl/variables.h index 902cf1e10f0dedcdbb61da7b850b8eba8afd7d9e..c48997400656ec2555c847adcf6a833c7330cfd1 100644 --- a/libwccl/variables.h +++ b/libwccl/variables.h @@ -63,6 +63,71 @@ public: std::string varname; }; +/** + * Exception class for invalid variable name errors in situations where + * it is not possible to return a NULL-equivalent + */ +class InvalidVariableName : public WcclError +{ +public: + InvalidVariableName(const std::string& varname) + : WcclError("Invalid variable name"), varname(varname) + {} + + ~InvalidVariableName() throw() {} + + std::string varname; +}; + +/** + * Base class for the "fast" variable accesors. + */ +class BaseAccesor +{ +public: + /// Variable name getter + const std::string get_name() const { + return varname_; + } +protected: + /// Protected ctor, only constructed by derived classes + BaseAccesor(const std::string& varname) + : varname_(varname) + { + } + + std::string varname_; +}; + +/** + * A "fast" accesor class for getting a variable off a Variables object. + * + * It should be faster to create an accesor object from the variables name and + * then use it multiple times than to use the name itself often. + * + * @todo the above is not the case yet. + * + * Objects of the accesor class can only be created by the Variables object, + * are valid only for that Variables object and its copies, and might stop + * working if the Variables object is touched in the wrong way in between the + * creation and use of a VariableAccesor. UB is meant by "stop working". + * Bad touch is not yet fully defined, removing a variable certainly is one. + */ +template<typename T> +class VariableAccesor : public BaseAccesor +{ +public: + +protected: + /// Protected ctor, only created by Variables::create_accesor + VariableAccesor(const std::string& varname) + : BaseAccesor(varname) + { + } + + friend class Variables; +}; + /** * A class for holding variables. * @@ -124,6 +189,44 @@ public: return detail::Vmap<T>::get(s); } + /** Create a "fast" accesor for a variable by name. + * + * Returns a special object which is valid for use in get_fast, which when + * used should be faster than calling get<T>(s) repeatedly. + * + * If the variable name is invalid or teh type doesn't match, an exception + * will be thrown, either VariableTypeMismatch or InvalidVariableName. + */ + template<typename T> + VariableAccesor<T> create_accesor(const std::string& s) { + BOOST_MPL_ASSERT(( boost::mpl::count<types, T> )); + if (!get<T>(s)) { + if (get<Value>(s)) { + throw VariableTypeMismatch(s); + } else { + throw InvalidVariableName(s); + } + } + return VariableAccesor<T>(s); + } + + /** Get a variable using a "fast" accesor. + * + * The accesor is required to have been created by this particular object, + * or a copy (either way), and the Variables object must have not had + * variables removed (adding new variables should not invalidate accesors). + * + * If the VariableAccesor comes form a different Variables object or there + * have been invalidating changes to this Variables object, behavior is not + * well defined. You might get a NULL, a different variable than you + * expected, or a segfault. + */ + template<typename T> + boost::shared_ptr<T> get_fast(const VariableAccesor<T>& a) const { + BOOST_MPL_ASSERT(( boost::mpl::count<types, T> )); + return get<T>(a.get_name()); + } + /** Get a variable, possibly default-creatng it. * * Returns the variable with the given name, or, if it does not exist, diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 167604e830421a6612c5260df0de5c9cc9739020..58d9f29202424e079da5495c35ea93dfc07a45c9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -11,10 +11,11 @@ add_executable(tests main.cpp position.cpp values.cpp + varaccess.cpp variables.cpp ) -target_link_libraries ( tests wccl ${Boost_LIBRARIES}) +target_link_libraries ( tests wccl ${Boost_LIBRARIES} antlr) include_directories(${Boost_INCLUDE_DIR}) link_directories(${Boost_LIBRARY_DIRS}) diff --git a/tests/varaccess.cpp b/tests/varaccess.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b10e781f599af0fbd22b55a7a006a0f2fa71e2d3 --- /dev/null +++ b/tests/varaccess.cpp @@ -0,0 +1,54 @@ +#include <boost/test/unit_test.hpp> +#include <boost/bind.hpp> + +#include <libwccl/variables.h> +#include <libpwrutils/foreach.h> + +#include <iostream> + +using namespace Wccl; + +BOOST_AUTO_TEST_SUITE(varaccess); + +struct VAfx +{ + Variables v; + VAfx() { + Variables v2; + v2.put("a", new Bool(true)); + v2.put("b", new Bool(true)); + v2.put("c", new Bool(true)); + v2.put("bb", new Bool(true)); + v2.put("aa", new Position(1)); + v2.put("aaa", new Position(2)); + v = v2; + } +}; + + +BOOST_FIXTURE_TEST_CASE(access, VAfx) +{ + std::vector<std::string> vnames; + vnames.push_back("a"); + vnames.push_back("b"); + vnames.push_back("c"); + vnames.push_back("bb"); + foreach (const std::string vn, vnames) { + VariableAccesor<Bool> a1 = v.create_accesor<Bool>(vn); + BOOST_CHECK(v.get_fast(a1) == v.get<Bool>(vn)); + v.set("a", Bool(false)); + BOOST_CHECK(v.get_fast(a1) == v.get<Bool>(vn)); + v.put("a", Bool(true)); + BOOST_CHECK(v.get_fast(a1) == v.get<Bool>(vn)); + } +} + +BOOST_FIXTURE_TEST_CASE(badaccess, VAfx) +{ + BOOST_CHECK_THROW(v.create_accesor<Bool>("asd"), InvalidVariableName); + BOOST_CHECK_THROW(v.create_accesor<Bool>("aaa"), VariableTypeMismatch); +} + + +BOOST_AUTO_TEST_SUITE_END() +