view tests/agcl/oldagsrc/rcalcx.syn @ 17:12171da8943f

Don't refer to CVS.
author David A. Holland
date Tue, 31 May 2022 01:56:37 -0400
parents 13d2b8934445
children
line wrap: on
line source

{/*
AnaGram, a System for Syntax Directed Parsing
RCALC.SYN: A Roman Numeral Calculator Example

Copyright (c) MCMXCIII, MCMXCVI, Parsifal Software.
All Rights Reserved.
*/}

c = 'c'
d = 'd'
i = 'i'
l = 'l'
m = 'm'
v = 'v'
x = 'x'

eof = 0                         // Input is a string

ws = ' ' + '\t'                 // Blanks and tabs

[
 ~allow macros                  // Simplify debugging
  disregard ws                  // Ignore blanks and tabs
  lexeme { roman numeral}       // No blanks inside a number
 ~case sensitive                // Allow upper/lower input
  context type = int            // Track context
  default token type = long     // All arithmetic uses longs
  pointer input                 // Input is array in memory
  reentrant parser
  parser file name = "#.cpp"
]


// Grammar definition

(void) calculation $
  -> expression:x, eof          =PCB.print_roman(x);


// Expression logic

expression
  -> term                       // Value of expression is value of term
  -> expression:x, '+', term:y  =x+y;
  -> expression:x, '-', term:y  =x-y;

term
  -> factor                     // Value of term is value of factor
  -> term:x, '*', factor:y      =x*y;
  -> term:x, '/', factor:y      =y ? x/y : PCB.divide_error(RULE_CONTEXT[2]);

factor
  -> roman numeral              // Value of factor is value of roman numeral
  -> "nihil"                    =0;
  -> '(', expression:x, ')'     =x;
  -> '-', factor:x              =-x;


// Roman Numeral Syntax

roman numeral
  -> thousands:x, hundreds:y    =x+y;
  -> thousands
  -> hundreds                   // Value of roman numeral is value of hundreds

thousands
  -> m                          =1000;
  -> thousands:x, m             =x+1000;

hundreds
  -> hundreds field:x, tens:y   =x+y;
  -> hundreds field
  -> tens                       // Value of hundreds is value of tens

hundreds field
  -> c, m                       =900;
  -> c, d                       =400;
  -> count hundreds

count hundreds
  -> c                          =100;
  -> d                          =500;
  -> count hundreds:x, c        =x+100;

tens
  -> tens field:x, units:y      =x+y;
  -> tens field
  -> units                      // Value of tens is value of units

tens field
  -> x, m                       =990;
  -> x, d                       =490;
  -> x, c                       =90;
  -> x, l                       =40;
  -> count tens

count tens
  -> x                          =10;
  -> l                          =50;
  -> count tens:x, x            =x+10;

units
  -> i, m                       =999;
  -> i, d                       =499;
  -> i, c                       =99;
  -> i, l                       =49;
  -> i, x                       =9;
  -> i, v                       =4;
  -> count units                // Value of "units" is value of "count units"

count units
  -> i                          =1;
  -> v                          =5;
  -> count units:x, i           =x+1;


{                               /* Embedded C */
#include <stdio.h>

#define PCB_TYPE Parser

class Parser : public rcalcx_pcb_type {
  int semantic_error;
public:
  Parser() {}
  void parse(char *);
  void syntax_error();
  int divide_error(int cn);
  void print_roman(long k);
};


void Parser::parse(char *text) {
  pointer = (unsigned char *) text;
  semantic_error = 0;
  rcalcx(this);
}

/* Macro Definitions */

#define GET_CONTEXT CONTEXT = PCB.column
#define SYNTAX_ERROR PCB.syntax_error()

/*

syntax_error() positions a '^' character under the input line at the
point where the syntax error was discovered and then writes the error
message.

*/

void Parser::syntax_error() {
  int k = column;
  while (k-- > 0) putchar(' ');
  printf("^? %s\n","ERRARE HUMANUM EST\n");
}


/*

divide_error() is called when an attempt is made to divide by zero. The
entire divisor is marked with '^' characters. "semantic_error" is set
in order to disable printing of a result.

*/

int Parser::divide_error(int cn) {
  int k = column - cn;
  while (cn--) putchar(' ');
  while (k--) putchar('^');
  puts(" DIVISOR NIHIL EST");
  semantic_error = 1;
  return 0;
}


/*

print_roman() prints a signed integer in upper case Roman numerals.

*/

void Parser::print_roman(long k) {
  if (semantic_error) {
    semantic_error = 0;
    return;
  }
  printf("  = ");
  if (k == 0)      {printf("NIHIL\n"); return;}

  if (k < 0)        putchar('-'), k=-k;

  while (k >= 1000) putchar('M'), k-=1000;

  if (k >= 900)     printf("CM"), k-=900;
  if (k >= 500)     putchar('D'), k-=500;
  if (k >= 400)     printf("CD"), k-=400;

  while (k >= 100)  putchar('C'), k-=100;

  if (k >= 90)      printf("XC"), k-=90;
  if (k >= 50)      putchar('L'), k-=50;
  if (k >= 40)      printf("XL"), k-=40;

  while (k >= 10)   putchar('X'), k-=10;

  if (k >= 9)       printf("IX"), k -= 9;
  if (k >= 5)       putchar('V'), k-=5;
  if (k >= 4)       printf("IV"), k-=4;

  while (k >= 1)    putchar('I'), k--;

  putchar('\n');
}


/* Main Program -- reads a line from stdin and calls parser */

int main(void) {
  char line[82];
  while (1) {
    Parser pcb;
    printf("#");
    if (gets(line) == NULL) break;
    pcb.parse(line);
  }
  return 0;
}

}                               // End of Embedded C