예제 #1
0
파일: sxlat.py 프로젝트: tom-pytel/sympad
def _xlat_f2a_Subs(expr=None, src=None, dst=None):
    def parse_subs(src, dst):
        if src is None:
            return ((AST.VarNull, AST.VarNull), )

        src = src.strip_paren.comma if src.strip_paren.is_comma else (
            src, )  # (src.strip_paren,)

        if dst is None:
            return tuple(it.zip_longest(src, (), fillvalue=AST.VarNull))

        dst = dst.strip_paren.comma if dst.strip_paren.is_comma else (
            dst, )  # (dst.stip_paren,)

        if len(dst) > len(src):
            return None

        return tuple(it.zip_longest(src, dst, fillvalue=AST.VarNull))

    # start here
    if expr is None:
        return AST('-subs', AST.VarNull, ((AST.VarNull, AST.VarNull), ))

    subs = parse_subs(src, dst)

    if subs is None:
        return None

    if expr.is_subs:
        return AST('-subs', expr.expr, expr.subs + subs)
    else:
        return AST('-subs', expr, subs)
예제 #2
0
파일: server.py 프로젝트: tom-pytel/sympad
	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 ())
예제 #3
0
파일: sxlat.py 프로젝트: tom-pytel/sympad
def _xlat_f2a_SymmetricDifference(*args):
    if len(args) != 2:
        return None

    if not args[0].is_sdiff:
        return AST('^^', args)

    return AST('^^', args[0].sdiff + (args[1], ))
예제 #4
0
파일: server.py 프로젝트: tom-pytel/sympad
def _present_vars (vars):
	asts = []

	for v, e in vars:
		if v != '_':
			if e.is_lamb:
				asts.append (AST ('=', ('-ufunc', v, tuple (('@', vv) for vv in e.vars)), e.lamb))
			else:
				asts.append (AST ('=', ('@', v), e))

	return asts
예제 #5
0
파일: sxlat.py 프로젝트: tom-pytel/sympad
def _xlat_f2a_slice(*args):
    if len(args) == 1:
        return AST('-slice', False, False if args[0] == AST.None_ else args[0],
                   None)
    if len(args) == 2:
        return AST('-slice', False if args[0] == AST.None_ else args[0],
                   False if args[1] == AST.None_ else args[1], None)
    else:
        return AST(
            '-slice', False if args[0] == AST.None_ else args[0],
            False if args[1] == AST.None_ else args[1], args[2] if
            args[2] != AST.None_ else None if args[1] == AST.None_ else False)
예제 #6
0
파일: sxlat.py 프로젝트: tom-pytel/sympad
def _xlat_f2a_Sum(ast=AST.VarNull, ab=None, **kw):
    if ab is None:
        return AST('-sum', ast, AST.VarNull, AST.VarNull, AST.VarNull)

    ab = ab.strip_paren

    if ab.is_var:
        return AST('-sum', ast, ab, AST.VarNull, AST.VarNull)
    elif ab.is_comma and ab.comma and ab.comma.len <= 3 and ab.comma[0].is_var:
        return AST('-sum', ast, *ab.comma,
                   *((AST.VarNull, ) * (3 - ab.comma.len)))

    return None
예제 #7
0
파일: sxlat.py 프로젝트: tom-pytel/sympad
def xlat_funcs2asts(
    ast,
    xlat,
    func_call=None,
    recurse=True
):  # translate eligible functions in tree to other AST representations
    if not isinstance(ast, AST):
        return ast

    if ast.is_func:
        xact = xlat.get(ast.func)
        args = ast.args
        ret = lambda: AST('-func', ast.func, args)

    elif ast.is_attr_func:
        xact = xlat.get(f'.{ast.attr}')
        args = (ast.obj, ) + ast.args
        ret = lambda: AST('.', args[0], ast.attr, tuple(args[1:]))

    else:
        xact = None

    if xact is not None:
        if recurse:
            args = AST(*(xlat_funcs2asts(a, xlat, func_call=func_call)
                         for a in args))

        try:
            if xact is True:  # True means execute function and use return value for ast, only happens for -func
                return func_call(
                    ast.func, args
                )  # not checking func_call None because that should never happen

            xargs, xkw = AST.args2kwargs(args)
            ast2 = xact(*xargs, **xkw)

            if ast2 is not None:
                return ast2

        except:
            pass

        return ret()

    if recurse:
        return AST(*(xlat_funcs2asts(a, xlat, func_call=func_call)
                     for a in ast))  #, **ast._kw)

    return ast
