Skip to content
Snippets Groups Projects
Commit 903c5a61 authored by Jan Trávníček's avatar Jan Trávníček
Browse files

Parsers

parent b0124c41
No related branches found
No related tags found
No related merge requests found
...@@ -23,6 +23,7 @@ enum class FEATURES { ...@@ -23,6 +23,7 @@ enum class FEATURES {
GNF, GNF,
CSG, CSG,
NON_CONTRACTING_GRAMMAR, NON_CONTRACTING_GRAMMAR,
CONTEXT_PRESERVING_UNRESTRICTED_GRAMMAR,
UNRESTRICTED_GRAMMAR UNRESTRICTED_GRAMMAR
}; };
   
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
namespace grammar { namespace grammar {
   
Grammar GrammarFromXMLParser::parse(std::list<sax::Token> &input) const { Grammar GrammarFromXMLParser::parse(std::list<sax::Token> &input) const {
return parse(input, std::set<FEATURES>({FEATURES::GRAMMAR, FEATURES::LEFT_LG, FEATURES::LEFT_RG, FEATURES::RIGHT_LG, FEATURES::RIGHT_RG, FEATURES::LG, FEATURES::CFG, FEATURES::EPSILON_FREE_CFG, FEATURES::CNF, FEATURES::GNF, FEATURES::CSG, FEATURES::NON_CONTRACTING_GRAMMAR, FEATURES::UNRESTRICTED_GRAMMAR})); return parse(input, std::set<FEATURES>({FEATURES::GRAMMAR, FEATURES::LEFT_LG, FEATURES::LEFT_RG, FEATURES::RIGHT_LG, FEATURES::RIGHT_RG, FEATURES::LG, FEATURES::CFG, FEATURES::EPSILON_FREE_CFG, FEATURES::CNF, FEATURES::GNF, FEATURES::CSG, FEATURES::NON_CONTRACTING_GRAMMAR, FEATURES::CONTEXT_PRESERVING_UNRESTRICTED_GRAMMAR, FEATURES::UNRESTRICTED_GRAMMAR}));
} }
   
Grammar GrammarFromXMLParser::parse(std::list<sax::Token>& input, const std::set<FEATURES>& features) const { Grammar GrammarFromXMLParser::parse(std::list<sax::Token>& input, const std::set<FEATURES>& features) const {
...@@ -71,12 +71,217 @@ UnrestrictedGrammar GrammarFromXMLParser::parseUnrestrictedGrammar(std::list<sax ...@@ -71,12 +71,217 @@ UnrestrictedGrammar GrammarFromXMLParser::parseUnrestrictedGrammar(std::list<sax
grammar.setNonterminalAlphabet(nonterminalAlphabet); grammar.setNonterminalAlphabet(nonterminalAlphabet);
grammar.setTerminalAlphabet(terminalAlphabet); grammar.setTerminalAlphabet(terminalAlphabet);
   
parseRules<UnrestrictedGrammar>(input, grammar); parseRules(input, grammar);
   
popToken(input, sax::Token::TokenType::END_ELEMENT, "UnrestrictedGrammar"); popToken(input, sax::Token::TokenType::END_ELEMENT, "UnrestrictedGrammar");
return grammar; return grammar;
} }
   
ContextPreservingUnrestrictedGrammar GrammarFromXMLParser::parseContextPreservingUnrestrictedGrammar(std::list<sax::Token>& input) const {
popToken(input, sax::Token::TokenType::START_ELEMENT, "ContextPreservingUnrestrictedGrammar");
std::set<alphabet::Symbol> nonterminalAlphabet = parseNonterminalAlphabet(input);
std::set<alphabet::Symbol> terminalAlphabet = parseTerminalAlphabet(input);
alphabet::Symbol initialSymbol = parseInitialSymbol(input);
ContextPreservingUnrestrictedGrammar grammar(initialSymbol);
grammar.setNonterminalAlphabet(nonterminalAlphabet);
grammar.setTerminalAlphabet(terminalAlphabet);
parseRules(input, grammar);
popToken(input, sax::Token::TokenType::END_ELEMENT, "ContextPreservingUnrestrictedGrammar");
return grammar;
}
NonContractingGrammar GrammarFromXMLParser::parseNoncontractingGrammar(std::list<sax::Token>& input) const {
popToken(input, sax::Token::TokenType::START_ELEMENT, "NonContractingGrammar");
std::set<alphabet::Symbol> nonterminalAlphabet = parseNonterminalAlphabet(input);
std::set<alphabet::Symbol> terminalAlphabet = parseTerminalAlphabet(input);
alphabet::Symbol initialSymbol = parseInitialSymbol(input);
NonContractingGrammar grammar(initialSymbol);
grammar.setNonterminalAlphabet(nonterminalAlphabet);
grammar.setTerminalAlphabet(terminalAlphabet);
parseRules(input, grammar);
popToken(input, sax::Token::TokenType::END_ELEMENT, "NonContractingGrammar");
return grammar;
}
CSG GrammarFromXMLParser::parseCSG(std::list<sax::Token>& input) const {
popToken(input, sax::Token::TokenType::START_ELEMENT, "CSG");
std::set<alphabet::Symbol> nonterminalAlphabet = parseNonterminalAlphabet(input);
std::set<alphabet::Symbol> terminalAlphabet = parseTerminalAlphabet(input);
alphabet::Symbol initialSymbol = parseInitialSymbol(input);
CSG grammar(initialSymbol);
grammar.setNonterminalAlphabet(nonterminalAlphabet);
grammar.setTerminalAlphabet(terminalAlphabet);
parseRules(input, grammar);
popToken(input, sax::Token::TokenType::END_ELEMENT, "CSG");
return grammar;
}
GNF GrammarFromXMLParser::parseGNF(std::list<sax::Token>& input) const {
popToken(input, sax::Token::TokenType::START_ELEMENT, "GNF");
std::set<alphabet::Symbol> nonterminalAlphabet = parseNonterminalAlphabet(input);
std::set<alphabet::Symbol> terminalAlphabet = parseTerminalAlphabet(input);
alphabet::Symbol initialSymbol = parseInitialSymbol(input);
GNF grammar(initialSymbol);
grammar.setNonterminalAlphabet(nonterminalAlphabet);
grammar.setTerminalAlphabet(terminalAlphabet);
parseRules(input, grammar);
popToken(input, sax::Token::TokenType::END_ELEMENT, "GNF");
return grammar;
}
CNF GrammarFromXMLParser::parseCNF(std::list<sax::Token>& input) const {
popToken(input, sax::Token::TokenType::START_ELEMENT, "CNF");
std::set<alphabet::Symbol> nonterminalAlphabet = parseNonterminalAlphabet(input);
std::set<alphabet::Symbol> terminalAlphabet = parseTerminalAlphabet(input);
alphabet::Symbol initialSymbol = parseInitialSymbol(input);
CNF grammar(initialSymbol);
grammar.setNonterminalAlphabet(nonterminalAlphabet);
grammar.setTerminalAlphabet(terminalAlphabet);
parseRules(input, grammar);
popToken(input, sax::Token::TokenType::END_ELEMENT, "CNF");
return grammar;
}
EpsilonFreeCFG GrammarFromXMLParser::parseEpsilonFreeCFG(std::list<sax::Token>& input) const {
popToken(input, sax::Token::TokenType::START_ELEMENT, "EpsilonFreeCFG");
std::set<alphabet::Symbol> nonterminalAlphabet = parseNonterminalAlphabet(input);
std::set<alphabet::Symbol> terminalAlphabet = parseTerminalAlphabet(input);
alphabet::Symbol initialSymbol = parseInitialSymbol(input);
EpsilonFreeCFG grammar(initialSymbol);
grammar.setNonterminalAlphabet(nonterminalAlphabet);
grammar.setTerminalAlphabet(terminalAlphabet);
parseRules(input, grammar);
popToken(input, sax::Token::TokenType::END_ELEMENT, "EpsilonFreeCFG");
return grammar;
}
CFG GrammarFromXMLParser::parseCFG(std::list<sax::Token>& input) const {
popToken(input, sax::Token::TokenType::START_ELEMENT, "CFG");
std::set<alphabet::Symbol> nonterminalAlphabet = parseNonterminalAlphabet(input);
std::set<alphabet::Symbol> terminalAlphabet = parseTerminalAlphabet(input);
alphabet::Symbol initialSymbol = parseInitialSymbol(input);
CFG grammar(initialSymbol);
grammar.setNonterminalAlphabet(nonterminalAlphabet);
grammar.setTerminalAlphabet(terminalAlphabet);
parseRules(input, grammar);
popToken(input, sax::Token::TokenType::END_ELEMENT, "CFG");
return grammar;
}
LG GrammarFromXMLParser::parseLG(std::list<sax::Token>& input) const {
popToken(input, sax::Token::TokenType::START_ELEMENT, "LG");
std::set<alphabet::Symbol> nonterminalAlphabet = parseNonterminalAlphabet(input);
std::set<alphabet::Symbol> terminalAlphabet = parseTerminalAlphabet(input);
alphabet::Symbol initialSymbol = parseInitialSymbol(input);
LG grammar(initialSymbol);
grammar.setNonterminalAlphabet(nonterminalAlphabet);
grammar.setTerminalAlphabet(terminalAlphabet);
parseRules(input, grammar);
popToken(input, sax::Token::TokenType::END_ELEMENT, "LG");
return grammar;
}
RightRG GrammarFromXMLParser::parseRightRG(std::list<sax::Token>& input) const {
popToken(input, sax::Token::TokenType::START_ELEMENT, "RightRG");
std::set<alphabet::Symbol> nonterminalAlphabet = parseNonterminalAlphabet(input);
std::set<alphabet::Symbol> terminalAlphabet = parseTerminalAlphabet(input);
alphabet::Symbol initialSymbol = parseInitialSymbol(input);
RightRG grammar(initialSymbol);
grammar.setNonterminalAlphabet(nonterminalAlphabet);
grammar.setTerminalAlphabet(terminalAlphabet);
parseRules(input, grammar);
popToken(input, sax::Token::TokenType::END_ELEMENT, "RightRG");
return grammar;
}
RightLG GrammarFromXMLParser::parseRightLG(std::list<sax::Token>& input) const {
popToken(input, sax::Token::TokenType::START_ELEMENT, "LeftLG");
std::set<alphabet::Symbol> nonterminalAlphabet = parseNonterminalAlphabet(input);
std::set<alphabet::Symbol> terminalAlphabet = parseTerminalAlphabet(input);
alphabet::Symbol initialSymbol = parseInitialSymbol(input);
RightLG grammar(initialSymbol);
grammar.setNonterminalAlphabet(nonterminalAlphabet);
grammar.setTerminalAlphabet(terminalAlphabet);
parseRules(input, grammar);
popToken(input, sax::Token::TokenType::END_ELEMENT, "RightLG");
return grammar;
}
LeftRG GrammarFromXMLParser::parseLeftRG(std::list<sax::Token>& input) const {
popToken(input, sax::Token::TokenType::START_ELEMENT, "LeftRG");
std::set<alphabet::Symbol> nonterminalAlphabet = parseNonterminalAlphabet(input);
std::set<alphabet::Symbol> terminalAlphabet = parseTerminalAlphabet(input);
alphabet::Symbol initialSymbol = parseInitialSymbol(input);
LeftRG grammar(initialSymbol);
grammar.setNonterminalAlphabet(nonterminalAlphabet);
grammar.setTerminalAlphabet(terminalAlphabet);
parseRules(input, grammar);
popToken(input, sax::Token::TokenType::END_ELEMENT, "LeftRG");
return grammar;
}
LeftLG GrammarFromXMLParser::parseLeftLG(std::list<sax::Token>& input) const {
popToken(input, sax::Token::TokenType::START_ELEMENT, "LeftLG");
std::set<alphabet::Symbol> nonterminalAlphabet = parseNonterminalAlphabet(input);
std::set<alphabet::Symbol> terminalAlphabet = parseTerminalAlphabet(input);
alphabet::Symbol initialSymbol = parseInitialSymbol(input);
LeftLG grammar(initialSymbol);
grammar.setNonterminalAlphabet(nonterminalAlphabet);
grammar.setTerminalAlphabet(terminalAlphabet);
parseRules(input, grammar);
popToken(input, sax::Token::TokenType::END_ELEMENT, "LeftLG");
return grammar;
}
std::set<alphabet::Symbol> GrammarFromXMLParser::parseNonterminalAlphabet(std::list<sax::Token> &input) const { std::set<alphabet::Symbol> GrammarFromXMLParser::parseNonterminalAlphabet(std::list<sax::Token> &input) const {
std::set<alphabet::Symbol> inputSymbols; std::set<alphabet::Symbol> inputSymbols;
popToken(input, sax::Token::TokenType::START_ELEMENT, "nonterminalAlphabet"); popToken(input, sax::Token::TokenType::START_ELEMENT, "nonterminalAlphabet");
...@@ -115,12 +320,6 @@ void GrammarFromXMLParser::parseRules(std::list<sax::Token> &input, T& grammar) ...@@ -115,12 +320,6 @@ void GrammarFromXMLParser::parseRules(std::list<sax::Token> &input, T& grammar)
popToken(input, sax::Token::TokenType::END_ELEMENT, "rules"); popToken(input, sax::Token::TokenType::END_ELEMENT, "rules");
} }
   
void GrammarFromXMLParser::parseRule(std::list<sax::Token>& input, UnrestrictedGrammar& grammar) const {
std::vector<alphabet::Symbol> lhs = parseRuleLHS(input);
std::vector<alphabet::Symbol> rhs = parseRuleRHS(input);
grammar.addRule(lhs, rhs);
}
void GrammarFromXMLParser::parseRule(std::list<sax::Token>& input, UnknownGrammar& grammar) const { void GrammarFromXMLParser::parseRule(std::list<sax::Token>& input, UnknownGrammar& grammar) const {
UnknownRule rule; UnknownRule rule;
   
...@@ -137,6 +336,85 @@ void GrammarFromXMLParser::parseRule(std::list<sax::Token>& input, UnknownGramma ...@@ -137,6 +336,85 @@ void GrammarFromXMLParser::parseRule(std::list<sax::Token>& input, UnknownGramma
grammar.addRule(rule); grammar.addRule(rule);
} }
   
void GrammarFromXMLParser::parseRule(std::list<sax::Token>& input, UnrestrictedGrammar& grammar) const {
std::vector<alphabet::Symbol> lhs = parseRuleLHS(input);
std::vector<alphabet::Symbol> rhs = parseRuleRHS(input);
grammar.addRule(lhs, rhs);
}
void GrammarFromXMLParser::parseRule(std::list<sax::Token>& input, ContextPreservingUnrestrictedGrammar& grammar) const {
std::vector<alphabet::Symbol> lhs = parseRuleLHS(input);
std::vector<alphabet::Symbol> rhs = parseRuleRHS(input);
grammar.addRule(lhs, rhs);
}
void GrammarFromXMLParser::parseRule(std::list<sax::Token>& input, NonContractingGrammar& grammar) const {
std::vector<alphabet::Symbol> lhs = parseRuleLHS(input);
std::vector<alphabet::Symbol> rhs = parseRuleRHS(input);
grammar.addRule(lhs, rhs);
}
void GrammarFromXMLParser::parseRule(std::list<sax::Token>& input, CSG& grammar) const {
std::vector<alphabet::Symbol> lhs = parseRuleLHS(input);
std::vector<alphabet::Symbol> rhs = parseRuleRHS(input);
grammar.addRule(lhs, rhs);
}
void GrammarFromXMLParser::parseRule(std::list<sax::Token>& input, GNF& grammar) const {
alphabet::Symbol lhs = parseRuleSingleSymbolLHS(input);
std::vector<alphabet::Symbol> rhs = parseRuleRHS(input);
grammar.addRule(lhs, rhs);
}
void GrammarFromXMLParser::parseRule(std::list<sax::Token>& input, CNF& grammar) const {
alphabet::Symbol lhs = parseRuleSingleSymbolLHS(input);
std::vector<alphabet::Symbol> rhs = parseRuleRHS(input);
grammar.addRule(lhs, rhs);
}
void GrammarFromXMLParser::parseRule(std::list<sax::Token>& input, EpsilonFreeCFG& grammar) const {
alphabet::Symbol lhs = parseRuleSingleSymbolLHS(input);
std::vector<alphabet::Symbol> rhs = parseRuleRHS(input);
grammar.addRule(lhs, rhs);
}
void GrammarFromXMLParser::parseRule(std::list<sax::Token>& input, CFG& grammar) const {
alphabet::Symbol lhs = parseRuleSingleSymbolLHS(input);
std::vector<alphabet::Symbol> rhs = parseRuleRHS(input);
grammar.addRule(lhs, rhs);
}
void GrammarFromXMLParser::parseRule(std::list<sax::Token>& input, LG& grammar) const {
alphabet::Symbol lhs = parseRuleSingleSymbolLHS(input);
std::vector<alphabet::Symbol> rhs = parseRuleRHS(input);
grammar.addRule(lhs, rhs);
}
void GrammarFromXMLParser::parseRule(std::list<sax::Token>& input, RightRG& grammar) const {
alphabet::Symbol lhs = parseRuleSingleSymbolLHS(input);
std::vector<alphabet::Symbol> rhs = parseRuleRHS(input);
grammar.addRule(lhs, rhs);
}
void GrammarFromXMLParser::parseRule(std::list<sax::Token>& input, RightLG& grammar) const {
alphabet::Symbol lhs = parseRuleSingleSymbolLHS(input);
std::vector<alphabet::Symbol> rhs = parseRuleRHS(input);
grammar.addRule(lhs, rhs);
}
void GrammarFromXMLParser::parseRule(std::list<sax::Token>& input, LeftRG& grammar) const {
alphabet::Symbol lhs = parseRuleSingleSymbolLHS(input);
std::vector<alphabet::Symbol> rhs = parseRuleRHS(input);
grammar.addRule(lhs, rhs);
}
void GrammarFromXMLParser::parseRule(std::list<sax::Token>& input, LeftLG& grammar) const {
alphabet::Symbol lhs = parseRuleSingleSymbolLHS(input);
std::vector<alphabet::Symbol> rhs = parseRuleRHS(input);
grammar.addRule(lhs, rhs);
}
std::vector<alphabet::Symbol> GrammarFromXMLParser::parseRuleLHS(std::list<sax::Token>& input) const { std::vector<alphabet::Symbol> GrammarFromXMLParser::parseRuleLHS(std::list<sax::Token>& input) const {
std::vector<alphabet::Symbol> lhs; std::vector<alphabet::Symbol> lhs;
popToken(input, sax::Token::TokenType::START_ELEMENT, "lhs"); popToken(input, sax::Token::TokenType::START_ELEMENT, "lhs");
...@@ -147,6 +425,13 @@ std::vector<alphabet::Symbol> GrammarFromXMLParser::parseRuleLHS(std::list<sax:: ...@@ -147,6 +425,13 @@ std::vector<alphabet::Symbol> GrammarFromXMLParser::parseRuleLHS(std::list<sax::
return lhs; return lhs;
} }
   
alphabet::Symbol GrammarFromXMLParser::parseRuleSingleSymbolLHS(std::list<sax::Token>& input) const {
popToken(input, sax::Token::TokenType::START_ELEMENT, "lhs");
alphabet::Symbol lhs = alib::FromXMLParsers::symbolParser.parse(input);
popToken(input, sax::Token::TokenType::END_ELEMENT, "lhs");
return lhs;
}
std::vector<alphabet::Symbol> GrammarFromXMLParser::parseRuleRHS(std::list<sax::Token>& input) const { std::vector<alphabet::Symbol> GrammarFromXMLParser::parseRuleRHS(std::list<sax::Token>& input) const {
std::vector<alphabet::Symbol> rhs; std::vector<alphabet::Symbol> rhs;
popToken(input, sax::Token::TokenType::START_ELEMENT, "rhs"); popToken(input, sax::Token::TokenType::START_ELEMENT, "rhs");
......
...@@ -12,6 +12,18 @@ ...@@ -12,6 +12,18 @@
#include "Grammar.h" #include "Grammar.h"
#include "GrammarFeatures.h" #include "GrammarFeatures.h"
#include "UnknownGrammar.h" #include "UnknownGrammar.h"
#include "Regular/LeftLG.h"
#include "Regular/LeftRG.h"
#include "Regular/RightLG.h"
#include "Regular/RightRG.h"
#include "ContextFree/LG.h"
#include "ContextFree/CFG.h"
#include "ContextFree/EpsilonFreeCFG.h"
#include "ContextFree/CNF.h"
#include "ContextFree/GNF.h"
#include "ContextSensitive/CSG.h"
#include "ContextSensitive/NonContractingGrammar.h"
#include "Unrestricted/ContextPreservingUnrestrictedGrammar.h"
#include "Unrestricted/UnrestrictedGrammar.h" #include "Unrestricted/UnrestrictedGrammar.h"
   
#include <list> #include <list>
...@@ -31,12 +43,26 @@ protected: ...@@ -31,12 +43,26 @@ protected:
alphabet::Symbol parseInitialSymbol(std::list<sax::Token> &input) const; alphabet::Symbol parseInitialSymbol(std::list<sax::Token> &input) const;
   
std::vector<alphabet::Symbol> parseRuleLHS(std::list<sax::Token>& input) const; std::vector<alphabet::Symbol> parseRuleLHS(std::list<sax::Token>& input) const;
alphabet::Symbol parseRuleSingleSymbolLHS(std::list<sax::Token>& input) const;
std::vector<alphabet::Symbol> parseRuleRHS(std::list<sax::Token>& input) const; std::vector<alphabet::Symbol> parseRuleRHS(std::list<sax::Token>& input) const;
   
template<class T> template<class T>
void parseRules(std::list<sax::Token> &input, T& grammar) const; void parseRules(std::list<sax::Token> &input, T& grammar) const;
void parseRule(std::list<sax::Token>& input, UnknownGrammar& grammar) const; void parseRule(std::list<sax::Token>& input, UnknownGrammar& grammar) const;
void parseRule(std::list<sax::Token>& input, UnrestrictedGrammar& grammar) const; void parseRule(std::list<sax::Token>& input, UnrestrictedGrammar& grammar) const;
void parseRule(std::list<sax::Token>& input, ContextPreservingUnrestrictedGrammar& grammar) const;
void parseRule(std::list<sax::Token>& input, NonContractingGrammar& grammar) const;
void parseRule(std::list<sax::Token>& input, CSG& grammar) const;
void parseRule(std::list<sax::Token>& input, GNF& grammar) const;
void parseRule(std::list<sax::Token>& input, CNF& grammar) const;
void parseRule(std::list<sax::Token>& input, EpsilonFreeCFG& grammar) const;
void parseRule(std::list<sax::Token>& input, CFG& grammar) const;
void parseRule(std::list<sax::Token>& input, LG& grammar) const;
void parseRule(std::list<sax::Token>& input, RightRG& grammar) const;
void parseRule(std::list<sax::Token>& input, RightLG& grammar) const;
void parseRule(std::list<sax::Token>& input, LeftRG& grammar) const;
void parseRule(std::list<sax::Token>& input, LeftLG& grammar) const;
   
virtual Grammar parse(std::list<sax::Token>& input) const; virtual Grammar parse(std::list<sax::Token>& input) const;
virtual Grammar parse(std::list<sax::Token>& input, const std::set<FEATURES>& features) const; virtual Grammar parse(std::list<sax::Token>& input, const std::set<FEATURES>& features) const;
...@@ -45,6 +71,18 @@ public: ...@@ -45,6 +71,18 @@ public:
   
UnknownGrammar parseUnknownGrammar(std::list<sax::Token>& input) const; UnknownGrammar parseUnknownGrammar(std::list<sax::Token>& input) const;
UnrestrictedGrammar parseUnrestrictedGrammar(std::list<sax::Token>& input) const; UnrestrictedGrammar parseUnrestrictedGrammar(std::list<sax::Token>& input) const;
ContextPreservingUnrestrictedGrammar parseContextPreservingUnrestrictedGrammar(std::list<sax::Token>& input) const;
NonContractingGrammar parseNoncontractingGrammar(std::list<sax::Token>& input) const;
CSG parseCSG(std::list<sax::Token>& input) const;
GNF parseGNF(std::list<sax::Token>& input) const;
CNF parseCNF(std::list<sax::Token>& input) const;
EpsilonFreeCFG parseEpsilonFreeCFG(std::list<sax::Token>& input) const;
CFG parseCFG(std::list<sax::Token>& input) const;
LG parseLG(std::list<sax::Token>& input) const;
RightRG parseRightRG(std::list<sax::Token>& input) const;
RightLG parseRightLG(std::list<sax::Token>& input) const;
LeftRG parseLeftRG(std::list<sax::Token>& input) const;
LeftLG parseLeftLG(std::list<sax::Token>& input) const;
}; };
   
} /* namespace grammar */ } /* namespace grammar */
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment