www.pudn.com > littleC.rar > CHAP29.LST


listing 1 
/* Get a token. */ 
int get_token(void) 
{ 
 
  register char *temp; 
 
  token_type = 0; tok = 0; 
 
  temp = token; 
  *temp = '\0'; 
 
  /* skip over white space */ 
  while(iswhite(*prog) && *prog) ++prog; 
 
  if(*prog == '\r') { 
    ++prog; 
    ++prog; 
    /* skip over white space */ 
    while(iswhite(*prog) && *prog) ++prog; 
  } 
 
  if(*prog == '\0') { /* end of file */ 
    *token = '\0'; 
    tok = FINISHED; 
    return (token_type = DELIMITER); 
  } 
 
  if(strchr("{}", *prog)) { /* block delimiters */ 
    *temp = *prog; 
    temp++; 
    *temp = '\0'; 
    prog++; 
    return (token_type = BLOCK); 
  } 
 
  /* look for comments */ 
  if(*prog == '/') 
    if(*(prog+1) == '*') { /* is a comment */ 
      prog += 2; 
      do { /* find end of comment */ 
        while(*prog != '*') prog++; 
        prog++; 
      } while (*prog != '/'); 
      prog++; 
    } 
 
  if(strchr("!<>=", *prog)) { /* is or might be 
                                 a relational operator */ 
    switch(*prog) { 
      case '=': if(*(prog+1) == '=') { 
          prog++; prog++; 
          *temp = EQ; 
          temp++; *temp = EQ; temp++; 
          *temp = '\0'; 
       } 
       break; 
      case '!': if(*(prog+1) == '=') { 
          prog++; prog++; 
          *temp = NE; 
          temp++; *temp = NE; temp++; 
          *temp = '\0'; 
       } 
       break; 
      case '<': if(*(prog+1) == '=') { 
          prog++; prog++; 
          *temp = LE; temp++; *temp = LE; 
       } 
       else { 
          prog++; 
          *temp = LT; 
       } 
       temp++; 
       *temp = '\0'; 
       break; 
      case '>': if(*(prog+1) == '=') { 
          prog++; prog++; 
          *temp = GE; temp++; *temp = GE; 
       } 
       else { 
         prog++; 
         *temp = GT; 
       } 
       temp++; 
       *temp = '\0'; 
       break; 
    } 
    if(*token) return (token_type = DELIMITER); 
  } 
 
  if(strchr("+-*^/%=;(),'", *prog)){ /* delimiter */ 
    *temp = *prog; 
    prog++; /* advance to next position */ 
    temp++; 
    *temp = '\0'; 
    return (token_type = DELIMITER); 
  } 
 
  if(*prog=='"') { /* quoted string */ 
    prog++; 
    while(*prog != '"'&& *prog != '\r') *temp++ = *prog++; 
    if(*prog == '\r') sntx_err(SYNTAX); 
    prog++; *temp = '\0'; 
    return (token_type = STRING); 
  } 
 
  if(isdigit(*prog)) { /* number */ 
    while(!isdelim(*prog)) *temp++ = *prog++; 
    *temp = '\0'; 
    return (token_type = NUMBER); 
  } 
 
  if(isalpha(*prog)) { /* var or command */ 
    while(!isdelim(*prog)) *temp++ = *prog++; 
    token_type = TEMP; 
  } 
 
  *temp = '\0'; 
 
  /* see if a string is a command or a variable */ 
  if(token_type==TEMP) { 
    tok = look_up(token); /* convert to internal rep */ 
    if(tok) token_type = KEYWORD; /* is a keyword */ 
    else token_type = IDENTIFIER; 
  } 
  return token_type; 
} 
 
listing 2 
/* Display an error message. */ 
void sntx_err(int error) 
{ 
  char *p, *temp; 
  int linecount = 0; 
  register int i; 
 
  static char *e[]= { 
    "syntax error", 
    "unbalanced parentheses", 
    "no expression present", 
    "equals sign expected", 
    "not a variable", 
    "parameter error", 
    "semicolon expected", 
    "unbalanced braces", 
    "function undefined", 
    "type specifier expected", 
    "too many nested function calls", 
    "return without call", 
    "parentheses expected", 
    "while expected", 
    "closing quote expected", 
    "not a string", 
    "too many local variables", 
    "division by zero" 
  }; 
  printf("\n%s", e[error]); 
  p = p_buf; 
  while(p != prog) {  /* find line number of error */ 
    p++; 
    if(*p == '\r') { 
      linecount++; 
    } 
  } 
  printf(" in line %d\n", linecount); 
 
  temp = p; 
  for(i=0; i < 20 && p > p_buf && *p != '\n'; i++, p--); 
  for(i=0; i < 30 && p <= temp; i++, p++) printf("%c", *p); 
 
  longjmp(e_buf, 1); /* return to safe point */ 
} 
 
listing 3 
/* Recursive descent parser for integer expressions 
   which may include variables and function calls. 
*/ 
#include  
#include  
#include  
#include  
#include  
#include  
 
#define NUM_FUNC        100 
#define NUM_GLOBAL_VARS 100 
#define NUM_LOCAL_VARS  200 
#define ID_LEN          31 
#define FUNC_CALLS      31 
#define PROG_SIZE       10000 
#define FOR_NEST        31 
 
enum tok_types {DELIMITER, IDENTIFIER, NUMBER, KEYWORD, 
                TEMP, STRING, BLOCK}; 
 
enum tokens {ARG, CHAR, INT, IF, ELSE, FOR, DO, WHILE, 
             SWITCH, RETURN, EOL, FINISHED, END}; 
 
enum double_ops {LT=1, LE, GT, GE, EQ, NE}; 
 
/* These are the constants used to call sntx_err() when 
   a syntax error occurs. Add more if you like. 
   NOTE: SYNTAX is a generic error message used when 
   nothing else seems appropriate. 
*/ 
enum error_msg 
     {SYNTAX, UNBAL_PARENS, NO_EXP, EQUALS_EXPECTED, 
      NOT_VAR, PARAM_ERR, SEMI_EXPECTED, 
      UNBAL_BRACES, FUNC_UNDEF, TYPE_EXPECTED, 
      NEST_FUNC, RET_NOCALL, PAREN_EXPECTED, 
      WHILE_EXPECTED, QUOTE_EXPECTED, NOT_TEMP, 
      TOO_MANY_LVARS, DIV_BY_ZERO}; 
 
extern char *prog;  /* current location in source code */ 
extern char *p_buf;  /* points to start of program buffer */ 
extern jmp_buf e_buf; /* hold environment for longjmp() */ 
 
