import sys
import argparse
import os
import json
import yaml
import xmltodict
import re
import traceback
import time
import random
import string
from functools import partial
from .tblfmt import SimpleTable
from .commandline import commandline
from .json2table import prepare_table
from json2html import JsonConverter
from collections import (deque,defaultdict)
from types import FunctionType
from pygments import highlight
from pygments.lexers import (JsonLexer,YamlLexer,XmlLexer,IniLexer,HtmlLexer,guess_lexer)
from pygments.lexers.python import PythonLexer
from pygments.formatters import Terminal256Formatter
from prompt_toolkit import PromptSession
from prompt_toolkit.completion import WordCompleter
from prompt_toolkit.lexers import PygmentsLexer

sys.path.append(os.getcwd())

print = partial(print,flush=True)

DSQ_DOT="_yx_dsq_dot_yx_"

def _json(ds) :
    return json.dumps(ds,indent=2,sort_keys=True)
def _xml(ds) :
    import xml.dom.minidom as dom
    import dicttoxml
    xmlstr = dicttoxml.dicttoxml(ds).decode()
    xml = dom.parseString(xmlstr)
    return xml.toprettyxml()
def _yaml(ds) :
    return yaml.dump(ds,default_flow_style=False,explicit_start=True, explicit_end=False)
def _l(ds,brk="\n") :
    return brk.join([str(i) for i in ds])
def _h(ds) :
    return JsonConverter(ds).json2html()
def _zh(ds) :
    return JsonConverter(ds,recursive=True,collapseandexpand=True).json2html()
def _l2t(xjson,header=None,maxcolwidth=100) :
    data,header=prepare_table(xjson,header)
    return SimpleTable(data=data,header=header,maxwidth=maxcolwidth)
def _l2pt(xjson,header=None) :
    data,header=prepare_table(xjson,header)
    return SimpleTable(data=data,header=header).repr_pivot()
def _t(data=list(),header=None,dataonly=False,maxcolwidth=100) :
    return SimpleTable(data=data,header=header,noheader=dataonly,maxwidth=maxcolwidth)
def _pt(data=list(),header=None,dataonly=False) :
    return SimpleTable(data=data,header=header,noheader=dataonly).repr_pivot()
def _flatlist(ds,ret=None) :
    if ret is None :
        ret = list()
    if type(ds) is list :
        for i in ds :
            _flatlist(i,ret)
    else :
        ret.append(ds)
    return ret
def _qx(cmd) :
    try :
        ret,out,err = commandline.qx(cmd)
        print(out)
        if err :
            print(err)
    except :
        traceback.print_exc()
def _rawstr(ds,last="_"):
    res = ""
    if type(ds) is dict :
        for k,v in ds.items() :
            if type(v) in [dict,list] :
                res += _rawstr(v,last+"."+k) 
            else :
                if type(v) is str :
                    vstr = '"{}"'.format(v.replace('"','\\"'))
                else :
                    vstr = str(v)
                res += last + "." + k +"="+ vstr  + "\n"
    if type(ds) is list :
        for i,v in enumerate(ds) :
            if type(v) in [dict,list] :
                res += _rawstr(v,last+"["+str(i)+"]") 
            else :
                if type(v) is str :
                    vstr = '"{}"'.format(v.replace('"','\"'))
                else :
                    vstr = str(v)
                res += last+"["+str(i)+"]=" + str(v) + "\n"
    return res

_fl=_flatlist
_j=_json
_x=_xml
_y=_yaml