예제 #8
0
def _vars_updated():
    global _VARS_FLAT

    vars = {
        v: a if a.is_lamb else AST.apply_vars(a, _VARS, mode=False)
        for v, a in _VARS.items()
    }  # flattened vars so sym and sparser don't need to do apply_vars()
    one = (f for f in filter(lambda f: _ENV.get(f), _ONE_FUNCS)
           )  # hidden functions for stuff like Gamma
    lamb = (va[0] for va in filter(lambda va: va[1].is_lamb, vars.items())
            )  # user lambda functions
    assfunc = (va[0] for va in filter(
        lambda va: va[1].is_var and va[1].var in AST.Func.PYBASE, vars.items())
               )  # user variables assigned to concrete functions
    funcs = {*one, *lamb, *assfunc}

    sym.set_sym_user_vars(vars)
    sym.set_sym_user_funcs(funcs)
    sparser.set_sp_user_vars(vars)
    sparser.set_sp_user_funcs(funcs)

    _UFUNC_MAP.clear()
    _SYM_MAP.clear()
    _SYM_VARS.clear()

    _VARS_FLAT = vars

    for v, a in vars.items():  # build ufunc and sym mapback dict
        if v != '_':
            if a.is_ufunc:
                _UFUNC_MAP.setdefault(a, set()).add(v)

            elif a.is_sym:
                _SYM_MAP.setdefault(a, set()).add(v)
                _SYM_VARS.add(v)
예제 #9
0
파일: sxlat.py 프로젝트: tom-pytel/sympad
def _xlat_f2a_Limit(ast=AST.VarNull,
                    var=AST.VarNull,
                    to=AST.VarNull,
                    dir=_AST_StrPlus):
    if var.is_var_nonconst:
        return AST('-lim', ast, var, to, *_xlat_f2a_Limit_dirs[dir])

    return None
예제 #10
0
파일: sxlat.py 프로젝트: tom-pytel/sympad
def _xlat_f2a_Integral(ast=None, dvab=None, *args, **kw):
    if ast is None:
        return AST('-intg', AST.VarNull, AST.VarNull)

    if dvab is None:
        vars = ast.free_vars

        if len(vars) == 1:
            return AST('-intg', ast, ('@', f'd{vars.pop ().var}'))

        return AST('-intg', ast, AST.VarNull)

    dvab = dvab.strip_paren
    ast2 = None

    if dvab.is_comma:
        if dvab.comma and dvab.comma[0].is_var:  #_nonconst:
            if dvab.comma.len == 1:
                ast2 = AST('-intg', ast, ('@', f'd{dvab.comma [0].var}'))
            elif dvab.comma.len == 2:
                ast2 = AST('-intg', ast, ('@', f'd{dvab.comma [0].var}'),
                           AST.Zero, dvab.comma[1])
            elif dvab.comma.len == 3:
                ast2 = AST('-intg', ast, ('@', f'd{dvab.comma [0].var}'),
                           dvab.comma[1], dvab.comma[2])

    elif dvab.is_var:
        ast2 = AST('-intg', ast, ('@', f'd{dvab.var}'))

    if ast2 is None:
        return None

    return _xlat_f2a_Integral(ast2, *args) if args else ast2
예제 #11
0
def _prepare_ass(ast):  # check and prepare for simple or tuple assignment
    if not ast.ass_valid:
        vars = None
    elif ast.ass_valid.error:
        raise RealityRedefinitionError(ast.ass_valid.error)

    else:
        vars, ast = ast.ass_valid.lhs, ast.ass_valid.rhs
        vars = list(vars.comma) if vars.is_comma else [vars]

    return AST.apply_vars(ast, _VARS_FLAT), vars
예제 #12
0
파일: sxlat.py 프로젝트: tom-pytel/sympad
def xlat_func2tex(ast, ast2tex):
    xact = _XLAT_FUNC2TEX.get(ast.func)

    if xact:
        args, kw = AST.args2kwargs(ast.args)

        try:
            return xact(ast2tex, *args, **kw)
        except:
            pass

    return None
예제 #13
0
파일: sxlat.py 프로젝트: tom-pytel/sympad
def _xlat_f2a_Lambda(args, expr):
    args = args.strip_paren
    args = args.comma if args.is_comma else (args, )
    vars = []

    for v in args:
        if not v.is_var_nonconst:
            return None

        vars.append(v.var)

    return AST('-lamb', expr, tuple(vars))