/* An array of these structures will hold the info 
   associated with global variables. 
*/ 
extern struct var_type { 
  char var_name[32]; 
  int v_type; 
  int value; 
}  global_vars[NUM_GLOBAL_VARS]; 
 
/*  This is the function call stack. */ 
extern struct func_type { 
  char func_name[32]; 
  int ret_type;  
  char *loc;  /* location of function entry point in file */ 
} func_stack[NUM_FUNC]; 
 
/* Keyword table */ 
extern struct commands { 
  char command[20]; 
  char tok; 
} table[]; 
 
/* "Standard library" functions are declared here so 
   they can be put into the internal function table that 
   follows. 
 */ 
int call_getche(void), call_putch(void); 
int call_puts(void), print(void), getnum(void); 
 
struct intern_func_type { 
  char *f_name; /* function name */ 
  int (*p)();   /* pointer to the function */ 
} intern_func[] = { 
  "getche", call_getche, 
  "putch", call_putch, 
  "puts", call_puts, 
  "print", print, 
  "getnum", getnum, 
  "", 0  /* null terminate the list */ 
}; 
 
extern char token[80]; /* string representation of token */ 
extern char token_type; /* contains type of token */ 
extern char tok; /* internal representation of token */ 
 
extern int ret_value; /* function return value */ 
 
void eval_exp0(int *value); 
void eval_exp(int *value); 
void eval_exp1(int *value); 
void eval_exp2(int *value); 
void eval_exp3(int *value); 
void eval_exp4(int *value); 
void eval_exp5(int *value); 
void atom(int *value); 
void sntx_err(int error), putback(void); 
void assign_var(char *var_name, int value); 
int isdelim(char c), look_up(char *s), iswhite(char c); 
int find_var(char *s), get_token(void); 
int internal_func(char *s); 
int is_var(char *s); 
char *find_func(char *name); 
void call(void); 
 
/* Entry point into parser. */ 
void eval_exp(int *value) 
{ 
  get_token(); 
  if(!*token) { 
    sntx_err(NO_EXP); 
    return; 
  } 
  if(*token == ';') { 
    *value = 0; /* empty expression */ 
    return; 
  } 
  eval_exp0(value); 
  putback(); /* return last token read to input stream */ 
} 
 
/* Process an assignment expression */ 
void eval_exp0(int *value) 
{ 
  char temp[ID_LEN];  /* holds name of var receiving 
                         the assignment */ 
  register int temp_tok; 
 
  if(token_type == IDENTIFIER) { 
    if(is_var(token)) {  /* if a var, see if assignment */ 
      strcpy(temp, token); 
      temp_tok = token_type; 
      get_token(); 
      if(*token == '=') {  /* is an assignment */ 
        get_token(); 
        eval_exp0(value);  /* get value to assign */ 
        assign_var(temp, *value);  /* assign the value */ 
        return; 
      } 
      else {  /* not an assignment */ 
        putback();  /* restore original token */ 
        strcpy(token, temp); 
        token_type = temp_tok; 
      } 
    } 
  } 
  eval_exp1(value); 
} 
 
/* Process relational operators. */ 
void eval_exp1(int *value) 
{ 
  int partial_value; 
  register char op; 
  char relops[7] = { 
    LT, LE, GT, GE, EQ, NE, 0 
  }; 
 
  eval_exp2(value); 
  op = *token; 
  if(strchr(relops, op)) { 
    get_token(); 
    eval_exp2(&partial_value); 
    switch(op) {  /* perform the relational operation */ 
      case LT: 
        *value = *value < partial_value; 
        break; 
      case LE: 
        *value = *value <= partial_value; 
        break; 
      case GT: 
        *value = *value > partial_value; 
        break; 
      case GE: 
        *value = *value >= partial_value; 
        break; 
      case EQ: 
        *value = *value == partial_value; 
        break; 
      case NE: 
        *value = *value != partial_value; 
        break; 
    } 
  } 
} 
 
/*  Add or subtract two terms. */ 
void eval_exp2(int *value) 
{ 
  register char  op; 
  int partial_value; 
 
  eval_exp3(value); 
  while((op = *token) == '+' || op == '-') { 
    get_token(); 
    eval_exp3(&partial_value); 
    switch(op) { /* add or subtract */ 
      case '-': 
        *value = *value - partial_value; 
        break; 
      case '+': 
        *value = *value + partial_value; 
        break; 
    } 
  } 
} 
 
/* Multiply or divide two factors. */ 
void eval_exp3(int *value) 
{ 
  register char  op; 
  int partial_value, t; 
 
  eval_exp4(value); 
  while((op = *token) == '*' || op == '/' || op == '%') { 
    get_token(); 
    eval_exp4(&partial_value); 
    switch(op) { /* mul, div, or modulus */ 
      case '*': 
        *value = *value * partial_value; 
        break; 
      case '/': 
        if(partial_value == 0) sntx_err(DIV_BY_ZERO);        
        *value = (*value) / partial_value; 
        break; 
      case '%': 
        t = (*value) / partial_value; 
        *value = *value-(t * partial_value); 
        break; 
    } 
  } 
} 
 
/* Is a unary + or -. */ 
void eval_exp4(int *value) 
{ 
  register char  op; 
 
  op = '\0'; 
  if(*token == '+' || *token == '-') { 
    op = *token; 
    get_token(); 
  } 
  eval_exp5(value); 
  if(op) 
    if(op == '-') *value = -(*value); 
} 
 
/* Process parenthesized expression. */ 
void eval_exp5(int *value) 
{ 
  if((*token == '(')) { 
    get_token(); 
    eval_exp0(value);   /* get subexpression */ 
    if(*token != ')') sntx_err(PAREN_EXPECTED); 
    get_token(); 
  } 
  else 
    atom(value); 
} 
 
/* Find value of number, variable, or function. */ 
void atom(int *value) 
{ 
  int i; 
 
  switch(token_type) { 
  case IDENTIFIER: 
    i = internal_func(token); 
    if(i!= -1) {  /* call "standard library" function */ 
      *value = (*intern_func[i].p)(); 
    } 
    else 
    if(find_func(token)) { /* call user-defined function */ 
      call(); 
      *value = ret_value; 
    } 
    else *value = find_var(token); /* get var's value */ 
    get_token(); 
    return; 
  case NUMBER: /* is numeric constant */ 
    *value = atoi(token); 
    get_token(); 
    return; 
  case DELIMITER: /* see if character constant */ 
    if(*token == '\'') { 
      *value = *prog; 
      prog++; 
      if(*prog!='\'') sntx_err(QUOTE_EXPECTED); 
      prog++; 
      get_token(); 
      return ; 
    } 
    if(*token==')') return; /* process empty expression */ 
    else sntx_err(SYNTAX); /* syntax error */ 
  default: 
    sntx_err(SYNTAX); /* syntax error */ 
  } 
} 
 
