feisty meow concerns codebase  2.140
test_state_machine.cpp
Go to the documentation of this file.
1 /*****************************************************************************\
2 * *
3 * Name : test_state_machine *
4 * Author : Chris Koeritz *
5 * *
6 * Purpose: *
7 * *
8 * Sets up a simple state machine for a pattern matcher. *
9 * *
10 *******************************************************************************
11 * Copyright (c) 1992-$now By Author. This program is free software; you can *
12 * redistribute it and/or modify it under the terms of the GNU General Public *
13 * License as published by the Free Software Foundation; either version 2 of *
14 * the License or (at your option) any later version. This is online at: *
15 * http://www.fsf.org/copyleft/gpl.html *
16 * Please send any updates to: fred@gruntose.com *
17 \*****************************************************************************/
18 
19 // This simple state machine recognizes all strings that are of the form
20 // *.[cho], which means any string with either .o, .c, or .h on the end.
21 
24 #include <basis/astring.h>
25 #include <basis/byte_array.h>
26 #include <basis/functions.h>
27 #include <basis/guards.h>
29 #include <filesystem/byte_filer.h>
30 #include <loggers/console_logger.h>
35 #include <unit_test/unit_base.h>
36 
37 #include <string.h>
38 
39 using namespace application;
40 using namespace basis;
41 using namespace configuration;
42 //using namespace mathematics;
43 using namespace filesystem;
44 using namespace loggers;
45 using namespace processes;
46 //using namespace structures;
47 //using namespace textual;
48 using namespace timely;
49 using namespace unit_test;
50 
51 #undef LOG
52 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), astring(s))
53 
55 
57 
58 //#undef LOG
59 //#define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
60 
61 class test_state_machine : public application_shell
62 {
63 public:
64  test_state_machine();
65  DEFINE_CLASS_NAME("test_state_machine");
66  int execute();
67  void setup_state_machine(transition_map &recog);
68  void print_instructions();
69 };
70 
72 {
73  switch (to_show) {
74  case GET_ANY: return "get_any";
75  case GOT_DOT: return "got_dot";
76  case GOT_CHO: return "got_cho";
77  case RECOGNIZED: return "recognized";
78  case REJECTED: return "rejected";
79  default: return "unknown!";
80  }
81 }
82 
83 #define TEST(action) \
84  if (!action) \
85  deadly_error(class_name(), "setup", a_sprintf("failed on %s", #action))
86 
87 void test_state_machine::setup_state_machine(transition_map &recog)
88 {
89  TEST(recog.add_state(GET_ANY));
90  TEST(recog.add_state(GOT_DOT));
91  TEST(recog.add_state(GOT_CHO));
92  TEST(recog.add_state(RECOGNIZED));
93  TEST(recog.add_state(REJECTED));
94 
95  TEST(recog.set_start(GET_ANY));
96 
97 // bogus for test!
98 //TEST(recog.add_range_transition(GET_ANY, 29378, 0, 0));
99 
100  TEST(recog.add_range_transition(GET_ANY, REJECTED, 0, 0));
101  TEST(recog.add_range_transition(GET_ANY, GET_ANY, 1, '.'-1));
102  TEST(recog.add_range_transition(GET_ANY, GOT_DOT, '.', '.'));
103  TEST(recog.add_range_transition(GET_ANY, GET_ANY, '.'+1, 255));
104 
105  // we wouldn't need so many cases if the update function made the
106  // jump out of GOT_DOT when an unsatisfactory pulse is seen?
107  TEST(recog.add_range_transition(GOT_DOT, REJECTED, '\0', '\0'));
108  TEST(recog.add_range_transition(GOT_DOT, GET_ANY, 1, '.'-1));
109  TEST(recog.add_range_transition(GOT_DOT, GOT_DOT, '.', '.'));
110  TEST(recog.add_range_transition(GOT_DOT, GET_ANY, '.'+1, 'c'-1));
111  TEST(recog.add_range_transition(GOT_DOT, GOT_CHO, 'c', 'c'));
112  TEST(recog.add_range_transition(GOT_DOT, GET_ANY, 'c'+1, 'h'-1));
113  TEST(recog.add_range_transition(GOT_DOT, GOT_CHO, 'h', 'h'));
114  TEST(recog.add_range_transition(GOT_DOT, GET_ANY, 'h'+1, 'o'-1));
115  TEST(recog.add_range_transition(GOT_DOT, GOT_CHO, 'o', 'o'));
116  TEST(recog.add_range_transition(GOT_DOT, GET_ANY, 'o'+1, 255));
118 
119  TEST(recog.add_range_transition(GOT_CHO, RECOGNIZED, '\0', '\0'));
120  TEST(recog.add_range_transition(GOT_CHO, GET_ANY, 1, '.'-1));
121  TEST(recog.add_range_transition(GOT_CHO, GOT_DOT, '.', '.'));
122  TEST(recog.add_range_transition(GOT_CHO, GET_ANY, '.'+1, 255));
123 
124  int hosed;
125  if (recog.validate(hosed) != transition_map::OKAY)
126  deadly_error(class_name(), "check_machine",
127  astring(astring::SPRINTF, "invalid state_machine due to state %d", hosed).s());
128 }
129 
130 test_state_machine::test_state_machine()
132 {}
133 
135 {
136  critical_events::alert_message("\
137 This program demonstrates the state machine class by constructing a machine\n\
138 that recognizes the regular expression *.[cho], which is all strings ending\n\
139 in a period followed by c, h, or o. The first parameter is the name of a\n\
140 file to load strings from.\n");
141 }
142 
143 int test_state_machine::execute()
144 {
145  FUNCDEF("execute");
146  transition_map recog;
147  setup_state_machine(recog);
148 
150  if (application::_global_argc < 2) {
151  // automating for unit test with a simple input file.
152  filename = "./input_data_state_machine.txt";
155  } else {
157  }
159  if (!filename) {
161  return 1;
162  }
164 
165  byte_filer fil(filename.s(), "r");
166 
167 //LOG(astring("filename is ") + filename.raw());
168 
169  while (!fil.eof()) {
170  state_machine m;
171  recog.reset(m);
172 
173  byte_array typed_string(901);
174 //need a getline function for byte filer...
175  fil.getline(typed_string, 900);
176 
177  int len = int(strlen((char *)typed_string.access()));
178  if (!len) continue; // skip blank lines...
179 
180  int position = 0;
181  while ( (m.current() != RECOGNIZED) && (m.current() != REJECTED) ) {
182  recog.pulse(m, typed_string[position++]);
183  }
184 
185  if (m.current() == RECOGNIZED) {
186  LOG((char *)typed_string.access());
187  } else if (m.current() == REJECTED) {
188  LOG("rejected...");
189  } else {
190  LOG("unknown final state!");
191  }
192  }
193  return 0;
194 }
195 
197 
198 HOOPLE_MAIN(test_state_machine, )
199 
int print_instructions(bool good, const astring &program_name)
Definition: checker.cpp:45
The application_shell is a base object for console programs.
Provides a dynamically resizable ASCII character string.
Definition: astring.h:35
const char * s() const
synonym for observe. the 's' stands for "string", if that helps.
Definition: astring.h:113
A very common template for a dynamic array of bytes.
Definition: byte_array.h:36
Provides file managment services using the standard I/O support.
Definition: byte_filer.h:32
Provides operations commonly needed on file names.
Definition: filename.h:64
Monitors objects with multiple states and the transitions between states.
Definition: state_machine.h:45
int current() const
returns the current state.
Definition: state_machine.h:79
The transition_map manages state machines and causes state changes to occur.
bool add_range_transition(int current, int next, int low, int high)
adds a transition that listens to triggers in the pulse() method.
bool set_start(int starting_state)
assigns a state as the first state.
bool pulse(state_machine &m, int trigger)
applies a "trigger" to possibly cause a range transition.
bool reset(state_machine &m)
bool add_state(int state_number)
registers a legal state in the transition_map.
basis::outcome validate(int &examine)
checks to that all required transition conditions are satisfied.
#define deadly_error(c, f, i)
#define DEFINE_CLASS_NAME(objname)
Defines the name of a class by providing a couple standard methods.
Definition: enhance_cpp.h:42
#define FUNCDEF(func_in)
FUNCDEF sets the name of a function (and plugs it into the callstack).
Definition: enhance_cpp.h:54
Provides macros that implement the 'main' program of an application.
#define HOOPLE_MAIN(obj_name, obj_args)
options that should work for most unix and linux apps.
Definition: hoople_main.h:61
Implements an application lock to ensure only one is running at once.
char ** _global_argv
The guards collection helps in testing preconditions and reporting errors.
Definition: array.h:30
A platform independent way to obtain the timestamp of a file.
Definition: byte_filer.cpp:37
A logger that sends to the console screen using the standard output device.
#include <time.h>
Definition: earth_time.cpp:37
Useful support functions for unit testing, especially within hoople.
Definition: unit_base.cpp:35
recognizer_states
@ RECOGNIZED
@ REJECTED
#define LOG(s)
astring state_text(recognizer_states to_show)
#define TEST(action)