view tests/agcl/parsifal/ss-sscx.syn @ 17:12171da8943f

Don't refer to CVS.
author David A. Holland
date Tue, 31 May 2022 01:56:37 -0400
parents 13d2b8934445
children
line wrap: on
line source

{/*
 Copyright 1992, Jerome T. Holland
 See the file COPYING for license and usage terms.

 An example syntax and supporting classes for implementing a spreadsheet

 Class definitions are found in sscxdefs.h

 This example is largely intended to serve as an illustration, and is
 incomplete in a number of ways. In particular, the expression syntax
 is that of C, except for identifiers, and only a few functions are
 implemented. More may be implemented trivially by following the
 patterns provided.

 The supported operations are:
   Conditional expressions:     ? :
   Logical operators:           !, &, |
   Comparison operators:        ==, !=, <, <=, >, >=
   Binary arithmetic operators: +, -, *, /
   Exponentiation:              **
   Unary arithmetic operators:  +, -
   Parentheses
   Function calls               abs, sin, asin, atan

 A note on the technique used for compilation:

 The basic approach is to build a Reverse Polish Notation stack.
 During parsing, the constants, cell locators, and operators are
 stacked on separate temporary stacks. When parsing is complete,
 these stacks are unloaded into arrays belonging to the cell
 expression object. Note that there is a one to one correspondence
 between items on the constant stack and fd (fetch double) operators
 in the op stack. Since there are no branching instructions, there
 there is one and ony one constant corresponding to a fd operation
 so it is sufficient to simply increment an index into the constant
 array as execution progresses, to know which constant to use. The
 same logic holds for cell locators.
*/

#include <math.h>
#include "agstk.h"
#include <string.h>
#include <errno.h>
#include "sscxdefs.h"

}

// -- CONFIGURATION SECTION ----------------------------
[
  disregard white space
  lexeme {real, cell name}
  pointer input
  parser name = parseKernel
  parser file name = "#.cpp"
  line numbers                       // For debugging
  escape backslashes                 // Comment this line if using a Borland compiler
]

input string $                       // specify grammar token
 -> expression, eof

expression
 -> logical or expression
 -> logical or expression, '?',
      expression, ':', expression                     =opStack.push(CellExpression::cond);

logical or expression
 -> logical and expression
 -> logical or expression, '|', logical and expression   =opStack.push(CellExpression::or);

logical and expression
 -> equality expression
 -> logical and expression, '&', equality expression     =opStack.push(CellExpression::and);

equality expression
 -> relational expression
 -> equality expression:x, "==", relational expression:y      =opStack.push(CellExpression::eq);
 -> equality expression:x, "!=", relational expression:y      =opStack.push(CellExpression::ne);

relational expression
 -> additive expression
 -> relational expression, '<',  additive expression       =opStack.push(CellExpression::lt);
 -> relational expression, "<=", additive expression       =opStack.push(CellExpression::le);
 -> relational expression, '>',  additive expression       =opStack.push(CellExpression::gt);
 -> relational expression, ">=", additive expression       =opStack.push(CellExpression::ge);

additive expression
 -> multiplicative expression
 -> additive expression, '+', multiplicative expression    =opStack.push(CellExpression::add);
 -> additive expression, '-', multiplicative expression    =opStack.push(CellExpression::sub);

multiplicative expression
 -> factor
 -> multiplicative expression:x, '*', factor:y             =opStack.push(CellExpression::mpy);
 -> multiplicative expression:x, '/', factor:y             =opStack.push(CellExpression::div);

factor
 -> primary
 -> primary, "**", factor                                  =opStack.push(CellExpression::pow);

primary
 -> real:x                                              =doubleStack.push(x), opStack.push(CellExpression::fd);
 -> cell name:n                                         =locatorStack.push(n), opStack.push(CellExpression::fc);
 -> "abs", '(', expression, ')'                         =opStack.push(CellExpression::abs);
 -> "sin", '(', expression, ')'                         =opStack.push(CellExpression::sin);
 -> "asin", '(', expression, ')'                         =opStack.push(CellExpression::sin);
 -> "atan", '(', expression, ')'                        =opStack.push(CellExpression::atan);
 -> "atan", '(', expression, ',', expression, ')'       =opStack.push(CellExpression::atan2);
 -> '(', expression, ')'
 -> '-', primary                                              =opStack.push(CellExpression::minus);
 -> '+', primary
 -> '!', primary                                              =opStack.push(CellExpression::not);

// -- LEXICAL UNITS ------------------------------------------------
digit   = '0-9'
eof     = 0

(void) white space
 -> ' ' + '\t' + '\f' + '\v' + '\r' + '\n'
 -> "/*", ~eof?..., "*/"                          // C style comment
 -> "//", ~(eof+'\n')?..., '\n'                 // C++ style comment

(double) real
 -> simple real
 -> simple real:x, 'e'+'E', '+'?,exponent:e            =x*pow(10,e);
 -> simple real:x, 'e'+'E', '-',exponent:e            =x*pow(10,-e);

(double) simple real
 -> integer part:i, '.', fraction part:f                      = i+f;
 -> integer part, '.'?
 -> '.', fraction part:f                                        = f;

(double) integer part
 -> digit:d                                                 = d-'0';
 -> integer part:x, digit:d                          = 10*x + d-'0';

(double) fraction part
 -> digit:d                                            =(d-'0')/10.;
 -> digit:d, fraction part:f                       =(d-'0' + f)/10.;