/* Display an error message. */ 
void sntx_err(int error) 
{ 
  char *p, *temp; 
  int linecount = 0; 
  register int i; 
 
  static char *e[]= { 
    "syntax error", 
    "unbalanced parentheses", 
    "no expression present", 
    "equals sign expected", 
    "not a variable", 
    "parameter error", 
    "semicolon expected", 
    "unbalanced braces", 
    "function undefined", 
    "type specifier expected", 
    "too many nested function calls", 
    "return without call", 
    "parentheses expected", 
    "while expected", 
    "closing quote expected", 
    "not a string", 
    "too many local variables", 
    "division by zero" 
  }; 
  printf("\n%s", e[error]); 
  p = p_buf; 
  while(p != prog) {  /* find line number of error */ 
    p++; 
    if(*p == '\r') { 
      linecount++; 
    } 
  } 
  printf(" in line %d\n", linecount); 
 
  temp = p; 
  for(i=0; i < 20 && p > p_buf && *p != '\n'; i++, p--); 
  for(i=0; i < 30 && p <= temp; i++, p++) printf("%c", *p); 
 
  longjmp(e_buf, 1); /* return to safe point */ 
} 
 
/* Get a token. */ 
int get_token(void) 
{ 
 
  register char *temp; 
 
  token_type = 0; tok = 0; 
 
  temp = token; 
  *temp = '\0'; 
 
  /* skip over white space */ 
  while(iswhite(*prog) && *prog) ++prog; 
 
  if(*prog == '\r') { 
    ++prog; 
    ++prog; 
    /* skip over white space */ 
    while(iswhite(*prog) && *prog) ++prog; 
  } 
 
  if(*prog == '\0') { /* end of file */ 
    *token = '\0'; 
    tok = FINISHED; 
    return (token_type = DELIMITER); 
  } 
 
  if(strchr("{}", *prog)) { /* block delimiters */ 
    *temp = *prog; 
    temp++; 
    *temp = '\0'; 
    prog++; 
    return (token_type = BLOCK); 
  } 
 
  /* look for comments */ 
  if(*prog == '/') 
    if(*(prog+1) == '*') { /* is a comment */ 
      prog += 2; 
      do { /* find end of comment */ 
        while(*prog != '*') prog++; 
        prog++; 
      } while (*prog != '/'); 
      prog++; 
    } 
 
  if(strchr("!<>=", *prog)) { /* is or might be 
                                 a relational operator */ 
    switch(*prog) { 
      case '=': if(*(prog+1) == '=') { 
          prog++; prog++; 
          *temp = EQ; 
          temp++; *temp = EQ; temp++; 
          *temp = '\0'; 
       } 
       break; 
      case '!': if(*(prog+1) == '=') { 
          prog++; prog++; 
          *temp = NE; 
          temp++; *temp = NE; temp++; 
          *temp = '\0'; 
       } 
       break; 
      case '<': if(*(prog+1) == '=') { 
          prog++; prog++; 
          *temp = LE; temp++; *temp = LE; 
       } 
       else { 
          prog++; 
          *temp = LT; 
       } 
       temp++; 
       *temp = '\0'; 
       break; 
      case '>': if(*(prog+1) == '=') { 
          prog++; prog++; 
          *temp = GE; temp++; *temp = GE; 
       } 
       else { 
         prog++; 
         *temp = GT; 
       } 
       temp++; 
       *temp = '\0'; 
       break; 
    } 
    if(*token) return(token_type = DELIMITER); 
  } 
 
  if(strchr("+-*^/%=;(),'", *prog)){ /* delimiter */ 
    *temp = *prog; 
    prog++; /* advance to next position */ 
    temp++; 
    *temp = '\0'; 
    return (token_type = DELIMITER); 
  } 
 
  if(*prog=='"') { /* quoted string */ 
    prog++; 
    while(*prog != '"'&& *prog != '\r') *temp++ = *prog++; 
    if(*prog == '\r') sntx_err(SYNTAX); 
    prog++; *temp = '\0'; 
    return (token_type = STRING); 
  } 
 
  if(isdigit(*prog)) { /* number */ 
    while(!isdelim(*prog)) *temp++ = *prog++; 
    *temp = '\0'; 
    return (token_type = NUMBER); 
  } 
 
  if(isalpha(*prog)) { /* var or command */ 
    while(!isdelim(*prog)) *temp++ = *prog++; 
    token_type = TEMP; 
  } 
 
  *temp = '\0'; 
 
  /* see if a string is a command or a variable */ 
  if(token_type==TEMP) { 
    tok = look_up(token); /* convert to internal rep */ 
    if(tok) token_type = KEYWORD; /* is a keyword */ 
    else token_type = IDENTIFIER; 
  } 
  return token_type; 
} 
 
/* Return a token to input stream. */ 
void putback(void) 
{ 
  char *t; 
 
  t = token; 
  for(; *t; t++) prog--; 
} 
 
/* Look up a token's internal representation in the 
   token table. 
*/ 
int look_up(char *s) 
{ 
  register int i; 
  char *p; 
 
  /* convert to lowercase */ 
  p = s; 
  while(*p) { *p = tolower(*p); p++; } 
 
  /* see if token is in table */ 
  for(i=0; *table[i].command; i++) { 
    if(!strcmp(table[i].command, s)) return table[i].tok; 
  } 
  return 0; /* unknown command */ 
} 
 
/* Return index of internal library function or -1 if 
   not found. 
*/ 
int internal_func(char *s) 
{ 
  int i; 
 
  for(i=0; intern_func[i].f_name[0]; i++) { 
    if(!strcmp(intern_func[i].f_name, s))  return i; 
  } 
  return -1; 
} 
 
/* Return true if c is a delimiter. */ 
int isdelim(char c) 
{ 
  if(strchr(" !;,+-<>'/*%^=()", c) || c == 9 || 
     c == '\r' || c == 0) return 1; 
  return 0; 
} 
 
/* Return 1 if c is space or tab. */ 
int iswhite(char c) 
{ 
  if(c == ' ' || c == '\t') return 1; 
  else return 0; 
} 
 
 
listing 4 
/* "Standard library" functions are declared here so 
   they can be put into the internal function table that 
   follows. 
 */ 
int call_getche(void), call_putch(void); 
int call_puts(void), print(void), getnum(void); 
 
struct intern_func_type { 
  char *f_name; /* function name */ 
  int (*p)();   /* pointer to the function */ 
} intern_func[] = { 
  "getche", call_getche, 
  "putch", call_putch, 
  "puts", call_puts, 
  "print", print, 
  "getnum", getnum, 
  "", 0  /* null terminate the list */ 
}; 
 