예제 #14
0
파일: sxlat.py 프로젝트: tom-pytel/sympad
def xlat_attr2tex(ast, ast2tex):
    if ast.is_attr_func:
        xact = _XLAT_ATTRFUNC2TEX.get(ast.attr)

        if xact:
            args, kw = AST.args2kwargs(ast.args)

            try:
                return xact(ast2tex, ast.obj, *args, **kw)
            except:
                pass

    return None
예제 #15
0
파일: sxlat.py 프로젝트: tom-pytel/sympad
def _xlat_f2a_Matrix(ast=AST.VarNull):
    if ast.is_var_null:
        return AST.MatEmpty

    if ast.is_brack:
        if not ast.brack:
            return AST.MatEmpty

        elif not ast.brack[
                0].is_brack:  # single layer or brackets, column matrix?
            return AST('-mat', tuple((c, ) for c in ast.brack))

        elif ast.brack[0].brack:
            rows = [ast.brack[0].brack]
            cols = len(rows[0])

            for row in ast.brack[1:-1]:
                if row.brack.len != cols:
                    break

                rows.append(row.brack)

            else:
                l = ast.brack[-1].brack.len

                if l <= cols:
                    if ast.brack.len > 1:
                        rows.append(ast.brack[-1].brack + (AST.VarNull, ) *
                                    (cols - l))

                    if l != cols:
                        return AST('-mat', tuple(rows))
                    elif cols > 1:
                        return AST('-mat', tuple(rows))
                    else:
                        return AST('-mat', tuple((r[0], ) for r in rows))

    return None
예제 #16
0
파일: sxlat.py 프로젝트: tom-pytel/sympad
def _xlat_f2a_Piecewise(*args):
    pcs = []

    if not args or args[0].is_var_null:
        return AST('-piece', ((AST.VarNull, AST.VarNull), ))

    if len(args) > 1:
        for c in args[:-1]:
            c = c.strip

            if not c.is_comma or c.comma.len != 2:
                return None

            pcs.append(c.comma)

    ast = args[-1]

    if not ast.is_paren:
        return None

    ast = ast.strip
    pcs = tuple(pcs)

    if not ast.is_comma:
        return AST('-piece', pcs + ((ast, AST.VarNull), ))
    elif ast.comma.len == 0:
        return AST('-piece', pcs + ())

    if not ast.comma[0].is_comma:
        if ast.comma.len == 1:
            return AST('-piece', pcs + ((ast.comma[0], AST.VarNull), ))
        elif ast.comma.len == 2:
            return AST(
                '-piece', pcs +
                ((ast.comma[0],
                  True if ast.comma[1] == AST.True_ else ast.comma[1]), ))

    return None
예제 #17
0
파일: sxlat.py 프로젝트: tom-pytel/sympad
def _xlat_f2a_Derivative(ast=AST.VarNull, *dvs, **kw):
    ds = []

    if not dvs:
        if ast.is_diffp:
            return AST('-diffp', ast.diffp, ast.count + 1)
        else:
            return AST('-diffp', ast, 1)

    else:
        dvs = list(dvs[::-1])

        while dvs:
            v = dvs.pop()

            if not v.is_var:
                return None

            ds.append(
                (v.var,
                 dvs.pop().as_int if dvs and dvs[-1].is_num_pos_int else 1))

    return AST('-diff', ast, 'd', tuple(ds))
예제 #18
0
파일: sxlat.py 프로젝트: tom-pytel/sympad
def _xlat_f2a_subs(expr, src=AST.VarNull, dst=None):
    def parse_subs(src, dst):
        if dst is not None:
            return ((src, dst), )

        src = src.strip_paren

        if src.is_dict:
            return src.dict
        elif src.op not in {',', '[', '-set'}:
            return None  # ((src, AST.VarNull),)

        else:
            subs = []

            for arg in src[1]:
                ast = arg.strip_paren

                if ast.op in {',', '['} and ast[1].len <= 2:
                    subs.append((ast[1] + (AST.VarNull, AST.VarNull))[:2])
                elif arg.is_paren and arg is src[1][-1]:
                    subs.append((ast, AST.VarNull))
                else:
                    return None

            return tuple(subs)

    # start here
    subs = parse_subs(src, dst)

    if subs is None:
        return None

    if expr.is_subs:  # collapse multiple subs into one
        return AST('-subs', expr.expr, expr.subs + subs)

    return AST('-subs', expr, subs)
