347 lines
6.5 KiB
C
347 lines
6.5 KiB
C
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include "states.h"
|
|
|
|
|
|
#define is_letter( c) ( isalpha( c) || isdigit( c) || c == '_')
|
|
#define save_or_put(c) if(previous_state==TEXT)putchar(c);else *bufptr++=c
|
|
|
|
|
|
|
|
/* This program processes the file 'as.c', and will take care of things like
|
|
* %d( <expression>), @func( arg), %$( ), etc.
|
|
* The main-loop is constructed as a finite automat.
|
|
*/
|
|
|
|
|
|
main()
|
|
{
|
|
char buffer[256], *bufptr = buffer;
|
|
int c, state = TEXT, previous_state = TEXT, depth = 0;
|
|
|
|
pr_header();
|
|
while ( ( c = getchar()) != EOF)
|
|
switch ( state) {
|
|
case TEXT :
|
|
switch ( c) {
|
|
case '/' : state = SLASH;
|
|
previous_state = TEXT;
|
|
save_or_put( c);
|
|
break;
|
|
case '"' : state = STRING;
|
|
previous_state = TEXT;
|
|
save_or_put( c);
|
|
break;
|
|
case '\'': state = CHAR_CONST;
|
|
previous_state = TEXT;
|
|
save_or_put( c);
|
|
break;
|
|
case '@' : state = AT;
|
|
break;
|
|
default : putchar( c);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
|
|
case SLASH :
|
|
state = ( c == '*' ? COMMENT : TEXT);
|
|
save_or_put( c);
|
|
break;
|
|
case COMMENT :
|
|
if ( c == '*')
|
|
state = STAR;
|
|
save_or_put( c);
|
|
break;
|
|
case STAR :
|
|
if ( c == '/')
|
|
state = previous_state;
|
|
else if ( c != '*')
|
|
state = COMMENT;
|
|
save_or_put( c);
|
|
break;
|
|
|
|
|
|
|
|
case STRING :
|
|
if ( c == '"')
|
|
state = previous_state;
|
|
else if ( c == '\\')
|
|
state = BACKSLASH_in_STRING;
|
|
save_or_put( c);
|
|
break;
|
|
|
|
case BACKSLASH_in_STRING :
|
|
state = STRING;
|
|
save_or_put( c);
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CHAR_CONST :
|
|
if ( c == '\'')
|
|
state = previous_state;
|
|
else if ( c == '\\')
|
|
state = BACKSLASH_in_CHAR_CONST;
|
|
save_or_put( c);
|
|
break;
|
|
|
|
case BACKSLASH_in_CHAR_CONST :
|
|
state = CHAR_CONST;
|
|
save_or_put( c);
|
|
break;
|
|
|
|
|
|
|
|
case AT : /* @if '(' <CONDITION> ')'
|
|
* @elsif '(' <CONDITION> ')'
|
|
* @else
|
|
* @fi
|
|
* @<IDENTIFIER> '(' <PARAM_LIST> ')'
|
|
*/
|
|
if ( is_letter( c))
|
|
*bufptr++ = c;
|
|
else {
|
|
ungetc( c, stdin);
|
|
state = WHITE_SPACE;
|
|
}
|
|
break;
|
|
|
|
case WHITE_SPACE :
|
|
if ( isspace( c))
|
|
*bufptr++ = c;
|
|
else if ( c == '(') {
|
|
*bufptr++ = c;
|
|
depth = 1;
|
|
state = CAL_or_QUEST;
|
|
}
|
|
else {
|
|
*bufptr = '\0';
|
|
pr_ELSE_or_FI( buffer);
|
|
bufptr = buffer;
|
|
ungetc( c, stdin);
|
|
state = TEXT;
|
|
}
|
|
break;
|
|
|
|
case CAL_or_QUEST : /* match ')' */
|
|
*bufptr++ =c;
|
|
switch ( c) {
|
|
case '(' : depth++;
|
|
break;
|
|
case ')' : depth--;
|
|
if ( depth == 0) {
|
|
*bufptr = '\0';
|
|
pr_CALL_or_QUEST( buffer);
|
|
bufptr = buffer;
|
|
state = TEXT;
|
|
}
|
|
break;
|
|
case '/' : state = SLASH;
|
|
previous_state = CAL_or_QUEST;
|
|
break;
|
|
case '"' : state = STRING;
|
|
previous_state = CAL_or_QUEST;
|
|
break;
|
|
case '\'': state = CHAR_CONST;
|
|
previous_state = CAL_or_QUEST;
|
|
break;
|
|
}
|
|
break;
|
|
default :
|
|
fprintf( stderr, "Unknown state : %d\n", state);
|
|
break;
|
|
}
|
|
exit( 0);
|
|
}
|
|
|
|
pr_header()
|
|
{
|
|
printf( "#include \"as_parser.h\"\n");
|
|
printf( "#line 1 \"as.c\"\n");
|
|
}
|
|
|
|
|
|
pr_ELSE_or_FI( str)
|
|
char *str;
|
|
{
|
|
if ( strncmp( str, "else", 4) == 0)
|
|
printf( "fprint( outfile, \"}\else {\");%s", str+4);
|
|
else if ( strncmp( str, "fi", 2) == 0)
|
|
printf( "fprint( outfile, \"}\");%s", str+2);
|
|
else
|
|
fprintf( stderr, "%s unexpected!!\n", str);
|
|
}
|
|
|
|
|
|
pr_CALL_or_QUEST( str)
|
|
char *str;
|
|
{
|
|
if ( strncmp( str, "if", 2) == 0 && !(is_letter( *(str+2))))
|
|
pr_if( str);
|
|
else if ( strncmp( str, "elsif", 5) == 0 && !(is_letter( *(str+5))))
|
|
pr_elsif( str);
|
|
else
|
|
pr_call( str);
|
|
}
|
|
|
|
|
|
/* Adjust 'cur_pos' when necessary */
|
|
|
|
pr_call( call)
|
|
char *call;
|
|
{
|
|
char c;
|
|
|
|
printf( "{");
|
|
if ( strncmp( "text", call, 4) == 0 && isdigit( *(call+4)))
|
|
printf( "cur_pos += %d;", *(call+4) - '0');
|
|
else if ( strncmp( "reloc", call, 5) == 0 && isdigit( *(call+5)))
|
|
printf( "cur_pos += %d;", *(call+5) - '0');
|
|
|
|
pr_text_with_conversions( call);
|
|
printf( "fprint( outfile, \";\");");
|
|
printf( "}");
|
|
for (; ( c = getchar()) != ';' ; putchar( c)); /* skip ';' */
|
|
}
|
|
|
|
pr_elsif( quest)
|
|
char *quest;
|
|
{
|
|
printf( "fprint( outfile, \"}\else if\");");
|
|
pr_text_with_conversions( quest+5);
|
|
printf( "fprint( outfile, \"{\");");
|
|
}
|
|
|
|
pr_if( quest)
|
|
char *quest;
|
|
{
|
|
pr_text_with_conversions( quest);
|
|
printf( "fprint( outfile, \"{\");");
|
|
}
|
|
|
|
|
|
pr_text_with_conversions( str)
|
|
char *str;
|
|
{
|
|
char *ptr, *next_conversion(), *pr_conversion();
|
|
|
|
while ( ptr = next_conversion( str)) {
|
|
/* ptr points to '%'-sign */
|
|
*ptr = '\0';
|
|
printf( "fprint( outfile, \"");
|
|
pr_string( str);
|
|
printf( "\");");
|
|
*ptr = '%';
|
|
str = pr_conversion( ptr);
|
|
}
|
|
printf( "fprint( outfile, \"");
|
|
pr_string( str);
|
|
printf( "\");");
|
|
}
|
|
|
|
|
|
pr_string( s)
|
|
char *s;
|
|
{
|
|
for ( ; *s != '\0'; s++)
|
|
switch ( *s) {
|
|
case '"' : printf( "\\\"");
|
|
break;
|
|
|
|
case '\\': printf( "\\\\");
|
|
break;
|
|
|
|
case '\n': printf( "\\n");
|
|
break;
|
|
|
|
default : printf( "%c", *s);
|
|
}
|
|
}
|
|
|
|
|
|
char *next_conversion( str)
|
|
char *str;
|
|
{
|
|
char *match();
|
|
|
|
while ( *str && *str != '%') {
|
|
switch ( *str++) {
|
|
case '\'' : str = match( '\'', str) + 1;
|
|
break;
|
|
case '"' : str = match( '"', str) + 1;
|
|
break;
|
|
}
|
|
}
|
|
return( *str == '%' ? str : (char *)0);
|
|
}
|
|
|
|
char *match( c, str)
|
|
char c, *str;
|
|
{
|
|
while ( *str && ( *str != c || *(str-1) == '\\'))
|
|
str++;
|
|
return( *str ? str : str-1);
|
|
}
|
|
|
|
char *match_bracket( str)
|
|
char *str;
|
|
|
|
/* find ')', but look at nesting '('-')' pairs, return position of ')'.
|
|
*/
|
|
{
|
|
int depth;
|
|
char *match();
|
|
|
|
depth = 1;
|
|
while ( *str && depth != 0) {
|
|
switch ( *str++) {
|
|
case '\'' : str = match( '\'', str+1) + 1;
|
|
break;
|
|
case '"' : str = match( '"', str+1) + 1;
|
|
break;
|
|
case '(' : depth++;
|
|
break;
|
|
case ')' : depth--;
|
|
break;
|
|
}
|
|
}
|
|
return( str-1);
|
|
}
|
|
|
|
|
|
char *find( c, str)
|
|
char c, *str;
|
|
{
|
|
while ( *str && *str != c)
|
|
str++;
|
|
return( str);
|
|
}
|
|
|
|
char *pr_conversion( str)
|
|
char *str;
|
|
|
|
/* str points to '%'-sign, returns pointer to first character AFTER the
|
|
* conversion
|
|
*/
|
|
{
|
|
char *start, *ptr, *match_bracket(), *find();
|
|
|
|
start = find( '(', str+1);
|
|
*start++ = '\0';
|
|
|
|
ptr = match_bracket( start);
|
|
*ptr = '\0';
|
|
|
|
if ( *(str+1) == '$')
|
|
printf( "eval( %s);", start);
|
|
else if ( strncmp( str+1, "dist", 4) == 0)
|
|
printf( "dist( %s);", start);
|
|
else
|
|
printf( "fprint( outfile, \"%%%s\", %s);", str+1, start);
|
|
|
|
return( ptr+1);
|
|
}
|