<commit_before>///<reference path='refs.ts'/>

module TDev.AST {
    class TsQuotingCtx
        extends QuotingCtx
    {
        public unUnicode(s:string)
        {
            s = s.replace(/\s+([a-z])/g, (v,l) => l.toUpperCase())
            s = s.replace(/[^a-zA-Z0-9]+/g, "_");
            if (s == "" || /^[0-9]/.test(s)) s = "_" + s;
            return s;
        }

    }

    class TsTokenWriter
        extends TokenWriter
    {
        private globalCtx = new TsQuotingCtx();

        public globalId(d:Decl)
        {
            var n = d.getName()
            if (d instanceof Action) {
                var a = <Action>d;
                if (!a.isAtomic)
                    n += "Async"

            }
            return this.jsid(this.globalCtx.quote(n, 0))
        }

        public sep():TokenWriter
        {
            if (" ([.".indexOf(this.lastChar) >= 0) return this;
            return super.sep();
        }

        public jsid(id:string) {
            if (!/^[a-zA-Z_]\w*$/.test(id))
                Util.oops("bad id: " + id)
            return this.sep().write(id);
        }

        public kw(k:string) { return this.keyword(k) }
    }

    class AsyncFinder
        extends ExprVisitor
    {
        public lastAsync:Call;

        visitCall(c:Call)
        {
            if (c.awaits())
                this.lastAsync = c;
            super.visitCall(c)
        }

    }

    class ConverterPrep
        extends NodeVisitor
    {
        private numAwaits = 0;

        visitAstNode(n:AstNode)
        {
            this.visitChildren(n);
        }

        visitInlineActions(s:InlineActions)
        {
            var pre = this.numAwaits;
            this.dispatch(s.expr);
            s._converterAwait = pre < this.numAwaits
            s.normalActions().forEach(a => a.name._converterAction = a)
            super.visitStmt(s)
        }

        visitStmt(s:Stmt)
        {
            var pre = this.numAwaits;
            super.visitStmt(s)
            s._converterAwait = pre < this.numAwaits
        }

        visitExprHolder(eh:ExprHolder)
        {
            if (eh.isAwait) 
                this.numAwaits++
        }
    }

