summaryrefslogtreecommitdiffhomepage
path: root/mixlib/mix_eval_scanner.l
diff options
context:
space:
mode:
Diffstat (limited to 'mixlib/mix_eval_scanner.l')
-rw-r--r--mixlib/mix_eval_scanner.l276
1 files changed, 276 insertions, 0 deletions
diff --git a/mixlib/mix_eval_scanner.l b/mixlib/mix_eval_scanner.l
new file mode 100644
index 0000000..8d0d3bd
--- /dev/null
+++ b/mixlib/mix_eval_scanner.l
@@ -0,0 +1,276 @@
+/* -*-c-*- ------------------ mix_eval_scanner.l :
+ * scanner used by mix_eval_t
+ * ------------------------------------------------------------------
+ * $Id: mix_eval_scanner.l,v 1.4 2005/09/20 19:43:13 jao Exp $
+ * ------------------------------------------------------------------
+ * Copyright (C) 2000, 2004 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+%{
+#include <string.h>
+#include "mix.h"
+#include "xmix_eval.h"
+
+#define YY_DECL \
+ mix_eval_result_t mix_eval_expr (mix_eval_data_ *data)
+
+/* keep track of current position in buffer */
+#define YY_USER_ACTION yypos += yyleng;
+
+#define RETURN_STATE(err) \
+ do { \
+ done = TRUE; \
+ state = err; \
+ BEGIN (INITIAL); \
+ } while (FALSE)
+
+#define CLEAN_UP() \
+ do { \
+ yy_delete_buffer (buffer); \
+ g_free (expr_cp); \
+ } while (FALSE)
+
+static mix_word_t eval_binop_ (const gchar *op, mix_word_t x, mix_word_t y);
+
+static int unput_word_ (mix_word_t word);
+
+/* { */
+/* gchar *value; */
+/* gint k, result; */
+/* value = g_strdup_printf ("%s%ld", */
+/* mix_word_is_negative (word)? "-":"+", */
+/* mix_word_magnitude (word)); */
+/* result = strlen (value); */
+/* for (k = result - 1; k >= 0; --k) */
+/* unput (value[k]); */
+/* g_free (value); */
+/* return result; */
+/* } */
+
+%}
+
+%option nomain
+%option caseless
+%option array
+%option stack
+%option noyywrap
+%option noyy_top_state
+%option noreject
+%option outfile="lex.yy.c"
+
+%s EVAL
+%s WEVAL
+
+ws [ \t]+
+digit [0-9]
+letter [A-Z]
+number [+-]?{digit}+
+mixchar [0-9A-Z .,'')(+*/=$<>@;:\-]
+locsymbol {digit}H
+flocsymbol {digit}F
+blocsymbol {digit}B
+symbol {digit}*{letter}+[A-Z0-9]*
+binops "+"|"-"|"/"|"//"|":"
+binop {binops}|"*"
+atexpr {digit}+|{symbol}|\*
+expr [+-]?{atexpr}({binop}{1}{atexpr})*
+fpart \({expr}\)
+wexpr {expr}({fpart})?(,{expr}({fpart})?)*
+
+
+%%
+
+%{
+ YY_BUFFER_STATE buffer;
+ mix_word_t expr_val = MIX_WORD_ZERO, wexpr_val = MIX_WORD_ZERO;
+ mix_word_t wexpr_val_tmp = MIX_WORD_ZERO;
+ mix_eval_result_t state = MIX_EVAL_OK;
+ gchar *expr_cp;
+ gint yypos = -22; /* to account for padding */
+ gboolean is_fp = FALSE, done = FALSE;
+ g_assert (data != NULL && data->expr != NULL);
+ /* make room enough to unput computed values */
+ expr_cp = g_strdup_printf ("%-20s%s"," ", data->expr);
+ buffer = yy_scan_string (expr_cp);
+ data->errpos = -1;
+%}
+
+
+<*><<EOF>> {
+ CLEAN_UP ();
+ return MIX_EVAL_INTERN;
+}
+
+<INITIAL>{
+ {ws} /* eat whitespace */
+ . {
+ if (!done && state == MIX_EVAL_OK) {
+ yypos -= yyleng; yyless (0);
+ yy_push_state (WEVAL);
+ } else {
+ CLEAN_UP ();
+ if (state == MIX_EVAL_OK) return (MIX_EVAL_SYNTAX);
+ data->errpos = yypos;
+ return state;
+ }
+ }
+ "\n" {
+ CLEAN_UP();
+ if (state == MIX_EVAL_OK)
+ data->value = wexpr_val;
+ else
+ data->errpos = yypos;
+ return state;
+ }
+}
+
+<WEVAL>{
+ {number}"(" {
+ is_fp = TRUE;
+ wexpr_val_tmp = mix_word_new (atol (yytext));
+ }
+ {number}")" {
+ glong val = atol (yytext);
+ if ( !is_fp ) {
+ RETURN_STATE (MIX_EVAL_MIS_PAREN);
+ } else if ( val < 0 || val > MIX_BYTE_MAX
+ || !mix_fspec_is_valid (mix_byte_new (val)) ) {
+ RETURN_STATE (MIX_EVAL_INV_FSPEC);
+ } else {
+ is_fp = FALSE;
+ wexpr_val = mix_word_store_field (mix_byte_new (val),
+ wexpr_val_tmp,
+ wexpr_val);
+ }
+ }
+ {number}/({ws}*\n)|[,()] {
+ wexpr_val = mix_word_new (atol (yytext));
+ }
+ {expr}/({ws}*\n)|[,()] {
+ if (yytext[0] != '*')
+ {
+ yypos -= yyleng;
+ yyless (0);
+ }
+ else
+ {
+ yypos -= yyleng - 1;
+ expr_val = mix_short_to_word_fast (data->loc);
+ yyless (1);
+ }
+ yy_push_state (EVAL);
+ }
+ ,/{expr} /* eat comma if followed by expression */
+ [\t\n ] { /* ok if not inside an f-part */
+ if ( is_fp ) {
+ RETURN_STATE (MIX_EVAL_MIS_PAREN);
+ }
+ unput (yytext[yyleng-1]); --yypos;
+ done = TRUE;
+ yy_pop_state ();
+ }
+ . RETURN_STATE (MIX_EVAL_SYNTAX);
+}
+
+<EVAL>{
+ {binop}{digit}+ {
+ const gchar *s = ( yytext[1] == '/' ) ? yytext+2 : yytext+1;
+ mix_word_t value = mix_word_new (atol (s));
+ expr_val = eval_binop_ (yytext, expr_val, value);
+ }
+ {binop}{symbol} {
+ const gchar *s = ( yytext[1] == '/' ) ? yytext+2 : yytext+1;
+ if ( !mix_symbol_table_is_defined (data->table, s) ) {
+ RETURN_STATE (MIX_EVAL_UNDEF_SYM);
+ }
+ expr_val = eval_binop_ (yytext, expr_val,
+ mix_symbol_table_value (data->table, s));
+ }
+ {binop}"*" {
+ expr_val = eval_binop_ (yytext, expr_val,
+ mix_short_to_word_fast (data->loc));
+ }
+ "*" yypos -= unput_word_ (mix_short_to_word_fast (data->loc));
+ {number} expr_val = mix_word_new (atol (yytext));
+ {symbol} {
+ if ( !mix_symbol_table_is_defined (data->table, yytext) ) {
+ RETURN_STATE (MIX_EVAL_UNDEF_SYM);
+ }
+ expr_val = mix_symbol_table_value (data->table, yytext);
+ }
+ [,)(\n\t ] {
+ unput (yytext[0]); --yypos;
+ yypos -= unput_word_ (expr_val);
+ yy_pop_state ();
+ }
+
+ . RETURN_STATE (MIX_EVAL_SYNTAX);
+}
+
+
+%%
+
+static mix_word_t
+eval_binop_ (const gchar *op, mix_word_t x, mix_word_t y)
+{
+ mix_word_t result = MIX_WORD_ZERO;
+ switch (op[0])
+ {
+ case '+':
+ result = mix_word_add (x,y);
+ break;
+ case '-':
+ result = mix_word_sub (x,y);
+ break;
+ case '*':
+ mix_word_mul (x, y, NULL, &result);
+ break;
+ case ':':
+ {
+ mix_word_t a;
+ mix_word_mul (x, 8, NULL, &a);
+ result = mix_word_add (a, y);
+ break;
+ }
+ case '/':
+ if ( strlen (op) > 1 && op[1] == '/' ) {
+ mix_word_div (x,MIX_WORD_ZERO,y, &result, NULL);
+ } else {
+ mix_word_div (MIX_WORD_ZERO, x, y, &result, NULL);
+ }
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ return result;
+}
+
+static int
+unput_word_ (mix_word_t word)
+{
+ gchar *value;
+ gint k, result;
+ value = g_strdup_printf ("%s%ld",
+ mix_word_is_negative (word)? "-":"+",
+ mix_word_magnitude (word));
+ result = strlen (value);
+ for (k = result - 1; k >= 0; --k)
+ unput (value[k]);
+ g_free (value);
+ return result;
+}