예제 #19
0
def _mapback(
    ast,
    assvar=None,
    exclude=set()
):  # map back ufuncs and symbols to the variables they are assigned to if possible
    if not isinstance(ast, AST):
        return ast

    if ast.is_var:
        if ast.var not in _SYM_VARS:
            return ast

        if ast.var == assvar:
            raise CircularReferenceError(
                'trying to assign unqualified symbol to variable of the same name'
            )

        return AST('-sym', ast.var)

    if ast.is_sym:
        vars = _SYM_MAP.get(ast)

        if not vars:
            return ast

        if ast.sym in vars:
            return AST('@', ast.sym)

        return AST('@', next(iter(vars)))

    if _UFUNC_MAPBACK:
        if ast.is_ass and ast.lhs.is_ufunc:
            return AST('=', ast.lhs, _mapback(ast.rhs, assvar, exclude))
        elif not ast.is_ufunc:
            return AST(*(_mapback(a, assvar, exclude) for a in ast))

        vars = _UFUNC_MAP.get(ast)

        if vars:  # prevent mapping to self on assignment
            if ast.ufunc in vars and ast.ufunc not in exclude:
                return AST('@', ast.ufunc)

            for var in vars:
                if var not in exclude:
                    return AST('@', var)

    return AST(*(_mapback(a, assvar, exclude) for a in ast))
예제 #20
0
파일: sxlat.py 프로젝트: tom-pytel/sympad
    def invert(ast):
        cmp = []
        lhs = ast.lhs

        for c in ast.cmp:
            v = _xlat_f2a_Add_invert.get(c[0])

            if v is None:
                return None

            cmp.append((v, lhs))

            lhs = c[1]

        return AST('<>', lhs, tuple(cmp[::-1]))
예제 #21
0
			def validate (ast): # validate ast rules have not been broken by garbling functions
				if not isinstance (ast, AST):
					return ast

				if ast.is_var:
					if ast.var in sparser.RESERVED_ALL or ast.var_name.startswith ('_'):
						return AST ('@', 'C')

				if ast.is_func: # the slice function is evil
					if ast.func == 'slice' and ast.args.len == 2 and ast.args [0] == AST.None_: # :x gets written as slice(x) but may come from slice(None, x)
						ast = AST ('-slice', AST.None_, ast.args [1], None)
					elif ast.func in _FORBIDDEN_SXLAT_FUNCS: # random spaces can create forbidden functions
						ast = AST ('-func', 'print', *ast [2:])

				elif ast.is_diff: # reserved words can make it into diff via dif or partialelse
					if any (v [0] in sparser.RESERVED_ALL for v in ast.dvs):
						return AST ('@', 'C')

				elif ast.is_intg: # same
					if ast.dv.as_var.var in sparser.RESERVED_ALL:
						return AST ('@', 'C')

				elif ast.is_slice: # the slice object is evil
					if ast.start == AST.None_ or ast.stop == AST.None_ or ast.step == AST.None_:
						raise ValueError ('malformed slice')
						# ast = AST ('-slice', ast.start, ast.stop, None)

				elif ast.is_ufunc: # remove spaces inserted into ufunc name
					if ' ' in ast.ufunc:
						ast = AST ('-ufunc', ast.ufunc_full.replace (' ', ''), ast.vars, ast.kw)

				elif ast.is_subs:
					if ast.expr.is_comma:
						ast = AST ('-subs', ('(', ast.expr), ast.subs)

				elif ast.is_sym: # remove spaces inserted into ufunc name
					if ' ' in ast.sym:
						ast = AST ('-sym', ast.sym.replace (' ', ''), ast.kw)

				return AST (*(validate (a) for a in ast))
