def parse_file(fname, *replacements, **kwargs):
    abspath = kwargs.get("abspath", False)
    if not abspath:
        fname = os.path.join(os.path.dirname(__file__), fname)

    if kwargs.get("inplace", False):
        assert not replacements
        if not pytest.config.byte_option:
            reader = JsonLoader(bytecode_expand=False)
        else:
            reader = JsonLoader(bytecode_expand=True)
        ast = reader.expand_to_ast(fname)
        return ast

    with file(fname) as f:
        s = f.read()
    for replace, with_ in replacements:
        assert s.count(replace) == 1
        s = s.replace(replace, with_)
    s = s.decode("utf-8")

    s = expand_string(s)
    ast = parse_module(s)

    # if not pytest.config.byte_option:
    #     s = expand_string(s)
    #     ast = parse_module(s)
    # else:
    #     s = expand_from_bytecode(s, True)
    #     ast = parse_module(s, bytecode_expand=True)

    return ast def run(p, v=None, stdlib=False, extra="", expect_to_fail=False):
    if pytest.config.new_pycket:
        return run_expr_result(p, expect_to_fail=expect_to_fail, v=v)
    return run_mod_expr(p,v=v,stdlib=stdlib, extra=extra) def run_fix(p, v=None, stdlib=False, extra="", equal_huh=False):
    new = pytest.config.new_pycket
    if new:
        return run_expr(p, v, equal_huh=equal_huh)
    ov = run_mod_expr(p,stdlib=stdlib, extra=extra)
    assert isinstance(ov, values.W_Fixnum)
    if v is not None:
        assert ov.value == v
    return ov.value def run_flo(p, v=None, stdlib=False, extra=""):
    ov = run_mod_expr(p,stdlib=stdlib, extra=extra)
    assert isinstance(ov, values.W_Flonum)
    if v is not None:
        assert ov.value == v
    return ov.value def run_top(p, v=None, stdlib=False, extra=""):
    return run_mod_expr(p,v=v,stdlib=stdlib, wrap=True, extra=extra) def execute(p, stdlib=False, extra=""):
    return run_mod_expr(p, stdlib=stdlib, extra=extra) def run_values(p, stdlib=False):
    e = "(call-with-values (lambda () %s) list)"%p
    v = run_mod_expr(e, stdlib=stdlib)
    return values.from_list(v) class JsonLoader(object):

    _immutable_fields_ = ["modtable", "bytecode_expand", "multiple_modules"]

    def __init__(self, bytecode_expand=False, multiple_modules=False, module_mapper=None):
        self.modtable = ModTable()
        self.bytecode_expand = bytecode_expand
        self.multi_mod_flag = multiple_modules
        self.multi_mod_mapper = module_mapper

    def _lib_string(self):
        return _BE if self.bytecode_expand else _FN

    # Expand and load the module without generating intermediate JSON files.
    def expand_to_ast(self, fname):
        assert fname is not None
        fname = rpath.realpath(fname)
        data = expand_file_rpython(fname, self._lib_string())
        self.modtable.enter_module(fname)
        module = self.to_module(pycket_json.loads(data))
        module = finalize_module(module)
        self.modtable.exit_module(fname, module)
        return module

    def load_json_ast_rpython(self, modname, fname):
        assert modname is not None
        modname = rpath.realpath(modname)
        self.modtable.enter_module(modname)

        if self.multi_mod_flag:
            mod_ast = self.multi_mod_mapper.get_mod(modname)
            module = self.to_module(mod_ast)
        else:
            data = readfile_rpython(fname)
            module = self.to_module(pycket_json.loads(data))

        module = finalize_module(module)
        self.modtable.exit_module(modname, module)
        return module

    def expand_file_cached(self, rkt_file):
        dbgprint("expand_file_cached", "", lib=self._lib_string(), filename=rkt_file)
        # bypass if we already have module_map from the multi-ast-json
        if not self.multi_mod_flag:
            try:
                json_file = ensure_json_ast_run(rkt_file, self.bytecode_expand)
            except PermException:
                return self.expand_to_ast(rkt_file)
        else:
            json_file = _json_name(rkt_file)
        return self.load_json_ast_rpython(rkt_file, json_file)

    def to_bindings(self, arr):
        varss = [None] * len(arr)
        rhss  = [None] * len(arr)
        for i, v in enumerate(arr):
            varr = v.value_array()
            names, defs = varr[0].value_array(), varr[1]
            fmls = [values.W_Symbol.make(x.value_string()) for x in names]
            rhs = self.to_ast(varr[1])
            assert isinstance(rhs, AST)
            varss[i] = fmls
            rhss[i]  = rhs
        return varss, rhss

    def _to_lambda(self, lam):
        fmls, rest = to_formals(lam["lambda"])
        sourceinfo = get_srcloc(lam)
        body = [self.to_ast(x) for x in lam["body"].value_array()]
        return make_lambda(fmls, rest, body, sourceinfo)

    def _to_require(self, fname, path=None):
        path = shorten_submodule_path(path)
        modtable = self.modtable
        if modtable.builtin(fname):
            return VOID
        assert fname is not None
        fname = rpath.realpath(fname)
        return Require(fname, self, path=path)

    def lazy_load(self, fname):
        modtable = self.modtable
        module = modtable.lookup(fname)
        if module is not None:
            return module
        return self.expand_file_cached(fname)

    def _parse_require(self, path):
        dbgprint("parse_require", path, self._lib_string(), path)
        fname, subs = path[0], path[1:]
        if fname in (".", ".."):
            # fname field is not used in this case, so we just give an idea of which
            # module we are in
            return Require(self.modtable.current_mod(), None, path=path)
        return self._to_require(fname, path=subs)

    def to_module(self, json):
        dbgprint("to_module", json, lib=self._lib_string(), filename="")

        # YYY
        obj = json.value_object()
        assert "body-forms" in obj, "got malformed JSON from expander"

        config = {}
        config_obj = getkey(obj, "config", type='o')
        if config_obj is not None:
            for k, v in config_obj.iteritems():
                config[k] = v.value_string()

            be_json = config.get("bytecode-expand", "false") == "true"
            if self.bytecode_expand != be_json:
                modname = getkey(obj, "module-name", type='s')
                raise ValueError('Byte-expansion is : %s, but "bytecode-expand" '
                                 'in json is : %s, in %s' %
                                 (self.bytecode_expand, be_json, modname))

        try:
            lang_arr = obj["language"].value_array()
        except KeyError:
            lang = None
        else:
            lang = self._parse_require([lang_arr[0].value_string()]) if lang_arr else None

        body = [self.to_ast(x) for x in getkey(obj, "body-forms", type='a')]
        name = getkey(obj, "module-name", type='s')
        return Module(name, body, config, lang=lang)

    @staticmethod
    def is_builtin_operation(rator):
        """ Sanity check for testing """
        if we_are_translated():
            return True
        if "source-name" not in rator:
            return False
        if "source-module" not in rator:
            return True
        if rator["source-module"].value_string() == "%kernel":
            return True
        return False

    def parse_if(self, cond, then, els):
        """ Avoid building branch of an if-expression when cond is a constant """
        cond = self.to_ast(cond)
        if not isinstance(cond, Quote):
            then = self.to_ast(then)
            els  = self.to_ast(els)
            return If(cond, then, els)
        branch = els if cond.w_val is values.w_false else then
        return self.to_ast(branch)

    def to_ast(self, json, get_req_mods_from_module_map=False):
        dbgprint("to_ast", json, lib=self._lib_string(), filename="")
        mksym = values.W_Symbol.make

        if json.is_array:
            arr = json.value_array()
            rator = arr[0].value_object()
            assert JsonLoader.is_builtin_operation(rator)
            ast_elem = rator["source-name"].value_string()
            if ast_elem == "begin":
                return Begin([self.to_ast(arr[i]) for i in range(1, len(arr))])
            if ast_elem == "#%expression":
                return self.to_ast(arr[1])
            if ast_elem == "set!":
                target = arr[1].value_object()
                var = None
                if "source-linklet" in target:
                    srcname = mksym(target["source-linklet"].value_string())
                    var = LinkletVar(srcname)
                elif "source-name" in target:
                    srcname = mksym(target["source-name"].value_string())
                    if "source-module" in target:
                        if target["source-module"].is_array:
                            path_arr = target["source-module"].value_array()
                            srcmod, path = parse_path(path_arr)
                        else:
                            srcmod = path = None

                        modname = mksym(target["module"].value_string()) if "module" in target else srcname
                        var = ModuleVar(modname, srcmod, srcname, path)
                    else:
                        srcmod = "#%kernel"
                        path   = None

                        modname = mksym(target["module"].value_string()) if "module" in target else srcname
                        var = ModuleVar(modname, srcmod, srcname, path)
                elif "lexical" in target:
                    var = CellRef(values.W_Symbol.make(target["lexical"].value_string()))
                else:
                    assert "toplevel" in target
                    var = ToplevelVar(mksym(target["toplevel"].value_string()))
                return SetBang(var, self.to_ast(arr[2]))
            if ast_elem == "#%top":
                assert 0
                return CellRef(mksym(arr[1].value_object()["symbol"].value_string()))
            # The parser now ignores `#%require` AST nodes.
            # The actual file to include is now generated by expander
            # as an object that is handled below.
            if ast_elem == "#%provide":
                return VOID
            assert 0, "Unexpected ast-element element: %s" % json.tostring()
        if json.is_object:
            obj = json.value_object()
            if "require" in obj:
                paths = obj["require"].value_array()
                requires = []
                for path in paths:
                    path = convert_path(path.value_array())
                    if not path:
                        continue
                    requires.append(self._parse_require(path))
                return Begin.make(requires) if requires else VOID
            if "begin0" in obj:
                fst = self.to_ast(obj["begin0"])
                rst = [self.to_ast(x) for x in obj["begin0-rest"].value_array()]
                if len(rst) == 0:
                    return fst
                else:
                    return Begin0.make(fst, rst)
            if "begin-for-syntax" in obj:
                body = [self.to_ast(x) for x in obj["begin-for-syntax"].value_array()]
                return BeginForSyntax(body)
            if "wcm-key" in obj:
                return WithContinuationMark(self.to_ast(obj["wcm-key"]),
                                            self.to_ast(obj["wcm-val"]),
                                            self.to_ast(obj["wcm-body"]))
            if "define-values" in obj:
                binders = obj["define-values"].value_array()
                display_names = obj["define-values-names"].value_array()
                fmls = [mksym(x.value_string()) for x in binders]
                disp_syms = [mksym(x.value_string()) for x in display_names]
                body = self.to_ast(obj["define-values-body"])
                return DefineValues(fmls, body, disp_syms)
            if "letrec-bindings" in obj:
                body = [self.to_ast(x) for x in obj["letrec-body"].value_array()]
                bindings = obj["letrec-bindings"].value_array()
                if len(bindings) == 0:
                    return Begin.make(body)
                else:
                    vs, rhss = self.to_bindings(bindings)
                    return make_letrec(list(vs), list(rhss), body)
            if "let-bindings" in obj:
                body = [self.to_ast(x) for x in obj["let-body"].value_array()]
                bindings = obj["let-bindings"].value_array()
                if len(bindings) == 0:
                    return Begin.make(body)
                else:
                    vs, rhss = self.to_bindings(bindings)
                    return make_let(vs, rhss, body)
            if "variable-reference" in obj:
                current_mod = self.modtable.current_mod()
                if obj["variable-reference"].is_bool: # assumes that only boolean here is #f
                    # the only things we load from JSON are the bootstrap linklets which
                    # should be run in unsafe mode
                    return VariableReference(None, current_mod, unsafe=True)
                else:
                    var = self.to_ast(obj["variable-reference"])
                    return VariableReference(var, current_mod, unsafe=True)
            if "lambda" in obj:
                    return CaseLambda([self._to_lambda(obj)])
            if "case-lambda" in obj:
                    lams = [self._to_lambda(v.value_object()) for v in obj["case-lambda"].value_array()]
                    return CaseLambda(lams)
            if "operator" in obj:
                rator = self.to_ast(obj["operator"])
                rands = [self.to_ast(x) for x in obj["operands"].value_array()]
                return App.make(rator, rands)
            if "test" in obj:
                return self.parse_if(obj["test"], obj["then"], obj["else"])
            if "quote" in obj:
                return Quote(to_value(obj["quote"]))
            if "quote-syntax" in obj:
                return QuoteSyntax(to_value(obj["quote-syntax"]))
            if "source-linklet" in obj:
                srcsym = mksym(obj["source-linklet"].value_string())
                return LinkletVar(srcsym)
            if "source-name" in obj:
                srcname = obj["source-name"].value_string()
                modname = obj["module"].value_string() if "module" in obj else None
                srcsym = mksym(srcname)
                modsym = mksym(modname) if modname else srcsym
                if "source-module" in obj:
                    if obj["source-module"].is_array:
                        path_arr = obj["source-module"].value_array()
                        srcmod, path = parse_path(path_arr)
                    else:
                        srcmod = path = None
                else:
                    srcmod = "#%kernel"
                    path   = None
                return ModuleVar(modsym, srcmod, srcsym, path=path)
            if "lexical" in obj:
                return LexicalVar(mksym(obj["lexical"].value_string()))
            if "toplevel" in obj:
                return ToplevelVar(mksym(obj["toplevel"].value_string()))
            if "module-name" in obj:
                return self.to_module(json)
        assert 0, "Unexpected json object: %s" % json.tostring() def expand(s, wrap=False, stdlib=False):
    data = expand_string(s)
    return pycket_json.loads(data) def expand_string(s, reuse=True, srcloc=True, byte_option=False, tmp_file_name=False):
    "NON_RPYTHON"
    global current_racket_proc
    from subprocess import Popen, PIPE

    if not byte_option:
        cmd = "racket %s --loop --stdin --stdout %s" % (_FN, "" if srcloc else "--omit-srcloc")
    else:
        tmp_module = tmp_file_name + '.rkt'
        cmd = "racket -l pycket/zo-expand -- --test --stdout %s" % tmp_module

    if current_racket_proc and reuse and current_racket_proc.poll() is None:
        process = current_racket_proc
    else:
        process = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE)
        if reuse:
            current_racket_proc = process
    if reuse:
        if not byte_option:
            process.stdin.write(s.encode("utf-8"))
            ## I would like to write something so that Racket sees EOF without
            ## closing the file. But I can't figure out how to do that. It
            ## must be possible, though, because bash manages it.
            #process.stdin.write(chr(4))
            process.stdin.write("\n\0\n")
            process.stdin.flush()
            #import pdb; pdb.set_trace()
        data = process.stdout.readline()
    else:
        (data, err) = process.communicate(s)
    if len(data) == 0:
        raise ExpandException("Racket did not produce output. Probably racket is not installed, or it could not parse the input.")
    # if err:
    #     raise ExpandException("Racket produced an error")
    return data def parse_module(json_string, bytecode_expand=False):
    json = pycket_json.loads(json_string)
    modtable = ModTable()
    reader = JsonLoader(bytecode_expand)
    module = reader.to_module(json)
    return finalize_module(module) def finalize_module(mod):
    from pycket.interpreter    import Context
    from pycket.assign_convert import assign_convert
    mod = Context.normalize_term(mod)
    mod = assign_convert(mod)
    return mod class JsonBase(object):
