view tests/agcl/oldagsrc/rcalcx.syn @ 21:1c9dac05d040

Add lint-style FALLTHROUGH annotations to fallthrough cases. (in the parse engine and thus the output code) Document this, because the old output causes warnings with gcc10.
author David A. Holland
date Mon, 13 Jun 2022 00:04:38 -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