listing 5 
/* Find the location of all functions in the program 
   and store global variables. */ 
void prescan(void) 
{ 
  char *p, *tp; 
  char temp[32]; 
  int datatype;  
  int brace = 0;  /* When 0, this var tells us that 
                     current source position is outside 
                     of any function. */ 
 
  p = prog; 
  func_index = 0; 
  do { 
    while(brace) {  /* bypass code inside functions */ 
      get_token(); 
      if(*token == '{') brace++; 
      if(*token == '}') brace--; 
    } 
 
    tp = prog; /* save current position */ 
    get_token(); 
    /* global var type or function return type */ 
    if(tok==CHAR || tok==INT) {  
      datatype = tok; /* save data type */ 
      get_token(); 
      if(token_type == IDENTIFIER) { 
        strcpy(temp, token); 
        get_token(); 
        if(*token != '(') { /* must be global var */ 
          prog = tp; /* return to start of declaration */ 
          decl_global(); 
        } 
        else if(*token == '(') {  /* must be a function */ 
          func_table[func_index].loc = prog; 
          func_table[func_index].ret_type = datatype; 
          strcpy(func_table[func_index].func_name, temp); 
          func_index++; 
          while(*prog != ')') prog++; 
          prog++; 
          /* now prog points to opening curly  
             brace of function */ 
        } 
        else putback(); 
      } 
    } 
    else if(*token == '{') brace++; 
  } while(tok != FINISHED); 
  prog = p; 
} 
 
listing 6 
/* An array of these structures will hold the info 
   associated with global variables. 
*/ 
struct var_type { 
  char var_name[ID_LEN]; 
  int v_type; 
  int value; 
}  global_vars[NUM_GLOBAL_VARS]; 
 
int gvar_index; /* index into global variable table */ 
 
/* Declare a global variable. */ 
void decl_global(void) 
{ 
  int vartype; 
 
  get_token();  /* get type */ 
 
  vartype = tok; /* save var type */ 
 
  do { /* process comma-separated list */ 
    global_vars[gvar_index].v_type = vartype; 
    global_vars[gvar_index].value = 0;  /* init to 0 */ 
    get_token();  /* get name */ 
    strcpy(global_vars[gvar_index].var_name, token); 
    get_token(); 
    gvar_index++; 
  } while(*token == ','); 
  if(*token != ';') sntx_err(SEMI_EXPECTED); 
} 
 
listing 7 
int main(int argc, char *argv[]) 
{ 
  if(argc != 2) { 
    printf("Usage: littlec \n"); 
    exit(1); 
  } 
 
  /* allocate memory for the program */ 
  if((p_buf = (char *) malloc(PROG_SIZE))==NULL) { 
    printf("Allocation Failure"); 
    exit(1); 
  } 
 
  /* load the program to execute */ 
  if(!load_program(p_buf, argv[1])) exit(1); 
  if(setjmp(e_buf)) exit(1); /* initialize long jump buffer */ 
 
  gvar_index = 0;  /* initialize global variable index */ 
 
  /* set program pointer to start of program buffer */ 
  prog = p_buf; 
  prescan(); /* find the location of all functions 
                and global variables in the program */ 
 
  lvartos = 0;     /* initialize local variable stack index */ 
  functos = 0;     /* initialize the CALL stack index */ 
 
  /* setup call to main() */ 
  prog = find_func("main"); /* find program starting point */ 
 
  if(!prog) { /* incorrect or missing main() function in program */ 
    printf("main() not found.\n"); 
    exit(1); 
  } 
 
  prog--; /* back up to opening ( */ 
  strcpy(token, "main"); 
  call(); /* call main() to start interpreting */ 
 
  return 0; 
} 
 
listing 8 
/* Interpret a single statement or block of code. When 
   interp_block() returns from its initial call, the final 
   brace (or a return) in main() has been encountered. 
*/ 
void interp_block(void) 
{ 
  int value; 
  char block = 0; 
 
  do { 
    token_type = get_token(); 
 
    /* If interpreting single statement, return on 
       first semicolon. 
    */ 
 
    /* see what kind of token is up */ 
    if(token_type == IDENTIFIER) { 
      /* Not a keyword, so process expression. */ 
      putback();  /* restore token to input stream for 
                     further processing by eval_exp() */ 
      eval_exp(&value);  /* process the expression */ 
      if(*token!=';') sntx_err(SEMI_EXPECTED); 
    } 
    else if(token_type==BLOCK) { /* if block delimiter */ 
      if(*token == '{') /* is a block */ 
        block = 1; /* interpreting block, not statement */ 
      else return; /* is a }, so return */ 
    } 
    else /* is keyword */ 
      switch(tok) { 
        case CHAR: 
        case INT:     /* declare local variables */ 
          putback(); 
          decl_local(); 
          break; 
        case RETURN:  /* return from function call */ 
          func_ret(); 
          return; 
        case IF:      /* process an if statement */ 
          exec_if(); 
          break; 
        case ELSE:    /* process an else statement */ 
          find_eob(); /* find end of else block 
                         and continue execution */ 
          break; 
        case WHILE:   /* process a while loop */ 
          exec_while(); 
          break; 
        case DO:      /* process a do-while loop */ 
          exec_do(); 
          break; 
        case FOR:     /* process a for loop */ 
          exec_for(); 
          break; 
        case END: 
          exit(0); 
      } 
  } while (tok != FINISHED && block); 
} 
 
listing 9 
/* A Little C interpreter. */ 
 
#include  
#include  
#include  
#include  
#include  
#include  
 
#define NUM_FUNC        100 
#define NUM_GLOBAL_VARS 100 
#define NUM_LOCAL_VARS  200 
#define NUM_BLOCK       100 
#define ID_LEN          31 
#define FUNC_CALLS      31 
#define NUM_PARAMS      31 
#define PROG_SIZE       10000 
#define LOOP_NEST       31 
 
enum tok_types {DELIMITER, IDENTIFIER, NUMBER, KEYWORD, 
                TEMP, STRING, BLOCK}; 
 
/* add additional C keyword tokens here */ 
enum tokens {ARG, CHAR, INT, IF, ELSE, FOR, DO, WHILE, 
             SWITCH, RETURN, EOL, FINISHED, END}; 
 
/* add additional double operators here (such as ->) */ 
enum double_ops {LT=1, LE, GT, GE, EQ, NE}; 
 
