def _execute_ass(ast, vars): # execute assignment if it was detected def set_vars(vars): nvars = {} for v, a in vars.items(): v = v.var if a.is_ufunc: if v in sparser.RESERVED_FUNCS: raise NameError( f'cannot assign undefined function to concrete function name {v!r}' ) if a.is_ufunc_anonymous: a = AST(a.op, v, *a[2:]) elif a.is_sym_anonymous: if a.is_sym_unqualified: raise CircularReferenceError( 'cannot asign unqualified anonymous symbol') a = AST(a.op, v, *a[2:]) nvars[v] = a try: # check for circular references AST.apply_vars(AST(',', tuple(('@', v) for v in nvars)), { **_VARS, **nvars }) except RecursionError: raise CircularReferenceError( "I'm sorry, Dave. I'm afraid I can't do that.") from None _VARS.update(nvars) return list(nvars.items()) # start here if not vars: # no assignment if not ast.is_ufunc: ast = _mapback(ast) _VARS['_'] = ast _vars_updated() return [ast] if len(vars) == 1: # simple assignment if ast.op not in {'-ufunc', '-sym'}: ast = _mapback(ast, vars[0].var, {vars[0].var}) vars = set_vars({vars[0]: ast}) else: # tuple assignment ast = ast.strip_paren if ast.op in {',', '[', '-set'}: asts = ast[1] else: asts = [] itr = iter(sym.ast2spt(ast)) for i in range(len(vars) + 1): try: ast = sym.spt2ast(next(itr)) except StopIteration: break if vars[i].is_ufunc_named: asts.append(AST.Ass.ufunc2lamb(vars[i], ast)) vars[i] = AST('@', vars[i].ufunc) else: asts.append(ast) if len(vars) < len(asts): raise ValueError( f'too many values to unpack (expected {len (vars)})') elif len(vars) > len(asts): raise ValueError( f'not enough values to unpack (expected {len (vars)}, got {len (asts)})' ) vasts = list(zip(vars, asts)) exclude = set(va[0].var for va in filter(lambda va: va[1].is_ufunc, vasts)) asts = [ a if a.op in {'-ufunc', '-sym'} else _mapback(a, v.var, exclude) for v, a in vasts ] vars = set_vars(dict(zip(vars, asts))) _vars_updated() return _present_vars(vars)
def evalexpr(ast): sym.ast2spt.set_precision(ast) if ast.is_func and ast.func in AST.Func.PLOT: # plotting? args, kw = AST.args2kwargs(AST.apply_vars(ast.args, _VARS), sym.ast2spt) ret = getattr(splot, ast.func)(*args, **kw) return { 'msg': [ 'Plotting not available because matplotlib is not installed.' ] } if ret is None else { 'img': ret } elif ast.op in { '@', '-func' } and ast[1] in AST.Func.ADMIN: # special admin function? asts = globals()[f'_admin_{ast [1]}']( *(ast.args if ast.is_func else ())) if isinstance(asts, str): return {'msg': [asts]} elif isinstance(asts, list) and isinstance(asts[0], str): return {'msg': asts} else: # not admin function, normal evaluation ast, vars = _prepare_ass(ast) if _SYMPAD_DEBUG: print('ast: ', ast, file=sys.stderr) try: spt, xlat = sym.ast2spt(ast, retxlat=True) # , _VARS) if _SYMPAD_DEBUG and xlat: print('xlat: ', xlat, file=sys.stderr) sptast = sym.spt2ast(spt) except: if _SYMPAD_DEBUG: print(file=sys.stderr) raise if _SYMPAD_DEBUG: try: print('spt: ', repr(spt), file=sys.stderr) except: pass print('spt type: ', type(spt), file=sys.stderr) try: print('spt args: ', repr(spt.args), file=sys.stderr) except: pass print('spt latex: ', sp.latex(spt), file=sys.stderr) print('spt ast: ', sptast, file=sys.stderr) print('spt tex: ', sym.ast2tex(sptast), file=sys.stderr) print('spt nat: ', sym.ast2nat(sptast), file=sys.stderr) print('spt py: ', sym.ast2py(sptast), file=sys.stderr) print(file=sys.stderr) asts = _execute_ass(sptast, vars) response = {} if asts and asts[0] != AST.None_: response.update({ 'math': [{ 'tex': sym.ast2tex(ast), 'nat': sym.ast2nat(ast), 'py': sym.ast2py(ast), } for ast in asts] }) return response
def _admin_env(*args): vars_updated = False def _envop(env, apply): nonlocal vars_updated msgs = [] for var, state in env.items(): if apply: _ENV[var] = state if var == 'EI': msgs.append( f'Uppercase E and I is {"on" if state else "off"}.') if apply: AST.EI(state) for var in (AST.E.var, AST.I.var): if var in _VARS: del _VARS[var] elif var == 'quick': msgs.append(f'Quick input mode is {"on" if state else "off"}.') if apply: sym.set_quick(state) _PARSER.set_quick(state) vars_updated = True elif var == 'pyS': msgs.append( f'Python S escaping is {"on" if state else "off"}.') if apply: sym.set_pyS(state) elif var == 'simplify': msgs.append( f'Post-evaluation simplify is {"on" if state else "off"}.') if apply: sym.set_simplify(state) elif var == 'matsimp': msgs.append( f'Matrix simplify is {"broken" if not spatch.SPATCHED else "on" if state else "off"}.' ) if apply: spatch.set_matmulsimp(state) elif var == 'ufuncmap': msgs.append( f'Undefined function map to variable is {"on" if state else "off"}.' ) if apply: global _UFUNC_MAPBACK _UFUNC_MAPBACK = state elif var == 'prodrat': msgs.append( f'Leading product rational is {"on" if state else "off"}.') if apply: sym.set_prodrat(state) elif var == 'doit': msgs.append(f'Expression doit is {"on" if state else "off"}.') if apply: sym.set_doit(state) elif var == 'strict': msgs.append( f'Strict LaTeX formatting is {"on" if state else "off"}.') if apply: sym.set_strict(state) elif var in _ONE_FUNCS: msgs.append(f'Function {var} is {"on" if state else "off"}.') if apply: vars_updated = True return msgs # start here if not args: return _envop(_ENV, False) env = OrderedDict() for arg in args: if arg.is_ass: var = arg.lhs.as_identifier if var: state = bool(sym.ast2spt(arg.rhs)) else: var = arg.as_identifier if var: if var[:2] == 'no': var, state = var[2:], False else: state = True if var is None: raise TypeError(f'invalid argument {sym.ast2nat (arg)!r}') elif var not in _ENV_OPTS: raise NameError(f'invalid environment setting {var!r}') env[var] = state ret = _envop(env, True) if vars_updated: _vars_updated() return ret