def dsq_main():
    parser = argparse.ArgumentParser()
    parser.add_argument(dest="code", nargs='?', default="_", help="code to compile. may be a file.")
    parser.add_argument("-f", "--infile", dest="infile", help="input file")
    parser.add_argument("-t", "--srctype", dest="srctype", default="JSON", help="JSON,YAML or XML")
    parser.add_argument("-i", "--indent", dest="indent", default=4, help="how many spaces for indent. default 4.")
    parser.add_argument("-l", "--rows", dest="rows",type=int, default=2**30, help="use this to shrink each list included. useful for DS including too many records.")
    parser.add_argument("-o", "--output", dest="outfile",default=None,help="write as well as console. ansi color kept.")
    parser.add_argument("-m", "--modules", dest="modules", help="import modules.")
    parser.add_argument("-K", "--keys", dest="keys_included", help="only keep these keys.")
    parser.add_argument("-E", "--nokeys", dest="keys_excluded", help="these keys should be excluded.")
    parser.add_argument("-F", "--functionize", dest="func", action="store_true", default=False, help="wrap code into an internal function",)
    parser.add_argument("-s", "--rawstr", dest="rawstr", action="store_true", default=False, help="output raw stings for easy grep",)
    parser.add_argument("-n", "--origin", dest="origin", action="store_true", default=False, help="no change to the input stream and take it as is. no expanding.")
    parser.add_argument("-I", "--interactive", dest="interactive", action="store_true", default=False, help="interactive mode",)
    parser.add_argument("-p", "--plain", dest="plain", action="store_true", default=False, help="force no color code",)
    parser.add_argument("-c", "--compact", dest="compact", action="store_true", default=False, help="dump data structure in compact mode",)
    parser.add_argument("-C", "--keepcolor", dest="keepcolor", action="store_true", default=False, help="do not remove ANSI color code from input stream.",)
    parser.add_argument("-X", "--debug", dest="debug", action="count", default=False, help="debug mode",)
    _x_args = parser.parse_args()

    class bcolors:
        HEADER = '\033[95m'
        OKBLUE = '\033[94m'
        OKCYAN = '\033[96m'
        OKGREEN = '\033[92m'
        WARNING = '\033[93m'
        FAIL = '\033[91m'
        ENDC = '\033[0m'
        BOLD = '\033[1m'
        UNDERLINE = '\033[4m'

    def warn(s) :
        return bcolors.WARNING + s + bcolors.ENDC
    def notify(s) :
        return bcolors.OKCYAN + s + bcolors.ENDC
    def critical(s) :
        return bcolors.FAIL + s + bcolors.ENDC
    def print_err(s,lvl=0) :
        if lvl == 1 :
            print(notify(s),file=sys.stderr)
            return
        if lvl == 2 :
            print(warn(s),file=sys.stderr)
            return
        if lvl == 3 :
            print(critical(s),file=sys.stderr)
            return
        print(s,file=sys.stderr)

    if _x_args.debug >=2 :
        print_err("# args = ",lvl=1)
        strargs = "\n".join([str(v)+"="+str(getattr(_x_args,v)) for v in vars(_x_args)])
        print_err(strargs)

    def supports_color():
        plat = sys.platform
        supported_platform = plat != 'Pocket PC' and (plat != 'win32' or 'ANSICON' in os.environ)
        is_a_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()
        m = re.search(r"^xterm",os.environ.get("TERM","n/a"),re.IGNORECASE)
        try :
            ret,out,err = commandline.qx("uname -s")
        except :
            return False
        iscygwin = re.search(r"cygwin",out,re.IGNORECASE)
        return (is_a_tty or iscygwin) and (m or supported_platform)
    if not _x_args.plain :
        colored = True
        if not supports_color() :
            colored = False
        if os.environ.get("force_ansicolor","") in ["true","yes","y","1"] :
            colored = True
        _x_args.plain = colored
    if _x_args.keys_included :
        _xdt_keys_included = dict()
        for w in  _x_args.keys_included.split(",") :
            recursive = 0
            if re.search(r"(\*|\/|\+)+$",w) :
                recursive = 1
                w = re.sub(r"(\*|\/|\+)+$","",w)
            if w :
                _xdt_keys_included[w.lower()] = recursive
    else :
        _xdt_keys_included = None
    if _x_args.keys_excluded :
        _xset_keys_excluded = set([w.lower() for w in _x_args.keys_excluded.split(",") if w])
    else :
        _xset_keys_excluded = None

    def cutkeys_helper(ds,inkeys,outkeys) :
        if not inkeys and not outkeys :
            return ds
        if type(ds) is dict :
            ds = {k:v for k, v in ds.items() if (not inkeys or k.lower() in inkeys) and (not outkeys or k.lower() not in outkeys)}
            for k,v in ds.items() :
                if not inkeys or (inkeys and k.lower() in inkeys and inkeys[k.lower()] != 1) :
                    ds[k] = cutkeys_helper(v,inkeys,outkeys)
        if type(ds) is list :
            for i,v in enumerate(ds) :
                if type(v) in [dict,list] :
                    ds[i] = cutkeys_helper(v,inkeys,outkeys)
        return ds
    def cutkeys(ds,inkeys=_xdt_keys_included,outkeys=_xset_keys_excluded) :
        ds = cutkeys_helper(ds,inkeys,outkeys)
        if _xset_keys_excluded :
            ds = cutkeys_helper(ds,inkeys=dict(),outkeys=outkeys)
        return ds

    def shrink_list(ds,pos="_") :
        if type(ds) is dict :
            for k,v in ds.items() :
                if type(v) in [dict,list] :
                    ds[k] = shrink_list(v,pos+".{}".format(k))
        if type(ds) is list :
            oldlen = len(ds)
            if _x_args.rows < oldlen :
                print_err("# {}[] {} -> {}".format(pos,oldlen,_x_args.rows),lvl=1)
            ds = ds[:_x_args.rows]
            for i, v in enumerate(ds) :
                if type(v) in [dict,list] :
                    ds[i] = shrink_list(v,pos+"[{}]".format(i))
        return ds

    def collect_keys(ds, dt) :
        if type(ds) is dict :
            for k, v in ds.items() :
                dt[k.lower()].add(k)
                collect_keys(v,dt)
        elif type(ds) in [list,tuple] :
            for i in ds :
                collect_keys(i,dt)
        else :
            pass

    def show_result(res) :
        def print_tee(s) :
            if _x_args.outfile :
                with open(_x_args.outfile,"a") as fw :
                    fw.write(s)
            print(s)
        if _x_args.outfile :
            with open(_x_args.outfile,"w") as fw :
                pass
            xprint = print_tee
        else :
            xprint = print

        if not res :
           return
        if type(res) is str :
            if _x_args.plain :
                xprint(res) 
                return
            try :
                if _x_args.rawstr :
                    xprint(highlight(res,IniLexer(),Terminal256Formatter()))
                    return
            except :
                pass
            try :
                xprint(highlight(res,HtmlLexer(),Terminal256Formatter()))
                return
            except :
                pass
            try :
                json.loads(res)
                if _x_args.rows != 2**30 :
                    res = json.loads(res)
                    res = shrink_list(res)
                    res = json.dumps(res,indent=2,sort_keys=True)
                xprint(highlight(res,JsonLexer(),Terminal256Formatter()))
                return
            except :
                pass
            try :
                yaml.safe_load(res)
                if _x_args.rows != 2**30 :
                    res = yaml.safe_load(res)
                    res = shrink_list(res)
                    res = yaml.dump(res,default_flow_style=False,explicit_start=True, explicit_end=False)
                xprint(highlight(res,YamlLexer(),Terminal256Formatter()))
                return
            except :
                pass
            try :
                xmltodict.parse(res)
                xprint(highlight(res,XmlLexer(),Terminal256Formatter()))
                return
            except :
                pass
            xprint(highlight(res,guess_lexer(res),Terminal256Formatter()))
        else :
            res = cutkeys(res)
            res = shrink_list(res)
            if _x_args.rawstr :
                if _x_args.plain :
                    xprint(_rawstr(res))
                else :
                    xprint(highlight(_rawstr(res),IniLexer(),Terminal256Formatter()))
            else :
                try :
                    if _x_args.plain :
                        if _x_args.compact :
                            xprint(json.dumps(res))
                        else :
                            xprint(json.dumps(res,indent=2))
                    else :
                        if _x_args.compact :
                            xprint(highlight(json.dumps(res),JsonLexer(),Terminal256Formatter()))
                        else :
                            xprint(highlight(json.dumps(res,indent=2),JsonLexer(),Terminal256Formatter()))
                except :
                    xprint(str(res))

    def removespaces(code) :
        if not code or "{" not in code or "}" not in code :
            return code
        xcode = code
        m = re.search(r"\{.*?\s+.*?\}",xcode) 
        while m :
            before = code[:m.start()]
            end = code[m.end():]
            chain = m.group(0)
            newchain = re.sub(r"\s+","",chain)
            xcode = before + newchain + end
            m = re.search(r"\{.*?\s+.*?\}",xcode) 
        return xcode
        
    def log_special(code) :
        ret = dict()
        if not code :
            return (code,ret)
        if not ("<" in code and ">" in code) :
            return (code,ret)
        m = re.search(r"\<\s*([^\<\>]+?)\s*\>",code) 
        while m :
            full = m.group(0)
            tokeep = m.group(1)
            special = "".join([random.choice(string.ascii_lowercase) for _ in range(10)])
            code = code.replace(full,special)
            ret[special] = tokeep
            if _x_args.debug > 2:
                print("# [passwd] {} -> {}".format(tokeep,special))
            m = re.search(r"\<\s*([^\<\>]+?)\s*\>",code) 
        return (code, ret)
            

    def choiceexpand(code) :
        if not code :
            return code
        m = re.search(r"((\w|\-|\d|\.|\[|\]|\(|\))+?)\.\{\s*(\S+?\s*(,\s*\S+?)*)\s*\}",code)
        if not m :
            return code
        if code.count("{") > 1 :
            print_err("# recusive choices are not supported.",lvl=2)
            return code
        before = code[:m.start()]
        end = code[m.end():]
        dt = m.group(1)
        keys = m.group(3)
        #print(keys)
        res = before + "{" + ",".join(["'{}'".format(k.replace(".",DSQ_DOT))  + ":" + "{}.{}".format(dt,k) for k in keys.split(",")]) + "}" + end
        if _x_args.debug > 2:
            print("# [choice] before = ",code)
            print("# [choice] after  = ",res)
        return res

    def insert_spaces(code) :
        return code.replace("(","( ").replace(")"," )")

    def listexpand(code) :
        def hasslice(code) :
            if re.search(r"\[(\d|\:|\-|\s)*\]",code) and not re.search(r"\[(\s|\d|\-)+\]",code) :
                return True
            return False
        if not code or not hasslice(code) :
            return code
        def listexpand_helper(code) :
            if not hasslice(code) :
                return code
            xcode = code
            tvar = "_" + "".join([random.choice(string.ascii_lowercase) for _ in range(3)])
            #l,x = xcode.split("[]",1)
            m = re.match(r"(.*?)(\[(\d|\:|\s|\-)*\])(.*)",code)
            b,i,a = m.group(1),m.group(2),m.group(4)
            if re.sub(r"\s+","",i) in [ "[]", "[:]" ] :
                i=""
            return "[ {} for {} in {} ]".format(listexpand_helper(tvar+a),tvar,b+i)
        res = ""
        xcode = insert_spaces(code)
        _x_m = re.search(r"\S*\[(\d|\:|\-|\s)*\]\S*",xcode,re.DOTALL)
        while _x_m :
            before = xcode[:_x_m.start()]
            end = xcode[_x_m.end():]
            chain = _x_m.group(0)
            res += before + listexpand_helper(chain)
            xcode = end
            _x_m = re.search(r"\S*\[(\d|\:|\-|\s)*\]\S*",xcode,re.DOTALL)
        res += xcode
        if _x_args.debug > 2:
            print("# [ list ] before = ",code)
            print("# [ list ] after  = ",res)
        return res

    def dotexpand(code,keydt=None) :
        chgsin = code
        _x_m = re.search(r"\{(\w|\.)+\}|(\w+|\]|\))(\.\w+|\.\+\w+)+",chgsin,re.DOTALL)
        res=""
        while _x_m :
            before = chgsin[:_x_m.start()]
            end = chgsin[_x_m.end():]
            chain = _x_m.group(0)
            newchain = ""
            words = chain.split(".") 
            for i,w in enumerate(words) :
                if w.startswith("+") or (i==len(words)-1 and end.startswith("(")) :
                    nw = w.lstrip("+")
                    newchain += "." + nw
                else :
                    try :
                        if newchain == "" :
                            stest = w
                        else :
                            stest = newchain + "." + w
                        eval(stest)
                        newchain = stest
                        continue
                    except :
                        pass
                    nw = w.lower()
                    if nw in keydt and len(keydt[nw]) == 1 :
                        nw = list(keydt[nw])[0]
                        if _x_args.debug and w != nw :
                            print_err("# keyword replcement : {} -> {}".format(w,nw),lvl=1)
                    if newchain :
                        newchain += "['"+ nw + "']"
                    else :
                        newchain += nw
            res += before + newchain 
            chgsin = end
            _x_m = re.search(r"\{(\w|\.)+\}|(\w+|\]|\))(\.\w+|\.\+\w+)+",chgsin,re.DOTALL)
        res = res+chgsin
        if _x_args.debug > 2:
            print("# [ dot  ] before = ",code)
            print("# [ dot  ] after  = ",res)
        return res

    def runcode(code,data=None) :
        _ = data
        if not code :
            return
        if not _x_args.origin :
            code = code.replace("\\n","\n")
            code = removespaces(code)
            code,passbook = log_special(code)
            code = listexpand(code)
            code = choiceexpand(code)
            code = dotexpand(code,_x_key_dict)
            code = code.replace(DSQ_DOT,".")
            if passbook :
                for k,v in passbook.items() :
                    code = code.replace(k,v)
        if _x_args.debug:
            print_err("# run : ",lvl=1)
            print_err(code)
        attempts=0 
        err=""
        while True :
            try :
                if attempts == 0 :
                    if re.search("^\w+\s*=\S+",code) or len(code.splitlines())>1 or re.search(r"^(for|while)\s+",code) :
                        attempts += 1
                        continue
                    if _x_args.debug >= 2:
                        print_err("# eval : ",lvl=1)
                        print_err(code)
                    res = eval(code)
                    show_result(res)
                    return
                elif attempts == 1 :
                    if _x_args.debug >= 2:
                        print_err("# exec :",lvl=1)
                        print_err(code)
                    exec(code)
                    return
                else :
                    print_err("# expanded code :",lvl=2)
                    print_err("{}".format(code),lvl=2)
                    print_err("# {}".format(err.splitlines()[-1]),lvl=2)
                    #if _x_args.debug :
                    #    print_err(err,lvl=2)
                    return
            except :
                err += traceback.format_exc()
                attempts += 1
                continue

    INPUT = None
    if _x_args.infile:
        if not os.path.isfile(_x_args.infile):
            print_err("# {} not exists.".format(_x_args.infile),lvl=2)
        with open(_x_args.infile, "r") as f:
            INPUT = f.read()
            INPUT = INPUT.strip()
    else : 
        if not _x_args.interactive :
            INPUT = sys.stdin.read()
            INPUT = INPUT.strip()
    if not INPUT :
        INPUT = json.dumps({})

    if _x_args.debug >= 3 :
        print_err("# INPUT :",lvl=1)
        print_err(INPUT)

    if not _x_args.keepcolor :
        INPUT = re.sub(r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]',"",INPUT)


    try :
       if _x_args.srctype.upper() == "JSON" :
            _ = json.loads(INPUT)
       elif _x_args.srctype.upper() == "YAML" :
            _ = yaml.safe_load(INPUT)
       elif _x_args.srctype.upper() == "XML" :
            _ = xmltodict.parse(INPUT)
       else :
        print_err("# unsupported file type.",lvl=2)
        return -1
    except :
        print_err("# invalid JSON/YAML/XML.",lvl=2)
        traceback.print_exc()
        return -1

    if _x_args.debug >= 2 :
        print_err("# data loaded :",lvl=1)
        print_err(json.dumps(_,indent=2))

    _x_key_dict = defaultdict(set)
    collect_keys(_,dt=_x_key_dict)
    if _x_args.debug >= 2 :
        print_err("# keys collected : {}".format(str(_x_key_dict)),lvl=1)
    keys_extra= "_,_t,_x,_y,_j,_l,_tbl,_l2t,_l2pt,_pt,_qx,_rawstr,_flatlist,_fl".split(",")
    _x_word_completer = WordCompleter(sorted(list(set([x for x in _x_key_dict.keys()]+keys_extra))))
        
    if _x_args.modules :
        _x_args.modules = os.path.expanduser(_x_args.modules)
        _x_args.modules = os.path.expandvars(_x_args.modules)
        for m in _x_args.modules.split(",")  :
            if m :
                m.strip()
                if m.startswith("from ") :
                    exec(m)
                elif os.path.isfile(m) :
                    mpath = os.path.abspath(os.path.dirname(m))
                    mfile = os.path.basename(m) 
                    sys.path.append(mpath)
                    mn = mfile.replace(".py","")
                    import importlib
                    globals()[mn] = importlib.import_module(mn)
                elif os.path.isdir(m) :
                    mpath = os.path.abspath(m) 
                    sys.path.append(m)
                else :
                    import importlib
                    globals()[m] = importlib.import_module(m)

    def run_as_func(code,_=_) :
        fname = "".join([random.choice(string.ascii_letters) for _ in range(20)])
        newcode = ""
        if _x_args.modules :
            for m in _x_args.modules.split(",")  :
                if m :
                    m.strip()
                    if m.startswith("from ") :
                        newcode += m + "\n"
                    else :
                        newcode += "import " + m + "\n"
        newcode += "def {}(_) :\n".format(fname)
        code = code.replace("\\n","\n")
        for ln in code.splitlines() :
            ln = ln.rstrip()
            newcode += " "*(int(_x_args.indent)) + ln + "\n"
        code = listexpand(newcode)
        code = choiceexpand(newcode)
        code = dotexpand(newcode,_x_key_dict)
        code = code.replace(DSQ_DOT,".")
        if _x_args.debug :
            print_err("# code to compile : ",lvl=1)
            print_err(code)
        xcode = compile(code,"<string>","exec")
        cobj = None
        for c in xcode.co_consts :
            if c and type(c) not in [str,int,tuple] :
                cobj = c
                break
        if not cobj :
            print_err("# no code object found.",lvl=2)
            sys.exit(-1)
        xfunc = FunctionType(cobj, globals())
        print(xfunc(_))

    if os.path.isfile(_x_args.code) :
        code = open(_x_args.code,"r").read()
    else :
        code = _x_args.code
    if re.match(r"^\s*\_\w+\s*$",code) :
        code = code + "(_)"

    xcode = code
    xcode = xcode.replace("\\n","\n")
    if any([re.match(r"^\s*return.*",ln) for ln in xcode.splitlines()]) \
       or any([re.match(r"^\s*return.*",ln) for ln in xcode.split(";")])  :
        _x_args.func = True

    if _x_args.code and not _x_args.func :
        if not (_x_args.interactive and _x_args.code == "_") :
            runcode(code,data=_)

    if _x_args.code and _x_args.func :
        x = run_as_func(code, _)
        if x :
            print(x)

    if _x_args.interactive :
        try :
            _x_session = PromptSession(lexer=PygmentsLexer(PythonLexer), completer=_x_word_completer)
        except :
            print_err("[qic] $ WARN: word completion disabled on combination of {}/{}".format(sys.platform,os.environ.get("TERM")),lvl=2)
            _x_session = None
        _x_dotkey = True
        _x_history = deque(maxlen=200)
        _x_cmd0=""
        _x_oldsin=""
        _x_block=False
        while True :
            _x_doeval=True
            res=""
            err=""
            try :
                if _x_session :
                    _x_sin = _x_session.prompt('[qic] $ ')
                else :
                    _x_sin = input('[qic] $ ')
            except KeyboardInterrupt:
                continue
            except EOFError :
                print_err("# IO Error. Pipeline intput is not supported in interactive mode, pls use (-f).",lvl=3)
                break
            if not _x_sin :
                continue
            if re.match(r"^\s*\_\w+\s*$",_x_sin) :
                _x_sin = _x_sin + "(_)"
            if _x_sin.startswith("'''")  :
                if _x_block :
                    _x_sin = _x_oldsin 
                    _x_oldsin=""
                    _x_block=False
                    _x_doeval = False
                else :
                    _x_block = True
                    continue
            if _x_block :
                if _x_oldsin :
                    _x_oldsin += "\n" + _x_sin
                else :
                    _x_oldsin += _x_sin
                continue
            lastc =[int(ord(c)) for c in _x_sin][-1]
            if lastc and lastc in [65,66,67,68] : 
                _x_sin = "\\hist"
            if _x_sin in ["quit()","\\q"] :
                break
            if _x_sin in ["dotkey"] :
                _x_dotkey = True
                continue
            if _x_sin in ["nodotkey","no dotkey"] :
                _x_dotkey = False
                continue
            mqx = re.match(r"!\s*(\S.*)",_x_sin)
            if mqx :
                res = _qx(mqx.group(1))
                show_result(res)
                continue
            if _x_sin in ["\\hist","\\history"] :
                if not _x_history :
                    print_err("# no history found.",lvl=2)
                for i,cmd in enumerate(_x_history) :
                    print_err("# {:3} : {}".format(i,cmd),lvl=1)
                continue
            _x_m1 = re.match(r"\\r (\d+)",_x_sin) 
            _x_m2 = re.match(r"\\(\d+)",_x_sin) 
            if _x_m1 or _x_m2:
                if _x_m1 :
                    ix = int(_x_m1.group(1))
                if _x_m2 :
                    ix = int(_x_m2.group(1))
                if ix < len(_x_history) :
                    _x_sin = _x_history[ix]
                else :
                    continue

            if re.search(r"\breturn\b",_x_sin) :
                x=run_as_func(_x_sin,_)
                if x :
                    print(x)
                continue

            _x_cmd0 = None
            if _x_dotkey and "." in _x_sin :
                _x_cmd0 = _x_sin
            try :
                if _x_cmd0 :
                    _x_history.append(_x_cmd0)
                elif not _x_sin.startswith("\\") :
                    _x_history.append(_x_sin)
                if _x_sin.startswith("\\") :
                    res = ""
                    err = "# command not recognized."
                else :
                    runcode(_x_sin,data=_)
            except :
                traceback.print_last()
                continue
            time.sleep(0.1)
        return 0


if __name__ == "__main__" :
    dsq_main()