/* These are the constants used to call sntx_err() when 
   a syntax error occurs. Add more if you like. 
   NOTE: SYNTAX is a generic error message used when 
   nothing else seems appropriate. 
*/ 
enum error_msg 
     {SYNTAX, UNBAL_PARENS, NO_EXP, EQUALS_EXPECTED, 
      NOT_VAR, PARAM_ERR, SEMI_EXPECTED, 
      UNBAL_BRACES, FUNC_UNDEF, TYPE_EXPECTED, 
      NEST_FUNC, RET_NOCALL, PAREN_EXPECTED, 
      WHILE_EXPECTED, QUOTE_EXPECTED, NOT_TEMP, 
      TOO_MANY_LVARS, DIV_BY_ZERO}; 
 
char *prog;    /* current location in source code */ 
char *p_buf;   /* points to start of program buffer */ 
jmp_buf e_buf; /* hold environment for longjmp() */ 
 
/* An array of these structures will hold the info 
   associated with global variables. 
*/ 
struct var_type { 
  char var_name[ID_LEN]; 
  int v_type; 
  int value; 
}  global_vars[NUM_GLOBAL_VARS]; 
 
struct var_type local_var_stack[NUM_LOCAL_VARS]; 
 
struct func_type { 
  char func_name[ID_LEN]; 
  int ret_type;  
  char *loc;  /* location of entry point in file */ 
} func_table[NUM_FUNC]; 
 
int call_stack[NUM_FUNC]; 
 
struct commands { /* keyword lookup table */ 
  char command[20]; 
  char tok; 
} table[] = { /* Commands must be entered lowercase */ 
  "if", IF, /* in this table. */ 
  "else", ELSE, 
  "for", FOR, 
  "do", DO, 
  "while", WHILE, 
  "char", CHAR, 
  "int", INT, 
  "return", RETURN, 
  "end", END, 
  "", END  /* mark end of table */ 
}; 
 
char token[80]; 
char token_type, tok; 
 
int functos;  /* index to top of function call stack */ 
int func_index; /* index into function table */ 
int gvar_index; /* index into global variable table */ 
int lvartos; /* index into local variable stack */ 
 
int ret_value; /* function return value */ 
 
void print(void), prescan(void); 
void decl_global(void), call(void), putback(void); 
void decl_local(void), local_push(struct var_type i); 
void eval_exp(int *value), sntx_err(int error); 
void exec_if(void), find_eob(void), exec_for(void); 
void get_params(void), get_args(void); 
void exec_while(void), func_push(int i), exec_do(void); 
void assign_var(char *var_name, int value); 
int load_program(char *p, char *fname), find_var(char *s); 
void interp_block(void), func_ret(void); 
int func_pop(void), is_var(char *s), get_token(void); 
char *find_func(char *name); 
 
int main(int argc, char *argv[]) 
{ 
  if(argc != 2) { 
    printf("Usage: littlec \n"); 
    exit(1); 
  } 
 
  /* allocate memory for the program */ 
  if((p_buf = (char *) malloc(PROG_SIZE))==NULL) { 
    printf("Allocation Failure"); 
    exit(1); 
  } 
 
  /* load the program to execute */ 
  if(!load_program(p_buf, argv[1])) exit(1); 
  if(setjmp(e_buf)) exit(1); /* initialize long jump buffer */ 
 
  gvar_index = 0;  /* initialize global variable index */ 
 
  /* set program pointer to start of program buffer */ 
  prog = p_buf; 
  prescan(); /* find the location of all functions 
                and global variables in the program */ 
 
  lvartos = 0;     /* initialize local variable stack index */ 
  functos = 0;     /* initialize the CALL stack index */ 
 
  /* setup call to main() */ 
  prog = find_func("main"); /* find program starting point */ 
 
  if(!prog) { /* incorrect or missing main() function in program */ 
    printf("main() not found.\n"); 
    exit(1); 
  } 
 
  prog--; /* back up to opening ( */ 
  strcpy(token, "main"); 
  call(); /* call main() to start interpreting */ 
 
  return 0; 
} 
 
/* Interpret a single statement or block of code. When 
   interp_block() returns from its initial call, the final 
   brace (or a return) in main() has been encountered. 
*/ 
void interp_block(void) 
{ 
  int value; 
  char block = 0; 
 
  do { 
    token_type = get_token(); 
 
    /* If interpreting single statement, return on 
       first semicolon. 
    */ 
 
    /* see what kind of token is up */ 
    if(token_type == IDENTIFIER) { 
      /* Not a keyword, so process expression. */ 
      putback();  /* restore token to input stream for 
                     further processing by eval_exp() */ 
      eval_exp(&value);  /* process the expression */ 
      if(*token!=';') sntx_err(SEMI_EXPECTED); 
    } 
    else if(token_type==BLOCK) { /* if block delimiter */ 
      if(*token == '{') /* is a block */ 
        block = 1; /* interpreting block, not statement */ 
      else return; /* is a }, so return */ 
    } 
    else /* is keyword */ 
      switch(tok) { 
        case CHAR: 
        case INT:     /* declare local variables */ 
          putback(); 
          decl_local(); 
          break; 
        case RETURN:  /* return from function call */ 
          func_ret(); 
          return; 
        case IF:      /* process an if statement */ 
          exec_if(); 
          break; 
        case ELSE:    /* process an else statement */ 
          find_eob(); /* find end of else block 
                         and continue execution */ 
          break; 
        case WHILE:   /* process a while loop */ 
          exec_while(); 
          break; 
        case DO:      /* process a do-while loop */ 
          exec_do(); 
          break; 
        case FOR:     /* process a for loop */ 
          exec_for(); 
          break; 
        case END: 
          exit(0); 
      } 
  } while (tok != FINISHED && block); 
} 
 
/* Load a program. */ 
int load_program(char *p, char *fname) 
{ 
  FILE *fp; 
  int i=0; 
 
  if((fp=fopen(fname, "rb"))==NULL) return 0; 
 
  i = 0; 
  do { 
    *p = getc(fp); 
    p++; i++; 
  } while(!feof(fp) && i=0; count--) { 
    i.value = temp[count]; 
    i.v_type = ARG; 
    local_push(i); 
  } 
} 
 
/* Get function parameters. */ 
void get_params(void) 
{ 
  struct var_type *p; 
  int i; 
 
  i = lvartos-1; 
  do { /* process comma-separated list of parameters */ 
    get_token(); 
    p = &local_var_stack[i]; 
    if(*token != ')' ) { 
      if(tok != INT && tok != CHAR) 
        sntx_err(TYPE_EXPECTED); 
 
      p->v_type = token_type; 
      get_token(); 
 
      /* link parameter name with argument already on 
         local var stack */ 
      strcpy(p->var_name, token); 
      get_token(); 
      i--; 
    } 
    else break; 
  } while(*token == ','); 
  if(*token != ')') sntx_err(PAREN_EXPECTED); 
} 
 