class JsonPrimitive(JsonBase):
class JsonNull(JsonPrimitive):
class JsonFalse(JsonPrimitive):
class JsonTrue(JsonPrimitive):
class JsonInt(JsonPrimitive):
class JsonFloat(JsonPrimitive):
class JsonString(JsonPrimitive):
class JsonObject(JsonBase):
class JsonArray(JsonBase):
class FakeSpace(object):
class OwnJSONDecoder(JSONDecoder):
    def __init__(self):
    def tostring(self):
    def is_primitive(self):
    def _unpack_deep(self):
    def value_array(self):
    def value_object(self):
    def value_string(self):
    def value_float(self):
    def value_int(self):
    def __init__(self):
    def is_primitive(self):
    def tostring(self):
    def _unpack_deep(self):
    def tostring(self):
    def _unpack_deep(self):
    def tostring(self):
    def _unpack_deep(self):
    def __init__(self, value):
    def tostring(self):
    def _unpack_deep(self):
    def value_int(self):
    def __init__(self, value):
    def tostring(self):
    def value_float(self):
    def _unpack_deep(self):
    def __init__(self, value):
    def tostring(self):
    def _unpack_deep(self):
    def value_string(self):
    def hash_w(self):
    def eq_w(self, w_other):
    def __init__(self, dct):
    def tostring(self):
    def _unpack_deep(self):
    def value_object(self):
    def __init__(self, lst):
    def tostring(self):
    def _unpack_deep(self):
    def value_array(self):
    def newtuple(self, items):
    def newdict(self):
    def newlist(self, items):
    def call_method(self, obj, name, arg):
    def call_function(self, w_func, *args_w):
    def setitem(self, d, key, value):
    def newutf8(self, x, ln):
    def newtext(self, x):
    def unicode_w(self, s):
    def newint(self, x):
    def newfloat(self, x):
    def __init__(self, s):
    def _raise(self, msg, *args):
    def decode_float(self, i):
    def _create_dict(self, dct):
