166 lines
5.0 KiB
C
166 lines
5.0 KiB
C
|
#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
|
||
|
);
|
||
|
}
|