/* Return from a function. */ 
void func_ret(void) 
{ 
  int value; 
 
  value = 0; 
  /* get return value, if any */ 
  eval_exp(&value); 
 
  ret_value = value; 
} 
 
/* Push a local variable. */ 
void local_push(struct var_type i) 
{ 
  if(lvartos > NUM_LOCAL_VARS) 
    sntx_err(TOO_MANY_LVARS); 
 
  local_var_stack[lvartos] = i; 
  lvartos++; 
} 
 
/* Pop index into local variable stack. */ 
int func_pop(void) 
{ 
  functos--; 
  if(functos < 0) sntx_err(RET_NOCALL); 
  return call_stack[functos]; 
} 
 
/* Push index of local variable stack. */ 
void func_push(int i) 
{ 
  if(functos>NUM_FUNC) 
   sntx_err(NEST_FUNC); 
  call_stack[functos] = i; 
  functos++; 
} 
 
/* Assign a value to a variable. */ 
void assign_var(char *var_name, int value) 
{ 
  register int i; 
 
  /* first, see if it's a local variable */ 
  for(i=lvartos-1; i >= call_stack[functos-1]; i--)  { 
    if(!strcmp(local_var_stack[i].var_name, var_name)) { 
      local_var_stack[i].value = value; 
      return; 
    } 
  } 
  if(i < call_stack[functos-1]) 
  /* if not local, try global var table */ 
    for(i=0; i < NUM_GLOBAL_VARS; i++) 
      if(!strcmp(global_vars[i].var_name, var_name)) { 
        global_vars[i].value = value; 
        return; 
      } 
  sntx_err(NOT_VAR); /* variable not found */ 
} 
 
/* Find the value of a variable. */ 
int find_var(char *s) 
{ 
  register int i; 
 
  /* first, see if it's a local variable */ 
  for(i=lvartos-1; i >= call_stack[functos-1]; i--) 
    if(!strcmp(local_var_stack[i].var_name, token)) 
      return local_var_stack[i].value; 
 
  /* otherwise, try global vars */ 
  for(i=0; i < NUM_GLOBAL_VARS; i++) 
    if(!strcmp(global_vars[i].var_name, s)) 
      return global_vars[i].value; 
 
  sntx_err(NOT_VAR); /* variable not found */ 
  return -1;  
} 
 
/* Determine if an identifier is a variable. Return 
   1 if variable is found; 0 otherwise. 
*/ 
int is_var(char *s) 
{ 
  register int i; 
 
  /* first, see if it's a local variable */ 
  for(i=lvartos-1; i >= call_stack[functos-1]; i--) 
    if(!strcmp(local_var_stack[i].var_name, token)) 
      return 1; 
 
  /* otherwise, try global vars */ 
  for(i=0; i < NUM_GLOBAL_VARS; i++) 
    if(!strcmp(global_vars[i].var_name, s)) 
      return 1; 
 
  return 0; 
} 
 
/* Execute an if statement. */ 
void exec_if(void) 
{ 
  int cond; 
 
  eval_exp(&cond); /* get if expression */ 
 
  if(cond) { /* is true so process target of IF */ 
    interp_block(); 
  } 
  else { /* otherwise skip around IF block and 
            process the ELSE, if present */ 
    find_eob(); /* find start of next line */ 
    get_token(); 
 
    if(tok != ELSE) { 
      putback();  /* restore token if 
                     no ELSE is present */ 
      return; 
    } 
    interp_block(); 
  } 
} 
 
/* Execute a while loop. */ 
void exec_while(void) 
{ 
  int cond; 
  char *temp; 
 
  putback(); 
  temp = prog;  /* save location of top of while loop */ 
  get_token(); 
  eval_exp(&cond);  /* check the conditional expression */ 
  if(cond) interp_block();  /* if true, interpret */ 
  else {  /* otherwise, skip around loop */ 
    find_eob(); 
    return; 
  } 
  prog = temp;  /* loop back to top */ 
} 
 
/* Execute a do loop. */ 
void exec_do(void) 
{ 
  int cond; 
  char *temp; 
 
  putback(); 
  temp = prog;  /* save location of top of do loop */ 
 
  get_token(); /* get start of loop */ 
  interp_block(); /* interpret loop */ 
  get_token(); 
  if(tok != WHILE) sntx_err(WHILE_EXPECTED); 
  eval_exp(&cond); /* check the loop condition */ 
  if(cond) prog = temp; /* if true loop; otherwise, 
                           continue on */ 
} 
 
/* Find the end of a block. */ 
void find_eob(void) 
{ 
  int brace; 
 
  get_token(); 
  brace = 1; 
  do { 
    get_token(); 
    if(*token == '{') brace++; 
    else if(*token == '}') brace--; 
  } while(brace); 
} 
 
/* Execute a for loop. */ 
void exec_for(void) 
{ 
  int cond; 
  char *temp, *temp2; 
  int brace ; 
 
  get_token(); 
  eval_exp(&cond);  /* initialization expression */ 
  if(*token != ';') sntx_err(SEMI_EXPECTED); 
  prog++; /* get past the ; */ 
  temp = prog; 
  for(;;) { 
    eval_exp(&cond);  /* check the condition */ 
    if(*token != ';') sntx_err(SEMI_EXPECTED); 
    prog++; /* get past the ; */ 
    temp2 = prog; 
 
    /* find the start of the for block */ 
    brace = 1; 
    while(brace) { 
      get_token(); 
      if(*token == '(') brace++; 
      if(*token == ')') brace--; 
    } 
 
    if(cond) interp_block();  /* if true, interpret */ 
    else {  /* otherwise, skip around loop */ 
      find_eob(); 
      return; 
    } 
    prog = temp2; 
    eval_exp(&cond); /* do the increment */ 
    prog = temp;  /* loop back to top */ 
  } 
} 
 
listing 10 
/* Declare a local variable. */ 
void decl_local(void) 
{ 
  struct var_type i; 
 
  get_token();  /* get type */ 
 
  i.v_type = tok; 
  i.value = 0;  /* init to 0 */ 
 
  do { /* process comma-separated list */ 
    get_token(); /* get var name */ 
    strcpy(i.var_name, token); 
    local_push(i); 
    get_token(); 
  } while(*token == ','); 
  if(*token != ';') sntx_err(SEMI_EXPECTED); 
} 
 
/* Push a local variable. */ 
void local_push(struct var_type i) 
{ 
  if(lvartos > NUM_LOCAL_VARS) 
    sntx_err(TOO_MANY_LVARS); 
 
  local_var_stack[lvartos] = i; 
  lvartos++; 
} 
 
