Design Pattern Examples
Overview of object-oriented design patterns
State_RemoveComments.c
Go to the documentation of this file.
1
6
7#include <stdlib.h>
8#include <stdio.h>
9#include <string.h>
11
15typedef enum
16{
30
31struct StateContext; // forward reference
37
42{
43 EOF_CHAR = (char)-1
44};
45
46
50typedef struct StateContext
51{
52 const char* inputText;
53 size_t textIndex;
54 char* outputText;
59
60//=============================================================================
61//=============================================================================
62
69static const char* _CurrentStateToString(CurrentState state)
70{
71 // Need something for formatted error string that persists beyond this function.
72 static char errorBuffer[64] = { 0 };
73 const char* stateAsString = NULL;
74
75 switch (state)
76 {
77 case State_Initial:
78 stateAsString = "Initial";
79 break;
80
82 stateAsString = "NormalText";
83 break;
84
86 stateAsString = "DoubleQuotedText";
87 break;
88
90 stateAsString = "SingleQuotedText";
91 break;
92
94 stateAsString = "EscapedDoubleQuoteText";
95 break;
96
98 stateAsString = "EscapedSingleQuoteText";
99 break;
100
102 stateAsString = "StartComment";
103 break;
104
106 stateAsString = "LineComment";
107 break;
108
110 stateAsString = "BlockComment";
111 break;
112
114 stateAsString = "EndBlockComment";
115 break;
116
117 case State_Done:
118 stateAsString = "Done";
119 break;
120
121 case State_Error:
122 stateAsString = "Error";
123 break;
124
125 default:
126 {
127 errorBuffer[0] = '\0';
128 int num_chars = snprintf(errorBuffer, sizeof(errorBuffer), "Unknown state (%d)", state);
129 if (num_chars >= 0)
130 {
131 stateAsString = errorBuffer;
132 }
133 else
134 {
135 stateAsString = "Error converting state to string";
136 }
137 }
138 break;
139 }
140
141
142 return stateAsString;
143}
144
145
151static char _GetNextCharacter(StateContext* context)
152{
153 char character = EOF_CHAR;
154
155 if (context != NULL)
156 {
157 if (context->inputText[context->textIndex] != '\0')
158 {
159 character = context->inputText[context->textIndex];
160 context->textIndex++;
161 }
162 }
163 return character;
164}
165
173static void _OutputCharacter(StateContext* context, char character)
174{
175 if (context != NULL && character != EOF_CHAR)
176 {
177 context->outputText[context->outputTextIndex] = character;
178 context->outputTextIndex++;
179 context->outputText[context->outputTextIndex] = '\0';
180 }
181}
182
183//=============================================================================
184//=============================================================================
185
205{
206 CurrentState nextState = State_Error;
207
208 if (context != NULL)
209 {
210 char character = _GetNextCharacter(context);
211 nextState = State_NormalText;
212
213 switch (character)
214 {
215 case EOF_CHAR:
216 nextState = State_Done;
217 break;
218
219 case '"':
220 _OutputCharacter(context, character);
221 nextState = State_DoubleQuotedText;
222 break;
223
224 case '\'':
225 _OutputCharacter(context, character);
226 nextState = State_SingleQuotedText;
227 break;
228
229 case '/':
230 nextState = State_StartComment;
231 break;
232
233 default:
234 _OutputCharacter(context, character);
235 break;
236 }
237 }
238
239 return nextState;
240}
241
260{
261 CurrentState nextState = State_Error;
262
263 if (context != NULL)
264 {
265 char character = _GetNextCharacter(context);
266 nextState = State_DoubleQuotedText;
267
268 switch (character)
269 {
270 case EOF_CHAR:
271 nextState = State_Done;
272 break;
273
274 case '"':
275 _OutputCharacter(context, character);
276 nextState = State_NormalText;
277 break;
278
279 case '\\':
280 _OutputCharacter(context, character);
282 break;
283
284 default:
285 _OutputCharacter(context, character);
286 break;
287 }
288 }
289
290 return nextState;
291}
292
311{
312 CurrentState nextState = State_Error;
313
314 if (context != NULL)
315 {
316 char character = _GetNextCharacter(context);
317 nextState = State_SingleQuotedText;
318 switch (character)
319 {
320 case EOF_CHAR:
321 nextState = State_Done;
322 break;
323
324 case '\'':
325 _OutputCharacter(context, character);
326 nextState = State_NormalText;
327 break;
328
329 case '\\':
330 _OutputCharacter(context, character);
332 break;
333
334 default:
335 _OutputCharacter(context, character);
336 break;
337 }
338 }
339
340 return nextState;
341}
342
362{
363 CurrentState nextState = State_Error;
364
365 if (context != NULL)
366 {
367 char character = _GetNextCharacter(context);
368 nextState = State_DoubleQuotedText;
369 switch (character)
370 {
371 case EOF_CHAR:
372 nextState = State_Done;
373 break;
374
375 default:
376 _OutputCharacter(context, character);
377 break;
378 }
379
380 }
381
382 return nextState;
383}
384
404{
405 CurrentState nextState = State_Error;
406
407 if (context != NULL)
408 {
409 char character = _GetNextCharacter(context);
410 nextState = State_SingleQuotedText;
411 switch (character)
412 {
413 case EOF_CHAR:
414 nextState = State_Done;
415 break;
416
417 default:
418 _OutputCharacter(context, character);
419 break;
420 }
421
422 }
423
424 return nextState;
425}
426
446{
447 CurrentState nextState = State_Error;
448
449 if (context != NULL)
450 {
451 char character = _GetNextCharacter(context);
452 nextState = State_StartComment;
453 switch (character)
454 {
455 case EOF_CHAR:
456 nextState = State_Done;
457 break;
458
459 case '/':
460 nextState = State_LineComment;
461 break;
462
463 case '*':
464 nextState = State_BlockComment;
465 break;
466
467 default:
468 // Not the start of a comment so output the leading slash
469 // that led to the state followed by the character we just
470 // processed.
471 _OutputCharacter(context, '/');
472 _OutputCharacter(context, character);
473 nextState = State_NormalText;
474 break;
475 }
476
477 }
478
479 return nextState;
480}
481
498{
499 CurrentState nextState = State_Error;
500
501 if (context != NULL)
502 {
503 char character = _GetNextCharacter(context);
504 nextState = State_LineComment;
505 switch (character)
506 {
507 case EOF_CHAR:
508 nextState = State_Done;
509 break;
510
511 case '\n':
512 _OutputCharacter(context, character);
513 nextState = State_NormalText;
514 break;
515
516 default:
517 // We are in a comment to be removed, so do nothing here.
518 break;
519 }
520
521 }
522
523 return nextState;
524}
525
542{
543 CurrentState nextState = State_Error;
544
545 if (context != NULL)
546 {
547 char character = _GetNextCharacter(context);
548 nextState = State_BlockComment;
549 switch (character)
550 {
551 case EOF_CHAR:
552 nextState = State_Done;
553 break;
554
555 case '*':
556 nextState = State_EndBlockComment;
557 break;
558
559 default:
560 // We are in a comment to be removed, so do nothing here.
561 break;
562 }
563 }
564
565 return nextState;
566}
567
585{
586 CurrentState nextState = State_Error;
587
588 if (context != NULL)
589 {
590 char character = _GetNextCharacter(context);
591 nextState = State_BlockComment;
592 switch (character)
593 {
594 case EOF_CHAR:
595 nextState = State_Done;
596 break;
597
598 case '/':
599 nextState = State_NormalText;
600 break;
601
602 default:
603 // We are still in a block comment to be removed, so do nothing here.
604 break;
605 }
606
607 }
608
609 return nextState;
610}
611
627{
628 CurrentState nextState = State_Error;
629
630 if (context != NULL)
631 {
632 // Do nothing (Yes! Another Null Object example!)
633 nextState = State_Done;
634 }
635
636 return nextState;
637}
638
639
640
641//=============================================================================
642//=============================================================================
643
648typedef struct
649{
653
669 { State_Initial , NULL }, // end of list
670};
671
680{
681 StateFunctionPtr functionPtr = NULL;
682
683 for (size_t index = 0; _stateHandlers[index].stateHandler != NULL; index++)
684 {
685 if (_stateHandlers[index].state == state)
686 {
687 functionPtr = _stateHandlers[index].stateHandler;
688 break;
689 }
690 }
691
692 if (functionPtr == NULL)
693 {
694 printf("Error! _GetStateFunction() received an unrecognized state. Cannot retrieve the state function.\n");
695 }
696
697 return functionPtr;
698}
699
700
713static bool _SetNextState(StateContext *context, CurrentState newState)
714{
715 bool success = false;
716
717 if (context != NULL)
718 {
719 success = true;
720 if (newState != context->currentState)
721 {
722 context->currentStateBehavior = _GetStateFunction(newState);
723 if (context->currentStateBehavior != NULL)
724 {
725 printf(" --> State Transition: %s -> %s\n",
727 _CurrentStateToString(newState));
728
729 context->currentState = newState;
730 }
731 else
732 {
733 success = false;
734 }
735 }
736 }
737
738 return success;
739}
740
741
743// State_RemoveComments()
745bool State_RemoveComments(const char* text, DynamicString* filteredText)
746{
747 bool success = false;
748
749 if (text != NULL && filteredText != NULL)
750 {
751 StateContext context = { 0 };
752 // Filtered text is guaranteed to be no larger than the raw text, so
753 // allocate a buffer the same size for the output text. This avoids
754 // a lot of messy out of memory error checking in the code.
755 context.outputText = calloc(1, strlen(text) + 1);
756 if (context.outputText != NULL)
757 {
758 context.inputText = text;
759 context.textIndex = 0;
760 context.outputTextIndex = 0;
761 context.currentState = State_Initial;
762 success = _SetNextState(&context, State_NormalText);
763 }
764 else
765 {
766 printf(" Error! Unable to allocate memory for the output text in the state machine!\n");
767 }
768
769 if (success)
770 {
771 while (context.currentState != State_Done && success)
772 {
773 CurrentState newState = context.currentStateBehavior(&context);
774 if (newState == State_Error)
775 {
776 printf(" Error! The state machine ran into an error condition (likely a NULL argument)!\n");
777 break;
778 }
779 success = _SetNextState(&context, newState);
780 }
781
782 if (success)
783 {
784 success = DynamicString_Set(filteredText, context.outputText);
785 free(context.outputText);
786 }
787 }
788 }
789
790 return success;
791}
static CurrentState _State_DoubleQuotedText_GoNextState(StateContext *context)
Handles the state of being inside a double-quote string where filtering is essentially turned off unt...
static void _OutputCharacter(StateContext *context, char character)
Save the character to the accumulation of the filtered text.
static CurrentState _State_EscapedSingleQuoteText_GoNextState(StateContext *context)
Handles the state of being in an escaped character sequence inside a single-quoted string....
static CurrentState _State_SingleQuotedText_GoNextState(StateContext *context)
Handles the state of being inside a single-quoted string where filtering is effectively turned off un...
static StateHandler _stateHandlers[]
Array of StateHandler objects that map CurrentState value to functions that transition from that stat...
CurrentState
Represents the current state of the state machine.
@ State_Done
Indicates processing is done.
@ State_StartComment
/ transitions to LineComment, * transitions to BlockComment, EOF_CHAR transitions to Done,...
@ State_NormalText
" transitions to QuotedText, / transitions to StartComment, EOF_CHAR transitions to Done
@ State_BlockComment
* transitions to EndBlockComment, EOF_CHAR transitions to Done
@ State_EscapedSingleQuoteText
\ transitions to SingleQuotedText, EOF_CHAR transitions to Done
@ State_DoubleQuotedText
\ transitions to EscapedDoubleQuoteText, " transitions to NormalText, EOF_CHAR transitions to Done
@ State_EscapedDoubleQuoteText
\ transitions to QuotedText, EOF_CHAR transitions to Done
@ State_EndBlockComment
/ transitions to NormalText, EOF_CHAR transitions to Done, all else transitions to BlockComment
@ State_SingleQuotedText
' transitions to EscapedSingleQuoteText, \ transitions to NormalText, EOF_CHAR transitions to Done
@ State_LineComment
\n transitions to NormalText, EOF_CHAR transitions to Done
@ State_Initial
State before the state machine actually starts. transitions to NormalText.
@ State_Error
Indicates an error occurred and the state machine needs to exit.
static CurrentState _State_LineComment_GoNextState(StateContext *context)
Handles the state of being in a line comment.
static bool _SetNextState(StateContext *context, CurrentState newState)
Helper method to transition the state machine to the specified state. Does nothing if the new state i...
static const char * _CurrentStateToString(CurrentState state)
Convert the CurrentState enumeration to a string for output purposes.
bool State_RemoveComments(const char *text, DynamicString *filteredText)
Entry point for callers to filter text. Removes C++-style line and block comments from the text.
static CurrentState _State_EndBlockComment_GoNextState(StateContext *context)
Handles the state of possibly being at the end of a block comment.
CurrentState(* StateFunctionPtr)(struct StateContext *)
Alias for a function pointer that takes a StateContext object and returns a value from the CurrentSta...
static StateFunctionPtr _GetStateFunction(CurrentState state)
Retrieve the function that is used to transition from the given state to another state.
static CurrentState _State_NormalText_GoNextState(StateContext *context)
Handles the state of normal text behavior.
static char _GetNextCharacter(StateContext *context)
Retrieve the next character from the input.
static CurrentState _State_BlockComment_GoNextState(StateContext *context)
Handles the state of being in a block comment.
_Constants
Contains constants used in the state machine.
@ EOF_CHAR
Indicates end of string.
static CurrentState _State_StartComment_GoNextState(StateContext *context)
Handles the state of being at the possible start of a line or block comment.
static CurrentState _State_EscapedDoubleQuoteText_GoNextState(StateContext *context)
Handles the state of being in an escaped character sequence inside a double-quoted string....
static CurrentState _State_Done_GoNextState(StateContext *context)
Handles the state of being done with input.
Declaration of the State_RemoveComments() function that uses a state machine to filter out comments o...
bool DynamicString_Set(DynamicString *string, const char *s)
Set the DynamicString object to the specified string, replacing whatever is in the DynamicString obje...
Definition: dynamicstring.c:73
Represents a string that can be grown dynamically.
Definition: dynamicstring.h:16
Represents the context in which the state machine runs.
StateFunctionPtr currentStateBehavior
Pointer to a function that acts on the current state.
char * outputText
Buffer into which output text is written, character by character.
size_t outputTextIndex
Index of the next point to put the output character.
CurrentState currentState
Value from the CurrentState enumeration indicating the current state of the machine.
const char * inputText
The text being filtered.
size_t textIndex
Index into the text being filtered.
Maps a value from the CurrentState enumeration to a function that handles the transition from that st...
CurrentState state
Value from the CurrentState enumeration.
StateFunctionPtr stateHandler
Function pointer to be called based on this state.