Ejemplo n.º 1
0
	def vars (self, request):
		asts = _sorted_vars ()

		return {'vars': [{
			'tex': sym.ast2tex (ast),
			'nat': sym.ast2nat (ast),
			'py' : sym.ast2py (ast),
			} for ast in asts]}
Ejemplo n.º 2
0
    def validate(self, request):
        ast, erridx, autocomplete, error = _PARSER.parse(request['text'])
        tex = nat = py = None

        if ast is not None:
            tex, xlattex = sym.ast2tex(ast, retxlat=True)
            nat, xlatnat = sym.ast2nat(ast, retxlat=True)
            py, xlatpy = sym.ast2py(ast, retxlat=True)

            if _SYMPAD_DEBUG:
                print('free:',
                      list(v.var for v in ast.free_vars),
                      file=sys.stderr)
                print('ast: ', ast, file=sys.stderr)

                if xlattex:
                    print('astt:', repr(xlattex), file=sys.stderr)

                if xlatnat:
                    print('astn:', repr(xlatnat), file=sys.stderr)

                if xlatpy:
                    print('astp:', repr(xlatpy), file=sys.stderr)

                print('tex: ', tex, file=sys.stderr)
                print('nat: ', nat, file=sys.stderr)
                print('py:  ', py, file=sys.stderr)
                print(file=sys.stderr)

        if isinstance(error, Exception):
            error = (f'{error.__class__.__name__}: '
                     if not isinstance(error, SyntaxError) else
                     '') + error.args[0].replace('\n', ' ').strip()

        return {
            'tex': tex,
            'nat': nat,
            'py': py,
            'erridx': erridx,
            'autocomplete': autocomplete,
            'error': error,
        }
