TDT4205/exercises/01/ps1_oyvindps/scanner.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
);
}