view examples/rcalc/rcalc.syn @ 7:57b2cc9b87f7

Use memcpy instead of strncpy when we know the length anyway. Modern gcc seems to think it knows how to detect misuse of strncpy, but it's wrong (in fact: very, very wrong) and the path of least resistance is to not try to fight with it.
author David A. Holland
date Mon, 30 May 2022 23:47:52 -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