/*
* libtib - Read, write, and evaluate TI BASIC programs
* Copyright (C) 2015-2016 Delwink, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, version 3 only.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
#include
#include
#include "tibchar.h"
#include "tiberr.h"
#include "tibeval.h"
#include "tibfunction.h"
#include "tiblst.h"
#include "tibvar.h"
enum math_operator_function_type
{
T,
TT
};
struct math_operator
{
union
{
TIB *(*t) (const TIB *);
TIB *(*tt) (const TIB *, const TIB *);
} func;
enum math_operator_function_type function_type;
int c;
int priority;
};
static const struct math_operator OPERATORS[] =
{
{ { .t = tib_factorial }, T, '!', 0 },
{ { .t = tib_toradians }, T, TIB_CHAR_DEGREE, 0 },
{ { .tt = tib_pow }, TT, '^', 1 },
{ { .tt = tib_mul }, TT, '*', 2 },
{ { .tt = tib_div }, TT, '/', 2 },
{ { .tt = tib_add }, TT, '+', 3 },
{ { .tt = tib_sub }, TT, '-', 3 }
};
#define NUM_MATH_OPERATORS (sizeof OPERATORS / sizeof (struct math_operator))
#define LAST_PRIORITY 3
static bool
is_var_char (int c)
{
return isupper (c) || TIB_CHAR_THETA == c;
}
static bool
needs_mult_common (int c)
{
return (isdigit (c) || is_var_char (c) || tib_is_var (c));
}
static bool
needs_mult_right (int c)
{
return (needs_mult_common (c) || tib_is_func (c));
}
static bool
needs_mult_left (int c)
{
return (needs_mult_common (c) || ')' == c);
}
bool
is_sign_operator (int c)
{
return ('+' == c || '-' == c);
}
static const struct math_operator *
get_math_operator (int c)
{
for (unsigned int i = 0; i < NUM_MATH_OPERATORS; ++i)
if (OPERATORS[i].c == c)
return &OPERATORS[i];
return NULL;
}
bool
is_math_operator (int c)
{
return get_math_operator (c) != NULL;
}
unsigned int
sign_count (const struct tib_expr *expr)
{
int i;
unsigned int out = 0;
tib_expr_foreach (expr, i)
if (is_sign_operator (expr->data[i]))
++out;
return out;
}
bool
contains_i (const struct tib_expr *expr)
{
int i;
tib_expr_foreach (expr, i)
if ('i' == expr->data[i])
return true;
return false;
}
static int
replace_epow10 (struct tib_expr *expr)
{
int i;
tib_expr_foreach (expr, i)
{
if (TIB_CHAR_EPOW10 == expr->data[i])
{
expr->data[i++] = '*';
const char *s = "10^";
for (; *s != '\0'; ++s, ++i)
{
int rc = tib_expr_insert (expr, i, *s);
if (rc)
return rc;
}
}
}
return 0;
}
static TIB *
single_eval (const struct tib_expr *expr)
{
int len = expr->len;
if (0 == len)
return tib_empty ();
if (1 == len && (is_var_char (expr->data[0]) || tib_is_var (expr->data[0])))
return tib_var_get (expr->data[0]);
int func = tib_eval_surrounded (expr);
if (func)
{
struct tib_expr temp;
tib_subexpr (&temp, expr, 1, len - 1);
return tib_call (func, &temp);
}
if (tib_eval_isnum (expr))
{
gsl_complex z;
tib_errno = tib_expr_parse_complex (expr, &z);
return tib_errno ? NULL : tib_new_complex (GSL_REAL (z), GSL_IMAG (z));
}
if (tib_eval_isstr (expr))
{
char *s = tib_expr_tostr (expr);
if (NULL == s)
return NULL;
TIB *temp = tib_new_str (s);
free (s);
return temp;
}
tib_errno = TIB_ESYNTAX;
return NULL;
}
TIB *
tib_eval (const struct tib_expr *in)
{
int i;
if (0 == in->len)
return tib_empty ();
/* check for store operator */
i = tib_expr_indexof (in, TIB_CHAR_STO);
if (i >= 0)
{
if (i != in->len - 2 || 0 == i)
{
tib_errno = TIB_ESYNTAX;
return NULL;
}
int c = in->data[i + 1];
if (!is_var_char (c))
{
tib_errno = TIB_ESYNTAX;
return NULL;
}
struct tib_expr e;
tib_subexpr (&e, in, 0, i);
TIB *stoval = tib_eval (&e);
if (NULL == stoval)
return NULL;
tib_errno = tib_var_set (c, stoval);
if (tib_errno)
{
tib_decref (stoval);
return NULL;
}
return stoval;
}
struct tib_expr expr = { .bufsize = 0 };
tib_errno = tib_exprcpy (&expr, in);
if (tib_errno)
return NULL;
/* check for implicit closing parentheses and close them */
tib_errno = tib_eval_close_parens (&expr);
if (tib_errno)
{
tib_expr_destroy (&expr);
return NULL;
}
/* replace power of 10 E character with "*10^" */
tib_errno = replace_epow10 (&expr);
if (tib_errno)
{
tib_expr_destroy (&expr);
return NULL;
}
/* add multiplication operators between implicit multiplications */
bool add = true;
tib_expr_foreach (&expr, i)
{
int c = expr.data[i];
if ('"' == c)
{
add = !add; /* don't change anything inside a string */
}
else if (add)
{
if (is_var_char (c) || tib_is_var (c))
{
if (i > 0 && needs_mult_left (expr.data[i - 1]))
tib_errno = tib_expr_insert (&expr, i++, '*');
if (!tib_errno && i < expr.len - 1
&& needs_mult_right (expr.data[i + 1]))
tib_errno = tib_expr_insert (&expr, ++i, '*');
}
else if (i > 0 && i < expr.len - 1)
{
if (tib_is_func (c) && needs_mult_left (expr.data[i - 1]))
tib_errno = tib_expr_insert (&expr, i++, '*');
else if (')' == c && needs_mult_right (expr.data[i + 1]))
tib_errno = tib_expr_insert (&expr, ++i, '*');
}
if (tib_errno)
{
tib_expr_destroy (&expr);
return NULL;
}
}
}
/* this is temp storage for internally-resolved portions */
struct tib_lst *resolved = tib_new_lst ();
if (!resolved)
{
tib_expr_destroy (&expr);
tib_errno = TIB_EALLOC;
return NULL;
}
/* this is to remember the operations to be executed */
struct tib_expr calc;
tib_errno = tib_expr_init (&calc);
if (tib_errno)
{
tib_expr_destroy (&expr);
tib_free_lst (resolved);
return NULL;
}
/* resolve operand expressions, and store the values for later */
int beg = 0, numpar = 0;
add = true;
tib_expr_foreach (&expr, i)
{
int c = expr.data[i];
if ('"' == c)
{
add = !add;
continue;
}
if (!add)
continue;
if (tib_is_func (c))
{
++numpar;
}
else if (')' == c)
{
if (0 == numpar)
{
tib_errno = TIB_ESYNTAX;
break;
}
--numpar;
}
const struct math_operator *oper;
if (0 == numpar && (oper = get_math_operator (c)) != NULL)
{
struct tib_expr sub;
tib_subexpr (&sub, &expr, beg, i);
TIB *part = single_eval (&sub);
if (!part)
break;
if (T == oper->function_type)
{
do
{
TIB *temp = oper->func.t (part);
tib_decref (part);
if (!temp)
break;
part = temp;
}
while (++i < expr.len
&& (oper = get_math_operator (expr.data[i])) != NULL
&& T == oper->function_type);
if (tib_errno)
break;
tib_errno = tib_lst_push (resolved, part);
tib_decref (part);
if (tib_errno)
break;
if (i < expr.len)
{
c = expr.data[i];
if (is_math_operator (c))
{
tib_errno = tib_expr_push (&calc, c);
if (tib_errno)
break;
++i;
}
else
{
tib_errno = tib_expr_push (&calc, '*');
if (tib_errno)
break;
}
beg = i;
}
}
else
{
tib_errno = tib_lst_push (resolved, part);
tib_decref (part);
if (tib_errno)
break;
tib_errno = tib_expr_push (&calc, c);
if (tib_errno)
break;
beg = i + 1;
}
}
}
if (!tib_errno)
{
const struct math_operator *oper;
if (tib_lst_len (resolved) == 0)
{
TIB *temp = single_eval (&expr);
if (!temp)
goto end;
tib_errno = tib_lst_push (resolved, temp);
tib_decref (temp);
if (tib_errno)
goto end;
}
else if (NULL == (oper = get_math_operator (expr.data[expr.len - 1]))
|| oper->function_type != T)
{
struct tib_expr sub;
tib_subexpr (&sub, &expr, beg, i);
TIB *temp = single_eval (&sub);
if (!temp)
goto end;
tib_errno = tib_lst_push (resolved, temp);
tib_decref (temp);
if (tib_errno)
goto end;
}
if (tib_lst_len (resolved) != calc.len + 1)
tib_errno = TIB_ESYNTAX;
}
tib_expr_destroy (&expr);
if (tib_errno)
goto end;
for (int priority = 1; priority <= LAST_PRIORITY; ++priority)
{
for (i = 0; i < calc.len; ++i)
{
const struct math_operator *oper = get_math_operator (calc.data[i]);
if (oper->priority != priority)
continue;
TIB *t;
unsigned int num_operands;
switch (oper->function_type)
{
case TT:
t = oper->func.tt (tib_lst_ref (resolved, i),
tib_lst_ref (resolved, i + 1));
if (!t)
goto end;
num_operands = 2;
break;
default:
tib_errno = TIB_ESYNTAX;
goto end;
}
for (unsigned int _ = 0; _ < num_operands; ++_)
tib_lst_remove (resolved, i);
tib_errno = tib_lst_insert (resolved, t, i);
tib_decref (t);
if (tib_errno)
goto end;
tib_expr_delete (&calc, i--);
}
}
end:
tib_expr_destroy (&calc);
TIB *out = NULL;
if (!tib_errno)
{
if (tib_lst_len (resolved) != 1)
{
tib_errno = TIB_ESYNTAX;
}
else
{
out = tib_lst_ref (resolved, 0);
tib_incref (out);
}
}
tib_free_lst (resolved);
return out;
}
int
tib_eval_surrounded (const struct tib_expr *expr)
{
int count = 0, opening = expr->data[0], len = expr->len;
if (len > 2 && tib_is_func (opening) && ')' == expr->data[len - 1])
{
count = 1;
for (int i = 1; i < len-1; ++i)
{
int c = expr->data[i];
if (tib_is_func (c))
++count;
else if (')' == c && --count == 0)
return 0;
}
return opening;
}
return 0;
}
static int
char_count (const struct tib_expr *expr, int c)
{
int i, count = 0;
tib_expr_foreach (expr, i)
if (c == expr->data[i])
++count;
return count;
}
int
i_count (const struct tib_expr *expr)
{
return char_count (expr, 'i');
}
static int
dot_count (const struct tib_expr *expr)
{
return char_count (expr, '.');
}
static bool
is_number_char (int c)
{
return (isdigit (c) || '.' == c || 'i' == c || is_sign_operator (c));
}
int
get_char_pos (const struct tib_expr *expr, int c, int which)
{
int i, found = 0;
tib_expr_foreach (expr, i)
{
if (c == expr->data[i] && ++found == which)
break;
}
return i;
}
static int
get_sign_pos (const struct tib_expr *expr, int which)
{
int i, found = 0;
tib_expr_foreach (expr, i)
{
if (is_sign_operator (expr->data[i]) && ++found == which)
break;
}
return i;
}
static bool
good_sign_pos (const struct tib_expr *expr, int numsign, int numi)
{
switch (numsign)
{
case 0:
return true;
case 1:
if (!numi && get_sign_pos (expr, 1) != 0)
return false;
break;
case 2:
if (!numi || get_sign_pos (expr, 1) != 0
|| get_sign_pos (expr, 2) > get_char_pos (expr, 'i', 1))
return false;
break;
default:
return false;
}
return true;
}
bool
tib_eval_isnum (const struct tib_expr *expr)
{
int signs = sign_count (expr);
int dots = dot_count (expr);
int is = i_count (expr);
if (signs > 2 || dots > 2 || is > 1)
return false;
if (!good_sign_pos (expr, signs, is))
return false;
if (is && get_char_pos (expr, 'i', 1) < expr->len - 1)
return false;
int i;
tib_expr_foreach (expr, i)
if (!is_number_char (expr->data[i]))
return false;
return true;
}
bool
tib_eval_isstr (const struct tib_expr *expr)
{
int len = expr->len;
if (len > 1 && '"' == expr->data[0])
{
for (int i = 1; i < len-1; ++i)
{
int c = expr->data[i];
if (c > 127 || '"' == c)
return false;
}
return true;
}
return false;
}
bool
tib_eval_islist (const struct tib_expr *expr)
{
int len = expr->len;
if (len > 2 && '{' == expr->data[0] && '}' == expr->data[len - 1])
{
for (int i = 1; i < len-1; ++i)
{
int c = expr->data[i];
if ('{' == c || '}' == c)
return false;
}
return true;
}
return false;
}
static bool
sub_isnum (const struct tib_expr *expr, int beg, int end)
{
if (end <= beg)
return false;
struct tib_expr temp;
int rc = tib_subexpr (&temp, expr, beg, end);
if (rc)
return false;
return tib_eval_isnum (&temp);
}
bool
tib_eval_ismatrix (const struct tib_expr *expr)
{
int len = expr->len;
if (len > 4 && '[' == expr->data[0] && '[' == expr->data[1]
&& ']' == expr->data[len - 1])
{
int i = 2;
int open_brackets = i, fdim = 1, dim = 1, beg = i, end = i;
bool first = true;
for (; i < len; ++i)
{
int c = expr->data[i];
switch (c)
{
case '[':
++open_brackets;
beg = i + 1;
break;
case ']':
--open_brackets;
if (!first && dim != fdim)
return false;
first = false;
dim = 1;
end = i - 1;
if (!sub_isnum (expr, beg, end))
return false;
break;
case ',':
if (first)
++fdim;
else
++dim;
end = i - 1;
if (!sub_isnum (expr, beg, end))
return false;
beg = i + 1;
break;
}
if ((0 == open_brackets && i != len-1) || open_brackets > 2)
return false;
}
return true;
}
return false;
}
int
tib_eval_close_parens (struct tib_expr *expr)
{
int count = 0, len = expr->len;
bool str = false;
for (int i = 0; i < len; ++i)
{
int c = expr->data[i];
if ('"' == c)
str = !str;
if (!str)
{
if (tib_is_func (c))
++count;
if (')' == c)
--count;
}
}
while (count--)
{
int rc = tib_expr_push (expr, ')');
if (rc)
return rc;
}
return 0;
}