diff tests/agcl/oldagsrc/rcalcx.syn @ 0:13d2b8934445

Import AnaGram (near-)release tree into Mercurial.
author David A. Holland
date Sat, 22 Dec 2007 17:52:45 -0500
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/agcl/oldagsrc/rcalcx.syn	Sat Dec 22 17:52:45 2007 -0500
@@ -0,0 +1,230 @@
+{/*
+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