(int) exponent
 -> digit:d                                                 = d-'0';
 -> exponent:x, digit:d                              = 10*x + d-'0';

(Locator) cell name           //value of name token is length of name string
 -> column:c, row:r                                         =Locator(r-1,c);
 -> '$',column:c, row:r                    =Locator(r-1,c).setAbsoluteCol();
 -> column:c, '$', row:r                   =Locator(r-1,c).setAbsoluteRow();
 -> '$', column:c, '$', row:r             =Locator(r-1,c).setAbsoluteRow().setAbsoluteCol();

(int) letter
 -> 'a-z':c          =c - 'a';
 -> 'A-Z':c          =c = 'A';

(int) column
 -> letter:n             =n;
 -> column:c, letter:n   =26*c + n;

(int) row
 -> digit:d               =d - '0';
 -> row:n, digit:d        =10*n + d - '0';

{

// Stacks for use in parsing and compiling

AgStack<double> doubleStack;
AgStack<Locator> locatorStack;
AgStack<CellExpression::Op> opStack;

// Data for a simple minded test

CellExpression *Locator::expression[3][3];
int Locator::maxRow = 3;
int Locator::maxCol = 3;

char *expressionText[3][3] = {
  {"17", "19", "-23"},
  {"2*a1", "b1 - a2", "a2+b2"},
  {"b2 + c2", "a3-a2", "a2/b2"}
};

/*
 Constructor for CellExpression

 Parses the input text and creates byte code for later execution by
 the value() function.
*/

CellExpression::CellExpression(char *text)
  : constant(0), nConstants(0),
    locator(0), nLocators(0),
    op(0), nOps(0),
    errorFlag(0), busy(0), cycle(0)
{
  doubleStack.reset();
  locatorStack.reset();
  opStack.reset();
  PCB.pointer = (unsigned char *) text;
  parseKernel();                      // Parse expression
  if (PCB.exit_flag != AG_SUCCESS_CODE) {errorFlag = 1; return;}
  // save parse results
  nConstants = doubleStack.size();
  nLocators = locatorStack.size();
  nOps = opStack.size();
  constant = new double[nConstants];
  locator  = new Locator[nLocators];
  op       = new Op[nOps];
  int i;
  for (i = nConstants; i--;) constant[i] = doubleStack.pop();
  for (i = nLocators; i--;) locator[i] = locatorStack.pop();
  for (i = nOps; i--;) op[i] = opStack.pop();
}

// Function to execute byte code

Number CellExpression::value() {
  if (errorFlag) return Number();
  if (busy) {                                // Check for cyclic definition
    errorFlag = cycle = 1;
    return Number();
  }
  busy = 1;
  int constantIndex = 0;
  int locatorIndex = 0;

  AgStack<Number> stack;                   // Arithmetic stack for byte code interpreter

  Number temp;                             // temporary variable
  int pc = 0;
  while (pc < nOps && errorFlag == 0) {
    switch (op[pc]) {
      case add: stack.pop(temp); stack.top() += temp; break;
      case sub: stack.pop(temp); stack.top() -= temp; break;
      case mpy: stack.pop(temp); stack.top() *= temp; break;
      case div: stack.pop(temp); stack.top() /= temp; break;
      case pow: stack.pop(temp); stack.top() = stack.top().pow(temp); break;
      case lt: stack.pop(temp); stack.top() = stack.top() < temp; break;
      case le: stack.pop(temp); stack.top() = stack.top() <= temp; break;
      case gt: stack.pop(temp); stack.top() = stack.top() > temp; break;
      case ge: stack.pop(temp); stack.top() = stack.top() >= temp; break;
      case eq: stack.pop(temp); stack.top() = stack.top() == temp; break;
      case ne: stack.pop(temp); stack.top() = stack.top() != temp; break;
      case and: stack.pop(temp); stack.top() = stack.top() != Number(0) & temp != Number(0); break;
      case or: stack.pop(temp); stack.top() = stack.top() != Number(0) | temp != Number(0); break;
      case minus: stack.top() = -stack.top(); break;
      case not: stack.top() = stack.top() == Number(0); break;
      case cond: {
        Number secondOption = stack.pop();
        Number firstOption = stack.pop();
        if (stack.top().bad) break;
        stack.top() = (stack.top() != Number(0)) ? firstOption : secondOption;
        break;
      }
      case abs: stack.top() = stack.top().abs(); break;
      case sin: stack.top() = stack.top().sin(); break;
      case asin: stack.top() = stack.top().asin(); break;
      case atan: stack.top() = stack.top().atan(); break;
      case atan2: temp = stack.pop(); stack.top() = stack.top().atan2(temp); break;
//  Fetch double
      case fd: stack.push(constant[constantIndex++]); break;
// Fetch cell value
      case fc: {
        CellExpression *x = locator[locatorIndex++].cellExpression();
        Number temp;
        if (x != 0) temp = x->value();
        stack.push(temp);
        break;
      }
    }
    pc++;
  }
  busy = 0;
  return stack.top();
}

// Rather trivial test

int main() {
  int i, j;
// Initialize CellExpresions
  for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) {
    Locator::expression[i][j] = new CellExpression(expressionText[i][j]);
  }
// Calculate and print out values
  for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) {
    printf("cell[%d][%d] = %G\n", i, j, (double) (Locator::expression[i][j]->value()));
  }
  return 0;
}

}