listing 11 
/* Call a function. */ 
void call(void) 
{ 
  char *loc, *temp; 
  int lvartemp; 
 
  loc = find_func(token); /* find entry point of function */ 
  if(loc == NULL) 
    sntx_err(FUNC_UNDEF); /* function not defined */ 
  else { 
    lvartemp = lvartos;  /* save local var stack index */ 
    get_args();  /* get function arguments */ 
    temp = prog; /* save return location */ 
    func_push(lvartemp);  /* save local var stack index */ 
    prog = loc;  /* reset prog to start of function */ 
    get_params(); /* load the function's parameters with 
                     the values of the arguments */ 
    interp_block(); /* interpret the function */ 
    prog = temp; /* reset the program pointer */ 
    lvartos = func_pop(); /* reset the local var stack */ 
  } 
} 
 
/* Push the arguments to a function onto the local 
   variable stack. */ 
void get_args(void) 
{ 
  int value, count, temp[NUM_PARAMS]; 
  struct var_type i; 
 
  count = 0; 
  get_token(); 
  if(*token != '(') sntx_err(PAREN_EXPECTED); 
 
  /* process a comma-separated list of values */ 
  do { 
    eval_exp(&value); 
    temp[count] = value;  /* save temporarily */ 
    get_token(); 
    count++; 
  } while(*token == ','); 
  count--; 
  /* now, push on local_var_stack in reverse order */ 
  for(; count>=0; count--) { 
    i.value = temp[count]; 
    i.v_type = ARG; 
    local_push(i); 
  } 
} 
 
/* Get function parameters. */ 
void get_params(void) 
{ 
  struct var_type *p; 
  int i; 
 
  i = lvartos-1; 
  do { /* process comma-separated list of parameters */ 
    get_token(); 
    p = &local_var_stack[i]; 
    if(*token != ')' ) { 
      if(tok != INT && tok != CHAR) 
        sntx_err(TYPE_EXPECTED); 
 
      p->v_type = token_type; 
      get_token(); 
 
      /* link parameter name with argument already on 
         local var stack */ 
      strcpy(p->var_name, token); 
      get_token(); 
      i--; 
    } 
    else break; 
  } while(*token == ','); 
  if(*token != ')') sntx_err(PAREN_EXPECTED); 
} 
 
listing 12 
/* Return from a function. */ 
void func_ret(void) 
{ 
  int value; 
 
  value = 0; 
  /* get return value, if any */ 
  eval_exp(&value); 
 
  ret_value = value; 
} 
 
listing 13 
/* Assign a value to a variable. */ 
void assign_var(char *var_name, int value) 
{ 
  register int i; 
 
  /* first, see if it's a local variable */ 
  for(i=lvartos-1; i >= call_stack[functos-1]; i--)  { 
    if(!strcmp(local_var_stack[i].var_name, var_name)) { 
      local_var_stack[i].value = value; 
      return; 
    } 
  } 
  if(i < call_stack[functos-1]) 
  /* if not local, try global var table */ 
    for(i=0; i < NUM_GLOBAL_VARS; i++) 
      if(!strcmp(global_vars[i].var_name, var_name)) { 
        global_vars[i].value = value; 
        return; 
      } 
  sntx_err(NOT_VAR); /* variable not found */ 
} 
 
listing 14 
/* Execute an if statement. */ 
void exec_if(void) 
{ 
  int cond; 
 
  eval_exp(&cond); /* get if expression */ 
 
  if(cond) { /* is true so process target of IF */ 
    interp_block(); 
  } 
  else { /* otherwise skip around IF block and 
            process the ELSE, if present */ 
    find_eob(); /* find start of next line */ 
    get_token(); 
 
    if(tok != ELSE) { 
      putback();  /* restore token if 
                     no ELSE is present */ 
      return; 
    } 
    interp_block(); 
  } 
} 
 
listing 15 
/* Execute a while loop. */ 
void exec_while(void) 
{ 
  int cond; 
  char *temp; 
 
  putback(); 
  temp = prog;  /* save location of top of while loop */ 
  get_token(); 
  eval_exp(&cond);  /* check the conditional expression */ 
  if(cond) interp_block();  /* if true, interpret */ 
  else {  /* otherwise, skip around loop */ 
    find_eob(); 
    return; 
  } 
  prog = temp;  /* loop back to top */ 
} 
 
listing 16 
/* Execute a do loop. */ 
void exec_do(void) 
{ 
  int cond; 
  char *temp; 
 
  putback(); 
  temp = prog;  /* save location of top of do loop */ 
 
  get_token(); /* get start of loop */ 
  interp_block(); /* interpret loop */ 
  get_token(); 
  if(tok != WHILE) sntx_err(WHILE_EXPECTED); 
  eval_exp(&cond); /* check the loop condition */ 
  if(cond) prog = temp; /* if true loop; otherwise, 
                           continue on */ 
} 
 
listing 17 
/* Execute a for loop. */ 
void exec_for(void) 
{ 
  int cond; 
  char *temp, *temp2; 
  int brace ; 
 
  get_token(); 
  eval_exp(&cond);  /* initialization expression */ 
  if(*token != ';') sntx_err(SEMI_EXPECTED); 
  prog++; /* get past the ; */ 
  temp = prog; 
  for(;;) { 
    eval_exp(&cond);  /* check the condition */ 
    if(*token != ';') sntx_err(SEMI_EXPECTED); 
    prog++; /* get past the ; */ 
    temp2 = prog; 
 
    /* find the start of the for block */ 
    brace = 1; 
    while(brace) { 
      get_token(); 
      if(*token == '(') brace++; 
      if(*token == ')') brace--; 
    } 
 
    if(cond) interp_block();  /* if true, interpret */ 
    else {  /* otherwise, skip around loop */ 
      find_eob(); 
      return; 
    } 
    prog = temp2; 
    eval_exp(&cond); /* do the increment */ 
    prog = temp;  /* loop back to top */ 
  } 
} 
 
listing 18 
int getche(void);    /* read a character from keyboard and  
                          return its value */ 
int putch(char ch);  /* write a character to the screen */ 
int puts(char *s);   /* write a string to the screen */ 
int getnum(void);    /* read an integer from the keyboard and 
                          return its value */ 
int print(char *s);  /* write a string to the screen */ 
or 
int print(int i);    /* write an integer to the screen */ 
 
listing 19 
/****** Internal Library Functions *******/ 
 
/* Add more of your own, here. */ 
 
#include   /* if your compiler does not 
                       support this  header file, 
                       remove it */ 
