summaryrefslogtreecommitdiffstats
path: root/katapult/plugins/catalogs/calculatorcatalog/parser.y
blob: b614a7025375100fbf80be4d074e74936a9bddb2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/* Author: Tobi Vollebregt */

/* Infix notation calculator. */

%{
#define YYLEX_PARAM parsercontrol

#include <math.h>
#include <stdio.h>
#include "calculatorcatalog.h"

//void yyerror(char const *);
#define yyerror(a,b)
%}

/* Be a reentrant parser. */
%pure_parser

%parse-param {CalculatorCatalog::ParserControl* parsercontrol}

/* Bison declarations.  */
%union {
	double    val;   /* For returning numbers.  */
	FunPtr   fptr;   /* For returning function pointers.  */
	int        id;   /* For returning variables. */
}

%token <val>  NUM    /* Simple double precision number.  */
%token <fptr> FUN    /* Function.  */
%token <id>   VAR    /* Variable. */
%type  <val>  exp

%right '='    /* assignment */
%left '-' '+'
%left '*' '/'
%left NEG     /* negation--unary minus */
%right '^'    /* exponentiation */

%{
static int yylex(YYSTYPE* lvalp, CalculatorCatalog::ParserControl* parsercontrol);
%}
		
%% /* The grammar follows.  */

line: exp            { parsercontrol->result = $1; }
| VAR '=' exp        { if (parsercontrol->assignments) parsercontrol->catalog->setVar($1, $3); }
;

exp: NUM             { $$ = $1;          }
| exp '+' exp        { $$ = $1 + $3;     }
| exp '-' exp        { $$ = $1 - $3;     }
| exp '*' exp        { $$ = $1 * $3;     }
| exp '/' exp        { $$ = $1 / $3;     }
| '-' exp  %prec NEG { $$ = -$2;         }
| exp '^' exp        { $$ = pow($1, $3); }
| '(' exp ')'        { $$ = $2;          }
| FUN '(' exp ')'    { $$ = (*$1)($3);   }
| VAR                { if ($1 == -1) {yyerror(parsercontrol, "undeclared variable"); YYABORT;} $$ = parsercontrol->catalog->getVar($1); }
;

%%

/* The lexical analyzer returns a double floating point
number on the stack and the token NUM, or the numeric code
of the character read if not a number.  It skips all blanks
and tabs, and returns 0 for end-of-input.  */

#include <ctype.h>
#include <string.h>

#define exp (parsercontrol->expression)

static int yylex(YYSTYPE* lvalp, CalculatorCatalog::ParserControl* parsercontrol)
{
	int c;

	/* Skip white space.  */
	while ((c = *(exp++)) == ' ') {
	}
	/* Return end-of-input.  */
	if (c == EOF) {
		return 0;
	}
	/* Process numbers.  */
	if (c == '.' || isdigit(c)) {
		char *endptr = 0;
		lvalp->val = strtod(exp - 1, &endptr);
		exp = endptr;
		return NUM;
	}
	/* Process IDs. */
	if (isalpha(c)) {
		int length = 0;
		char f[128];

		--exp;
		do {
			f[length] = tolower(c);
			++length;
		} while ((c = exp[length]) != 0 && isalpha(c));
		f[length] = 0;

		/* Process functions. */
		const CalculatorCatalog::Function* const functions = parsercontrol->catalog->functionTable();
		for (int i = 0; functions[i].name; ++i) {
			if (length == functions[i].length && strcmp(f, functions[i].name) == 0) {
				lvalp->fptr = functions[i].fptr;
				exp += length;
				return FUN;
			}
		}

		/* Process variables. */
		lvalp->id = parsercontrol->catalog->getVarID(f);
		exp += length;
		return VAR;
	}
	/* Return a single char.  */
	return c;
}