view examples/rcalc/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 MCMXCIII, MCMXCVI, Parsifal Software. All Rights Reserved.
 * Copyright MMVII David A. Holland. All Rights Reserved.
 *
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 */}

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

eof = 0 + '\n'                  // Input is a one-line string

ws = ' ' + '\t' + '\r'          // Blanks, tabs, and stray CRs

[
 ~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
]


// 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(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;
  -> 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;
  -> 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;
  -> 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()

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(void) {
  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(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[1024];
  while (1) {
    printf("#");
    fflush(stdout);
    if (fgets(line, sizeof(line), stdin) == NULL) break;
    rcalc_pcb.pointer = (unsigned char *) line;
    rcalc();
  }
  return 0;
}

}                               // End of Embedded C