예제 #22
0
파일: sxlat.py 프로젝트: tom-pytel/sympad
def _xlat_pyS(ast, need=False):  # Python S(1)/2 escaping where necessary
    if not isinstance(ast, AST):
        return ast, False

    if ast.is_num:
        if need:
            return AST('-func', 'S', (ast, )), True
        else:
            return ast, False

    if ast.is_comma or ast.is_brack:
        return AST(ast.op, tuple(_xlat_pyS(a)[0] for a in ast[1])), False

    if ast.is_curly or ast.is_paren or ast.is_minus:
        expr, has = _xlat_pyS(ast[1], need)

        return AST(ast.op, expr), has

    if ast.is_add or ast.is_mul:
        es = [_xlat_pyS(a) for a in ast[1][1:]]
        has = any(e[1] for e in es)
        e0 = _xlat_pyS(ast[1][0], need and not has)
        es = (e0[0], ) + tuple(e[0] for e in es)

        return (AST('+', es)
                if ast.is_add else AST('*', es, ast.exp)), has or e0[1]

    if ast.is_div:
        denom, has = _xlat_pyS(ast.denom)
        numer = _xlat_pyS(ast.numer, not has)[0]

        return AST('/', numer, denom), True

    if ast.is_pow:
        exp, has = _xlat_pyS(ast.exp)
        base = _xlat_pyS(ast.base, not (has or exp.is_num_pos))[0]

        return AST('^', base, exp), True

    es = [_xlat_pyS(a) for a in ast]

    return AST (*tuple (e [0] for e in es)), \
      ast.op in {'=', '<>', '@', '.', '|', '!', '-log', '-sqrt', '-func', '-lim', '-sum', '-diff', '-intg', '-mat', '-piece', '-lamb', '||', '^^', '&&', '-or', '-and', '-not', '-ufunc', '-subs'} or any (e [1] for e in es)
예제 #23
0
def start_server(logging=True):
    if not logging:
        Handler.log_message = lambda *args, **kwargs: None

    if ('--ugly', '') in __OPTS or ('-u', '') in __OPTS:
        _DISPLAYSTYLE[0] = 0

    for opt, _ in __OPTS:
        opt = opt.lstrip('-')

        if opt in _ENV_OPTS_ALL:
            _admin_env(AST('@', opt))

    _START_ENV.update(_ENV)
    _vars_updated()

    if not __ARGV:
        host, port = _DEFAULT_ADDRESS
    else:
        host, port = (re.split(
            r'(?<=\]):' if __ARGV[0].startswith('[') else ':', __ARGV[0]) +
                      [_DEFAULT_ADDRESS[1]])[:2]
        host, port = host.strip('[]'), int(port)

    try:
        httpd = HTTPServer((host, port), Handler)
        thread = threading.Thread(target=httpd.serve_forever, daemon=True)

        thread.start()

        return httpd

    except OSError as e:
        if e.errno != 98:
            raise

        print(
            f'Port {port} seems to be in use, try specifying different port as a command line parameter, e.g. localhost:9001'
        )

        sys.exit(-1)
예제 #24
0
				def sanitize (ast): # prune or reformat information not encoded same across different representations and asts which are not possible from parsing
					if not isinstance (ast, AST):
						return ast

					# elif ast.is_ass:
					# 	return AST ('<>', sanitize (AST ('(', ast.lhs) if ast.lhs.is_comma else ast.lhs), (('==', sanitize (AST ('(', ast.rhs) if ast.rhs.is_comma else ast.rhs)),))

					elif ast.is_minus:
						if ast.minus.is_num_pos:
							return AST ('#', f'-{ast.minus.num}')

					elif ast.is_paren:
						if not ast.paren.is_comma:
							return sanitize (ast.paren)

					elif ast.is_mul:
						return AST ('*', tuple (sanitize (a) for a in ast.mul))

					elif ast.is_log:
						return AST ('-log', sanitize (ast.log))

					elif ast.is_sqrt:
						return AST ('-sqrt', sanitize (ast.rad))

					elif ast.is_func:
						if ast.func == 'And':
							args = sanitize (ast.args)
							ast2 = sxlat._xlat_f2a_And (*args, force = True)

							if ast2 is not None:
								ast = ast2
							else:
								return AST ('-and', args)

					elif ast.is_sum:
						if ast.from_.is_comma:
							return AST ('-sum', sanitize (ast.sum), ast.svar, sanitize (AST ('(', ast.from_) if ast.from_.is_comma else ast.from_), ast.to)

					elif ast.is_diff:
						if len (set (dv [0] for dv in ast.dvs)) == 1 and ast.is_diff_partial:
							return AST ('-diff', sanitize (ast.diff), 'd', ast.dvs)

					elif ast.is_intg:
						if ast.intg is None:
							return AST ('-intg', AST.One, *tuple (sanitize (a) for a in ast [2:]))

					elif ast.is_piece:
						if ast.piece [-1] [1] == AST.True_:
							ast = AST ('-piece', ast.piece [:-1] + ((ast.piece [-1] [0], True),))

						if ast.piece.len == 1 and ast.piece [0] [1] is True:
							ast = ast.piece [0] [0]

					elif ast.is_slice:
						ast = AST ('-slice', False if ast.start == AST.None_ else ast.start, False if ast.stop == AST.None_ else ast.stop, AST ('@', 'C') if ast.step == AST.None_ else False if ast.step is None else ast.step)

					elif ast.is_and:
						args = sanitize (ast.and_)
						ast2 = sxlat._xlat_f2a_And (*args, force = True)

						if ast2 is not None:
							ast = ast2

					elif ast.is_ufunc:
						if ast.is_ufunc_explicit:
							ast = AST ('-ufunc', ast.ufunc, *ast [2:])

					return AST (*tuple (sanitize (a) for a in ast))#, **ast._kw)