def loads(s):
def _convert(data):
import pycket.prims
import pycket.entry_point
import sys
import pytest
        import pycket.values
        import pycket.values
from rpython import conftest
from rpython.rlib.nonconst import NonConstant
from rpython.jit.metainterp.test.test_ajit import LLJitMixin
from pycket.test.testhelper import parse_file
from pycket.interpreter import *
from pycket.values import *
from pycket.test.testhelper import run, run_fix, run_flo, run_top, execute, run_values
from pycket.expand import JsonLoader, expand, expand_string, parse_module, finalize_module
from pycket import pycket_json
        from pycket.expand import expand_string
        from pycket.json import loads

    def test_countdown_x(self):
        self.run_string("""
        #lang pycket
        (letrec ([countdown (lambda (n m) (if (< n 0) m (countdown (- n 1) (+ 1 m))))])
          (countdown 1000 1000))
        """)

    def test_countdown_nested(self):
        self.run_string("""
        #lang pycket
        (let ([sub 1])
          (define (nested n)
            (let countdown ([n n]) (if (< n 0) 1 (countdown (- n sub))))
            (if (< n 0) 1 (nested (- n sub))))
          (nested 10))
        """)


    def test_countdown_loop(self):
        self.run_string("""
        #lang pycket
        (let countdown ([n 1000]) (if (< n 0) 1 (countdown (- n 1))))
        """)

    def test_countdown_many_lets(self):
        self.run_string("""
        #lang pycket
        (define (id x) x)
        (let countdown ([n 1000]) (if (< n 0) 1 (id (id (id (id (countdown (- n 1))))))))
        """)

    def test_bistable_loop(self):
        self.run_string("""
        #lang pycket
        (let ()
            (define (countdown n sub2?)
                (if (< n 0) 1
                    (if sub2?
                        (countdown (- n 2) #f)
                        (countdown (- n 1) #t)
                        )))
            (countdown 1000 #f)
        )
        """)


    def test_setbang(self):

        loader = JsonLoader(bytecode_expand=False)
        ast = loader.to_ast(expand("""
        #lang pycket
        (let ([n 1000])
          (letrec ([countdown (lambda () (if (< n 0) 1 (begin (set! n (- n 1)) (countdown))))])
            (countdown)))
        """))

        def interp_w():
            val = interpret_one(ast)
            ov = check_one_val(val)
            assert isinstance(ov, W_Fixnum)
            return ov.value

        assert interp_w() == 1

        self.meta_interp(interp_w, [], listcomp=True, listops=True, backendopt=True)

    def test_side_exit(self):
        ast = to_ast(expand("""
        (let ()
            (define (count-positive l sum)
                (if (null? l) sum
                    (if (> (car l) 0)
                        (count-positive (cdr l) (+ (car l) sum))
                        (count-positive (cdr l) sum)
                        )))
            (count-positive (list -1 1 1 1 1 -1 2 3 -5 1 2 2 -5 6 4 3 -5) 0))
        """))


        def interp_w():
            val = interpret_one(ast)
            ov = check_one_val(val)
            assert isinstance(ov, W_Fixnum)
            return ov.value

        assert interp_w() == 27

        self.meta_interp(interp_w, [], listcomp=True, listops=True, backendopt=True)


    # needs to be fixed to use modules
    def run_string(self, str):
        _json = expand_string(str)
        env = ToplevelEnv()

        def interp_w():
            loader = JsonLoader(False)
            ast = parse_module(_json)
            env.globalconfig.load(ast)
            env.commandline_arguments = []
            interpret_module(ast, env)

        interp_w() # check that it runs

        # ast = parse_module(expand_string(str))
        self.meta_interp(interp_w, [], listcomp=True, listops=True, backendopt=True)

    def test_imp_vec(self):

        self.run_string("""
        #lang pycket
        (let ([v (impersonate-vector (make-vector 1000 5)
                                     (lambda (x y z) (unless (integer? z) (error 'fail)) z)
                                     (lambda (x y z) z))])
             (let lp ([n 0] [i 0])
               (if (>= i 1000) n (lp (+ n (vector-ref v i)) (+ 1 i)))))
        """)

    def run_file(self, fname, run_untranslated=True):

        ast = parse_file(fname)