#include  
#include  
 
extern char *prog; /* points to current location in program */ 
extern char token[80]; /* holds string representation of token */ 
extern char token_type; /* contains type of token */ 
extern char tok; /* holds the internal representation of token */ 
 
enum tok_types {DELIMITER, IDENTIFIER, NUMBER, KEYWORD, 
                TEMP, STRING, BLOCK}; 
 
/* These are the constants used to call sntx_err() when 
   a syntax error occurs. Add more if you like. 
   NOTE: SYNTAX is a generic error message used when 
   nothing else seems appropriate. 
*/ 
enum error_msg 
     {SYNTAX, UNBAL_PARENS, NO_EXP, EQUALS_EXPECTED, 
      NOT_VAR, PARAM_ERR, SEMI_EXPECTED, 
      UNBAL_BRACES, FUNC_UNDEF, TYPE_EXPECTED, 
      NEST_FUNC, RET_NOCALL, PAREN_EXPECTED, 
      WHILE_EXPECTED, QUOTE_EXPECTED, NOT_STRING, 
      TOO_MANY_LVARS, DIV_BY_ZERO}; 
 
int get_token(void); 
void sntx_err(int error), eval_exp(int *result); 
void putback(void); 
 
/* Get a character from console. (Use getchar() if 
   your compiler does not support _getche().) */ 
int call_getche() 
{ 
  char ch; 
  ch = _getche(); 
  while(*prog!=')') prog++; 
  prog++;   /* advance to end of line */ 
  return ch; 
} 
 
/* Put a character to the display. */ 
int call_putch() 
{ 
  int value; 
 
  eval_exp(&value); 
  printf("%c", value); 
  return value; 
} 
 
/* Call puts(). */ 
int call_puts(void) 
{ 
  get_token(); 
  if(*token!='(') sntx_err(PAREN_EXPECTED); 
  get_token(); 
  if(token_type!=STRING) sntx_err(QUOTE_EXPECTED); 
  puts(token); 
  get_token(); 
  if(*token!=')') sntx_err(PAREN_EXPECTED); 
 
  get_token(); 
  if(*token!=';') sntx_err(SEMI_EXPECTED); 
  putback(); 
  return 0; 
} 
 
/* A built-in console output function. */ 
int print(void) 
{ 
  int i; 
 
  get_token(); 
  if(*token!='(')  sntx_err(PAREN_EXPECTED); 
 
  get_token(); 
  if(token_type==STRING) { /* output a string */ 
    printf("%s ", token); 
  } 
  else {  /* output a number */ 
   putback(); 
   eval_exp(&i); 
   printf("%d ", i); 
  } 
 
  get_token(); 
 
  if(*token!=')') sntx_err(PAREN_EXPECTED); 
 
  get_token(); 
  if(*token!=';') sntx_err(SEMI_EXPECTED); 
  putback(); 
  return 0; 
} 
 
/* Read an integer from the keyboard. */ 
int getnum(void) 
{ 
  char s[80]; 
 
  gets(s); 
  while(*prog != ')') prog++; 
  prog++;  /* advance to end of line */ 
  return atoi(s); 
} 
 
 
listing 20 
/* Little C Demonstration Program #1. 
 
   This program demonstrates all features 
   of C that are recognized by Little C. 
*/ 
 
int i, j;   /* global vars */ 
char ch; 
 
int main() 
{ 
  int i, j;  /* local vars */ 
 
  puts("Little C Demo Program."); 
 
  print_alpha(); 
 
  do { 
    puts("enter a number (0 to quit): "); 
    i = getnum(); 
    if(i < 0 ) { 
      puts("numbers must be positive, try again"); 
    } 
    else { 
      for(j = 0; j < i; j=j+1) { 
        print(j); 
        print("summed is"); 
        print(sum(j)); 
        puts(""); 
      } 
    } 
  } while(i!=0); 
 
  return 0; 
} 
 
/* Sum the values between 0 and num. */ 
int sum(int num) 
{ 
  int running_sum; 
 
  running_sum = 0; 
 
  while(num) { 
    running_sum = running_sum + num; 
    num = num - 1; 
  } 
  return running_sum; 
} 
 
/* Print the alphabet. */ 
int print_alpha() 
{ 
  for(ch = 'A'; ch<='Z'; ch = ch + 1) { 
    putch(ch); 
  } 
  puts(""); 
 
  return 0; 
} 
 
listing 21 
/* Nested loop example. */ 
int main() 
{ 
  int i, j, k; 
 
  for(i = 0; i < 5; i = i + 1) { 
    for(j = 0; j < 3; j = j + 1) { 
      for(k = 3; k ; k = k - 1) { 
        print(i); 
        print(j); 
        print(k); 
        puts(""); 
      } 
    } 
  } 
  puts("done"); 
 
  return 0; 
} 
 
listing 22 
/* Assigments as operations. */ 
int main() 
{ 
  int a, b; 
 
  a = b = 10; 
 
  print(a); print(b); 
 
  while(a=a-1) { 
    print(a); 
    do { 
       print(b); 
    } while((b=b-1) > -10); 
  } 
 
  return 0; 
} 
 
listing 23 
/* This program demonstrates recursive functions. */ 
 
/* return the factorial of i */ 
int factr(int i) 
{ 
  if(i<2) { 
    return 1; 
  } 
  else { 
     return i * factr(i-1); 
  } 
} 
 
int main() 
{ 
  print("Factorial of 4 is: "); 
  print(factr(4)); 
 
  return 0; 
} 
 
listing 24 
/* A more rigorous example of function arguments. */ 
 
int f1(int a, int b) 
{ 
  int count; 
 
  print("in f1"); 
 
  count = a; 
  do { 
    print(count); 
  } while(count=count-1); 
 
  print(a); print(b); 
  print(a*b); 
  return a*b; 
} 
 
int f2(int a, int x, int y) 
{ 
  print(a); print(x); 
  print(x / a); 
  print(y*x); 
 
  return 0; 
} 
 
int main() 
{ 
  f2(10, f1(10, 20), 99); 
 
  return 0; 
} 
 
listing 25 
/* The loop statements. */ 
int main() 
{ 
  int a; 
  char ch; 
 
  /* the while */ 
  puts("Enter a number: "); 
  a = getnum(); 
  while(a) { 
    print(a); 
    print(a*a); 
    puts(""); 
    a = a - 1; 
  } 
 
  /* the do-while */ 
  puts("enter characters, 'q' to quit"); 
  do { 
     ch = getche(); 
  } while(ch !='q'); 
 
  /* the for */ 
  for(a=0; a<10; a = a + 1) { 
     print(a); 
  } 
 
  return 0; 
}