    export class Converter 
        extends NodeVisitor 
    {
        private tw = new TsTokenWriter();
        private localCtx = new TsQuotingCtx();
        private currAsync:Call;

        constructor(private app:App)
        {
            super()
        }

        public run()
        {
            new ConverterPrep().dispatch(this.app)
            this.dispatch(this.app)
            return this.tw.finalize()
        }

        private localName(l:LocalDef)
        {
            return this.tw.jsid(this.localCtx.quote(l.getName(), l.nodeId))
        }

        private type(t:Kind)
        {
            if (/^(String|Number|Boolean)$/.test(t.toString()))
                this.tw.jsid(t.toString().toLowerCase())
            else if (t.getRoot() == api.core.Collection) {
                this.type(t.getParameter(0))
                this.tw.op0("[]")
            } else {
                //TODO
                this.tw.kind(this.app, t)
            }
            return this.tw
        }

        private localDef(l:LocalDef)
        {
            this.localName(l).op0(":")
            return this.type(l.getKind())
        }

        visitAstNode(n:AstNode)
        {
            this.visitChildren(n);
        }

        private infixPri(e:Expr)
        {
            var p = e.getCalledProperty()
            if (!p) return 0
            if (p.parentKind == api.core.String) {
                if (p.getName() == "equals")
                    return 5
                // + in JS
                if (p == api.core.StringConcatProp)
                    return 10
            }
            return p.getInfixPriority() || 0
        }

        visitCall(e:Call)
        {
            if (e == this.currAsync) {
                this.tw.jsid("_")
                return
            }

            if (e.awaits()) {
                this.tw.write(" /* ASYNC */ ")
            }

            this.visitCallInner(e)
        }

        visitCallInner(e:Call)
        {
            var p = e.getCalledProperty()
            var infixPri = this.infixPri(e)
            var pn = p.parentKind.toString() + "->" + p.getName()
            
            var params = (pp:Expr[]) => {
                this.tw.op0("(")
                pp.forEach((p, i) => {
                    if (i > 0) this.tw.op(",")
                    this.dispatch(p)
                })
                this.tw.op0(")")
            }

            if (infixPri) {
                if (p.getName() == "-" && e.args[0].getLiteral() === 0.0) {
                    this.tw.op0("-")
                    this.dispatch(e.args[1])
                    return
                }

                var doParen = e => {
                    if (this.infixPri(e) && this.infixPri(e) <= infixPri 
                        && e.getCalledProperty().getName() != p.getName())
                    {
                        this.tw.op0("(")
                        this.dispatch(e)
                        this.tw.op0(")")
                    } else this.dispatch(e)
                }

                if (e.args.length == 1) {
                    this.printOp(p.getName())
                    doParen(e.args[0])
                } else {
                    doParen(e.args[0])
                    var nn = p.getName()
                    if (nn == "equals") nn = "=="
                    this.printOp(nn)
                    doParen(e.args[1])
                }

            } else if (e.referencedData()) {
                this.tw.globalId(e.referencedData())
            } else if (e.referencedLibrary()) {
                this.tw.globalId(e.referencedLibrary())
            } else if (e.referencedRecord()) {
                this.tw.globalId(e.referencedRecord())
            } else if (e.referencedRecordField()) {
                this.tightExpr(e.args[0])
                this.tw.op0(".");
                this.simpleId(e.referencedRecordField().getName())<commit_msg>converter:  few more APIs<commit_after>///<reference path='refs.ts'/>

module TDev.AST {
    class TsQuotingCtx
        extends QuotingCtx
    {
        public unUnicode(s:string)
        {
            s = s.replace(/\s+([a-z])/g, (v,l) => l.toUpperCase())
            s = s.replace(/[^a-zA-Z0-9]+/g, "_");
            if (s == "" || /^[0-9]/.test(s)) s = "_" + s;
            return s;
        }

    }

    class TsTokenWriter
        extends TokenWriter
    {
        private globalCtx = new TsQuotingCtx();

        public globalId(d:Decl)
        {
            var n = d.getName()
            if (d instanceof Action) {
                var a = <Action>d;
                if (!a.isAtomic)
                    n += "Async"

            }
            return this.jsid(this.globalCtx.quote(n, 0))
        }

        public sep():TokenWriter
        {
            if (" ([.".indexOf(this.lastChar) >= 0) return this;
            return super.sep();
        }

        public jsid(id:string) {
            if (!/^[a-zA-Z_]\w*$/.test(id))
                Util.oops("bad id: " + id)
            return this.sep().write(id);
        }

        public kw(k:string) { return this.keyword(k) }
    }

    class AsyncFinder
        extends ExprVisitor
    {
        public lastAsync:Call;

        visitCall(c:Call)
        {
            if (c.awaits())
                this.lastAsync = c;
            super.visitCall(c)
        }

    }

    class ConverterPrep
        extends NodeVisitor
    {
        private numAwaits = 0;

        visitAstNode(n:AstNode)
        {
            this.visitChildren(n);
        }

        visitInlineActions(s:InlineActions)
        {
            var pre = this.numAwaits;
            this.dispatch(s.expr);
            s._converterAwait = pre < this.numAwaits
            s.normalActions().forEach(a => a.name._converterAction = a)
            super.visitStmt(s)
        }

        visitStmt(s:Stmt)
        {
            var pre = this.numAwaits;
            super.visitStmt(s)
            s._converterAwait = pre < this.numAwaits
        }

        visitExprHolder(eh:ExprHolder)
        {
            if (eh.isAwait) 
                this.numAwaits++
        }
    }

    export class Converter 
        extends NodeVisitor 
    {
        private tw = new TsTokenWriter();
        private localCtx = new TsQuotingCtx();
        private currAsync:Call;

        constructor(private app:App)
        {
            super()
        }

        public run()
        {
            new ConverterPrep().dispatch(this.app)
            this.dispatch(this.app)
            return this.tw.finalize()
        }

        private localName(l:LocalDef)
        {
            return this.tw.jsid(this.localCtx.quote(l.getName(), l.nodeId))
        }

        private type(t:Kind)
        {
            if (/^(String|Number|Boolean)$/.test(t.toString()))
                this.tw.jsid(t.toString().toLowerCase())
            else if (t.getRoot() == api.core.Collection) {
                this.type(t.getParameter(0))
                this.tw.op0("[]")
            } else {
                //TODO
                this.tw.kind(this.app, t)
            }
            return this.tw
        }

        private localDef(l:LocalDef)
        {
            this.localName(l).op0(":")
            return this.type(l.getKind())
        }

        visitAstNode(n:AstNode)
        {
            this.visitChildren(n);
        }

        private infixPri(e:Expr)
        {
            var p = e.getCalledProperty()
            if (!p) return 0
            if (p.getName() == "is invalid")
                return 5 // '== null'
            if (p.parentKind == api.core.String) {
                if (p.getName() == "equals" || p.getName() == "is empty")
                    return 5
                // + in JS
                if (p == api.core.StringConcatProp)
                    return 10
            } else if (p.parentKind == api.core.Boolean) {
                if (p.getName() == "not") {
                    var a0 = (<Call>e).args[0].getCalledProperty()
                    if (a0 && a0.getName() == "is invalid")
                        return 5 // '!= null'
                    if (a0 && a0.parentKind == api.core.String && a0.getName() == "is empty")
                        return 5 // '!= ""'
                    return 50
                }
            }

            return p.getInfixPriority() || 0
        }

        visitCall(e:Call)
        {
            if (e == this.currAsync) {
                this.tw.jsid("_")
                return
            }

            if (e.awaits()) {
                this.tw.write(" /* ASYNC */ ")
            }

            this.visitCallInner(e)
        }

        visitCallInner(e:Call)
        {
            var p = e.getCalledProperty()
            var infixPri = this.infixPri(e)
            var pn = p.parentKind.toString() + "->" + p.getName()
            
            var params = (pp:Expr[]) => {
                this.tw.op0("(")
                pp.forEach((p, i) => {
                    if (i > 0) this.tw.op(",")
                    this.dispatch(p)
                })
                this.tw.op0(")")
            }

            if (infixPri) {
                if (p.getName() == "-" && e.args[0].getLiteral() === 0.0) {
                    this.tw.op0("-")
                    this.dispatch(e.args[1])
                    return
                }

                var doParen = e => {
                    if (this.infixPri(e) && this.infixPri(e) <= infixPri 
                        && e.getCalledProperty().getName() != p.getName())
                    {
                        this.tw.op0("(")
                        this.dispatch(e)
                        this.tw.op0(")")
                    } else this.dispatch(e)
                }

                if (p.getName() == "is invalid") {
                    doParen(e.args[0])
                    this.tw.sep().write("== null")
                } else if (p.getName() == "is empty") {
                    var inner0 = e.args[0].getCalledProperty()
                    if (inner0 && inner0.getName() == "or empty") {
                        this.tw.op0("!")
                        this.tightExpr((<Call>e.args[0]).args[1])
                    } else {
                        doParen(e.args[0])
                        this.tw.sep().write("== \"\"")
                    }
                } else if (infixPri == 5 && p.getName() == "not") {
                    doParen((<Call>e.args[0]).args[0])
                    switch (e.args[0].getCalledProperty().getName()) {
                    case "is invalid": this.tw.sep().write("!= null"); break;
                    case "is empty": this.tw.sep().write("!= \"\""); break;
                    default: Util.die()
                    }
                } else if (e.args.length == 1) {
                    this.printOp(p.getName())
                    doParen(e.args[0])
                } else {
                    doParen(e.args[0])
                    var nn = p.getName()
                    if (nn == "equals") nn = "=="
                    this.printOp(nn)
                    doParen(e.args[1])
                }

            } else if (e.referencedData()) {
                this.tw.globalId(e.referencedData())
            } else if (e.referencedLibrary()) {
                this.tw.globalId(e.referencedLibrary())
            } else if (e.referencedRecord()) {
                this.tw.globalId(e.referencedRecord())
            } else if (e.referencedRecordField()) {
                this.tightExpr(e.args[0])
                this.tw.op0(".");
                this.simpleId(e.referencedRecordField().getName())
{'fraction_non_alphanumeric': 0.13602033405954975, 'fraction_numerical': 0.006318082788671024, 'mean_word_length': 1.0909505010628606, 'pattern_counts': {'":': 4, '<': 16, '<?xml version=': 0, '>': 26, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6670231', 'n_tokens_mistral': 4186, 'n_tokens_neox': 3976, 'n_words': 667}