Ejemplo n.º 3
0
def test (argv = None):
	global DEPTH, CURLYS

	funcs = {'N', 'O', 'S', 'beta', 'gamma', 'Gamma', 'Lambda', 'zeta'}

	sym.set_sym_user_funcs (funcs)
	sparser.set_sp_user_funcs (funcs)
	sym.set_strict (True)

	# sxlat._SX_XLAT_AND = False # turn off py And translation because it mangles things

	depth   = 3
	single  = None
	quick   = False
	topexpr = None
	opts, _ = getopt (sys.argv [1:] if argv is None else argv, 'tnpiqScd:e:E:', ['tex', 'nat', 'py', 'dump', 'show', 'se', 'showerr', 'inf', 'infinite', 'nc', 'nocurlys', 'ns', 'nospaces', 'rs', 'randomspaces', 'tp', 'transpose', 'quick', 'nopyS', 'cross', 'depth=', 'expr=', 'topexpr='])

	if ('-q', '') in opts or ('--quick', '') in opts:
		parser.set_quick (True)
		quick = True

	if ('-S', '') in opts or ('--nopyS', '') in opts:
		sym.set_pyS (False)

	for opt, arg in opts:
		if opt in ('-d', '--depth'):
			depth = int (arg)
		elif opt in ('-e', '--expr'):
			single = [arg]
		elif opt in ('-E', '--topexpr'):
			topexpr = globals ().get (f'expr_{arg}')

	if topexpr is None:
		topexpr = expr
	else:
		EXPRS.remove (topexpr)

	if ('--dump', '') in opts:
		DEPTH = 0

		for e in EXPRS:
			print (e ())

		sys.exit (0)

	dotex     = ('--tex', '') in opts or ('-t', '') in opts
	donat     = ('--nat', '') in opts or ('-n', '') in opts
	dopy      = ('--py', '') in opts or ('-p', '') in opts
	showerr   = ('--se', '') in opts or ('--showerr', '') in opts
	CURLYS    = not (('--nc', '') in opts or ('--nocurlys', '') in opts)
	spaces    = not (('--ns', '') in opts or ('--nospaces', '') in opts)
	rndspace  = ('--rs', '') in opts or ('--randomspaces', '') in opts
	transpose = ('--tp', '') in opts or ('--transpose', '') in opts
	show      = ('--show', '') in opts
	infinite  = (('-i', '') in opts or ('--inf', '') in opts or ('--infinite', '') in opts)
	docross   = ('--cross', '') in opts or ('-c', '') in opts

	if not (dotex or donat or dopy):
		dotex = donat = dopy = True

	if infinite and not single:
		expr_func = (lambda: topexpr ()) if spaces else (lambda: topexpr ().replace (' ', ''))
	else:
		expr_func = iter (single or filter (lambda s: s [0] != '#', _EXPRESSIONS)).__next__

	try:
		while 1:
			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))

			def check_double_curlys (ast):
				if not isinstance (ast, AST):
					return False
				elif ast.is_curly and ast.curly.is_curly:
					return True

				return any (check_double_curlys (a) for a in ast)

			# start here
			status = []
			DEPTH  = depth
			text   = expr_func ()

			if text and infinite and not single and rndspace: # insert a random space to break shit
				i    = randrange (0, len (text))
				text = f'{text [:i]} {text [i:]}'

			if transpose: # transpose random block of text to another location overwriting that location
				s0, s1, d0, d1 = (randrange (len (text)) for _ in range (4))
				s0, s1         = sorted ((s0, s1))
				d0, d1         = sorted ((d0, d1))
				text           = text [:d0] + text [s0 : s1] + text [d1:]

			status.append (f'text: {text}')
			ast = parse (text) # fixstuff (parse (text))
			status.append (f'ast:  {ast}')

			err = None

			if not ast:
				if single or (not infinite and not quick) or showerr:
					err = ValueError ("error parsing")

			if ast and not err:
				try:
					ast2 = validate (ast)

				except Exception as e: # make sure garbling functions did not create an invalid ast
					if single or showerr:
						err = e
					else:
						ast = None

			if ast and not err:
				if ast2 != ast:
					status.append (f'astv: {ast2}')

					ast = ast2

				if dopy and any (a.is_ass for a in (ast.scolon if ast.is_scolon else (ast,))): # reject assignments at top level if doing py because all sorts of mangling goes on there, we just want expressions in that case
					if single or showerr:
						err = ValueError ("disallowed assignment")
					else:
						ast = None

			if err or not ast:
				if err and not showerr:
					raise err

				if showerr:
					print (f'\n{text} ... {err}')

				continue

			if show:
				print (f'{text}\n')

			sxlat._SX_XLAT_AND = False # turn off py And translation because it mangles things

			for rep in ('tex', 'nat', 'py'):
				if locals () [f'do{rep}']:
					symfunc     = getattr (sym, f'ast2{rep}')

					status.append (f'sym.ast2{rep} ()')

					text1       = symfunc (ast)
					status [-1] = f'{rep}1: {" " if rep == "py" else ""}{text1}'

					status.append ('parse ()')

					rast, rpre  = parse (text1, retprepost = True) # fixstuff (parse (text1))

					if not rast:
						raise ValueError (f"error parsing")

					if check_double_curlys (rpre):
						status [-1] = f'astd: {rpre}'

						raise ValueError ("doubled curlys")

					status [-1:] = [f'ast:  {rast}', f'sym.ast2{rep} ()']
					text2        = symfunc (rast)
					status [-1]  = f'{rep}2: {" " if rep == "py" else ""}{text2}'

					if text2 != text1:
						raise ValueError ("doesn't match")

					del status [-3:]

			if docross and dotex + donat + dopy > 1:
				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)

				sxlat._SX_XLAT_AND = True # turn on py And translation because it is needed here

				# start here
				ast = sanitize (ast)
				status.append (f'ast:  {ast}')

				if dotex:
					tex1 = sym.ast2tex (ast)
					status.append (f'tex1: {tex1}')
					ast2 = ast = sanitize (parse (tex1)).flat

					if donat:
						status.append (f'ast:  {ast2}')
						nat  = sym.ast2nat (ast2)
						status.append (f'nat:  {nat}')
						ast2 = parse (nat)

					if dopy:
						try:
							sym._SYM_MARK_PY_ASS_EQ = True # allow xlat of marked Eq functions which indicate assignment in python text

							status.append (f'ast:  {ast2}')
							py   = sym.ast2py (ast2, ass2cmp = False)
							status.append (f'py:   {py}')
							ast2 = parse (py)

						finally:
							sym._SYM_MARK_PY_ASS_EQ = False # allow xlat of marked Eq functions which indicate assignment in python text

					try:
						if dopy:
							sxlat._SX_READ_PY_ASS_EQ = True # allow xlat of marked Eq functions which indicate assignment in python text

						status.append (f'ast:  {ast2}')
						tex2 = sym.ast2tex (ast2)
						status.append (f'tex2: {tex2}')
						ast2 = sanitize (parse (tex2)).flat

					finally:
						sxlat._SX_READ_PY_ASS_EQ = False # allow xlat of marked Eq functions which indicate assignment in python text

				elif donat: # TODO: add more status updates for intermediate steps like above
					nat1 = sym.ast2nat (ast)
					status.append (f'nat1: {nat1}')
					ast2 = ast = sanitize (parse (nat1)).flat

					try:
						sym._SYM_MARK_PY_ASS_EQ = True # allow xlat of marked Eq functions which indicate assignment in python text

						status.append (f'ast:  {ast2}')
						py   = sym.ast2py (ast2, ass2cmp = False)
						status.append (f'py:   {py}')
						ast2 = parse (py)

					finally:
						sym._SYM_MARK_PY_ASS_EQ = False # allow xlat of marked Eq functions which indicate assignment in python text

					try:
						sxlat._SX_READ_PY_ASS_EQ = True # allow xlat of marked Eq functions which indicate assignment in python text

						status.append (f'ast:  {ast2}')
						nat2 = sym.ast2nat (ast2)
						status.append (f'nat2: {nat2}')
						ast2 = sanitize (parse (nat2)).flat

					finally:
						sxlat._SX_READ_PY_ASS_EQ = False # allow xlat of marked Eq functions which indicate assignment in python text

				if ast2 != ast:
					status.extend ([f'ast:  {ast2}', f'org:  {ast}'])

					raise ValueError ("doesn't match across representations")

	except (KeyboardInterrupt, StopIteration):
		pass

	except:
		print ('Exception!\n')
		print ('\n'.join (status))
		print ()

		raise

	finally:
		sxlat._SX_XLAT_AND = True

	return True
Ejemplo n.º 4
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