#define _XOPEN_SOURCE 500 #include <stdio.h> #include <string.h> #include <stdlib.h> #define ERROR -1 #define N_STATES 8 #define ACCEPT N_STATES // Buffer for accepted characters, to interpret when in accepting state char lexical_buffer[256]; // Program internal state int state = 0, // Current state in the automaton transition_table[N_STATES][256], // Table form of the automaton lexical_position = 0, // Track position in character buffer x = 421, y = 298, // Set starting point at page center dx = 0, dy = 0; // Horizontal/vertical shift values /* * When a complete command has been scanned, this * function translates it into postscript */ void interpret_lexical_buffer ( void ); /* * Prepare the table form of the automaton */ void initialize_transition_table ( void ) { memset ( transition_table, 0, N_STATES*256*sizeof(int) ); for ( int s=0; s<N_STATES; s++ ) for ( int i=0; i<256; i++ ) transition_table[s][i] = ERROR; // Transition table is based on the following table: // ┌───────┬───┬───┬───┬───┬───┬───────┬───┬───┬────────┐ // │ State │ d │ x │ y │ = │ - │ [0-9] │ g │ o │ \n │ // ├───────┼───┼───┼───┼───┼───┼───────┼───┼───┼────────┤ // │ 0 │ 1 │ │ │ │ │ │ 5 │ │ │ // │ 1 │ │ 2 │ 2 │ │ │ │ │ │ │ // │ 2 │ │ │ │ 3 │ │ │ │ │ │ // │ 3 │ │ │ │ │ 4 │ 4 │ │ │ │ // │ 4 │ │ │ │ │ │ 4 │ │ │ ACCEPT │ // │ 5 │ │ │ │ │ │ │ │ 6 │ │ // │ 6 │ │ │ │ │ │ │ │ │ ACCEPT │ // └───────┴───┴───┴───┴───┴───┴───────┴───┴───┴────────┘ // Table is created from regex /(go|d(x|y)=-?[0-9]+)\n/g /* STATE 0 */ transition_table[0]['d'] = 1; transition_table[0]['g'] = 5; /* STATE 1 */ transition_table[1]['x'] = 2; transition_table[1]['y'] = 2; /* STATE 2 */ transition_table[2]['='] = 3; /* STATE 3 */ transition_table[3]['-'] = 4; for (int c = '0'; c <= '9'; c++) transition_table[3][c] = 4; /* STATE 5 */ transition_table[4]['\n'] = ACCEPT; for (int c = '0'; c <= '9'; c++) transition_table[4][c] = 4; /* STATE 6 */ transition_table[5]['o'] = 6; /* STATE 7 */ transition_table[6]['\n'] = ACCEPT; } /* Reset the automaton after completing a command */ void reset ( void ) { state = 0; lexical_position = 0; memset ( lexical_buffer, 0, 256*sizeof(char) ); } /* * Main function: * - Initialize the program to start drawing * - Scan command * - Translate accepted command string into postscript * - Reset automaton * - Repeat */ int main () { // Prepare the table representation of the automaton initialize_transition_table(); // Start drawing from the initial coordinates printf ( "<< /PageSize [842 595] >> setpagedevice\n" ); printf ( "%d %d moveto\n", x, y ); // Read characters until end-of-file or failure int read = 0; while ( read != EOF ) { // Fetch the next character and store it in the lexical buffer read = getchar(); lexical_buffer[lexical_position] = read; lexical_position += 1; // Change state according to the table state = transition_table[state][read]; // Check for error or accepting states: // * stop the program on error // * interpret the contents of the lexical buffer when valid switch (state) { case ERROR: fprintf ( stderr, "Invalid input\n" ); exit ( EXIT_FAILURE ); break; case ACCEPT: interpret_lexical_buffer(); reset(); break; } } printf ( "stroke\n" ); printf ( "showpage\n" ); exit ( EXIT_SUCCESS ); } /* This function interprets correct statements after they have been accepted */ void interpret_lexical_buffer ( void ) { if ( lexical_buffer[1] == 'x' ) sscanf ( &lexical_buffer[3], "%d", &dx ); else if ( lexical_buffer[1] == 'y' ) sscanf ( &lexical_buffer[3], "%d", &dy ); else if ( strncmp ( lexical_buffer, "go", 2 ) == 0 ) { x = x + dx; y = y + dy; printf ( "%d %d lineto\n", x, y ); printf ( "%d %d moveto\n", x, y ); } else fprintf ( stderr, "Unknown command accepted by the scanner:\n%s\n", lexical_buffer ); }