view tests/agcl/oldagsrc/rcalc.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          =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 : divide_error(pcb_pointer, 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>


/* Macro Definitions */

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

int semantic_error = 0;         /* Divide by zero flag */


/*

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 syntax_error(PCB_DECL) {
  int k = PCB.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 divide_error(PCB_DECL, int cn) {
  int k = PCB.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 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) {
    PCB_TYPE pcb;
    printf("#");
    if (gets(line) == NULL) break;
    pcb.pointer = (unsigned char *) line;
    rcalc(&pcb);
  }
  return 0;
}

}                               // End of Embedded C