├── .gitignore ├── .vscode └── settings.json ├── CMakeLists.txt ├── LICENSE ├── README.md ├── examples ├── BatchTesting │ ├── README.txt │ ├── test.sh │ └── test_grammars │ │ ├── buckey │ │ └── sphinx_mode.gram │ │ └── public_rules │ │ ├── hello.gram │ │ ├── helloworld.gram │ │ └── lots_of_rules.gram ├── ExpansionReplacementTest.cpp ├── ExpansionReplacementTest │ ├── ExpansionReplacementTest.cpp │ └── Makefile ├── ExpansionTypeTest.cpp ├── ExpansionTypeTest │ ├── ExpansionTypeTest.cpp │ └── Makefile ├── Matching.cpp ├── Matching │ ├── Makefile │ └── Matching.cpp ├── ParseAndOutputJSGF.cpp └── ParseAndOutputJSGF │ ├── Makefile │ ├── ParseAndOutputJSGF.cpp │ └── hello.gram ├── include └── jsgfkitxx │ ├── AlternativeSet.hpp │ ├── Expansion.hpp │ ├── Grammar.hpp │ ├── KleeneStar.hpp │ ├── MatchInfo.hpp │ ├── MatchResult.hpp │ ├── MatchTracker.hpp │ ├── OptionalGrouping.hpp │ ├── PlusOperator.hpp │ ├── RequiredGrouping.hpp │ ├── Rule.hpp │ ├── RuleReference.hpp │ ├── Sequence.hpp │ ├── Tag.hpp │ ├── Token.hpp │ └── UnparsedSection.hpp ├── jsgfkitxx.pc.in ├── main.cpp ├── src ├── AlternativeSet.cpp ├── Expansion.cpp ├── Grammar.cpp ├── KleeneStar.cpp ├── MatchInfo.cpp ├── MatchResult.cpp ├── MatchTracker.cpp ├── OptionalGrouping.cpp ├── PlusOperator.cpp ├── RequiredGrouping.cpp ├── Rule.cpp ├── RuleReference.cpp ├── Sequence.cpp ├── Tag.cpp ├── Token.cpp └── UnparsedSection.cpp └── test ├── BatchTest ├── grammars │ ├── buckey │ │ └── sphinx_mode.gram │ ├── optional_testing │ │ ├── bare_sequence.gram │ │ ├── lone_kleene.gram │ │ ├── lone_plus.gram │ │ ├── optional_alternate_set.gram │ │ ├── optional_parenthesis.gram │ │ ├── optional_parenthesis2.gram │ │ ├── optional_sequence.gram │ │ ├── required_grouping.gram │ │ ├── required_set.gram │ │ ├── sequence_plus.gram │ │ └── sequence_with_tag.gram │ └── public_rules │ │ ├── hello.gram │ │ ├── helloworld.gram │ │ ├── lots_of_rules.gram │ │ ├── optional_at_beginning.gram │ │ └── optional_at_end.gram ├── optional_test.csv ├── rules_test.csv └── test.sh ├── CMakeLists.txt ├── ExpansionTypeTest.cpp ├── IsOptionalTest.cpp ├── MatchingTest.cpp └── ParseAndOutputJSGF.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # C::B Files 35 | *.cbp 36 | *.layout 37 | *.depend 38 | 39 | # Documentaion 40 | docs/* 41 | doc/* 42 | 43 | build/ -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "C_Cpp.default.cppStandard": "c++11", 3 | "C_Cpp.default.includePath": [ 4 | "${workspaceFolder}/include" 5 | ], 6 | "files.associations": { 7 | "iostream": "cpp", 8 | "string": "cpp", 9 | "array": "cpp", 10 | "atomic": "cpp", 11 | "bit": "cpp", 12 | "*.tcc": "cpp", 13 | "bitset": "cpp", 14 | "cctype": "cpp", 15 | "chrono": "cpp", 16 | "clocale": "cpp", 17 | "cmath": "cpp", 18 | "condition_variable": "cpp", 19 | "cstdarg": "cpp", 20 | "cstddef": "cpp", 21 | "cstdint": "cpp", 22 | "cstdio": "cpp", 23 | "cstdlib": "cpp", 24 | "cstring": "cpp", 25 | "ctime": "cpp", 26 | "cwchar": "cpp", 27 | "cwctype": "cpp", 28 | "deque": "cpp", 29 | "map": "cpp", 30 | "unordered_map": "cpp", 31 | "vector": "cpp", 32 | "exception": "cpp", 33 | "algorithm": "cpp", 34 | "functional": "cpp", 35 | "iterator": "cpp", 36 | "memory": "cpp", 37 | "memory_resource": "cpp", 38 | "numeric": "cpp", 39 | "optional": "cpp", 40 | "random": "cpp", 41 | "ratio": "cpp", 42 | "regex": "cpp", 43 | "string_view": "cpp", 44 | "system_error": "cpp", 45 | "tuple": "cpp", 46 | "type_traits": "cpp", 47 | "utility": "cpp", 48 | "fstream": "cpp", 49 | "initializer_list": "cpp", 50 | "iosfwd": "cpp", 51 | "istream": "cpp", 52 | "limits": "cpp", 53 | "new": "cpp", 54 | "ostream": "cpp", 55 | "shared_mutex": "cpp", 56 | "sstream": "cpp", 57 | "stdexcept": "cpp", 58 | "streambuf": "cpp", 59 | "typeinfo": "cpp" 60 | } 61 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project(jsgfkitxx 4 | VERSION 1.1 5 | LANGUAGES CXX 6 | ) 7 | 8 | set(JSGF_KIT_XX_SOURCES 9 | src/Grammar.cpp 10 | src/Expansion.cpp 11 | src/Rule.cpp 12 | src/MatchInfo.cpp 13 | src/MatchResult.cpp 14 | src/Tag.cpp 15 | src/UnparsedSection.cpp 16 | src/KleeneStar.cpp 17 | src/PlusOperator.cpp 18 | src/OptionalGrouping.cpp 19 | src/RequiredGrouping.cpp 20 | src/Sequence.cpp 21 | src/AlternativeSet.cpp 22 | src/Token.cpp 23 | src/RuleReference.cpp 24 | ) 25 | 26 | add_library(jsgfkitxx STATIC ${JSGF_KIT_XX_SOURCES}) 27 | target_include_directories(jsgfkitxx PUBLIC ${PROJECT_SOURCE_DIR}/include) 28 | 29 | include(GNUInstallDirs) 30 | 31 | #Set variables for the .pc file 32 | set(prefix "${CMAKE_INSTALL_PREFIX}") 33 | set(exec_prefix "${CMAKE_INSTALL_PREFIX}") 34 | set(libdir "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") 35 | set(includedir "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}") 36 | configure_file(jsgfkitxx.pc.in jsgfkitxx.pc @ONLY) 37 | 38 | install(TARGETS jsgfkitxx 39 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 40 | ) 41 | 42 | install(FILES ${KIT_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/jsgfkitxx) 43 | 44 | install(FILES jsgfkitxx.pc DESTINATION ${CMAKE_INSTALL_DATAROOT_DIR}/pkgconfig) 45 | 46 | # Build Tests 47 | enable_testing () 48 | add_subdirectory(test) 49 | add_test (NAME ParseTest COMMAND test/parsetest) 50 | add_test (NAME BatchTest COMMAND test/BatchTest/test.sh) 51 | add_test (NAME ExpansionTypeTest COMMAND test/expansiontypetest) 52 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSGFKit_Plus_Plus 2 | A static cross-platform library for parsing, generating, manipulating, and matching strings against JSGF grammars. 3 | This is a C++ port of the Java JSGFKit with a few additional features. 4 | 5 | ## Building 6 | Run the below commands to compile with CMake: 7 | ```bash 8 | cmake CMakeLists.txt -Bbuild 9 | cd build 10 | make 11 | ``` 12 | 13 | ## Linking and Including 14 | To include and link against the JSGF Kit++ static library, use `pkg-config`: 15 | ```bash 16 | pkg-config --libs --cflags jsgfkit 17 | ``` 18 | 19 | ## Documentation 20 | A hosted version of the Doxygen generated docs can be found here: http://personal.psu.edu/~txs5620/jsgfkitxx/ 21 | 22 | Doxygen documentation comments are in the code, to generate HTML documentation files from it, run `doxygen Doxyfile` and the html docs will be outputted into the `docs` subdirectory. 23 | 24 | ## Example programs 25 | A few example programs come with this library and can be found in the `examples` subdirectory. 26 | To build an example program, `cd` into its directory and simply run `make`. 27 | If compilation complains about missing headers or undefined references, you will probably need to adjust the `JSGF_KIT_LD` and `JSGF_KIT_CFLAGS` variables in the `Makefile` to point to the correct library path and include directories (just run `pkg-config --cflags jsgfkit` and `pkg-config --libs jsgfkit` and copy the output in). 28 | 29 | ## Purpose 30 | The original purpose of this program was to manipulate JSGF grammars for use with the CMU pocketsphinx toolkit, which has a built in JSGF parser that transforms JSGF grammars into finite state grammars (FSGs), but in the process the Tag unary operator is lost. These tags provide valuable context information when matching speaker/user input against command grammars, and this library solves the issue by allowing the program to match the resulting string from cmu sphinx against a grammar and returning the matching tags. This library also provides methods of easily manipulating grammars and dynamically generating them. 31 | -------------------------------------------------------------------------------- /examples/BatchTesting/README.txt: -------------------------------------------------------------------------------- 1 | Batch Testing Program 2 | 3 | This bash shell script runs dozens of JSGF grammars through the example program ParseAndOutputJSGF. 4 | It requires you to build the example program and copy the resulting binary into this directory. -------------------------------------------------------------------------------- /examples/BatchTesting/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "JSGF Kit++ Batch Grammar Tester" 4 | if [ ! -f ParseAndOutputJSGF ]; then 5 | echo "ERROR: Please build and copy the ParseAndOutputJSGF example program into this directory!" 6 | exit -2; 7 | fi 8 | 9 | for g in test_grammars/*/*; do 10 | ./ParseAndOutputJSGF $g &> grammar_test.log 11 | if [ ! $? -eq 0 ]; then 12 | echo "Test failed on grammar: $g ! Logged to grammar_test.log" 13 | exit -1; 14 | fi 15 | done 16 | 17 | echo "Tests passed!" -------------------------------------------------------------------------------- /examples/BatchTesting/test_grammars/buckey/sphinx_mode.gram: -------------------------------------------------------------------------------- 1 | #JSGF v1.0;\ 2 | grammar sphinx-mode;\ 3 | public = {pause} | {resume} | {push-to-talk} | {toggle};\ 4 | = pause speech recognition;\ 5 | = resume speech recognition;\ 6 | = toggle speech recognition;\ 7 | = ((enable | enter | begin | start) {enable} | (disable | exit | end | stop) {disable}) push to talk [mode]; -------------------------------------------------------------------------------- /examples/BatchTesting/test_grammars/public_rules/hello.gram: -------------------------------------------------------------------------------- 1 | #JSGFv1.0; 2 | grammar testing2; 3 | public = rarg | blarg; -------------------------------------------------------------------------------- /examples/BatchTesting/test_grammars/public_rules/helloworld.gram: -------------------------------------------------------------------------------- 1 | #JSGFv1.0; 2 | grammar helloworld1; 3 | public = hello ; 4 | = world | earth | Earth; -------------------------------------------------------------------------------- /examples/BatchTesting/test_grammars/public_rules/lots_of_rules.gram: -------------------------------------------------------------------------------- 1 | #JSGFv1.0; 2 | grammar testing2; 3 | public = | | | | hello [world]; 4 | public = garg +; 5 | = blarg* ; 6 | = hello | hi [there how are you?]; 7 | = let\'S make sure that eSc4p1ng & token (parsing works); -------------------------------------------------------------------------------- /examples/ExpansionReplacementTest.cpp: -------------------------------------------------------------------------------- 1 | #include "Grammar.hpp" 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | /** 8 | * \file 9 | * An example that shows how to match test string against Rules and extract the tags that the input matched. 10 | * Functions used: Grammar::matchesRule(), Grammar::getMatchingTags() 11 | * 12 | */ 13 | 14 | int main() { 15 | // A string representing the grammar that we will be using. 16 | string s = "grammar matching-test;\ 17 | public = ;\ 18 | = face {face_tag} | move {move_tag} | fire laser {fire_tag} | quit {quit_tag};\ 19 | = left | right | up | down;\ 20 | "; 21 | 22 | cout << "Parsing example Grammar..." << endl; 23 | Grammar g; 24 | Grammar::parseGrammarFromString(s, g); 25 | 26 | cout << "Parsed:" << endl; 27 | cout << g.getText() << endl; // Grammar::getText returns a JSGF text to define the grammar 28 | 29 | cout << "Replacing the 'laser' token with fire (laser | water gun | engines)..." << endl; 30 | 31 | shared_ptr replacement(new AlternativeSet()); 32 | replacement->addChild(shared_ptr(new Token("laser"))); 33 | replacement->addChild(shared_ptr(new Token("water gun"))); 34 | replacement->addChild(shared_ptr(new Token("engines"))); 35 | 36 | g.getRule("action")->getRuleExpansion()->getChild(2)->getChild(1)->replaceChild(replacement, 1); 37 | 38 | cout << endl << "New grammar:" << endl; 39 | cout << g.getText() << endl; 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /examples/ExpansionReplacementTest/ExpansionReplacementTest.cpp: -------------------------------------------------------------------------------- 1 | #include "Grammar.hpp" 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | /** 8 | * \file 9 | * An example that shows how to match test string against Rules and extract the tags that the input matched. 10 | * Functions used: Grammar::matchesRule(), Grammar::getMatchingTags() 11 | * 12 | */ 13 | 14 | int main() { 15 | // A string representing the grammar that we will be using. 16 | string s = "grammar matching-test;\ 17 | public = ;\ 18 | = face {face_tag} | move {move_tag} | fire laser {fire_tag} | quit {quit_tag};\ 19 | = left | right | up | down;\ 20 | "; 21 | 22 | cout << "Parsing example Grammar..." << endl; 23 | Grammar g; 24 | Grammar::parseGrammarFromString(s, g); 25 | 26 | cout << "Parsed:" << endl; 27 | cout << g.getText() << endl; // Grammar::getText returns a JSGF text to define the grammar 28 | 29 | cout << "Replacing the 'laser' token with fire (laser | water gun | engines)..." << endl; 30 | 31 | shared_ptr replacement(new AlternativeSet()); 32 | replacement->addChild(shared_ptr(new Token("laser"))); 33 | replacement->addChild(shared_ptr(new Token("water gun"))); 34 | replacement->addChild(shared_ptr(new Token("engines"))); 35 | 36 | g.getRule("action")->getRuleExpansion()->getChild(2)->getChild(1)->replaceChild(replacement, 1); 37 | 38 | cout << endl << "New grammar:" << endl; 39 | cout << g.getText() << endl; 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /examples/ExpansionReplacementTest/Makefile: -------------------------------------------------------------------------------- 1 | #Makefile for the example ExpansionReplacementTest program. 2 | 3 | CC=gcc 4 | CXX=g++ 5 | CXXFLAGS=-Wall 6 | JSGF_KIT_CFLAGS=-I/usr/local/include/jsgfkit 7 | JSGF_KIT_LD=-L/usr/local/lib -ljsgfkit 8 | 9 | ExpansionReplacementTest: ExpansionReplacementTest.cpp 10 | $(CXX) $(CXXFLAGS) $(JSGF_KIT_CFLAGS) $< $(JSGF_KIT_LD) -o $@ 11 | 12 | clean: 13 | rm ExpansionReplacementTest 14 | 15 | -------------------------------------------------------------------------------- /examples/ExpansionTypeTest.cpp: -------------------------------------------------------------------------------- 1 | #include "Grammar.hpp" 2 | #include "Expansion.hpp" 3 | 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | void testCall(Expansion * e) { 10 | cout << e->getText() << endl; 11 | if(e->getType() == TOKEN) { 12 | cout << "TOEKN" << endl; 13 | } 14 | } 15 | 16 | int main() { 17 | cout << "sa" << endl; 18 | Grammar g; 19 | Grammar::parseGrammarFromString("grammar test;\ 20 | public = one;", g); 21 | cout << "s" << endl; 22 | g.walkGrammar(testCall); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /examples/ExpansionTypeTest/ExpansionTypeTest.cpp: -------------------------------------------------------------------------------- 1 | #include "Grammar.hpp" 2 | #include "Expansion.hpp" 3 | 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | void testCall(Expansion * e) { 10 | cout << Grammar::printExpansionType(e) << ":" << e->getText() << endl; 11 | } 12 | 13 | int main() { 14 | Grammar g; 15 | Grammar::parseGrammarFromString("grammar test;\ 16 | public = one;", g); 17 | g.walkGrammar(testCall); 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /examples/ExpansionTypeTest/Makefile: -------------------------------------------------------------------------------- 1 | #Makefile for the example ExpansionTypeTest program. 2 | 3 | CC=gcc 4 | CXX=g++ 5 | CXXFLAGS=-Wall 6 | JSGF_KIT_CFLAGS=-I/usr/local/include/jsgfkit 7 | JSGF_KIT_LD=-L/usr/local/lib -ljsgfkit 8 | 9 | ExpansionTypeTest: ExpansionTypeTest.cpp 10 | $(CXX) $(CXXFLAGS) $(JSGF_KIT_CFLAGS) $< $(JSGF_KIT_LD) -o $@ 11 | 12 | clean: 13 | rm ExpansionTypeTest -------------------------------------------------------------------------------- /examples/Matching.cpp: -------------------------------------------------------------------------------- 1 | #include "Grammar.hpp" 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | /** 8 | * \file 9 | * An example that shows how to match test string against Rules and extract the tags that the input matched. 10 | * Functions used: Grammar::matchesRule(), Grammar::getMatchingTags() 11 | * 12 | */ 13 | 14 | int main() { 15 | // A string representing the grammar that we will be using. 16 | string s = "grammar matching-test;\ 17 | public = ;\ 18 | = face {face_tag} | move {move_tag} | fire laser {fire_tag} | quit {quit_tag};\ 19 | = left | right | up | down;\ 20 | "; 21 | 22 | cout << "Parsing example Grammar..." << endl; 23 | Grammar g; 24 | Grammar::parseGrammarFromString(s, g); 25 | 26 | cout << "Parsed:" << endl; 27 | cout << g.getText() << endl; // Grammar::getText returns a JSGF text to define the grammar 28 | bool done = false; 29 | while(!done) { 30 | cout << "Type in a query and see if it matches one of the rules:"; 31 | string response; 32 | getline(cin, response); // Get an input test string from the user 33 | 34 | MatchResult result = g.match(response); // Match the test string against the command 35 | 36 | if(!result.matches) { // If no match 37 | cout << "Your response doesn't match the Rule!" << endl; 38 | } 39 | else { 40 | cout << "Your response matched the <" << result.getMatchingRule()->getRuleName() << "> Rule!" << endl; 41 | cout << "Matching tags: "; 42 | vector tags = result.getMatchingTags(); // Get a vector/vector of the tags that this test string matched 43 | for(string s : tags) { // Print them out 44 | cout << s << ", "; 45 | 46 | if(s == "quit_tag") { 47 | done = true; 48 | break; 49 | } 50 | } 51 | } 52 | } 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /examples/Matching/Makefile: -------------------------------------------------------------------------------- 1 | #Makefile for the example Matching test program. 2 | 3 | CC=gcc 4 | CXX=g++ 5 | CXXFLAGS=-Wall 6 | JSGF_KIT_CFLAGS=-I/usr/local/include/jsgfkit 7 | JSGF_KIT_LD=-L/usr/local/lib -ljsgfkit 8 | 9 | Matching: Matching.cpp 10 | $(CXX) $(CXXFLAGS) $(JSGF_KIT_CFLAGS) $< $(JSGF_KIT_LD) -o $@ 11 | 12 | clean: 13 | rm Matching 14 | 15 | -------------------------------------------------------------------------------- /examples/Matching/Matching.cpp: -------------------------------------------------------------------------------- 1 | #include "Grammar.hpp" 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | /** 8 | * \file 9 | * An example that shows how to match test string against Rules and extract the tags that the input matched. 10 | * Functions used: Grammar::matchesRule(), Grammar::getMatchingTags() 11 | * 12 | */ 13 | 14 | int main() { 15 | // A string representing the grammar that we will be using. 16 | string s = "grammar matching-test;\ 17 | public = ;\ 18 | = face {face_tag} | move {move_tag} | fire laser {fire_tag} | quit {quit_tag};\ 19 | = left | right | up | down;\ 20 | "; 21 | 22 | cout << "Parsing example Grammar..." << endl; 23 | Grammar g; 24 | Grammar::parseGrammarFromString(s, g); 25 | 26 | cout << "Parsed:" << endl; 27 | cout << g.getText() << endl; // Grammar::getText returns a JSGF text to define the grammar 28 | bool done = false; 29 | while(!done) { 30 | cout << "Type in a query and see if it matches one of the rules:"; 31 | string response; 32 | getline(cin, response); // Get an input test string from the user 33 | 34 | MatchResult result = g.match(response); // Match the test string against the command 35 | 36 | if(!result.matches) { // If no match 37 | cout << "Your response doesn't match the Rule!" << endl; 38 | } 39 | else { 40 | cout << "Your response matched the <" << result.getMatchingRule()->getRuleName() << "> Rule!" << endl; 41 | cout << "Matching tags: "; 42 | vector tags = result.getMatchingTags(); // Get a vector/vector of the tags that this test string matched 43 | for(string s : tags) { // Print them out 44 | cout << s << ", "; 45 | 46 | if(s == "quit_tag") { 47 | cout << endl; 48 | done = true; 49 | break; 50 | } 51 | } 52 | cout << endl; 53 | } 54 | } 55 | 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /examples/ParseAndOutputJSGF.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Grammar.hpp" 3 | 4 | using namespace std; 5 | 6 | /** 7 | * \file ParseAndOutputJSGF.cpp 8 | * A simple program that shows how to parse a JSGF string to create a new Grammar object and then generate JSGF text using that Grammar object. 9 | */ 10 | 11 | int main() 12 | { 13 | // string that we will be parsing to create a JSGF Grammar object 14 | string s = "\ 15 | grammar test-gram;\ 16 | public = hello {greeting} {hi} there; \ 17 | = [blarg | (rarg | hraaanngggg)];\ 18 | = hi {greeting} how are (you {you} | they {others}) today;\ 19 | "; 20 | //Parsing the string to create a Grammar object 21 | Grammar g; 22 | Grammar::parseGrammarFromString(s, g); 23 | 24 | //Generate the JSGF definitiopn for the public command Rule 25 | cout << "Generating Rule:" << endl; 26 | cout << g.getRule("command")->getRuleString() << endl << endl;; 27 | 28 | //Generate a text version of our Grammar object 29 | cout << "PARSED GRAMMAR:" << endl; 30 | cout << g.getText() << endl << endl; 31 | 32 | cout << "Test Done" << endl; 33 | cout << "Press enter to exit" << endl; 34 | getline(cin, s); 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /examples/ParseAndOutputJSGF/Makefile: -------------------------------------------------------------------------------- 1 | #Makefile for the example ParseAndOutputJSGF program. 2 | 3 | CC=gcc 4 | CXX=g++ 5 | CXXFLAGS=-Wall 6 | JSGF_KIT_CFLAGS=-I/usr/local/include/jsgfkit 7 | JSGF_KIT_LD=-L/usr/local/lib -ljsgfkit 8 | 9 | ParseAndOutputJSGF: ParseAndOutputJSGF.cpp 10 | $(CXX) $(CXXFLAGS) $(JSGF_KIT_CFLAGS) $< $(JSGF_KIT_LD) -o $@ 11 | 12 | clean: 13 | rm ParseAndOutputJSGF 14 | 15 | -------------------------------------------------------------------------------- /examples/ParseAndOutputJSGF/ParseAndOutputJSGF.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Grammar.hpp" 3 | 4 | using namespace std; 5 | 6 | /** 7 | * \file ParseAndOutputJSGF.cpp 8 | * A simple program that shows how to parse a JSGF string to create a new Grammar object and then generate JSGF text using that Grammar object. 9 | * You can either use it with no command line options and it will use a built in sample grammar. 10 | * It can also open up a file given via the command line, parse it, and echo it. 11 | */ 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | // string that we will be parsing to create a JSGF Grammar object 16 | string s = "\ 17 | grammar test-gram;\ 18 | public = hello {greeting} {hi} there; \ 19 | = [blarg | (rarg | hraaanngggg)];\ 20 | = hi {greeting} how are (you {you} | they {others}) today;\ 21 | "; 22 | Grammar g; 23 | if(argc > 1) { 24 | std::ifstream inputFile; 25 | inputFile.open(argv[1]); 26 | if(inputFile.good()) { 27 | Grammar::parseGrammar(inputFile, g); 28 | } 29 | } 30 | else { 31 | //Parsing the string to create a Grammar object 32 | Grammar::parseGrammarFromString(s, g); 33 | } 34 | 35 | //Generate a text version of our Grammar object 36 | cout << "PARSED GRAMMAR:" << endl; 37 | cout << g.getText() << endl << endl; 38 | 39 | cout << "Test Done" << endl; 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /examples/ParseAndOutputJSGF/hello.gram: -------------------------------------------------------------------------------- 1 | #JSGFv1.0; 2 | grammar testing2; 3 | public = rarg | blarg; -------------------------------------------------------------------------------- /include/jsgfkitxx/AlternativeSet.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ALTERNATIVESET_H 2 | #define ALTERNATIVESET_H 3 | 4 | #include "jsgfkitxx/Expansion.hpp" 5 | #include "jsgfkitxx/Token.hpp" 6 | #include 7 | #include 8 | #include 9 | 10 | class AlternativeSet : public Expansion 11 | { 12 | private: 13 | std::vector weights; //TODO: Implement this 14 | std::vector> expansions; 15 | 16 | public: 17 | /** Default constructor */ 18 | AlternativeSet(); 19 | /** Default destructor */ 20 | ~AlternativeSet(); 21 | 22 | Expansion * clone(); 23 | ExpansionType getType() const override; 24 | bool hasChild() const; 25 | bool isOptional() const; 26 | std::shared_ptr getChild(const unsigned int index = 0) const; 27 | std::vector> getChildren() const; 28 | unsigned int childCount() const; 29 | 30 | void replaceChild(std::shared_ptr newChild, const unsigned long index = 0); 31 | void removeChild(Expansion & e); 32 | void removeChild(const unsigned int i); 33 | void addChild(std::shared_ptr e); 34 | void addChild(std::string token); 35 | std::string getText() const; 36 | }; 37 | 38 | #endif // ALTERNATIVESET_H 39 | -------------------------------------------------------------------------------- /include/jsgfkitxx/Expansion.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #ifndef EXPANSION_H 6 | #define EXPANSION_H 7 | 8 | enum ExpansionType { 9 | UNPARSED_SECTION, EXPANSION, ALTERNATE_SET, SEQUENCE, RULE_REFERENCE, TAG, TOKEN, KLEENE_STAR, PLUS_OPERATOR, REQUIRED_GROUPING, OPTIONAL_GROUPING 10 | }; 11 | 12 | /** 13 | * virtual class that serves as a container to be extended by all other Expansions. 14 | */ 15 | class Expansion 16 | { 17 | public: 18 | /** Default constructor */ 19 | Expansion(); 20 | virtual ExpansionType getType() const { return EXPANSION; } 21 | virtual ~Expansion(); 22 | 23 | /// Returns the JSGF text representing this Expansion and its children. 24 | virtual std::string getText() const { return "EXPANSIONDEFAULT"; }; 25 | 26 | /// Returns true if the Expansion has a child Expansion. Tokens will always return false. 27 | virtual bool hasChild() const { return false; }; 28 | 29 | /// Returns true if the Expansion and/or its children are completely optional 30 | virtual bool isOptional() const { return false; }; 31 | 32 | /// Returns the number of child expansions this Expansion has. Tokens will always return false. 33 | virtual unsigned int childCount() const { return 0; }; 34 | 35 | /// Returns the child Expansion. Returns nullptr if there is no child Expansion. If there are multiple children, returns the child Expansions at the specified index. Default is index 0. 36 | virtual std::shared_ptr getChild(const unsigned int index = 0) const { return nullptr; }; 37 | 38 | /// Returns a pointer to a deep clone of this Expansion. The clone() method of all children Expansions are also called when cloning this Expansion. 39 | virtual Expansion * clone() { return nullptr; }; 40 | 41 | /// Replaces the child at the specified index with the given Expansion 42 | virtual void replaceChild(std::shared_ptr newChild, const unsigned long index = 0) { }; 43 | }; 44 | 45 | #define EXPANSION_IS_TOKEN(e) ((e)->getType() == TOKEN) 46 | #define EXPANSION_IS_SEQUENCE(e) ((e)->getType() == SEQUENCE) 47 | #define EXPANSION_IS_ALTERNATE_SET(e) ((e)->getType() == ALTERNATE_SET) 48 | #define EXPANSION_IS_PLUS_OPERATOR(e) ((e)->getType() == PLUS_OPERATOR) 49 | #define EXPANSION_IS_KLEENE_STAR(e) ((e)->getType() == KLEENE_STAR) 50 | #define EXPANSION_IS_RULE_REFERENCE(e) ((e)->getType() == RULE_REFERENCE) 51 | #define EXPANSION_IS_TAG(e) ((e)->getType() == TAG) 52 | #define EXPANSION_IS_REQUIRED_GROUPING(e) ((e)->getType() == REQUIRED_GROUPING) 53 | #define EXPANSION_IS_OPTIONAL_GROUPING(e) ((e)->getType() == OPTIONAL_GROUPING) 54 | #define EXPANSION_IS_UNPARSED_SECTION(e) ((e)->getType() == UNPARSED_SECTION) 55 | 56 | #endif // EXPANSION_H 57 | -------------------------------------------------------------------------------- /include/jsgfkitxx/Grammar.hpp: -------------------------------------------------------------------------------- 1 | #ifndef GRAMMAR_H 2 | #define GRAMMAR_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "Expansion.hpp" 15 | #include "Rule.hpp" 16 | #include "UnparsedSection.hpp" 17 | #include "Sequence.hpp" 18 | #include "Token.hpp" 19 | #include "KleeneStar.hpp" 20 | #include "PlusOperator.hpp" 21 | #include "MatchInfo.hpp" 22 | #include "OptionalGrouping.hpp" 23 | #include "RequiredGrouping.hpp" 24 | #include "Tag.hpp" 25 | #include "RuleReference.hpp" 26 | #include "AlternativeSet.hpp" 27 | #include "MatchInfo.hpp" 28 | #include "MatchResult.hpp" 29 | 30 | typedef std::vector> MatchVector; 31 | 32 | /** 33 | * \mainpage 34 | * JSGF Kit++ is a C++ port of the Java JSGF Kit library. 35 | * JSGF Kit++ can parse JSGF grammars to produce Grammar objects. 36 | * These Grammar objects can be manipulated by adding or removing Rule objects and manipulating the rule Expansion for each Rule. 37 | * Test/input strings can be tested/matched against Rules inside of grammars, and JSGF Kit++ provides functions to extract the tags that the test string matches. 38 | * Please see the examples folder for example programs for parsing, manipulating, and testing strings against Grammar objects using JSGF Kit Plus Plus. 39 | * 40 | */ 41 | 42 | class Grammar 43 | { 44 | private: 45 | 46 | 47 | protected: 48 | // Member data 49 | std::string name; 50 | std::vector> rules; 51 | 52 | //Matching 53 | MatchVector getMatchingExpansions(std::shared_ptr e, std::string words[], unsigned int wordCount, unsigned int wordPosition) const; 54 | 55 | // Parsing functions 56 | static Expansion * parseAlternativeSets(std::vector & exp); 57 | static void parseRuleReferences(const std::vector & expansions, std::vector & returnExpansions); 58 | static void parseRequiredGroupings(const std::vector & expansions, std::vector & returnExpansions); 59 | static void parseOptionalGroupings(const std::vector & expansions, std::vector & returnExpansions); 60 | static void parseUnaryOperators(const std::vector & expansions, std::vector & returnExpansions); 61 | static std::vector parseTokensFromString(std::string part); 62 | static void trimUnparsedSections(std::vector & expansions); 63 | static bool isEmptyUnparsedSection(Expansion * e); 64 | 65 | public: 66 | /** Default constructor */ 67 | Grammar(); 68 | /** 69 | * Constructor that specifies the grammar name 70 | * \param [in] string Name of the Grammar 71 | */ 72 | Grammar(const std::string & grammarName); 73 | 74 | Grammar(std::istream & inputStream); 75 | Grammar(std::istream * inputStream); 76 | 77 | /** Default destructor */ 78 | ~Grammar(); 79 | 80 | bool writeGrammar(std::ofstream & outputStream) const; 81 | 82 | void addRule(std::shared_ptr r); 83 | std::shared_ptr getRule(const std::string & name) const; 84 | std::vector> getRules() const; 85 | bool removeRule(const std::string & ruleName); 86 | 87 | std::string getName() const; 88 | void setName(const std::string & s); 89 | 90 | std::string getText() const; 91 | 92 | // Parsing 93 | static Expansion * parseExpansionsFromString(const std::string & input); 94 | static void parseGrammarFromString(const std::string & s, Grammar & g); 95 | static void parseGrammar(ifstream & f, Grammar & g); 96 | 97 | // Matching 98 | std::string getMatchingPublicRule(std::string test) const; 99 | MatchResult match(std::string test) const; 100 | MatchVector matchesRule(std::shared_ptr rule, const std::string & test) const; 101 | MatchVector matchesRule(const std::string & ruleName, const std::string & test) const; 102 | std::vector getMatchingTags(const std::string & test) const; 103 | static std::vector getMatchingTags(std::vector> matchInfo); 104 | 105 | // Utility 106 | void walkGrammar(void (* callback)(Expansion *)); 107 | void walkExpansion(Expansion * e, void (* callback)(Expansion *)); 108 | static std::string printExpansionType(Expansion * e); 109 | 110 | // Helper functions 111 | static bool isSpecialCharacter(char c); 112 | static std::string trimString(std::string input); 113 | static std::vector splitString(const std::string & s, const std::string & rgx_str); 114 | static bool stringContains(const std::string & part, const std::string & search); 115 | static bool stringStartsWith(const std::string & s, const std::string & test); 116 | static bool stringEndsWith(const std::string & s, const std::string & test); 117 | static std::string replaceAll(const std::string & s, const std::string & re, const std::string & replacement); 118 | static std::string replaceFirst(const std::string & s, const std::string & re, const std::string & replacement); 119 | 120 | static std::regex specialCharacterRegex; 121 | 122 | static inline std::string <rim(std::string &s); 123 | static inline std::string &rtrim(std::string &s); 124 | }; 125 | 126 | #endif // GRAMMAR_H 127 | -------------------------------------------------------------------------------- /include/jsgfkitxx/KleeneStar.hpp: -------------------------------------------------------------------------------- 1 | #ifndef KLEENESTAR_H 2 | #define KLEENESTAR_H 3 | 4 | #include "jsgfkitxx/Expansion.hpp" 5 | 6 | 7 | class KleeneStar : public Expansion 8 | { 9 | private: 10 | std::shared_ptr childExpansion; 11 | 12 | public: 13 | /** Default constructor */ 14 | KleeneStar(); 15 | KleeneStar(std::shared_ptr e); 16 | /** Default destructor */ 17 | ~KleeneStar(); 18 | 19 | Expansion * clone(); 20 | void replaceChild(std::shared_ptr newChild, const unsigned long index = 0); 21 | ExpansionType getType() const; 22 | bool hasChild() const; 23 | bool isOptional() const; 24 | std::shared_ptr getChild(const unsigned int index = 0) const; 25 | void setChild(std::shared_ptr e); 26 | unsigned int childCount() const; 27 | 28 | std::string getText() const; 29 | }; 30 | 31 | #endif // KLEENESTAR_H 32 | -------------------------------------------------------------------------------- /include/jsgfkitxx/MatchInfo.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MATCHINFO_H 2 | #define MATCHINFO_H 3 | #include "Expansion.hpp" 4 | #include 5 | 6 | class MatchInfo 7 | { 8 | private: 9 | std::shared_ptr expansion; //!< Member variable "expansion" 10 | std::string matchingSection; //!< Member variable "matchingSection" 11 | 12 | public: 13 | /** Default constructor */ 14 | MatchInfo(std::shared_ptr e, std::string matchingStringPart); 15 | 16 | /** Access expansion 17 | * \return The current value of expansion 18 | */ 19 | std::shared_ptr getExpansion() { return expansion; } 20 | /** Set expansion 21 | * \param val New value to set 22 | */ 23 | void setExpansion(Expansion * val) { expansion.reset(); expansion = std::shared_ptr (val); } 24 | 25 | /** Access matchingSection 26 | * \return The current value of matchingSection 27 | */ 28 | std::string getMatchingSection() { return matchingSection; } 29 | /** Set matchingSection 30 | * \param val New value to set 31 | */ 32 | void setMatchingSection(std::string val) { matchingSection = val; } 33 | }; 34 | 35 | #endif // MATCHINFO_H 36 | -------------------------------------------------------------------------------- /include/jsgfkitxx/MatchResult.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MATCHRESULT_H 2 | #define MATCHRESULT_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "jsgfkitxx/MatchInfo.hpp" 9 | #include "jsgfkitxx/Rule.hpp" 10 | 11 | typedef std::vector> MatchVector; 12 | 13 | class MatchResult 14 | { 15 | public: 16 | /** Default constructor means that no match was found */ 17 | MatchResult(); 18 | /// Construct for when a match is found 19 | MatchResult(std::shared_ptr rule, MatchVector ml); 20 | std::vector getMatchingTags(); 21 | /// If true, a match was found. If false, no match was found. 22 | const bool matches; 23 | const std::shared_ptr getMatchingRule(); 24 | 25 | protected: 26 | std::shared_ptr matchingRule; 27 | MatchVector matchVector; 28 | }; 29 | 30 | #endif // MATCHRESULT_H 31 | -------------------------------------------------------------------------------- /include/jsgfkitxx/MatchTracker.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MATCHINFO_H 2 | #define MATCHINFO_H 3 | #include "jsgfkitxx/Expansion.hpp" 4 | #include 5 | #include 6 | 7 | class MatchTracker 8 | { 9 | private: 10 | 11 | protected: 12 | std::shared_ptr> wordList; 13 | 14 | public: 15 | /** Default constructor */ 16 | MatchTracker(std::shared_ptr> words, unsigned int startIndex); 17 | 18 | MatchTracker(MatchTracker & t); 19 | 20 | ///Stores the index of the current word that is trying to be matched 21 | unsigned int position; 22 | 23 | ///Stores a vector of matched tags 24 | std::vector matchedTags; 25 | 26 | ///Stores a vector of matched rules 27 | std::vector matchedRules; 28 | 29 | static MatchTracker matchExpansion(std::shared_ptr> words, unsigned int startIndex, std::shared_ptr startExpansion); 30 | 31 | bool reachedEnd; 32 | bool successful; 33 | }; 34 | #endif // MATCHINFO_H -------------------------------------------------------------------------------- /include/jsgfkitxx/OptionalGrouping.hpp: -------------------------------------------------------------------------------- 1 | #ifndef OPTIONALGROUPING_H 2 | #define OPTIONALGROUPING_H 3 | 4 | #include "jsgfkitxx/Expansion.hpp" 5 | 6 | 7 | class OptionalGrouping : public Expansion 8 | { 9 | private: 10 | std::shared_ptr childExpansion; 11 | 12 | public: 13 | /** Default constructor */ 14 | OptionalGrouping(); 15 | OptionalGrouping(std::shared_ptr e); 16 | /** Default destructor */ 17 | ~OptionalGrouping(); 18 | 19 | Expansion * clone(); 20 | void replaceChild(std::shared_ptr newChild, const unsigned long index = 0); 21 | ExpansionType getType() const; 22 | bool hasChild() const; 23 | bool isOptional() const; 24 | std::shared_ptr getChild(const unsigned int index = 0) const; 25 | void setChild(std::shared_ptr e); 26 | unsigned int childCount() const; 27 | 28 | std::string getText() const; 29 | }; 30 | 31 | #endif // OPTIONALGROUPING_H 32 | -------------------------------------------------------------------------------- /include/jsgfkitxx/PlusOperator.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PLUSOPERATOR_H 2 | #define PLUSOPERATOR_H 3 | 4 | #include "jsgfkitxx/Expansion.hpp" 5 | 6 | 7 | class PlusOperator : public Expansion 8 | { 9 | private: 10 | std::shared_ptr childExpansion; 11 | 12 | public: 13 | /** Default constructor */ 14 | PlusOperator(); 15 | PlusOperator(std::shared_ptr e); 16 | /** Default destructor */ 17 | ~PlusOperator(); 18 | 19 | Expansion * clone(); 20 | void replaceChild(std::shared_ptr newChild, const unsigned long index = 0); 21 | ExpansionType getType() const; 22 | bool hasChild() const; 23 | bool isOptional() const; 24 | std::shared_ptr getChild(const unsigned int index = 0) const; 25 | void setChild(std::shared_ptr e); 26 | unsigned int childCount() const; 27 | 28 | std::string getText() const; 29 | }; 30 | #endif // PLUSOPERATOR_H 31 | -------------------------------------------------------------------------------- /include/jsgfkitxx/RequiredGrouping.hpp: -------------------------------------------------------------------------------- 1 | #ifndef REQUIREDGROUPING_H 2 | #define REQUIREDGROUPING_H 3 | 4 | #include "jsgfkitxx/Expansion.hpp" 5 | 6 | 7 | class RequiredGrouping : public Expansion 8 | { 9 | private: 10 | std::shared_ptr childExpansion; 11 | 12 | public: 13 | /** Default constructor */ 14 | RequiredGrouping(); 15 | RequiredGrouping(std::shared_ptr e); 16 | /** Default destructor */ 17 | ~RequiredGrouping(); 18 | 19 | Expansion * clone(); 20 | void replaceChild(std::shared_ptr newChild, const unsigned long index = 0); 21 | ExpansionType getType() const; 22 | bool hasChild() const; 23 | bool isOptional() const; 24 | std::shared_ptr getChild(const unsigned int index = 0) const; 25 | void setChild(std::shared_ptr e); 26 | unsigned int childCount() const; 27 | 28 | std::string getText() const; 29 | }; 30 | 31 | #endif // REQUIREDGROUPING_H 32 | -------------------------------------------------------------------------------- /include/jsgfkitxx/Rule.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RULE_H 2 | #define RULE_H 3 | #include "Expansion.hpp" 4 | #include 5 | 6 | class Rule 7 | { 8 | private: 9 | bool isVisible; /// If true, the Rule is "public". If false, the Rule is not "public" 10 | std::string name; 11 | std::shared_ptr ruleExpansion; 12 | 13 | public: 14 | /** Default constructor */ 15 | Rule(); 16 | Rule(std::string n, bool visible, std::shared_ptr e); 17 | 18 | /** Default destructor */ 19 | ~Rule(); 20 | 21 | void setPrivate() { isVisible = false; } /// Makes the Rule private 22 | void setPublic() { isVisible = true; } /// Makes the Rule public 23 | std::shared_ptr getRuleExpansion() const { return ruleExpansion; } 24 | void setRuleExpansion(std::shared_ptr e) { ruleExpansion = e; } 25 | void setRuleName(const std::string & newName) { name = newName; } 26 | std::string getRuleName() { return name; } 27 | bool isPublic() const { return isVisible; } 28 | 29 | /** 30 | * Returns a generated line of JSGF that defines this Rule. 31 | * \return string The generated JSGF text that declares this Rule. 32 | * 33 | */ 34 | std::string getRuleString() const; 35 | 36 | }; 37 | 38 | #endif // RULE_H 39 | -------------------------------------------------------------------------------- /include/jsgfkitxx/RuleReference.hpp: -------------------------------------------------------------------------------- 1 | #ifndef RULEREFERENCE_H 2 | #define RULEREFERENCE_H 3 | 4 | #include "jsgfkitxx/Expansion.hpp" 5 | 6 | 7 | class RuleReference : public Expansion 8 | { 9 | private: 10 | std::string ruleName; //!< Member variable "ruleName;" 11 | 12 | public: 13 | /** Default constructor */ 14 | RuleReference(std::string ruleN); 15 | 16 | /** Default destructor */ 17 | ~RuleReference(); 18 | 19 | /** Access ruleName; 20 | * \return The current value of ruleName; 21 | */ 22 | std::string getRuleName() const; 23 | 24 | /** Set ruleName; 25 | * \param val New value to set 26 | */ 27 | void setRuleName(std::string val); 28 | 29 | bool hasChild() const; 30 | bool isOptional() const; 31 | void replaceChild(std::shared_ptr newChild, const unsigned long index = 0); 32 | unsigned int childCount() const; 33 | Expansion * clone(); 34 | std::shared_ptr getChild(const unsigned int index = 0) const; 35 | ExpansionType getType() const; 36 | std::string getText() const; 37 | }; 38 | 39 | #endif // RULEREFERENCE_H 40 | -------------------------------------------------------------------------------- /include/jsgfkitxx/Sequence.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SEQUENCE_H 2 | #define SEQUENCE_H 3 | 4 | #include "jsgfkitxx/Expansion.hpp" 5 | #include 6 | #include 7 | 8 | class Sequence : public Expansion 9 | { 10 | private: 11 | std::vector> children; //!< Member variable "children" 12 | 13 | public: 14 | /** Default constructor */ 15 | Sequence(); 16 | /** Default destructor */ 17 | ~Sequence(); 18 | 19 | /** Clone function, makes a deep copy of the Sequence */ 20 | Expansion * clone(); 21 | void replaceChild(std::shared_ptr newChild, const unsigned long index = 0); 22 | ExpansionType getType() const; 23 | bool hasChild() const; 24 | bool isOptional() const; 25 | std::shared_ptr getChild(const unsigned int index = 0) const; 26 | std::string getText() const; 27 | 28 | /** Access children 29 | * \return The current value of children 30 | */ 31 | std::vector> getChildren() const; 32 | 33 | void addChild(std::shared_ptr e); 34 | void removeChild(Expansion & e); 35 | unsigned int childCount() const; 36 | 37 | /** 38 | * Static Helper function that checks to see if the provided expansion is a sequence, and if it is, checks to see if the Sequence has only one child. If it has only one child, it sets the provided shared_pointer to point to the child Expansion. 39 | * TLDR; Simplifies singular child Sequence's to singular Expansions 40 | * \param [in,out] s Expansion that will be simplified if it is a Sequence with 1 child expansion 41 | */ 42 | static void simplifySequence(std::shared_ptr s); 43 | }; 44 | 45 | #endif // SEQUENCE_H 46 | -------------------------------------------------------------------------------- /include/jsgfkitxx/Tag.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TAG_H 2 | #define TAG_H 3 | 4 | #include 5 | #include 6 | 7 | #include "jsgfkitxx/Expansion.hpp" 8 | 9 | class Tag : public Expansion 10 | { 11 | private: 12 | std::shared_ptr childExpansion; //!< Member variable "childExpansion" 13 | std::vector strings; 14 | int tagCount; 15 | 16 | public: 17 | /** Default constructor */ 18 | Tag(std::shared_ptr e); 19 | Tag(std::shared_ptr e, std::string tag); 20 | Tag(std::shared_ptr e, unsigned short tagCount, std::string tags[]); 21 | ~Tag(); 22 | 23 | Expansion * clone(); 24 | 25 | /** Access childExpansion 26 | * \return The current value of childExpansion 27 | */ 28 | std::shared_ptr getChild(const unsigned int index = 0) const; 29 | 30 | /** 31 | * Returns a vector of strings of the tags the child Expansion is tagged with. (All of the strings between the { } ) 32 | */ 33 | std::vector getTags() const; 34 | 35 | /** Set childExpansion 36 | * \param val Disassociates with the current child and tags the new specified child Expansion. 37 | */ 38 | void setChildExpansion(std::shared_ptr val); 39 | 40 | ExpansionType getType() const; 41 | 42 | bool hasChild() const; 43 | unsigned int childCount() const; 44 | void replaceChild(std::shared_ptr newChild, const unsigned long index = 0); 45 | std::string getText() const; 46 | void addTag(std::string t); /// Adds the specified tag to the vector of tags. 47 | void removeTag(std::string t); /// Removes the specified tag from the vector of tags if it exists. 48 | int getTagCount() const; /// Returns the number of tags attached to the child Expansion. Can be multiple. 49 | }; 50 | 51 | #endif // TAG_H 52 | -------------------------------------------------------------------------------- /include/jsgfkitxx/Token.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TOKEN_H 2 | #define TOKEN_H 3 | #include "Expansion.hpp" 4 | #include 5 | 6 | using namespace std; 7 | 8 | class Token : public Expansion 9 | { 10 | private: 11 | string text; //!< Member variable "text" 12 | 13 | public: 14 | /** Default constructor */ 15 | Token(); 16 | Token(string s); 17 | /** Default destructor */ 18 | ~Token(); 19 | 20 | Expansion * clone(); 21 | 22 | /** Access text 23 | * \return The current value of text 24 | */ 25 | string getText() const; 26 | /** Set text 27 | * \param val New value to set 28 | */ 29 | void setText(string val); 30 | 31 | ExpansionType getType() const; 32 | void replaceChild(std::shared_ptr newChild, const unsigned long index = 0); 33 | 34 | unsigned int childCount() const; 35 | bool hasChild() const; 36 | bool isOptional() const; 37 | shared_ptr getChild(const unsigned int index = 0) const; 38 | }; 39 | 40 | #endif // TOKEN_H 41 | -------------------------------------------------------------------------------- /include/jsgfkitxx/UnparsedSection.hpp: -------------------------------------------------------------------------------- 1 | #ifndef UNPARSEDSECTION_H 2 | #define UNPARSEDSECTION_H 3 | 4 | #include "jsgfkitxx/Expansion.hpp" 5 | #include 6 | 7 | using namespace std; 8 | 9 | /// An internal class used by the parser to generate the Expansions 10 | class UnparsedSection : public Expansion 11 | { 12 | private: 13 | string section; 14 | 15 | public: 16 | /** Default constructor */ 17 | UnparsedSection(); 18 | UnparsedSection(string s); 19 | 20 | /** Default destructor */ 21 | ~UnparsedSection(); 22 | Expansion * clone(); // Really should never be used but should still be implemented 23 | 24 | ExpansionType getType() const; 25 | 26 | ///Will always return true 27 | bool hasChild() const; 28 | void replaceChild(std::shared_ptr newChild, const unsigned long index = 0); 29 | 30 | unsigned int childCount() const; 31 | 32 | /// Will always return nullptr 33 | shared_ptr getChild(const unsigned int index = 0) const; 34 | 35 | string getSection() const; 36 | void setSection(string s); 37 | string getText() const; 38 | }; 39 | 40 | #endif // UNPARSEDSECTION_H 41 | -------------------------------------------------------------------------------- /jsgfkitxx.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: jsgfkitxx 7 | Description: A C++ library for parsing and manipulating Java Speech Grammar Format files. 8 | Version: 1.0 9 | Cflags: -I${includedir}/jsgfkitxx 10 | Libs: -L${libdir} -ljsgfkitxx 11 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Grammar.hpp" 3 | 4 | using namespace std; 5 | 6 | int main() 7 | { 8 | string a; 9 | string s = "#JSGF v1.0;\ 10 | grammar sphinx-mode;\ 11 | public = {pause} | {resume} | {push-to-talk} | {toggle};\ 12 | = pause speech recognition;\ 13 | = resume speech recognition;\ 14 | = toggle speech recognition;\ 15 | = ((enable | enter | begin | start) {enable} | (disable | exit | end | stop) {disable}) push to talk [mode];"; 16 | Grammar g; 17 | Grammar::parseGrammarFromString(s, g); 18 | cout << "PARSED GRAMMAR:" << endl; 19 | cout << g.getText() << endl; 20 | 21 | cout << "Rule Match Testing..." << endl; 22 | getline(cin, a); 23 | cout << a << endl; 24 | 25 | std::string ruleName = g.getMatchingPublicRule(a); 26 | if(ruleName != "") { 27 | shared_ptr r = g.getRule(ruleName); 28 | if(r) { 29 | cout << "Matched rule: " << r->getRuleName() << endl; 30 | cout << "Matching tags:" << endl; 31 | 32 | vector t = Grammar::getMatchingTags(g.matchesRule(r, a)); 33 | for(string s : t) { 34 | cout << "T:" << s << endl; 35 | } 36 | } 37 | else { 38 | cout << "Error returning Rule from ruleName!" << endl; 39 | } 40 | } 41 | else { 42 | cout << "Found no match!" << endl; 43 | } 44 | 45 | 46 | cout << "Test Done" << endl; 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /src/AlternativeSet.cpp: -------------------------------------------------------------------------------- 1 | #include "jsgfkitxx/AlternativeSet.hpp" 2 | 3 | AlternativeSet::AlternativeSet() 4 | { 5 | //ctor 6 | } 7 | 8 | AlternativeSet::~AlternativeSet() 9 | { 10 | std::vector>::iterator it = expansions.begin(); 11 | while(it != expansions.end()) { 12 | (*it).reset(); 13 | it++; 14 | } 15 | } 16 | 17 | Expansion * AlternativeSet::clone() { 18 | AlternativeSet * aset = new AlternativeSet(); 19 | std::vector> c = this->getChildren(); 20 | for(std::vector>::iterator childIterator = c.begin(); childIterator != c.end(); childIterator++) { 21 | aset->addChild(std::shared_ptr((*childIterator)->clone())); 22 | } 23 | return aset; 24 | } 25 | 26 | unsigned int AlternativeSet::childCount() const { 27 | return distance(expansions.begin(), expansions.end()); 28 | } 29 | 30 | std::vector> AlternativeSet::getChildren() const { 31 | return expansions; 32 | } 33 | 34 | std::shared_ptr AlternativeSet::getChild(const unsigned int index) const { 35 | return expansions[index]; 36 | } 37 | 38 | ExpansionType AlternativeSet::getType() const { 39 | return ALTERNATE_SET; 40 | } 41 | 42 | bool AlternativeSet::hasChild() const { 43 | return expansions.empty(); 44 | } 45 | 46 | bool AlternativeSet::isOptional() const { 47 | std::vector>::const_iterator it = expansions.begin(); 48 | while(it != expansions.end()) { 49 | if((*it)->isOptional()) { // If at least one of the children is optional, then this set can be matched no matter what. 50 | return true; 51 | } 52 | it++; 53 | } 54 | return false; 55 | } 56 | 57 | void AlternativeSet::addChild(std::shared_ptr e) { 58 | expansions.push_back(e); 59 | } 60 | 61 | void AlternativeSet::addChild(std::string token) { 62 | expansions.push_back(shared_ptr(new Token(token))); 63 | } 64 | 65 | void AlternativeSet::replaceChild(std::shared_ptr newChild, const unsigned long index) { 66 | expansions[index] = newChild; 67 | } 68 | 69 | /// TODO: Test to make sure this method works, it doesnt look quite right... 70 | void AlternativeSet::removeChild(Expansion & e) { 71 | if(std::count(expansions.begin(), expansions.end(), std::shared_ptr(&e)) != 0) { 72 | std::find(expansions.begin(), expansions.end(), std::shared_ptr(&e)); 73 | } 74 | } 75 | 76 | void AlternativeSet::removeChild(const unsigned int i) { 77 | expansions.erase(expansions.begin() + i); 78 | } 79 | 80 | std::string AlternativeSet::getText() const { 81 | //TODO: Implement weights 82 | 83 | std::vector>::const_iterator it = expansions.begin(); 84 | std::string s = "("; 85 | while(it != expansions.end()) { 86 | s.append((*it)->getText()); 87 | s.append(" | "); 88 | it++; 89 | } 90 | 91 | s = s.substr(0, s.size() - 3); 92 | return s + ")"; 93 | } 94 | -------------------------------------------------------------------------------- /src/Expansion.cpp: -------------------------------------------------------------------------------- 1 | #include "jsgfkitxx/Expansion.hpp" 2 | #include 3 | 4 | Expansion::Expansion() 5 | { 6 | //ctor 7 | } 8 | 9 | Expansion::~Expansion() { 10 | } 11 | -------------------------------------------------------------------------------- /src/Grammar.cpp: -------------------------------------------------------------------------------- 1 | #include "jsgfkitxx/Grammar.hpp" 2 | #include 3 | using namespace std; 4 | 5 | std::regex Grammar::specialCharacterRegex = std::regex("[;=<>*+\\[\\]()|{} ]"); /// Regex that matches for JSGF special characters that cannot be unescaped in non-quoted Tokens 6 | 7 | Grammar::Grammar() /// Default constructor. Creates an empty Grammar object with the grammar name set to "default" 8 | { 9 | name = "default"; 10 | } 11 | 12 | /** 13 | * Constructor with name. 14 | * \param string grammarName - name of the Grammar to be created 15 | */ 16 | Grammar::Grammar(const string & grammarName) 17 | { 18 | name = grammarName; 19 | } 20 | 21 | Grammar::Grammar(std::istream * inputStream) { 22 | std::string statement = ""; 23 | while(getline(*inputStream, statement, ';')) { 24 | statement = Grammar::trimString(statement); 25 | //Remove extra whitespace between characters 26 | statement = Grammar::replaceAll(statement, " {2,}", " "); 27 | 28 | if (Grammar::stringStartsWith(statement, "#")) 29 | { 30 | continue; // Line is a comment, skip it. 31 | } 32 | else if (Grammar::stringStartsWith(statement, "grammar ")) 33 | { 34 | vector parts = Grammar::splitString(statement, " "); 35 | setName(parts[1]); 36 | } 37 | else if (Grammar::stringStartsWith(statement,"public <")) 38 | { 39 | statement = Grammar::replaceFirst(statement, "public ", ""); 40 | vector parts = Grammar::splitString(statement, "="); 41 | string ruleName = Grammar::replaceAll(parts[0],"<|>", ""); 42 | ruleName = Grammar::trimString(ruleName); 43 | Expansion * exp = Grammar::parseExpansionsFromString(parts[1]); 44 | addRule(shared_ptr(new Rule(ruleName, true, shared_ptr(exp)))); 45 | } 46 | else if (Grammar::stringStartsWith(statement,"<")) 47 | { 48 | vector parts = Grammar::splitString(statement, "="); 49 | string ruleName = Grammar::replaceAll(parts[0],"<|>", ""); 50 | ruleName = Grammar::trimString(ruleName); 51 | Expansion * exp = Grammar::parseExpansionsFromString(parts[1]); 52 | addRule(shared_ptr(new Rule(ruleName, false, shared_ptr(exp)))); 53 | } 54 | } 55 | } 56 | 57 | Grammar::Grammar(istream & inputStream) { 58 | std::string statement = ""; 59 | while(getline(inputStream, statement, ';')) { 60 | statement = Grammar::trimString(statement); 61 | //Remove extra whitespace between characters 62 | statement = Grammar::replaceAll(statement, " {2,}", " "); 63 | 64 | if (Grammar::stringStartsWith(statement, "#")) 65 | { 66 | continue; // Line is a comment, skip it. 67 | } 68 | else if (Grammar::stringStartsWith(statement, "grammar ")) 69 | { 70 | vector parts = Grammar::splitString(statement, " "); 71 | setName(parts[1]); 72 | } 73 | else if (Grammar::stringStartsWith(statement,"public <")) 74 | { 75 | statement = Grammar::replaceFirst(statement, "public ", ""); 76 | vector parts = Grammar::splitString(statement, "="); 77 | string ruleName = Grammar::replaceAll(parts[0],"<|>", ""); 78 | ruleName = Grammar::trimString(ruleName); 79 | Expansion * exp = Grammar::parseExpansionsFromString(parts[1]); 80 | addRule(shared_ptr(new Rule(ruleName, true, shared_ptr(exp)))); 81 | } 82 | else if (Grammar::stringStartsWith(statement,"<")) 83 | { 84 | vector parts = Grammar::splitString(statement, "="); 85 | string ruleName = Grammar::replaceAll(parts[0],"<|>", ""); 86 | ruleName = Grammar::trimString(ruleName); 87 | Expansion * exp = Grammar::parseExpansionsFromString(parts[1]); 88 | addRule(shared_ptr(new Rule(ruleName, false, shared_ptr(exp)))); 89 | } 90 | } 91 | } 92 | 93 | Grammar::~Grammar() /// Default destructor, removes its ownership of Rule objects 94 | { 95 | for(shared_ptr & r : rules) 96 | { 97 | r.reset(); ///TODO: Double check this, should we really be resetting this? 98 | } 99 | } 100 | 101 | std::string Grammar::getName() const { 102 | return name; 103 | } 104 | 105 | void Grammar::setName(const std::string & s) { 106 | name = s; 107 | } 108 | 109 | bool Grammar::writeGrammar(std::ofstream & outputStream) const { 110 | outputStream << getText(); 111 | ///TODO: Make this return value mean something 112 | return true; 113 | } 114 | 115 | /** 116 | * Adds the specified Rule to the Grammar. 117 | * \param [in] rule 118 | */ 119 | void Grammar::addRule(shared_ptr r) 120 | { 121 | rules.push_back(r); 122 | } 123 | 124 | /** 125 | * Generates the JSGF grammar and returns it as a string. 126 | * \return string 127 | */ 128 | string Grammar::getText() const 129 | { 130 | string s = "grammar " + name + ";\n"; 131 | 132 | for(shared_ptr r : rules) 133 | { 134 | s += r->getRuleString() + "\n"; 135 | } 136 | 137 | return s; 138 | } 139 | 140 | /** 141 | * Returns the rule name of the public rule that matches the test string. If no matching public rule is found, an empty string is returned. 142 | * \param string String to test against public rules. 143 | * \return string 144 | */ 145 | std::string Grammar::getMatchingPublicRule(std::string test) const { 146 | test = Grammar::trimString(test); 147 | test = Grammar::replaceAll(test, " {2,}", " "); 148 | for(shared_ptr r : rules) { 149 | if(r->isPublic()) { 150 | MatchVector m = matchesRule(r, test); 151 | if(m.size() != 0) { 152 | return r->getRuleName(); 153 | } 154 | } 155 | } 156 | return ""; 157 | } 158 | 159 | /** Returns a MatchResult object that contains info about the rule that the test string matches. An empty MatchResult object is returned if no match is found. 160 | * \param string String to test against the Grammar's rules 161 | * \return MatchResult 162 | */ 163 | MatchResult Grammar::match(std::string test) const { 164 | test = Grammar::trimString(test); 165 | test = Grammar::replaceAll(test, " {2,}", " "); 166 | for(shared_ptr r : rules) { 167 | MatchVector m = matchesRule(r, test); 168 | if(m.size() != 0) { 169 | return MatchResult(r, m); 170 | } 171 | } 172 | return MatchResult(); 173 | } 174 | 175 | void Grammar::parseGrammar(ifstream & f, Grammar & g) { 176 | std::string statement = ""; 177 | while(getline(f, statement, ';')) { 178 | statement = Grammar::trimString(statement); 179 | //Remove extra whitespace between characters 180 | statement = Grammar::replaceAll(statement, " {2,}", " "); 181 | 182 | if (Grammar::stringStartsWith(statement, "#")) 183 | { 184 | continue; // Line is a comment, skip it. 185 | } 186 | else if (Grammar::stringStartsWith(statement, "grammar ")) 187 | { 188 | vector parts = Grammar::splitString(statement, " "); 189 | g.setName(parts[1]); 190 | } 191 | else if (Grammar::stringStartsWith(statement,"public <")) 192 | { 193 | statement = Grammar::replaceFirst(statement, "public ", ""); 194 | vector parts = Grammar::splitString(statement, "="); 195 | string ruleName = Grammar::replaceAll(parts[0],"<|>", ""); 196 | ruleName = Grammar::trimString(ruleName); 197 | Expansion * exp = Grammar::parseExpansionsFromString(parts[1]); 198 | g.addRule(shared_ptr(new Rule(ruleName, true, shared_ptr(exp)))); 199 | } 200 | else if (Grammar::stringStartsWith(statement,"<")) 201 | { 202 | vector parts = Grammar::splitString(statement, "="); 203 | string ruleName = Grammar::replaceAll(parts[0],"<|>", ""); 204 | ruleName = Grammar::trimString(ruleName); 205 | Expansion * exp = Grammar::parseExpansionsFromString(parts[1]); 206 | g.addRule(shared_ptr(new Rule(ruleName, false, shared_ptr(exp)))); 207 | } 208 | } 209 | } 210 | 211 | /** 212 | * Parses a new Grammar object from string s 213 | * \param [in] string s 214 | * \return Grammar * 215 | */ 216 | void Grammar::parseGrammarFromString(const string & s, Grammar & grammar) 217 | { 218 | //string noComments = Grammar::replaceAll(s, "(\\#+.*[\\n|\\r|\\v])|([//]+.*[\\n|\\r|\\v])", ""); // Remove all commented out lines 219 | //vector statements = Grammar::splitString(s, ";"); // Split into statements with each semicolon, NOTE: semicolons within quotes are not protected! Lookbehinds are not supported in C++11 https://stackoverflow.com/questions/14538687/using-regex-lookbehinds-in-c11#14539500 220 | string statement; 221 | stringstream ss(s); 222 | ///https://www.reddit.com/r/cpp_questions/comments/931zq5/any_way_to_simulate_multi_line_regex_with_c11/ 223 | ///Why can't C++11 just have multiline regex? Ugh. 224 | 225 | while(getline(ss, statement, ';')) { /// Note: Semicolons are not protected! 226 | statement = Grammar::trimString(statement); 227 | //Remove extra whitespace between characters 228 | statement = Grammar::replaceAll(statement, " {2,}", " "); 229 | 230 | if (Grammar::stringStartsWith(statement, "#")) 231 | { 232 | continue; // Line is a comment, skip it. 233 | } 234 | else if (Grammar::stringStartsWith(statement, "grammar ")) 235 | { 236 | vector parts = Grammar::splitString(statement, " "); 237 | grammar.setName(parts[1]); 238 | } 239 | else if (Grammar::stringStartsWith(statement,"public <")) 240 | { 241 | statement = Grammar::replaceFirst(statement, "public ", ""); 242 | vector parts = Grammar::splitString(statement, "="); 243 | string ruleName = Grammar::replaceAll(parts[0],"<|>", ""); 244 | ruleName = Grammar::trimString(ruleName); 245 | Expansion * exp = Grammar::parseExpansionsFromString(parts[1]); 246 | grammar.addRule(std::make_shared(ruleName, true, shared_ptr(exp))); 247 | } 248 | else if (Grammar::stringStartsWith(statement,"<")) 249 | { 250 | vector parts = Grammar::splitString(statement, "="); 251 | string ruleName = Grammar::replaceAll(parts[0],"<|>", ""); 252 | ruleName = Grammar::trimString(ruleName); 253 | Expansion * exp = Grammar::parseExpansionsFromString(parts[1]); 254 | grammar.addRule(std::make_shared(ruleName, false, shared_ptr(exp))); 255 | } 256 | } 257 | } 258 | 259 | Expansion * Grammar::parseExpansionsFromString(const string & input) 260 | { 261 | vector inVector = parseTokensFromString(input); 262 | vector outVector; 263 | parseRuleReferences(inVector, outVector); 264 | inVector.clear(); 265 | inVector.swap(outVector); 266 | parseRequiredGroupings(inVector, outVector); 267 | inVector.clear(); 268 | inVector.swap(outVector); 269 | parseOptionalGroupings(inVector, outVector); 270 | inVector.clear(); 271 | inVector.swap(outVector); 272 | parseUnaryOperators(inVector, outVector); // Don't swap and clear again, end of scope will destroy the inVector anyways 273 | return parseAlternativeSets(outVector); 274 | } 275 | 276 | void Grammar::parseUnaryOperators(const vector & expansions, vector & output) 277 | { 278 | vector tempExp; 279 | //Parse Plus and Unary operators 280 | //Note: Each symbol cannot be nested 281 | bool expansionFound = false; //We found an expansion that a Unary operator can be applied to, check to see if the next char is a unary operator 282 | Expansion * selectedExpansion; 283 | vector::const_iterator expansionIterator = expansions.begin(); 284 | 285 | while(expansionIterator != expansions.end()) 286 | { 287 | if (EXPANSION_IS_UNPARSED_SECTION(*expansionIterator)) 288 | { 289 | if (expansionFound) 290 | { 291 | UnparsedSection * up = (UnparsedSection *) *expansionIterator; 292 | if (Grammar::stringStartsWith(up->getSection(), "*")) // Kleene star operator 293 | { 294 | KleeneStar *ks = new KleeneStar(shared_ptr(selectedExpansion)); 295 | tempExp.push_back(ks); 296 | string newUnprocessedText = up->getSection(); 297 | newUnprocessedText = newUnprocessedText.replace(up->getSection().find("*"), 1, ""); // Remove the ) token from the UnparsedSection 298 | UnparsedSection * u = new UnparsedSection(Grammar::trimString(newUnprocessedText)); 299 | tempExp.push_back(u); 300 | expansionFound = false; 301 | } 302 | else if (Grammar::stringStartsWith(up->getSection(), "+")) // Plus operator 303 | { 304 | PlusOperator *ps = new PlusOperator(shared_ptr(selectedExpansion)); 305 | tempExp.push_back(ps); 306 | string newUnprocessedText = up->getSection(); 307 | newUnprocessedText = newUnprocessedText.replace(up->getSection().find("+"), 1, ""); // Remove the ) token from the UnparsedSection 308 | UnparsedSection * u = new UnparsedSection(Grammar::trimString(newUnprocessedText)); 309 | tempExp.push_back(u); 310 | expansionFound = false; 311 | } 312 | else 313 | { 314 | tempExp.push_back(selectedExpansion); 315 | tempExp.push_back(*expansionIterator); 316 | expansionFound = false; 317 | } 318 | } 319 | else 320 | { 321 | tempExp.push_back(*expansionIterator); 322 | } 323 | } 324 | else 325 | { 326 | if (expansionFound) // If we already had found an expansion before this and we didnt find a unary operator after it, add it to the vector of processed expansions 327 | { 328 | tempExp.push_back(selectedExpansion); 329 | } 330 | expansionFound = true; 331 | selectedExpansion = *expansionIterator; 332 | } 333 | expansionIterator++; 334 | } 335 | 336 | if (expansionFound) // If we reached the end of the loop with a taggable expansion selected, but didnt add it to the vector of expressions 337 | { 338 | tempExp.push_back(selectedExpansion); 339 | } 340 | 341 | vector exp = tempExp; 342 | 343 | tempExp.clear(); 344 | bool foundLegalExpansion = false; 345 | bool foundStart = false; 346 | bool tagStarted = false; 347 | string currentTag = ""; 348 | Tag * tagExpansion = nullptr; 349 | selectedExpansion = nullptr; // Reset the selected Expansion 350 | 351 | expansionIterator = exp.begin(); 352 | //NOTE: A single expansion is allowed to have multiple tags! 353 | while(expansionIterator != exp.end()) 354 | { 355 | if (foundLegalExpansion) 356 | { 357 | if (foundStart) 358 | { 359 | if (EXPANSION_IS_UNPARSED_SECTION(*expansionIterator)) // Could contain the ending } 360 | { 361 | UnparsedSection * up = (UnparsedSection *) *expansionIterator; 362 | if (Grammar::stringStartsWith(up->getSection(), "}")) // Found the end of the tag! 363 | { 364 | 365 | if(!tagStarted) 366 | { 367 | tagExpansion = new Tag(shared_ptr(selectedExpansion)); 368 | tagStarted = true; 369 | } 370 | 371 | tagExpansion->addTag(currentTag); 372 | currentTag = ""; // Reset the tag string contents 373 | string upText = up->getSection().replace(up->getSection().find("}"), 1, ""); // Remove the } token from the UnparsedSection 374 | upText = Grammar::trimString(upText); 375 | 376 | if (Grammar::stringEndsWith(upText, "{")) // Test to see if another tag follows this one 377 | { 378 | foundStart = true; 379 | upText = upText.substr(0, upText.length() - 1); // Remove the last char, which should be the { symbol 380 | } 381 | else 382 | { 383 | tagStarted = false; 384 | foundStart = false; 385 | tempExp.push_back(tagExpansion); 386 | foundLegalExpansion = false; 387 | } 388 | UnparsedSection * u = new UnparsedSection(Grammar::trimString(upText)); 389 | tempExp.push_back(u); 390 | } 391 | else 392 | { 393 | currentTag += up->getSection(); 394 | delete up; 395 | } 396 | } 397 | else 398 | { 399 | currentTag += (*expansionIterator)->getText(); // Add the expansions text contents to the tag string 400 | delete *expansionIterator; 401 | } 402 | } 403 | else 404 | { 405 | // Looking for a starting bracket { 406 | if (EXPANSION_IS_UNPARSED_SECTION(*expansionIterator)) // May contain the { we're looking for 407 | { 408 | UnparsedSection * up = (UnparsedSection *) *expansionIterator; 409 | if (Grammar::stringEndsWith(up->getSection(), "{")) // Found the start of the tag! 410 | { 411 | foundStart = true; 412 | string newUnprocessedText = up->getSection().substr(0, up->getSection().length() - 1); // Remove the last char, which should be the { symbol 413 | UnparsedSection * u = new UnparsedSection(Grammar::trimString(newUnprocessedText)); 414 | tempExp.push_back(u); // Add the updated UnprocessedText section to the vector 415 | } 416 | else 417 | { 418 | tempExp.push_back(selectedExpansion); 419 | tempExp.push_back(*expansionIterator); 420 | foundLegalExpansion = false; 421 | } 422 | } 423 | else // No tag possible for the selected expansion, but check to see if this expansion can be tagged 424 | { 425 | tempExp.push_back(selectedExpansion); 426 | if (!((*expansionIterator)->getType() == PLUS_OPERATOR || (*expansionIterator)->getType() == KLEENE_STAR)) 427 | { 428 | selectedExpansion = *expansionIterator; 429 | tagStarted = false; 430 | } 431 | else // current expansion cannot be tagged, begin search for taggable expansion over again 432 | { 433 | foundLegalExpansion = false; 434 | tempExp.push_back(*expansionIterator); 435 | } 436 | } 437 | } 438 | } 439 | else // Looking for a expansion that is taggable 440 | { 441 | if (!(EXPANSION_IS_PLUS_OPERATOR(*expansionIterator) || EXPANSION_IS_KLEENE_STAR(*expansionIterator) || EXPANSION_IS_UNPARSED_SECTION(*expansionIterator))) 442 | { 443 | foundLegalExpansion = true; // Found a taggable expansion, select it and start searching for tags 444 | selectedExpansion = *expansionIterator; 445 | tagStarted = false; 446 | } 447 | else // Unary operators and UnparsedSections cannot be tagged, pass over them 448 | { 449 | tempExp.push_back(*expansionIterator); 450 | } 451 | } 452 | expansionIterator++; 453 | } 454 | 455 | if (foundLegalExpansion) // Reached end of loop and had selected a taggable expansion, but no tags found 456 | { 457 | tempExp.push_back(selectedExpansion); 458 | } 459 | 460 | output = tempExp; 461 | } 462 | 463 | void Grammar::parseOptionalGroupings(const vector & expansions, vector & returnExpansions) 464 | { 465 | vector tempExp; 466 | vector exp = expansions; 467 | vector children; 468 | unsigned short nestCount = 0; 469 | char startChar = '['; 470 | char endChar = ']'; 471 | 472 | vector::iterator expansionIterator = exp.begin(); 473 | while(expansionIterator != exp.end()) 474 | { 475 | if (EXPANSION_IS_UNPARSED_SECTION(*expansionIterator)) 476 | { 477 | 478 | UnparsedSection * up = (UnparsedSection *) *expansionIterator; 479 | 480 | string childString; 481 | string outsideString; 482 | string part; 483 | 484 | unsigned int stringSize = up->getSection().size(); 485 | part = up->getSection(); 486 | 487 | //char cstr[stringSize + 1]; 488 | //up->getSection().copy(cstr,stringSize); 489 | 490 | for (unsigned int i =0; i < stringSize; i++) 491 | { 492 | char c = part[i]; 493 | if (c == startChar) 494 | { 495 | nestCount++; 496 | if (nestCount == 1) 497 | { 498 | if (outsideString.size() > 0) 499 | { 500 | UnparsedSection * u = new UnparsedSection(outsideString); 501 | tempExp.push_back(u); 502 | } 503 | outsideString = ""; 504 | continue; 505 | } 506 | } 507 | else if (c == endChar) 508 | { 509 | nestCount--; 510 | if (nestCount == 0) // Transition to the end of the string 511 | { 512 | UnparsedSection * u = new UnparsedSection(childString); 513 | children.push_back(u); 514 | vector parsedChildren; 515 | parseOptionalGroupings(children, parsedChildren); 516 | children.clear(); // Using children as next return value 517 | parseUnaryOperators(parsedChildren, children); 518 | ///TODO: Fix this 519 | shared_ptr child = shared_ptr(parseAlternativeSets(children)); 520 | OptionalGrouping *og = new OptionalGrouping(child); 521 | tempExp.push_back(og); 522 | childString = ""; 523 | children.clear(); 524 | } 525 | } 526 | 527 | if (nestCount >= 1) 528 | { 529 | childString += c; 530 | } 531 | else if (c != endChar) 532 | { 533 | outsideString += c; 534 | } 535 | } 536 | 537 | if (outsideString.size() > 0) 538 | { 539 | UnparsedSection * u = new UnparsedSection(outsideString); 540 | tempExp.push_back(u); 541 | } 542 | 543 | if (childString.size() > 0) 544 | { 545 | if (nestCount > 0) 546 | { 547 | UnparsedSection * u = new UnparsedSection(childString); 548 | children.push_back(u); 549 | } 550 | else 551 | { 552 | UnparsedSection * u = new UnparsedSection(childString); 553 | tempExp.push_back(u); 554 | } 555 | } 556 | delete *expansionIterator; 557 | } 558 | else 559 | { 560 | if (nestCount >= 1) // Element is part of this grouping's children 561 | { 562 | children.push_back(*expansionIterator); 563 | } 564 | else 565 | { 566 | tempExp.push_back(*expansionIterator); 567 | } 568 | } 569 | expansionIterator++; 570 | } 571 | 572 | returnExpansions = tempExp; 573 | } 574 | 575 | void Grammar::parseRequiredGroupings(const vector & expansions, vector & returnExpansions) 576 | { 577 | vector tempExp; 578 | vector exp = expansions; 579 | vector children; 580 | unsigned short nestCount = 0; 581 | char startChar = '('; 582 | char endChar = ')'; 583 | 584 | vector::iterator expansionIterator = exp.begin(); 585 | while(expansionIterator != exp.end()) 586 | { 587 | if (EXPANSION_IS_UNPARSED_SECTION(*expansionIterator)) 588 | { 589 | UnparsedSection * up = (UnparsedSection *) *expansionIterator; 590 | string childString; 591 | string outsideString; 592 | string part; 593 | unsigned int stringSize = up->getSection().size(); 594 | part = up->getSection(); 595 | 596 | //char cstr[stringSize+1]; 597 | //up->getSection().copy(cstr,stringSize); 598 | 599 | for (unsigned int i =0; i < stringSize; i++) 600 | { 601 | char c = part[i]; 602 | if (c == startChar) 603 | { 604 | nestCount++; 605 | if (nestCount == 1) 606 | { 607 | if (outsideString.size() > 0) 608 | { 609 | UnparsedSection * u = new UnparsedSection(outsideString); 610 | tempExp.push_back(u); 611 | } 612 | outsideString = ""; 613 | continue; 614 | } 615 | } 616 | else if (c == endChar) 617 | { 618 | nestCount--; 619 | if (nestCount == 0) // Transition to the end of the string 620 | { 621 | UnparsedSection * u = new UnparsedSection(childString); 622 | children.push_back(u); 623 | vector parsedChildren; 624 | parseRequiredGroupings(children, parsedChildren); 625 | children.clear(); // Using children as next return value 626 | parseOptionalGroupings(parsedChildren, children); 627 | parsedChildren.clear(); // Using parsedChildren as next return value 628 | parseUnaryOperators(children, parsedChildren); 629 | children.clear(); // Using children as next return value 630 | ///TODO: Fix this, compiles but ends up destroying all child expansions because new input/output idiom returns void and relies upon references. 631 | ///Find other parsing methods that also use the incorrect form. 632 | shared_ptr child = shared_ptr(parseAlternativeSets(parsedChildren)); 633 | RequiredGrouping *rg = new RequiredGrouping(child); 634 | tempExp.push_back(rg); 635 | childString = ""; 636 | children.clear(); 637 | } 638 | } 639 | 640 | if (nestCount >= 1) 641 | { 642 | childString += c; 643 | } 644 | else if (c != endChar) 645 | { 646 | outsideString += c; 647 | } 648 | } 649 | 650 | if (outsideString.size() > 0) 651 | { 652 | UnparsedSection * u = new UnparsedSection(outsideString); 653 | tempExp.push_back(u); 654 | } 655 | 656 | if (childString.size() > 0) 657 | { 658 | if (nestCount > 0) 659 | { 660 | UnparsedSection * u = new UnparsedSection(childString); 661 | children.push_back(u); 662 | } 663 | else 664 | { 665 | UnparsedSection * u = new UnparsedSection(childString); 666 | tempExp.push_back(u); 667 | } 668 | } 669 | 670 | delete *expansionIterator; 671 | } 672 | else 673 | { 674 | if (nestCount >= 1) // Element is part of this grouping's children 675 | { 676 | children.push_back(*expansionIterator); 677 | } 678 | else 679 | { 680 | tempExp.push_back(*expansionIterator); 681 | } 682 | } 683 | expansionIterator++; 684 | } 685 | 686 | returnExpansions = tempExp; 687 | } 688 | 689 | void Grammar::parseRuleReferences(const vector & expansions, vector & returnExpansions) 690 | { 691 | //Parse Rule References because they have next highest precedence 692 | //Pattern: "<"+TOKEN+">" 693 | vector tempExp; // Temporary vector that will be copied into exp 694 | vector exp = expansions; 695 | bool startSearch; // True = looking for a < 696 | bool endSearch; // True = looking for a > 697 | bool tokenSearch; // True = hoping that the next Expansion is a Token object containing the name of the rule that is being referenced 698 | Token * selectedToken; // Only set to null to avoid compiler warnings. 699 | unsigned short iterationCount = 0; 700 | 701 | bool iterationNeeded = true; 702 | while (iterationNeeded) 703 | { 704 | vector::iterator expansionIterator = exp.begin(); 705 | iterationNeeded = false; 706 | tempExp.clear(); 707 | startSearch = true; 708 | endSearch = false; 709 | tokenSearch = false; 710 | selectedToken = nullptr; 711 | 712 | while(expansionIterator != exp.end()) 713 | { 714 | if (startSearch) 715 | { 716 | if (EXPANSION_IS_UNPARSED_SECTION(*expansionIterator)) 717 | { 718 | UnparsedSection * up = (UnparsedSection *) *expansionIterator; 719 | if (Grammar::stringEndsWith(up->getSection(), "<")) 720 | { 721 | startSearch = false; 722 | tokenSearch = true; 723 | //Found the < that starts the rule reference, so we need to remove it from the old UnparsedSection and add the new UnparsedSection to the vector 724 | string newUnprocessedText = up->getSection().substr(0, up->getSection().length() - 1); // Remove the last char, which should be the < symbol 725 | UnparsedSection * u = new UnparsedSection(Grammar::trimString(newUnprocessedText)); 726 | tempExp.push_back(u); // Add the updated UnprocessedText section to the vector 727 | } 728 | else 729 | { 730 | tempExp.push_back(up); // UnparsedSection does not end in a < so it must not contain the start to a rule reference 731 | } 732 | } 733 | else 734 | { 735 | tempExp.push_back(*expansionIterator); // Not an UnparsedSection with text, continue on 736 | } 737 | } 738 | else if (endSearch) 739 | { 740 | if (EXPANSION_IS_UNPARSED_SECTION(*expansionIterator)) 741 | { 742 | UnparsedSection * up = (UnparsedSection *) *expansionIterator; 743 | if (Grammar::stringStartsWith(up->getSection(), ">")) 744 | { 745 | endSearch = false; 746 | startSearch = true; 747 | RuleReference * rr = new RuleReference(selectedToken->getText()); 748 | tempExp.push_back(rr); 749 | //Found the > that ends the rule reference, so we need to remove it from the old UnparsedSection and add the new UnparsedSection to the vector 750 | string newUnprocessedText = up->getSection().replace(up->getSection().find(">"), 1, ""); // Remove the > token from the UnparsedSection 751 | UnparsedSection * u = new UnparsedSection(Grammar::trimString(newUnprocessedText)); 752 | tempExp.push_back(u); 753 | iterationNeeded = true; 754 | } 755 | else 756 | { 757 | throw "Found start of rule reference, but did not find > immediately afterwards! found: " + (*expansionIterator)->getText(); 758 | } 759 | } 760 | else 761 | { 762 | throw "Found start of rule reference, but did not find rule name immediately afterwards! found: " + (*expansionIterator)->getText(); 763 | } 764 | } 765 | else if (tokenSearch) 766 | { 767 | if (EXPANSION_IS_TOKEN(*expansionIterator)) 768 | { 769 | endSearch = true; 770 | tokenSearch = false; 771 | selectedToken = (Token *) *expansionIterator; 772 | } 773 | else 774 | { 775 | throw "Found < character denoting rule reference, but did not find rule name immediately afterwards! found: " + (*expansionIterator)->getText(); 776 | } 777 | } 778 | else 779 | { 780 | tempExp.push_back(*expansionIterator); 781 | } 782 | 783 | expansionIterator++; 784 | } 785 | exp.swap(tempExp); 786 | tempExp.clear(); 787 | iterationCount++; 788 | } 789 | 790 | returnExpansions = exp; 791 | } 792 | 793 | vector Grammar::parseTokensFromString(std::string part) 794 | { 795 | vector exp; 796 | //Parse Tokens because they have the highest precedence 797 | string passed; // All characters that are not part of a token 798 | unsigned int position = 0; 799 | bool escapedMode; // If turned true, the next character will be added to the currentToken string without being evaluated 800 | bool quotedMode; // If turned true, all characters between quotes are added to the currentToken string 801 | char quoteType; // Which quote character to seek for to signify end of quote 802 | bool tokenMode = false; // If True, test and add the following characters to the currentToken string 803 | string currentToken; 804 | unsigned int stringLength = part.size(); 805 | 806 | char * charArray = new char [stringLength + 1]; 807 | strncpy(charArray, part.c_str(), stringLength); 808 | char a; // This holds the current character that is being scanned 809 | while (position < stringLength) 810 | { 811 | escapedMode = false; 812 | quotedMode = false; 813 | quoteType = '\''; 814 | tokenMode = false; 815 | currentToken = ""; // This holds the string of characters that are being scanned into one Token 816 | a = charArray[position]; 817 | 818 | while (!tokenMode && position < stringLength) 819 | { 820 | a = charArray[position]; 821 | if(!Grammar::isSpecialCharacter(a)) 822 | { 823 | UnparsedSection * up = new UnparsedSection(); 824 | up->setSection(Grammar::trimString(passed)); 825 | exp.push_back(up); 826 | passed = ""; 827 | tokenMode = true; 828 | //DO NOT INCREMENT THE POSITION COUNTER, WE ARE LETTING THE NEXT LOOP EVALUATE THIS CHARACTER 829 | } 830 | else 831 | { 832 | passed += a; 833 | position++; 834 | } 835 | } 836 | 837 | if (!tokenMode) // We reached the end of the string without finding a token, so add what we passed over 838 | { 839 | UnparsedSection * up = new UnparsedSection(); 840 | up->setSection(Grammar::trimString(passed)); 841 | exp.push_back(up); 842 | passed = ""; 843 | } 844 | 845 | while (tokenMode && position < stringLength) 846 | { 847 | a = charArray[position]; // Retrieve the current char we are using 848 | if (escapedMode) // escape mode ensures that the next character is not processed literally 849 | { 850 | escapedMode = false; 851 | currentToken += a; 852 | position++; 853 | } 854 | else 855 | { 856 | if (a == '\\') // Test to see if char triggers escape mode 857 | { 858 | escapedMode = true; 859 | position++; 860 | } 861 | else if (quotedMode) // Token is either like: "blarg" or: 'blarg' 862 | { 863 | if (a == quoteType) 864 | { 865 | quotedMode = false; 866 | tokenMode = false; 867 | currentToken += quoteType; // The last character is a part of the token (Either ' or " ) 868 | position++; // Last character was added to the token, so it was processed, increment the position counter 869 | //Entire token has now been scanned into currentToken 870 | Token * t = new Token(currentToken); 871 | exp.push_back(t); 872 | currentToken = ""; 873 | } 874 | else // Characters within the quotes 875 | { 876 | currentToken += a; 877 | position++; 878 | } 879 | } 880 | else if (a == '\"') 881 | { 882 | quotedMode = true; 883 | quoteType = '\"'; 884 | currentToken += a; 885 | position++; 886 | } 887 | else if (a == '\'') 888 | { 889 | quotedMode = true; 890 | quoteType = '\''; 891 | currentToken += a; 892 | position++; 893 | } 894 | else if (Grammar::isSpecialCharacter(a)) {// Check to see if char matches special characters 895 | tokenMode = false; 896 | //Entire token has now been scanned into currentToken 897 | Token * t = new Token(currentToken); 898 | exp.push_back(t); 899 | currentToken = ""; 900 | passed = a; 901 | position++; 902 | } 903 | else 904 | { 905 | currentToken += a; 906 | position++; 907 | } 908 | } 909 | } 910 | if (tokenMode) // Reached end of string before end of token 911 | { 912 | Token * t = new Token(currentToken); 913 | exp.push_back(t); 914 | currentToken = ""; 915 | } 916 | } 917 | delete[] charArray; 918 | 919 | if (!tokenMode) // We reached the end of the string without finding a token, so add what we passed over 920 | { 921 | UnparsedSection * up = new UnparsedSection(); 922 | up->setSection(Grammar::trimString(passed)); 923 | exp.push_back(up); 924 | } 925 | 926 | Grammar::trimUnparsedSections(exp); 927 | 928 | return exp; 929 | } 930 | 931 | bool Grammar::isEmptyUnparsedSection(Expansion * e) { 932 | if(EXPANSION_IS_UNPARSED_SECTION(e)) { 933 | UnparsedSection * u = (UnparsedSection *) e; 934 | std::string s = u->getSection(); 935 | if(s.size() == 0) { 936 | delete e; 937 | return true; 938 | } 939 | if(s == " ") { 940 | delete e; 941 | return true; 942 | } 943 | if(s == " ") { 944 | delete e; 945 | return true; 946 | } 947 | if(s == " ") { 948 | delete e; 949 | return true; 950 | } 951 | u->setSection(trimString(s)); 952 | return false; 953 | } 954 | else { 955 | return false; 956 | } 957 | } 958 | 959 | void Grammar::trimUnparsedSections(std::vector & exp) { 960 | exp.erase(std::remove_if(exp.begin(), exp.end(), Grammar::isEmptyUnparsedSection), exp.end()); 961 | } 962 | 963 | Expansion * Grammar::parseAlternativeSets(vector & exp) { 964 | //vector tempExp; 965 | //Remove all leftover UnparsedSections 966 | vector::iterator expansionIterator; 967 | 968 | trimUnparsedSections(exp); 969 | 970 | Sequence * currentSequence = new Sequence(); // CREATED ON THE FREE STORE! 971 | AlternativeSet * aset = new AlternativeSet(); // CREATED ON THE FREE STORE! 972 | expansionIterator = exp.begin(); 973 | 974 | while (expansionIterator != exp.end()) 975 | { 976 | if (EXPANSION_IS_UNPARSED_SECTION(*expansionIterator)) 977 | { 978 | UnparsedSection * up = (UnparsedSection *) *expansionIterator; 979 | if (Grammar::stringContains(up->getSection(), "|")) 980 | { 981 | shared_ptr a; 982 | if(currentSequence->childCount() == 1) 983 | { 984 | a = currentSequence->getChild(); 985 | delete currentSequence; 986 | } 987 | else { 988 | a.reset(currentSequence); 989 | } 990 | 991 | aset->addChild(a); 992 | currentSequence = new Sequence(); 993 | } 994 | else 995 | { 996 | currentSequence->addChild(std::shared_ptr (up)); 997 | } 998 | } 999 | else 1000 | { 1001 | currentSequence->addChild(std::shared_ptr(*expansionIterator)); 1002 | } 1003 | expansionIterator++; 1004 | } 1005 | 1006 | Expansion * output; 1007 | if (aset->childCount() > 0) 1008 | { 1009 | std::shared_ptr a; 1010 | if(currentSequence->childCount() == 1) 1011 | { 1012 | a = currentSequence->getChild(); 1013 | delete currentSequence; 1014 | } 1015 | else { 1016 | a.reset(currentSequence); 1017 | } 1018 | 1019 | aset->addChild(a); 1020 | output = aset; 1021 | } 1022 | else 1023 | { 1024 | delete aset; // aset has no children yet, and we are not returning it. so it must be deleted, and deleting it won't result in any dangling pointers 1025 | 1026 | if(currentSequence->childCount() == 1) 1027 | { 1028 | Expansion * e = currentSequence->getChild()->clone(); 1029 | delete currentSequence; 1030 | output = e; 1031 | } 1032 | else 1033 | { 1034 | output = currentSequence; 1035 | } 1036 | } 1037 | 1038 | return output; 1039 | } 1040 | 1041 | /** 1042 | * Returns a pointer to a Rule object that has the specified name. Returns nullptr if not rule can be found! 1043 | * \param [in] ruleName 1044 | * \return shared_ptr 1045 | * \return nullptr if no rule can be found 1046 | */ 1047 | shared_ptr Grammar::getRule(const string & ruleName) const 1048 | { 1049 | for(shared_ptr r : rules) 1050 | { 1051 | if(r->getRuleName() == ruleName) 1052 | { 1053 | return r; 1054 | } 1055 | } 1056 | return nullptr; 1057 | } 1058 | 1059 | /** Returns a vector of shared ptrs to Rules of all the Rules in this Grammar 1060 | * 1061 | */ 1062 | vector> Grammar::getRules() const { 1063 | return rules; 1064 | } 1065 | 1066 | /** Attempts to remove the specified Rule from the list of rules. 1067 | * Returns true on success, returns false if the rule with the specified name could not be found. 1068 | * \param ruleName A string holding the name of the rule that should be removed 1069 | * 1070 | */ 1071 | bool Grammar::removeRule(const string & ruleName) { 1072 | for(vector>::iterator r = rules.begin(); r != rules.end(); r++) { 1073 | if((*r)->getRuleName() == ruleName) { 1074 | rules.erase(r); 1075 | return true; 1076 | } 1077 | } 1078 | return false; 1079 | } 1080 | 1081 | /** Walks through all of the expansions in every rule. Calls the callback function for each expansion it walks across. 1082 | * \param callback A void returning function that expects a pointer to an Expansion for its args. The expansion pointer is the expansion that was encountered 1083 | * 1084 | * 1085 | */ 1086 | void Grammar::walkGrammar(void (* callback)(Expansion *)) { 1087 | for(shared_ptr r : rules) { 1088 | walkExpansion(r->getRuleExpansion().get(), callback); 1089 | } 1090 | } 1091 | 1092 | /// Walks through the given expansion and all of its children. Calls the callback function for each expansion it walks across. 1093 | void Grammar::walkExpansion(Expansion * e, void (* callback)(Expansion *)) { 1094 | callback(e); 1095 | if(e->hasChild()) { 1096 | for(unsigned int i = 0; i < e->childCount(); i++) { 1097 | Grammar::walkExpansion(e->getChild(i).get(), callback); 1098 | } 1099 | } 1100 | } 1101 | 1102 | /** 1103 | * Matches the array of word strings against the specified Expansion. Mainly used internally. Use Grammar::matchedRule() for testing for matches. 1104 | * \param [in] e The Expansion being tested against 1105 | * \param [in] words[] array of strings that are being matched against the Expansion 1106 | * \param [in] wordCount Number of words that are being matched (usually is the the length of words[]) 1107 | * \param [in] wordPosition Index of the word to start matching at 1108 | * \return vector> - std::vector of MatchInfo objects 1109 | */ 1110 | vector> Grammar::getMatchingExpansions(shared_ptr e, string words[], unsigned int wordCount, unsigned int wordPosition) const 1111 | { 1112 | vector> matchVector; 1113 | 1114 | if (EXPANSION_IS_TOKEN(e)) 1115 | { 1116 | Token * t = (Token *) e.get(); 1117 | if (t->getText() == (words[wordPosition])) 1118 | { 1119 | string matchedPart = words[wordPosition]; 1120 | matchVector.push_back(shared_ptr(new MatchInfo(shared_ptr(e), words[wordPosition]))); 1121 | } 1122 | else 1123 | { 1124 | // No match 1125 | } 1126 | } 1127 | else if (EXPANSION_IS_RULE_REFERENCE(e)) 1128 | { 1129 | RuleReference * ref = (RuleReference *) e.get(); 1130 | 1131 | Rule * rule = getRule(ref->getRuleName()).get(); // Try to get the rule 1132 | if(rule) 1133 | { 1134 | vector> m1 = (getMatchingExpansions(rule->getRuleExpansion(), words, wordCount, wordPosition)); 1135 | if (m1.size() != 0) 1136 | { 1137 | matchVector.push_back(std::make_shared(e, "")); // Need to mark that the rule was matched! 1138 | matchVector.insert(matchVector.end(), m1.begin(), m1.end()); 1139 | } 1140 | } 1141 | } 1142 | else if (EXPANSION_IS_OPTIONAL_GROUPING(e)) 1143 | { 1144 | OptionalGrouping * og = (OptionalGrouping *) e.get(); 1145 | vector> m1 = getMatchingExpansions(og->getChild(), words, wordCount, wordPosition); 1146 | if (m1.size() == 0) 1147 | { 1148 | // Optional, so it can match. Used for sequences 1149 | } 1150 | else 1151 | { 1152 | //Matches 1153 | matchVector.push_back(shared_ptr(new MatchInfo(shared_ptr(e), ""))); 1154 | vector> moreMatches = getMatchingExpansions(og->getChild(), words, wordCount, wordPosition); 1155 | matchVector.insert(matchVector.end(), moreMatches.begin(), moreMatches.end()); 1156 | } 1157 | } 1158 | else if (EXPANSION_IS_REQUIRED_GROUPING(e)) 1159 | { 1160 | RequiredGrouping * rg = (RequiredGrouping *) e.get(); 1161 | vector> m1 = getMatchingExpansions(rg->getChild(), words, wordCount, wordPosition); 1162 | 1163 | if (m1.size() != 0) 1164 | { 1165 | matchVector.push_back(shared_ptr(new MatchInfo(shared_ptr(e), ""))); 1166 | matchVector.insert(matchVector.end(), m1.begin(), m1.end()); 1167 | } 1168 | } 1169 | else if (EXPANSION_IS_TAG(e)) 1170 | { 1171 | Tag * t = (Tag *) e.get(); 1172 | vector> m1 = getMatchingExpansions(t->getChild(), words, wordCount, wordPosition); 1173 | 1174 | if (m1.size() != 0) 1175 | { 1176 | matchVector.push_back(shared_ptr(new MatchInfo(shared_ptr(e), ""))); 1177 | matchVector.insert(matchVector.end(), m1.begin(), m1.end()); 1178 | } 1179 | } 1180 | else if (EXPANSION_IS_ALTERNATE_SET(e)) 1181 | { 1182 | AlternativeSet * as = (AlternativeSet *) e.get(); 1183 | for (shared_ptr x : as->getChildren()) 1184 | { 1185 | vector> m1 = getMatchingExpansions(x, words, wordCount, wordPosition); 1186 | 1187 | if (m1.size() == 0 && (EXPANSION_IS_KLEENE_STAR(x) || EXPANSION_IS_OPTIONAL_GROUPING(x))) // Stupid OptionalGrouping 1188 | { 1189 | continue; 1190 | } 1191 | 1192 | if (m1.size() != 0) 1193 | { 1194 | matchVector.push_back(shared_ptr(new MatchInfo(shared_ptr(e), ""))); 1195 | matchVector.insert(matchVector.end(), m1.begin(), m1.end()); // Found a match! Add it to the vector 1196 | break; 1197 | } 1198 | } 1199 | } 1200 | else if (EXPANSION_IS_SEQUENCE(e)) 1201 | { 1202 | Sequence * seq = (Sequence *) e.get(); 1203 | vector> localMatchVector; 1204 | vector> expansions = seq->getChildren(); 1205 | unsigned int matchedCount = 0; 1206 | 1207 | for (shared_ptr x : expansions) 1208 | { 1209 | vector> m1 = getMatchingExpansions(x, words, wordCount, wordPosition); 1210 | if (m1.size() == 0 && (EXPANSION_IS_KLEENE_STAR(x) || EXPANSION_IS_OPTIONAL_GROUPING(x))) // Stupid OptionalGrouping 1211 | { 1212 | matchedCount++; // Still counts a match 1213 | continue; 1214 | } 1215 | 1216 | if (m1.size() != 0) 1217 | { 1218 | matchedCount++; 1219 | for (shared_ptr localMatch : m1) 1220 | { 1221 | if(localMatch->getMatchingSection() != "") 1222 | { 1223 | wordPosition += Grammar::splitString(localMatch->getMatchingSection(), " ").size(); 1224 | } 1225 | } 1226 | localMatchVector.insert(localMatchVector.end(), m1.begin(), m1.end()); // Found a match! Add it to the vector 1227 | } 1228 | else // Doesn't match! Sequence aborted. 1229 | { 1230 | localMatchVector.clear(); 1231 | break; 1232 | } 1233 | 1234 | if (wordPosition > wordCount - 1) // Sequence is longer than provided words! Abort! 1235 | { 1236 | break; 1237 | } 1238 | } 1239 | 1240 | unsigned int requiredMatched = 0; 1241 | for(shared_ptr x : expansions) { 1242 | if(!EXPANSION_IS_OPTIONAL_GROUPING(x) && !EXPANSION_IS_KLEENE_STAR(x)) { 1243 | requiredMatched++; 1244 | } 1245 | } 1246 | 1247 | //std::cout << "Needed: " << seq->childCount() << " Got: " << matchedCount << std::endl; 1248 | if (matchedCount < requiredMatched) // Not all of the required matches were met! 1249 | { 1250 | localMatchVector.clear(); 1251 | } 1252 | 1253 | if (localMatchVector.size() != 0) 1254 | { 1255 | matchVector.push_back(shared_ptr(new MatchInfo(shared_ptr(e), ""))); 1256 | matchVector.insert(matchVector.end(), localMatchVector.begin(), localMatchVector.end()); 1257 | } 1258 | } 1259 | else if (EXPANSION_IS_KLEENE_STAR(e)) 1260 | { 1261 | KleeneStar * ks = (KleeneStar *) e.get(); 1262 | bool done = false; 1263 | vector> m1; 1264 | matchVector.push_back(shared_ptr(new MatchInfo(shared_ptr(e), ""))); 1265 | while(!done) 1266 | { 1267 | if (wordPosition > wordCount - 1) 1268 | { 1269 | break; 1270 | } 1271 | m1 = getMatchingExpansions(ks->getChild(), words, wordCount, wordPosition); 1272 | if (m1.size() == 0) 1273 | { 1274 | // No matches 1275 | done = true; 1276 | } 1277 | else 1278 | { 1279 | //Matches 1280 | for (shared_ptr mi2 : m1) 1281 | { 1282 | if(mi2->getMatchingSection() != "") 1283 | { 1284 | wordPosition += Grammar::splitString(mi2->getMatchingSection(), " ").size(); 1285 | } 1286 | } 1287 | matchVector.insert(matchVector.end(), m1.begin(), m1.end()); 1288 | matchVector.push_back(shared_ptr(new MatchInfo(shared_ptr(e), ""))); 1289 | } 1290 | } 1291 | } 1292 | else if (EXPANSION_IS_PLUS_OPERATOR(e)) 1293 | { 1294 | PlusOperator * po = (PlusOperator *) e.get(); 1295 | bool done = false; 1296 | vector> m1; 1297 | while(!done) 1298 | { 1299 | if (wordPosition > wordCount-1) 1300 | { 1301 | break; 1302 | } 1303 | m1 = getMatchingExpansions(po->getChild(), words, wordCount, wordPosition); 1304 | if (m1.size() == 0) 1305 | { 1306 | // No matches 1307 | done = true; 1308 | } 1309 | else 1310 | { 1311 | //Matches 1312 | matchVector.push_back(shared_ptr(new MatchInfo(shared_ptr(e), ""))); 1313 | for (shared_ptr mi2 : m1) 1314 | { 1315 | if(mi2->getMatchingSection() != "") 1316 | { 1317 | wordPosition += Grammar::splitString(mi2->getMatchingSection(), " ").size(); 1318 | } 1319 | } 1320 | matchVector.insert(matchVector.end(), m1.begin(), m1.end()); 1321 | } 1322 | } 1323 | } 1324 | 1325 | return matchVector; 1326 | } 1327 | 1328 | 1329 | /** 1330 | * Matches a string of words (test) against the specified Rule. 1331 | * If the test string does not match the Rule, will return a vector with a length/size of 0. 1332 | * \param [in] rule Rule being matched against 1333 | * \param [in] test std::string being matched 1334 | * \return vector> - vector of MatchInfo objects. Can have a size of 0 if test string does not match. 1335 | */ 1336 | vector> Grammar::matchesRule(const shared_ptr rule, const string & test) const { 1337 | vector words = Grammar::splitString(test, " "); 1338 | string* wordArray = &words[0]; 1339 | vector> m1 = getMatchingExpansions(rule->getRuleExpansion(), wordArray, words.size(), 0); 1340 | unsigned int matchCount = 0; 1341 | for (shared_ptr mi2 : m1) { 1342 | //std::cout << "MI: " << mi2->getMatchingSection() << ", " << matchCount << ", " << printExpansionType(mi2->getExpansion().get()) << std::endl; 1343 | 1344 | if (mi2->getMatchingSection() != "") { 1345 | matchCount++; 1346 | } 1347 | } 1348 | if(matchCount != words.size()) { // Must match all the words! 1349 | m1.clear(); 1350 | } 1351 | 1352 | return m1; 1353 | } 1354 | 1355 | /** 1356 | * Overloading convenience function for vector> Grammar::matchesRule(shared_ptr rule, string test) that accepts a string for the rule name. 1357 | * \param [in] ruleName Name of the Rule to be matched against 1358 | * \param [in] test string that will be matched against the Rule 1359 | * \return vector> - see vector> Grammar::matchesRule(shared_ptr rule, string test) 1360 | */ 1361 | vector> Grammar::matchesRule(const string & ruleName, const string & test) const { 1362 | shared_ptr r = getRule(ruleName); 1363 | if(!r) { // Check to make sure rule exists 1364 | vector> m; 1365 | return m; 1366 | } 1367 | return matchesRule(r, test); 1368 | } 1369 | 1370 | vector Grammar::getMatchingTags(vector> matchInfo) { 1371 | vector matchedTags; 1372 | for(shared_ptr m : matchInfo) { 1373 | if(m->getExpansion().get()->getType() == TAG) { 1374 | Tag * t = (Tag *) m->getExpansion().get(); 1375 | vector s = t->getTags(); 1376 | matchedTags.insert(matchedTags.end(), s.begin(), s.end()); 1377 | } 1378 | } 1379 | return matchedTags; 1380 | } 1381 | 1382 | std::vector Grammar::getMatchingTags(const std::string & test) const { 1383 | MatchResult m = match(test); 1384 | if(m.matches) { 1385 | return m.getMatchingTags(); 1386 | } 1387 | std::vector v; 1388 | return v; 1389 | } 1390 | 1391 | /** 1392 | * Static helper function that replaces the first occurrence of regex re in string s with the replacement string 1393 | * \param [in] s string that is being modified 1394 | * \param [in] re A string representing a C regex that will be matches against string s 1395 | * \param [in] replacement A string that will replace the first match of re against string s 1396 | * \return string The modified string with the first occurrence of re replaced with replacement 1397 | */ 1398 | string Grammar::replaceFirst(const string & s, const string & re, const string & replacement) 1399 | { 1400 | regex reg (re); 1401 | return regex_replace(s, reg, replacement, regex_constants::format_first_only); 1402 | } 1403 | 1404 | /** 1405 | * Static helper function that replaces all occurrences of regex re in string s with the replacement string 1406 | * \param [in] s string that is being modified 1407 | * \param [in] re A string representing a C regex that will be matches against string s 1408 | * \param [in] replacement A string that will replace the first match of re against string s 1409 | * \return string The modified string with all occurrences of re replaced with replacement 1410 | */ 1411 | string Grammar::replaceAll(const string & s, const string & re, const string & replacement) 1412 | { 1413 | regex reg (re); 1414 | return regex_replace(s, reg, replacement); 1415 | } 1416 | 1417 | ///Compliments to: https://stackoverflow.com/questions/16749069/c-split-string-by-regex for the below code 1418 | vector Grammar::splitString(const string & s, const string & rgx_str) 1419 | { 1420 | vector elems; 1421 | 1422 | regex rgx (rgx_str); 1423 | 1424 | sregex_token_iterator iter(s.begin(), s.end(), rgx, -1); 1425 | sregex_token_iterator end; 1426 | 1427 | while (iter != end) 1428 | { 1429 | elems.push_back(*iter); 1430 | ++iter; 1431 | } 1432 | 1433 | return elems; 1434 | } 1435 | 1436 | bool Grammar::stringContains(const string & part, const string & search) 1437 | { 1438 | size_t p = part.find(search); 1439 | return p != string::npos; 1440 | } 1441 | 1442 | bool Grammar::stringStartsWith(const string & s, const string & test) 1443 | { 1444 | return s.find(Grammar::trimString(test)) == 0; 1445 | } 1446 | 1447 | bool Grammar::stringEndsWith(const string & s, const string & test) 1448 | { 1449 | return (s.find(Grammar::trimString(test)) == s.length() - test.length()) && s.find(test) != string::npos; 1450 | } 1451 | 1452 | ///Helper function that returns true if char c is one of: ";=<>*+[]()|{}" 1453 | bool Grammar::isSpecialCharacter(char c) { 1454 | return c==';' || c=='=' || c=='<' || c=='>' ||\ 1455 | c=='*' || c=='+' || c=='[' || c==']' ||\ 1456 | c=='(' || c==')' || c=='|' || c=='{' ||\ 1457 | c=='}' || c==' '; 1458 | } 1459 | 1460 | ///Helper function that returns a string representation of the type of Expansion 1461 | std::string Grammar::printExpansionType(Expansion * e) { 1462 | switch(e->getType()) { 1463 | case UNPARSED_SECTION: 1464 | return "UNPARSED_SECTION"; 1465 | break; 1466 | case KLEENE_STAR: 1467 | return "KLEENE_STAR"; 1468 | break; 1469 | case PLUS_OPERATOR: 1470 | return "PLUS_OPERATOR"; 1471 | break; 1472 | case REQUIRED_GROUPING: 1473 | return "REQUIRED_GROUPING"; 1474 | break; 1475 | case OPTIONAL_GROUPING: 1476 | return "OPTIONAL_GROUPING"; 1477 | break; 1478 | case SEQUENCE: 1479 | return "SEQUENCE"; 1480 | break; 1481 | case TOKEN: 1482 | return "TOKEN"; 1483 | break; 1484 | case ALTERNATE_SET: 1485 | return "ALTERNATIVE_SET"; 1486 | break; 1487 | case TAG: 1488 | return "TAG"; 1489 | break; 1490 | default: 1491 | return "UNKNOWN TYPE"; 1492 | break; 1493 | } 1494 | return "ERROR"; 1495 | } 1496 | 1497 | ///Thanks for this trimming function: https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring 1498 | 1499 | // trim from start 1500 | inline std::string &Grammar::ltrim(std::string &s) { 1501 | s.erase(s.begin(), std::find_if(s.begin(), s.end(), 1502 | std::not1(std::ptr_fun(std::isspace)))); 1503 | return s; 1504 | } 1505 | 1506 | // trim from end 1507 | inline std::string &Grammar::rtrim(std::string &s) { 1508 | s.erase(std::find_if(s.rbegin(), s.rend(), 1509 | std::not1(std::ptr_fun(std::isspace))).base(), s.end()); 1510 | return s; 1511 | } 1512 | 1513 | 1514 | inline string Grammar::trimString(string input) 1515 | { 1516 | return ltrim(rtrim(input)); 1517 | } 1518 | -------------------------------------------------------------------------------- /src/KleeneStar.cpp: -------------------------------------------------------------------------------- 1 | #include "jsgfkitxx/KleeneStar.hpp" 2 | 3 | KleeneStar::KleeneStar() 4 | { 5 | //ctor 6 | } 7 | 8 | KleeneStar::~KleeneStar() 9 | { 10 | childExpansion.reset(); 11 | } 12 | 13 | Expansion * KleeneStar::clone() { 14 | return new KleeneStar(std::shared_ptr(this->getChild()->clone())); 15 | } 16 | 17 | KleeneStar::KleeneStar(std::shared_ptr e) { 18 | childExpansion = e; 19 | } 20 | 21 | std::string KleeneStar::getText() const { 22 | std::string s = childExpansion->getText(); 23 | s.append("*"); 24 | return s; 25 | } 26 | 27 | ExpansionType KleeneStar::getType() const { 28 | return KLEENE_STAR; 29 | } 30 | 31 | unsigned int KleeneStar::childCount() const { 32 | return hasChild() ? 1 : 0; 33 | } 34 | 35 | bool KleeneStar::hasChild() const { 36 | return childExpansion != nullptr; 37 | } 38 | 39 | bool KleeneStar::isOptional() const { 40 | return true; 41 | } 42 | 43 | void KleeneStar::replaceChild(std::shared_ptr newChild, const unsigned long index) { 44 | childExpansion = newChild; 45 | } 46 | 47 | std::shared_ptr KleeneStar::getChild(const unsigned int index) const { 48 | return childExpansion; 49 | } 50 | 51 | void KleeneStar::setChild(std::shared_ptr e) { 52 | childExpansion.reset(); 53 | childExpansion = e; 54 | } 55 | -------------------------------------------------------------------------------- /src/MatchInfo.cpp: -------------------------------------------------------------------------------- 1 | #include "jsgfkitxx/MatchInfo.hpp" 2 | 3 | MatchInfo::MatchInfo(std::shared_ptr e, std::string matchingStringPart) 4 | { 5 | expansion = e; 6 | matchingSection = matchingStringPart; 7 | } 8 | -------------------------------------------------------------------------------- /src/MatchResult.cpp: -------------------------------------------------------------------------------- 1 | #include "jsgfkitxx/MatchResult.hpp" 2 | #include "jsgfkitxx/Grammar.hpp" 3 | 4 | MatchResult::MatchResult() : matches(false) { } 5 | 6 | MatchResult::MatchResult(std::shared_ptr rule, MatchVector ml) : matches(true) 7 | { 8 | matchingRule = rule; 9 | matchVector = ml; 10 | } 11 | 12 | std::vector MatchResult::getMatchingTags() { 13 | return Grammar::getMatchingTags(matchVector); 14 | } 15 | 16 | const std::shared_ptr MatchResult::getMatchingRule() { 17 | return matchingRule; 18 | } 19 | -------------------------------------------------------------------------------- /src/MatchTracker.cpp: -------------------------------------------------------------------------------- 1 | #include "matchtracker.h" 2 | 3 | MatchTracker::MatchTracker(std::shared_ptr> words, unsigned int startIndex) 4 | { 5 | position = startIndex; 6 | wordList = words; 7 | reachedEnd = false; 8 | successful = false; 9 | } 10 | 11 | MatchTracker::MatchTracker(MatchTracker & t) { 12 | position = t.position; 13 | wordList = t.wordList; 14 | matchedTags = t.matchedTags; 15 | matchedRules = t.matchedRules; 16 | } 17 | 18 | MatchTracker MatchTracker::matchExpansion(std::shared_ptr> words, unsigned int startIndex, std::shared_ptr startExpansion) { 19 | MatchTracker t(words, startIndex); 20 | 21 | } -------------------------------------------------------------------------------- /src/OptionalGrouping.cpp: -------------------------------------------------------------------------------- 1 | #include "jsgfkitxx/OptionalGrouping.hpp" 2 | 3 | OptionalGrouping::OptionalGrouping() 4 | { 5 | //ctor 6 | } 7 | 8 | OptionalGrouping::~OptionalGrouping() 9 | { 10 | childExpansion.reset(); 11 | } 12 | 13 | Expansion * OptionalGrouping::clone() { 14 | return new OptionalGrouping(std::shared_ptr(this->getChild()->clone())); 15 | } 16 | 17 | OptionalGrouping::OptionalGrouping(std::shared_ptr e) { 18 | childExpansion = e; 19 | } 20 | 21 | std::string OptionalGrouping::getText() const { 22 | std::string s = "["; 23 | s.append(childExpansion->getText()); 24 | s.append("]"); 25 | return s; 26 | } 27 | 28 | ExpansionType OptionalGrouping::getType() const { 29 | return OPTIONAL_GROUPING; 30 | } 31 | 32 | unsigned int OptionalGrouping::childCount() const { 33 | return hasChild() ? 1 : 0; 34 | } 35 | 36 | void OptionalGrouping::replaceChild(std::shared_ptr newChild, const unsigned long index) { 37 | childExpansion = newChild; 38 | } 39 | 40 | bool OptionalGrouping::hasChild() const { 41 | return childExpansion != nullptr; 42 | } 43 | 44 | bool OptionalGrouping::isOptional() const { 45 | return true; 46 | } 47 | 48 | std::shared_ptr OptionalGrouping::getChild(const unsigned int index) const { 49 | return childExpansion; 50 | } 51 | 52 | void OptionalGrouping::setChild(std::shared_ptr e) { 53 | childExpansion = e; 54 | } 55 | -------------------------------------------------------------------------------- /src/PlusOperator.cpp: -------------------------------------------------------------------------------- 1 | #include "jsgfkitxx/PlusOperator.hpp" 2 | 3 | PlusOperator::PlusOperator() 4 | { 5 | //ctor 6 | } 7 | 8 | PlusOperator::PlusOperator(std::shared_ptr e) 9 | { 10 | childExpansion = e; 11 | } 12 | 13 | PlusOperator::~PlusOperator() 14 | { 15 | childExpansion.reset(); 16 | } 17 | 18 | Expansion * PlusOperator::clone() { 19 | return new PlusOperator(std::shared_ptr(this->getChild()->clone())); 20 | } 21 | 22 | std::string PlusOperator::getText() const { 23 | std::string s = childExpansion->getText(); 24 | s.append("+"); 25 | return s; 26 | } 27 | 28 | ExpansionType PlusOperator::getType() const { 29 | return PLUS_OPERATOR; 30 | } 31 | 32 | unsigned int PlusOperator::childCount() const { 33 | return hasChild() ? 1 : 0; 34 | } 35 | 36 | bool PlusOperator::hasChild() const { 37 | return childExpansion != nullptr; 38 | } 39 | 40 | bool PlusOperator::isOptional() const { 41 | if(this->hasChild()) { 42 | return this->getChild(0)->isOptional(); 43 | } 44 | return false; 45 | } 46 | 47 | void PlusOperator::replaceChild(std::shared_ptr newChild, const unsigned long index) { 48 | childExpansion = newChild; 49 | } 50 | 51 | std::shared_ptr PlusOperator::getChild(const unsigned int index) const { 52 | return childExpansion; 53 | } 54 | 55 | void PlusOperator::setChild(std::shared_ptr e) { 56 | childExpansion = e; 57 | } 58 | -------------------------------------------------------------------------------- /src/RequiredGrouping.cpp: -------------------------------------------------------------------------------- 1 | #include "jsgfkitxx/RequiredGrouping.hpp" 2 | 3 | RequiredGrouping::RequiredGrouping() 4 | { 5 | //ctor 6 | } 7 | 8 | RequiredGrouping::RequiredGrouping(std::shared_ptr e) 9 | { 10 | childExpansion = e; 11 | } 12 | 13 | RequiredGrouping::~RequiredGrouping() 14 | { 15 | childExpansion.reset(); 16 | } 17 | 18 | Expansion * RequiredGrouping::clone() { 19 | return new RequiredGrouping(std::shared_ptr(this->getChild()->clone())); 20 | } 21 | 22 | std::string RequiredGrouping::getText() const { 23 | std::string s = "("; 24 | s.append(childExpansion->getText()); 25 | s.append(")"); 26 | return s; 27 | } 28 | 29 | ExpansionType RequiredGrouping::getType() const { 30 | return REQUIRED_GROUPING; 31 | } 32 | 33 | unsigned int RequiredGrouping::childCount() const { 34 | return hasChild() ? 1 : 0; 35 | } 36 | 37 | bool RequiredGrouping::hasChild() const { 38 | return childExpansion != nullptr; 39 | } 40 | 41 | bool RequiredGrouping::isOptional() const { 42 | if(this->hasChild()) { 43 | return this->getChild(0)->isOptional(); 44 | } 45 | return true; 46 | } 47 | 48 | void RequiredGrouping::replaceChild(std::shared_ptr newChild, const unsigned long index) { 49 | childExpansion = newChild; 50 | } 51 | 52 | std::shared_ptr RequiredGrouping::getChild(const unsigned int index) const { 53 | return childExpansion; 54 | } 55 | 56 | void RequiredGrouping::setChild(std::shared_ptr e) { 57 | childExpansion = e; 58 | } 59 | -------------------------------------------------------------------------------- /src/Rule.cpp: -------------------------------------------------------------------------------- 1 | #include "jsgfkitxx/Rule.hpp" 2 | #include "jsgfkitxx/Grammar.hpp" 3 | 4 | Rule::Rule() 5 | { 6 | //ctor 7 | } 8 | 9 | Rule::Rule(std::string n, bool visible, std::shared_ptr e) 10 | { 11 | name = n; 12 | isVisible = visible; 13 | ruleExpansion = e; 14 | } 15 | 16 | Rule::~Rule() 17 | { 18 | ruleExpansion.reset(); 19 | } 20 | 21 | std::string Rule::getRuleString() const { 22 | if(isVisible) { 23 | return Grammar::trimString("public <" + name + "> = " + ruleExpansion->getText()) + ";"; 24 | } 25 | else { 26 | return Grammar::trimString("<" + name + "> = " + ruleExpansion->getText()) + ";"; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/RuleReference.cpp: -------------------------------------------------------------------------------- 1 | #include "jsgfkitxx/RuleReference.hpp" 2 | 3 | RuleReference::RuleReference(std::string ruleN) 4 | { 5 | ruleName = ruleN; 6 | } 7 | 8 | RuleReference::~RuleReference() 9 | { 10 | //dtor 11 | } 12 | 13 | Expansion * RuleReference::clone() { 14 | return new RuleReference(this->getRuleName()); 15 | } 16 | 17 | std::string RuleReference::getText() const { 18 | return "<" + ruleName + ">"; 19 | } 20 | 21 | /** Access ruleName; 22 | * \return The current value of ruleName; 23 | */ 24 | std::string RuleReference::getRuleName() const { 25 | return ruleName; 26 | } 27 | 28 | /** Set ruleName; 29 | * \param val New value to set 30 | */ 31 | void RuleReference::setRuleName(std::string val) { 32 | ruleName = val; 33 | } 34 | 35 | bool RuleReference::hasChild() const { 36 | return false; 37 | } 38 | 39 | bool RuleReference::isOptional() const { 40 | ///TODO: Access the list of rules and check to see if the rule expansion is optional 41 | return false; 42 | } 43 | 44 | unsigned int RuleReference::childCount() const { 45 | return 0; 46 | } 47 | 48 | std::shared_ptr RuleReference::getChild(const unsigned int index) const { 49 | return nullptr; 50 | } 51 | 52 | void RuleReference::replaceChild(std::shared_ptr newChild, const unsigned long index) { 53 | // Do nothing cause there are no children 54 | } 55 | 56 | ExpansionType RuleReference::getType() const { 57 | return RULE_REFERENCE; 58 | } 59 | -------------------------------------------------------------------------------- /src/Sequence.cpp: -------------------------------------------------------------------------------- 1 | #include "jsgfkitxx/Sequence.hpp" 2 | #include 3 | Sequence::Sequence() 4 | { 5 | //ctor 6 | } 7 | 8 | Sequence::~Sequence() 9 | { 10 | 11 | } 12 | 13 | Expansion * Sequence::clone() { 14 | Sequence * s = new Sequence(); 15 | std::vector> c = this->getChildren(); 16 | for(std::vector>::iterator childIterator = c.begin(); childIterator != c.end(); childIterator++) { 17 | s->addChild(std::shared_ptr((*childIterator)->clone())); 18 | } 19 | return s; 20 | } 21 | 22 | void Sequence::addChild(std::shared_ptr e) { 23 | children.push_back(e); 24 | } 25 | 26 | ///TODO: Implement this, and probably should be done better 27 | void Sequence::removeChild(Expansion & e) { 28 | if(std::count(children.begin(), children.end(), std::shared_ptr(&e)) != 0) { 29 | std::find(children.begin(), children.end(), std::shared_ptr(&e)); 30 | } 31 | } 32 | 33 | std::string Sequence::getText() const { 34 | std::vector>::const_iterator it; 35 | std::string s = ""; 36 | for(it = children.begin(); it != children.end(); it++) { 37 | s.append((*it)->getText()); 38 | s.append(" "); 39 | } 40 | return s; 41 | } 42 | 43 | unsigned int Sequence::childCount() const { 44 | return distance(children.begin(), children.end()); 45 | } 46 | 47 | /** Access children 48 | * \return The current value of children 49 | */ 50 | std::vector> Sequence::getChildren() const { 51 | return children; 52 | } 53 | 54 | bool Sequence::hasChild() const { 55 | return children.empty(); 56 | } 57 | 58 | ///Returns true if every child can be optional 59 | bool Sequence::isOptional() const { 60 | std::vector>::const_iterator it; 61 | for(it = children.begin(); it != children.end(); it++) { 62 | if(!(*it)->isOptional()) { 63 | return false; 64 | } 65 | } 66 | return true; 67 | } 68 | 69 | std::shared_ptr Sequence::getChild(const unsigned int index) const { 70 | return children[index]; 71 | } 72 | 73 | /** 74 | * Static Helper function that checks to see if the provided expansion is a sequence, and if it is, checks to see if the Sequence has only one child. If it has only one child, it sets the provided shared_pointer to point to the child Expansion. 75 | * TLDR; Simplifies singular child Sequence's to singular Expansions 76 | * \param [in,out] s Expansion that will be simplified if it is a Sequence with 1 child expansion 77 | */ 78 | void Sequence::simplifySequence(std::shared_ptr s) { 79 | if(s->getType() == SEQUENCE) { 80 | Sequence * sq = (Sequence*) s.get(); 81 | if(sq->childCount() == 1) { // We have a sequence that has only one child. We need to extract the child, destroy the sequence, and set the expansion to the extracted child 82 | s = sq->getChild(); // clone() makes a deep copy 83 | } 84 | else { 85 | //Do nothing to s 86 | } 87 | } 88 | } 89 | 90 | void Sequence::replaceChild(std::shared_ptr newChild, const unsigned long index) { 91 | children[index] = newChild; 92 | } 93 | 94 | ExpansionType Sequence::getType() const { 95 | return SEQUENCE; 96 | } 97 | -------------------------------------------------------------------------------- /src/Tag.cpp: -------------------------------------------------------------------------------- 1 | #include "jsgfkitxx/Tag.hpp" 2 | #include 3 | 4 | std::string Tag::getText() const { 5 | std::string s = childExpansion->getText(); 6 | std::vector::const_iterator it; 7 | for(it = strings.begin(); it != strings.end(); it++){ 8 | s.append(" {" + *it + "}"); 9 | } 10 | return s; 11 | } 12 | 13 | bool Tag::hasChild() const { 14 | return true; 15 | } 16 | 17 | void Tag::addTag(std::string t) { 18 | strings.push_back(t); 19 | } 20 | 21 | void Tag::removeTag(std::string t) { 22 | strings.erase(std::find(strings.begin(), strings.end(), t)); 23 | } 24 | 25 | int Tag::getTagCount() const 26 | { 27 | return tagCount; 28 | } 29 | 30 | Expansion * Tag::clone() { 31 | Tag * t = new Tag(std::shared_ptr(this->getChild()->clone())); 32 | std::vector tags = strings; 33 | std::vector::iterator it; 34 | for(it = tags.begin(); it != tags.end(); it++){ 35 | t->addTag(*it); 36 | } 37 | return t; 38 | } 39 | 40 | Tag::~Tag() { 41 | childExpansion.reset(); 42 | } 43 | 44 | Tag::Tag(std::shared_ptr e) 45 | { 46 | childExpansion = e; 47 | tagCount = 0; 48 | } 49 | 50 | Tag::Tag(std::shared_ptr e, std::string tag) { 51 | childExpansion = e; 52 | strings.push_back(tag); 53 | tagCount++; 54 | } 55 | 56 | Tag::Tag(std::shared_ptr e, unsigned short numberOfTags, std::string tags[]) { 57 | childExpansion = e; 58 | for(unsigned short i = 0; i < numberOfTags; i++) { 59 | strings.push_back(tags[i]); 60 | } 61 | tagCount += numberOfTags; 62 | } 63 | 64 | /** Access childExpansion 65 | * \return The current value of childExpansion 66 | */ 67 | std::shared_ptr Tag::getChild(const unsigned int index) const { 68 | return childExpansion; 69 | } 70 | 71 | void Tag::replaceChild(std::shared_ptr newChild, const unsigned long index) { 72 | childExpansion = newChild; 73 | } 74 | 75 | /** 76 | * Returns a vector of strings of the tags the child Expansion is tagged with. (All of the strings between the { } ) 77 | */ 78 | std::vector Tag::getTags() const { 79 | std::vector v; 80 | std::vector::const_iterator it; 81 | for(it = strings.begin(); it != strings.end(); it++){ 82 | v.push_back(*it); 83 | } 84 | return v; 85 | } 86 | 87 | /** Set childExpansion 88 | * \param val Disassociates with the current child and tags the new specified child Expansion. 89 | */ 90 | void Tag::setChildExpansion(std::shared_ptr val) { 91 | childExpansion = val; 92 | } 93 | 94 | ExpansionType Tag::getType() const { 95 | return TAG; 96 | } 97 | 98 | unsigned int Tag::childCount() const { 99 | return hasChild() ? 1 : 0; 100 | } 101 | -------------------------------------------------------------------------------- /src/Token.cpp: -------------------------------------------------------------------------------- 1 | #include "jsgfkitxx/Token.hpp" 2 | 3 | Token::Token() 4 | { 5 | //ctor 6 | } 7 | 8 | Token::Token(std::string s) { 9 | text = s; 10 | } 11 | 12 | Token::~Token() 13 | { 14 | 15 | } 16 | 17 | Expansion * Token::clone() { 18 | return new Token(this->getText()); 19 | } 20 | 21 | std::string Token::getText() const { 22 | return text; 23 | } 24 | 25 | bool Token::hasChild() const { 26 | return false; 27 | } 28 | 29 | bool Token::isOptional() const { 30 | return false; 31 | } 32 | 33 | std::shared_ptr Token::getChild(const unsigned int index) const { 34 | return nullptr; 35 | } 36 | 37 | /** Set text 38 | * \param val New value to set 39 | */ 40 | void Token::setText(std::string val) { 41 | text = val; 42 | } 43 | 44 | unsigned int Token::childCount() const { 45 | return 0; 46 | } 47 | 48 | void Token::replaceChild(std::shared_ptr newChild, const unsigned long index) { 49 | // Do nothing because Token's don't have children 50 | } 51 | 52 | ExpansionType Token::getType() const { 53 | return TOKEN; 54 | } 55 | -------------------------------------------------------------------------------- /src/UnparsedSection.cpp: -------------------------------------------------------------------------------- 1 | #include "jsgfkitxx/UnparsedSection.hpp" 2 | #include 3 | 4 | UnparsedSection::UnparsedSection() 5 | { 6 | //ctor 7 | } 8 | 9 | UnparsedSection::UnparsedSection(std::string s) 10 | { 11 | section = s; 12 | } 13 | 14 | UnparsedSection::~UnparsedSection() 15 | { 16 | 17 | } 18 | 19 | Expansion * UnparsedSection::clone() { 20 | return new UnparsedSection(this->getSection()); 21 | } 22 | 23 | std::string UnparsedSection::getSection() const { 24 | return section; 25 | } 26 | 27 | void UnparsedSection::setSection(std::string s) { 28 | section = s; 29 | } 30 | 31 | bool UnparsedSection::hasChild() const { 32 | return false; 33 | } 34 | 35 | unsigned int UnparsedSection::childCount() const { 36 | return 0; 37 | } 38 | 39 | std::shared_ptr UnparsedSection::getChild(const unsigned int index) const { 40 | return nullptr; 41 | } 42 | 43 | void UnparsedSection::replaceChild(std::shared_ptr newChild, const unsigned long index) { 44 | // Do nothing cause there are no children 45 | } 46 | 47 | std::string UnparsedSection::getText() const { 48 | return "UNPARSED SECTION:" + section; 49 | } 50 | 51 | ExpansionType UnparsedSection::getType() const { return UNPARSED_SECTION; } 52 | -------------------------------------------------------------------------------- /test/BatchTest/grammars/buckey/sphinx_mode.gram: -------------------------------------------------------------------------------- 1 | #JSGF v1.0;\ 2 | grammar sphinx-mode;\ 3 | public = {pause} | {resume} | {push-to-talk} | {toggle};\ 4 | = pause speech recognition;\ 5 | = resume speech recognition;\ 6 | = toggle speech recognition;\ 7 | = ((enable | enter | begin | start) {enable} | (disable | exit | end | stop) {disable}) push to talk [mode]; -------------------------------------------------------------------------------- /test/BatchTest/grammars/optional_testing/bare_sequence.gram: -------------------------------------------------------------------------------- 1 | #JSGFv1.0; 2 | grammar testing2; 3 | public = hello there world; 4 | -------------------------------------------------------------------------------- /test/BatchTest/grammars/optional_testing/lone_kleene.gram: -------------------------------------------------------------------------------- 1 | #JSGFv1.0; 2 | grammar testing2; 3 | public = hello*; 4 | -------------------------------------------------------------------------------- /test/BatchTest/grammars/optional_testing/lone_plus.gram: -------------------------------------------------------------------------------- 1 | #JSGFv1.0; 2 | grammar testing2; 3 | public = hello+; 4 | -------------------------------------------------------------------------------- /test/BatchTest/grammars/optional_testing/optional_alternate_set.gram: -------------------------------------------------------------------------------- 1 | #JSGFv1.0; 2 | grammar testing2; 3 | public = hello* | [hi] | howdy*; 4 | -------------------------------------------------------------------------------- /test/BatchTest/grammars/optional_testing/optional_parenthesis.gram: -------------------------------------------------------------------------------- 1 | #JSGFv1.0; 2 | grammar testing2; 3 | public = ( hello* ); 4 | -------------------------------------------------------------------------------- /test/BatchTest/grammars/optional_testing/optional_parenthesis2.gram: -------------------------------------------------------------------------------- 1 | #JSGFv1.0; 2 | grammar testing2; 3 | public = (hello)*; 4 | -------------------------------------------------------------------------------- /test/BatchTest/grammars/optional_testing/optional_sequence.gram: -------------------------------------------------------------------------------- 1 | #JSGFv1.0; 2 | grammar testing2; 3 | public = hello* [there] world*; 4 | -------------------------------------------------------------------------------- /test/BatchTest/grammars/optional_testing/required_grouping.gram: -------------------------------------------------------------------------------- 1 | #JSGFv1.0; 2 | grammar testing2; 3 | public = (hello there) world; 4 | -------------------------------------------------------------------------------- /test/BatchTest/grammars/optional_testing/required_set.gram: -------------------------------------------------------------------------------- 1 | #JSGFv1.0; 2 | grammar testing2; 3 | public = hello | hi | howdy; 4 | -------------------------------------------------------------------------------- /test/BatchTest/grammars/optional_testing/sequence_plus.gram: -------------------------------------------------------------------------------- 1 | #JSGFv1.0; 2 | grammar testing2; 3 | public = hello+ there world; 4 | -------------------------------------------------------------------------------- /test/BatchTest/grammars/optional_testing/sequence_with_tag.gram: -------------------------------------------------------------------------------- 1 | #JSGFv1.0; 2 | grammar testing2; 3 | public = hello {greeting} there world; 4 | -------------------------------------------------------------------------------- /test/BatchTest/grammars/public_rules/hello.gram: -------------------------------------------------------------------------------- 1 | #JSGFv1.0; 2 | grammar testing2; 3 | public = rarg | blarg; -------------------------------------------------------------------------------- /test/BatchTest/grammars/public_rules/helloworld.gram: -------------------------------------------------------------------------------- 1 | #JSGFv1.0; 2 | grammar helloworld1; 3 | public = hello ; 4 | = world | earth | Earth; -------------------------------------------------------------------------------- /test/BatchTest/grammars/public_rules/lots_of_rules.gram: -------------------------------------------------------------------------------- 1 | #JSGFv1.0; 2 | grammar testing2; 3 | public = | | | | hello [world]; 4 | public = garg +; 5 | = blarg* ; 6 | = hello | hi [there how are you?]; 7 | = let\'S make sure that eSc4p1ng & token (parsing works); -------------------------------------------------------------------------------- /test/BatchTest/grammars/public_rules/optional_at_beginning.gram: -------------------------------------------------------------------------------- 1 | #JSGFv1.0; 2 | grammar optionalbracketsbeginning; 3 | public = hello* there world; 4 | -------------------------------------------------------------------------------- /test/BatchTest/grammars/public_rules/optional_at_end.gram: -------------------------------------------------------------------------------- 1 | #JSGFv1.0; 2 | grammar optionalbrackets; 3 | public = hello {hi} [world {world}]; 4 | -------------------------------------------------------------------------------- /test/BatchTest/optional_test.csv: -------------------------------------------------------------------------------- 1 | grammars/optional_testing/bare_sequence.gram,test,required 2 | grammars/optional_testing/lone_plus.gram,test,required 3 | grammars/optional_testing/lone_kleene.gram,test,optional 4 | grammars/optional_testing/required_set.gram,test,required 5 | grammars/optional_testing/required_grouping.gram,test,required 6 | grammars/optional_testing/optional_parenthesis.gram,test,optional 7 | grammars/optional_testing/optional_parenthesis2.gram,test,optional 8 | grammars/optional_testing/sequence_plus.gram,test,required 9 | grammars/optional_testing/sequence_with_tag.gram,test,required 10 | grammars/optional_testing/optional_alternate_set.gram,test,optional 11 | grammars/optional_testing/optional_sequence.gram,test,optional 12 | -------------------------------------------------------------------------------- /test/BatchTest/rules_test.csv: -------------------------------------------------------------------------------- 1 | grammars/public_rules/hello.gram,blarg,rarg 2 | grammars/public_rules/helloworld.gram,hello earth,hello 3 | grammars/public_rules/optional_at_end.gram,hello world,working 4 | grammars/public_rules/optional_at_end.gram,hello,working 5 | grammars/public_rules/optional_at_beginning.gram,hello there world,working 6 | grammars/public_rules/optional_at_beginning.gram,hello hello hello there world,working 7 | grammars/public_rules/optional_at_beginning.gram,there world,working 8 | grammars/public_rules/repeated_sequence.gram,hello hello world,working 9 | grammars/public_rules/repeated_sequence.gram,hello hello hello hello world,working 10 | grammars/public_rules/repeated_sequence.gram,hello world, 11 | grammars/public_rules/lots_of_rules.gram,blarg blarg blarg blarg,rarg 12 | grammars/public_rules/lots_of_rules.gram,hello,rarg 13 | grammars/public_rules/lots_of_rules.gram,hello let'S make sure that eSc4p1ng & token (parsing works),rarg 14 | grammars/public_rules/lots_of_rules.gram,hello world,rarg 15 | grammars/public_rules/lots_of_rules.gram,hello world,rarg 16 | -------------------------------------------------------------------------------- /test/BatchTest/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "JSGF Kit++ Batch Grammar Tester" 4 | if [ ! -f parsetest ]; then 5 | if [ -f ../parsetest ]; then 6 | cp ../parsetest parsetest 7 | else 8 | echo "ERROR: Please build and copy the parsetest example program into this directory!" 9 | exit -2; 10 | fi 11 | fi 12 | 13 | if [ ! -f jsgfmatch ]; then 14 | if [ -f ../jsgfmatch ]; then 15 | cp ../jsgfmatch jsgfmatch 16 | else 17 | echo "ERROR: Please build and copy the jsgfmatch example program into this directory!" 18 | exit -2; 19 | fi 20 | fi 21 | if [ ! -f optionaltest ]; then 22 | if [ -f ../optionaltest ]; then 23 | cp ../optionaltest optionaltest 24 | else 25 | echo "ERROR: Please build and copy the optionaltest example program into this directory!" 26 | exit -2; 27 | fi 28 | fi 29 | 30 | 31 | echo "Running parsing tests..." 32 | for g in grammars/*/*; do 33 | ./parsetest $g &> grammar_test.log 34 | if [ ! $? -eq 0 ]; then 35 | echo "Test failed on grammar: $g ! Logged to grammar_test.log" 36 | exit -1; 37 | fi 38 | done 39 | 40 | echo "Running optional detection tests..." 41 | while IFS=, read -r grammar rule expectedResult 42 | do 43 | a=$( echo $rule | ./optionaltest $grammar ) 44 | if [ "$a" != "$expectedResult" ]; then 45 | echo "For grammar $grammar , the \"$rule\" rule was declared \"$a\", but \"$expectedResult\" was expected!" 46 | exit -1; 47 | fi 48 | done < optional_test.csv 49 | 50 | echo "Running matching tests..." 51 | echo "Testing rules..." 52 | while IFS=, read -r grammar inputTest expectedResult 53 | do 54 | a=$( echo $inputTest | ./jsgfmatch -r -f $grammar ) 55 | if [ "$a" != "$expectedResult" ]; then 56 | echo "For grammar $grammar , input of \"$inputTest\" produced \"$a\", but \"$expectedResult\" was expected!" 57 | exit -1; 58 | fi 59 | done < rules_test.csv 60 | 61 | echo "Tests passed!" 62 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable(parse-test ParseAndOutputJSGF.cpp) 3 | target_link_libraries(parse-test PRIVATE jsgfkitxx) 4 | target_include_directories(parse-test 5 | PUBLIC ${PROJECT_BINARY_DIR}/include 6 | ) 7 | 8 | add_executable(expansion-type-test ExpansionTypeTest.cpp) 9 | target_link_libraries(expansion-type-test PRIVATE jsgfkitxx) 10 | target_include_directories(expansion-type-test 11 | PUBLIC ${PROJECT_BINARY_DIR}/include 12 | ) 13 | 14 | add_executable(optional-test IsOptionalTest.cpp) 15 | target_link_libraries(optional-test jsgfkit) 16 | target_include_directories(jsgfkitxx 17 | PUBLIC ${PROJECT_BINARY_DIR}/include 18 | ) 19 | 20 | add_executable(jsgf-match MatchingTest.cpp) 21 | target_link_libraries(jsgf-match jsgfkit) 22 | target_include_directories(jsgfkitxx 23 | PUBLIC ${PROJECT_BINARY_DIR}/include 24 | ) 25 | -------------------------------------------------------------------------------- /test/ExpansionTypeTest.cpp: -------------------------------------------------------------------------------- 1 | #include "jsgfkitxx/Grammar.hpp" 2 | #include "jsgfkitxx/Expansion.hpp" 3 | 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | void testCall(Expansion * e) { 10 | cout << e->getText() << endl; 11 | if(e->getType() == TOKEN) { 12 | cout << "TOKEN" << endl; 13 | } 14 | } 15 | 16 | int main() { 17 | cout << "sa" << endl; 18 | Grammar g; 19 | Grammar::parseGrammarFromString("grammar test;\ 20 | public = one;", g); 21 | cout << "s" << endl; 22 | g.walkGrammar(testCall); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /test/IsOptionalTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "grammar.h" 3 | 4 | using namespace std; 5 | 6 | /** 7 | * \file IsOptionalTest.cpp 8 | * A simple program that tests to see if the user specified rule is optional or not. 9 | */ 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | Grammar g; 14 | if(argc > 1) { 15 | std::ifstream inputFile; 16 | inputFile.open(argv[1]); 17 | if(inputFile.good()) { 18 | Grammar::parseGrammar(inputFile, g); 19 | } 20 | } 21 | else { 22 | //Parsing the string to create a Grammar object 23 | cerr << "Please specify the grammar file!" << endl; 24 | return -1; 25 | } 26 | 27 | //Generate a text version of our Grammar object 28 | //cout << "PARSED GRAMMAR:" << endl; 29 | //cout << g.getText() << endl << endl; 30 | 31 | string test; 32 | getline(cin,test); 33 | 34 | shared_ptr r = g.getRule(test); 35 | if(r != nullptr) { 36 | string s = r->getRuleExpansion()->isOptional() ? "optional" : "required"; 37 | cout << s << endl; 38 | } 39 | else { 40 | cerr << "Could not find rule: "<< test << "!" << endl; 41 | return -1; 42 | } 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /test/MatchingTest.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "grammar.h" 8 | 9 | using namespace std; 10 | 11 | /** 12 | * \file MatchingTest.cpp 13 | * A simple program that shows how to parse a JSGF string to create a new Grammar object and then match an input string against the grammar's rules. 14 | * Takes a file given via the command line, parses it, and then matches an input string taken from stdin to match against it. 15 | * Can print the name of the rule it matches, or the tags that it matches. This can be specified through the command line options. 16 | */ 17 | 18 | void printUsage() { 19 | cout << "JSGF Matching" << endl << \ 20 | "Usage:" << endl << \ 21 | "\tjsgfmatch -f GRAMMAR_FILE (-r | -t)" << endl << endl << \ 22 | "\t-f\tSpecifies the path to the grammar file to parse." << endl << \ 23 | "\t-r\tSpecifies to print out the public rule that the input matches, if any." << endl << \ 24 | "\t-t\tSpecifies to print out the tag(s) that the input matches, if any." << endl << endl <<\ 25 | "\t-p\tSpecifies to print out the grammar once it is parsed." << endl <<\ 26 | "When printing matching tags, if there are multiple tags, they will be printed out separated by commas in order of appearance. The tags will not include the { } characters." << endl << endl << \ 27 | "Author: Tyler Sengia (tylersengia@gmail.com)" << endl; 28 | } 29 | 30 | int main(int argc, char *argv[]) 31 | { 32 | // Options specified by the user via command line 33 | bool printRule = false; 34 | bool printTags = false; 35 | bool printParsed = false; 36 | bool grammarSet = false; 37 | char * grammarPath; 38 | 39 | //Parse command line options 40 | int opt; 41 | while((opt = getopt(argc, argv, "rtpf:")) != -1) { 42 | switch(opt) { 43 | case 'r': 44 | printRule = true; 45 | break; 46 | case 'p': 47 | printParsed = true; 48 | break; 49 | case 't': 50 | printTags = true; 51 | break; 52 | case 'f': 53 | grammarSet = true; 54 | grammarPath = optarg; 55 | break; 56 | default: 57 | cerr << "Unrecognized command option" << endl; 58 | printUsage(); 59 | return -1; 60 | break; 61 | } 62 | } 63 | 64 | if(!grammarSet) { 65 | cerr << "Please specify the grammar file to parse!" << endl; 66 | printUsage(); 67 | return -1; 68 | } 69 | 70 | if (!printRule && !printTags) { 71 | cerr << "Please specify to print either tags (-t) or rules (-r)!" << endl; 72 | printUsage(); 73 | return -1; 74 | } 75 | 76 | Grammar g; 77 | std::ifstream inputFile; 78 | inputFile.open(grammarPath); 79 | if(inputFile.good()) { 80 | Grammar::parseGrammar(inputFile, g); 81 | } 82 | else { 83 | cerr << "Failed to open the specified grammar file!" << endl; 84 | return -1; 85 | } 86 | 87 | if(printParsed) { 88 | cout << g.getText() << endl; 89 | } 90 | 91 | string test = ""; 92 | getline(cin, test); 93 | 94 | // MatchResult r = g.match(test); 95 | 96 | //Print out the public rule the input matches 97 | if(printRule) { 98 | string s = g.getMatchingPublicRule(test); 99 | if(s != "") { 100 | cout << s << endl; 101 | } 102 | } 103 | 104 | //Print out the matching tags, separatd by commas 105 | if(printTags) { 106 | vector tags = g.getMatchingTags(test); 107 | if(tags.size() > 0) { 108 | unsigned short i = 0; 109 | for(; i < tags.size()-1; i++) { 110 | cout << tags[i] << ","; 111 | } 112 | if(i == tags.size()-1) { 113 | cout << tags[i]; 114 | } 115 | } 116 | } 117 | 118 | return 0; 119 | } 120 | -------------------------------------------------------------------------------- /test/ParseAndOutputJSGF.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "jsgfkitxx/Grammar.hpp" 3 | 4 | using namespace std; 5 | 6 | /** 7 | * \file ParseAndOutputJSGF.cpp 8 | * A simple program that shows how to parse a JSGF string to create a new Grammar object and then generate JSGF text using that Grammar object. 9 | * You can either use it with no command line options and it will use a built in sample grammar. 10 | * It can also open up a file given via the command line, parse it, and echo it. 11 | */ 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | // string that we will be parsing to create a JSGF Grammar object 16 | string s = "\ 17 | grammar test-gram;\ 18 | public = hello {greeting} {hi} there; \ 19 | = [blarg | (rarg | hraaanngggg)];\ 20 | = hi {greeting} how are (you {you} | they {others}) today;\ 21 | "; 22 | Grammar g; 23 | if(argc > 1) { 24 | std::ifstream inputFile; 25 | inputFile.open(argv[1]); 26 | if(inputFile.good()) { 27 | Grammar::parseGrammar(inputFile, g); 28 | } 29 | } 30 | else { 31 | //Parsing the string to create a Grammar object 32 | Grammar::parseGrammarFromString(s, g); 33 | } 34 | 35 | //Generate a text version of our Grammar object 36 | cout << "PARSED GRAMMAR:" << endl; 37 | cout << g.getText() << endl << endl; 38 | 39 | cout << "Test Done" << endl; 40 | return 0; 41 | } 42 | --------------------------------------------------------------------------------