1 /* -*-c-*- ------------------ mix_eval_scanner.l :
2  * scanner used by mix_eval_t
3  * ------------------------------------------------------------------
4  * Copyright (C) 2000, 2004, 2006, 2007, 2008, 2019 Free Software Foundation, Inc.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  */
21 
22 %{
23 #include <string.h>
24 #include "mix.h"
25 #include "xmix_eval.h"
26 
27 #define YY_DECL							\
28   mix_eval_result_t  mix_eval_expr (mix_eval_data_ *data)
29 
30 /* keep track of current position in buffer */
31 #define YY_USER_ACTION yypos += yyleng;
32 
33 #define RETURN_STATE(err)			\
34   do {						\
35     done = TRUE;                                \
36     state = err;				\
37     BEGIN (INITIAL);				\
38   } while (FALSE)
39 
40 #define CLEAN_UP()				\
41   do {						\
42     yy_delete_buffer (buffer);			\
43     g_free (expr_cp);				\
44  } while (FALSE)
45 
46 static mix_word_t eval_binop_ (const gchar *op, mix_word_t x, mix_word_t y);
47 static int unput_word_ (mix_word_t word);
48 
49 %}
50 
51 %option nomain
52 %option caseless
53 %option array
54 %option stack
55 %option noinput
56 %option noyywrap
57 %option noyy_top_state
58 %option outfile="lex.yy.c"
59 
60 %s  EVAL
61 %s  WEVAL
62 
63 ws	[ \t]+
64 digit	[0-9]
65 letter	[A-Z]
66 number	[+-]?{digit}+
67 mixchar [0-9A-Z .,'')(+*/=$<>@;:\-]
68 locsymbol   {digit}H
69 flocsymbol  {digit}F
70 blocsymbol  {digit}B
71 symbol	{digit}*{letter}+[A-Z0-9]*
72 binops	"+"|"-"|"/"|"//"|":"
73 binop	{binops}|"*"
74 atexpr	{digit}+|{symbol}|\*
75 expr	[+-]?{atexpr}({binop}{1}{atexpr})*
76 fpart   \({expr}\)
77 wexpr   {expr}({fpart})?(,{expr}({fpart})?)*
78 
79 
80 %%
81 
82 %{
83   YY_BUFFER_STATE buffer;
84   mix_word_t expr_val = MIX_WORD_ZERO, wexpr_val = MIX_WORD_ZERO;
85   mix_word_t wexpr_val_tmp = MIX_WORD_ZERO;
86   mix_eval_result_t state = MIX_EVAL_OK;
87   gchar *expr_cp;
88   gint yypos = -22; /* to account for padding */
89   gboolean is_fp = FALSE, done = FALSE;
90   g_assert (data != NULL && data->expr != NULL);
91   /* make room enough to unput computed values */
92   expr_cp = g_strdup_printf ("%-20s%s"," ", data->expr);
93   buffer = yy_scan_string (expr_cp);
94   data->errpos = -1;
95 %}
96 
97 
98 <*><<EOF>> {
99   CLEAN_UP ();
100   return MIX_EVAL_INTERN;
101 }
102 
103 <INITIAL>{
104   {ws} /* eat whitespace */
105   . {
106     if (!done && state == MIX_EVAL_OK) {
107        yypos -= yyleng; yyless (0);
108       yy_push_state (WEVAL);
109     } else {
110       CLEAN_UP ();
111       if (state == MIX_EVAL_OK) return (MIX_EVAL_SYNTAX);
112       data->errpos = yypos;
113       return state;
114     }
115   }
116   "\n" {
117     CLEAN_UP();
118     if (state == MIX_EVAL_OK)
119 	data->value = wexpr_val;
120     else
121 	data->errpos = yypos;
122     return state;
123   }
124 }
125 
126 <WEVAL>{
127   {number}"(" {
128     is_fp = TRUE;
129     wexpr_val_tmp = mix_word_new (atol (yytext));
130   }
131   {number}")"  {
132     glong val  = atol (yytext);
133     if ( !is_fp ) {
134       RETURN_STATE (MIX_EVAL_MIS_PAREN);
135     }  else if ( val < 0 || val > MIX_BYTE_MAX
136          || !mix_fspec_is_valid (mix_byte_new (val)) ) {
137       RETURN_STATE (MIX_EVAL_INV_FSPEC);
138     } else {
139       is_fp = FALSE;
140       wexpr_val = mix_word_store_field (mix_byte_new (val),
141 					wexpr_val_tmp,
142 					wexpr_val);
143     }
144   }
145   {number}/({ws}*\n)|[,()] {
146     wexpr_val = mix_word_new (atol (yytext));
147   }
148   {expr}/({ws}*\n)|[,()] {
149     if (yytext[0] != '*')
150       {
151 	yypos -= yyleng;
152 	yyless (0);
153       }
154     else
155       {
156 	yypos -= yyleng - 1;
157 	expr_val = mix_short_to_word_fast (data->loc);
158 	yyless (1);
159       }
160     yy_push_state (EVAL);
161   }
162   ,/{expr} /* eat comma if followed by expression */
163   [\t\n ] { /* ok if not inside an f-part */
164     if ( is_fp ) {
165       RETURN_STATE (MIX_EVAL_MIS_PAREN);
166     }
167     unput (yytext[yyleng-1]); --yypos;
168     done = TRUE;
169     yy_pop_state ();
170   }
171   .  RETURN_STATE (MIX_EVAL_SYNTAX);
172 }
173 
174 <EVAL>{
175   {binop}{digit}+ {
176     const gchar *s = ( yytext[1] == '/' ) ? yytext+2 : yytext+1;
177     mix_word_t value = mix_word_new (atol (s));
178     expr_val = eval_binop_ (yytext, expr_val, value);
179   }
180   {binop}{symbol} {
181     const gchar *s = ( yytext[1] == '/' ) ? yytext+2 : yytext+1;
182     if ( !mix_symbol_table_is_defined (data->table, s) ) {
183       RETURN_STATE (MIX_EVAL_UNDEF_SYM);
184     }
185     expr_val = eval_binop_ (yytext, expr_val,
186 			    mix_symbol_table_value (data->table, s));
187   }
188   {binop}"*" {
189     expr_val = eval_binop_ (yytext, expr_val,
190 			    mix_short_to_word_fast (data->loc));
191   }
192   "*"  yypos -= unput_word_ (mix_short_to_word_fast (data->loc));
193   {number}    expr_val = mix_word_new (atol (yytext));
194   {symbol} {
195     if ( !mix_symbol_table_is_defined (data->table, yytext) ) {
196       RETURN_STATE (MIX_EVAL_UNDEF_SYM);
197     }
198     expr_val = mix_symbol_table_value (data->table, yytext);
199   }
200   [,)(\n\t ]   {
201     unput (yytext[0]); --yypos;
202     yypos -= unput_word_ (expr_val);
203     yy_pop_state ();
204   }
205 
206   .   RETURN_STATE (MIX_EVAL_SYNTAX);
207 }
208 
209 
210 %%
211 
212 static mix_word_t
213 eval_binop_ (const gchar *op, mix_word_t x, mix_word_t y)
214 {
215   mix_word_t result = MIX_WORD_ZERO;
216   switch (op[0])
217     {
218     case '+':
219       result = mix_word_add (x,y);
220       break;
221     case '-':
222       result = mix_word_sub (x,y);
223       break;
224     case '*':
225       mix_word_mul (x, y, NULL, &result);
226       break;
227     case ':':
228       {
229 	mix_word_t a;
230 	mix_word_mul (x, 8, NULL, &a);
231 	result = mix_word_add (a, y);
232 	break;
233       }
234     case '/':
235       if ( strlen (op) > 1 && op[1] == '/' ) {
236 	mix_word_div (x,MIX_WORD_ZERO,y, &result, NULL);
237       } else {
238 	mix_word_div (MIX_WORD_ZERO, x, y, &result, NULL);
239       }
240       break;
241     default:
242       g_assert_not_reached ();
243     }
244   return result;
245 }
246 
247 static int
248 unput_word_ (mix_word_t word)
249 {
250   gchar *value;
251   gint k, result;
252   value = g_strdup_printf ("%s%ld",
253                            mix_word_is_negative (word)? "-":"+",
254                            mix_word_magnitude (word));
255   result = strlen (value);
256   for (k = result - 1; k >= 0; --k)
257     unput (value[k]);
258   g_free (value);
259   return result;
260 }
261