Mercurial > ~dholland > hg > ag > index.cgi
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; } }