예제 #25
0
        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
예제 #26
0
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)
예제 #27
0
파일: sxlat.py 프로젝트: tom-pytel/sympad
def _xlat_f2a_And(
    *args,
    canon=False,
    force=False
):  # patch together out of order extended comparison objects potentially inverting comparisons
    def concat(lhs, rhs):
        return AST('<>', lhs.lhs, lhs.cmp + rhs.cmp)

    def invert(ast):
        cmp = []
        lhs = ast.lhs

        for c in ast.cmp:
            v = _xlat_f2a_Add_invert.get(c[0])

            if v is None:
                return None

            cmp.append((v, lhs))

            lhs = c[1]

        return AST('<>', lhs, tuple(cmp[::-1]))

    def match(ast):
        li, ll = None, 0
        ri, rl = None, 0

        for i in range(len(args)):
            if args[i].is_cmp:
                if ast.lhs == args[i].cmp[-1][1] and (li is None
                                                      or args[i].cmp.len > ll):
                    li, ll = i, args[i].cmp.len

                if ast.cmp[-1][1] == args[i].lhs and (ri is None
                                                      or args[i].cmp.len > rl):
                    ri, rl = i, args[i].cmp.len

        return li, ri, ll + rl

    def canonicalize(ast):
        return invert(ast) if (canon and ast.is_cmp and sum(
            (r[0] == '>') - (r[0] == '<') for r, c in ast.cmp) > 0) else ast

    def count_ops(ast):
        if ast.is_and:
            return ast.and_.len - 1 + sum(count_ops(a) for a in ast.and_)
        elif ast.is_cmp:
            return ast.cmp.len + count_ops(ast.lhs) + sum(
                count_ops(ra[1]) for ra in ast.cmp)

        return 0

    # start here
    if not _SX_XLAT_AND and not force:
        return None  # AST ('-func', 'And', args)

    and_ = AST('-and', tuple(args))  # simple and
    andc = [args[0]]  # build concatenated and from comparisons

    for arg in args[1:]:
        if arg.is_cmp and andc[-1].is_cmp and arg.lhs == andc[-1].cmp[-1][1]:
            andc[-1] = AST('<>', andc[-1].lhs, andc[-1].cmp + arg.cmp)
        else:
            andc.append(arg)

    andc = AST('-and', tuple(andc)) if len(andc) > 1 else andc[0]
    itr = iter(args)
    args = []

    for arg in itr:  # build optimized and
        if not args or not arg.is_cmp:
            args.append(arg)

        else:
            while 1:
                li, ri, l = match(arg)
                argv = invert(arg)

                if argv is not None:
                    liv, riv, lv = match(argv)

                    if lv > l:
                        li, ri = liv, riv
                        arg = argv

                if li is None or li == ri:
                    if ri is None:
                        args.append(arg)
                        break

                    else:
                        arg = concat(arg, args[ri])
                        del args[ri]

                elif ri is None:
                    arg = concat(args[li], arg)
                    del args[li]

                else:
                    i1, i2 = min(li, ri), max(li, ri)
                    arg = concat(concat(args[li], arg), args[ri])

                    del args[i2], args[i1]

    if len(args) == 1:
        ast = canonicalize(args[0])
    else:
        ast = AST('-and', tuple(canonicalize(a) for a in args))

    return min(andc, and_, ast, key=lambda a: count_ops(a))
예제 #28
0
파일: sxlat.py 프로젝트: tom-pytel/sympad
 def concat(lhs, rhs):
     return AST('<>', lhs.lhs, lhs.cmp + rhs.cmp)
예제 #29
0
def _admin_envreset(*args):
    return ['Environment has been reset.'
            ] + _admin_env(*(AST('@', var if state else f'no{var}')
                             for var, state in _START_ENV.items()))
예제 #30
0
    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