"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        if (typeof b !== "function" && b !== null)
            throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __assign = (this && this.__assign) || function () {
    __assign = Object.assign || function(t) {
        for (var s, i = 1, n = arguments.length; i < n; i++) {
            s = arguments[i];
            for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
                t[p] = s[p];
        }
        return t;
    };
    return __assign.apply(this, arguments);
};
var __read = (this && this.__read) || function (o, n) {
    var m = typeof Symbol === "function" && o[Symbol.iterator];
    if (!m) return o;
    var i = m.call(o), r, ar = [], e;
    try {
        while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
    }
    catch (error) { e = { error: error }; }
    finally {
        try {
            if (r && !r.done && (m = i["return"])) m.call(i);
        }
        finally { if (e) throw e.error; }
    }
    return ar;
};
var __values = (this && this.__values) || function(o) {
    var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
    if (m) return m.call(o);
    if (o && typeof o.length === "number") return {
        next: function () {
            if (o && i >= o.length) o = void 0;
            return { value: o && o[i++], done: !o };
        }
    };
    throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Parser = exports.JSONParseError = exports.MichelineParseError = void 0;
var scan_1 = require("./scan");
var micheline_1 = require("./micheline");
var macros_1 = require("./macros");
var MichelineParseError = /** @class */ (function (_super) {
    __extends(MichelineParseError, _super);
    /**
     * @param token A token caused the error
     * @param message An error message
     */
    function MichelineParseError(token, message) {
        var _this = _super.call(this, message) || this;
        _this.token = token;
        Object.setPrototypeOf(_this, MichelineParseError.prototype);
        return _this;
    }
    return MichelineParseError;
}(Error));
exports.MichelineParseError = MichelineParseError;
var JSONParseError = /** @class */ (function (_super) {
    __extends(JSONParseError, _super);
    /**
     * @param node A node caused the error
     * @param message An error message
     */
    function JSONParseError(node, message) {
        var _this = _super.call(this, message) || this;
        _this.node = node;
        Object.setPrototypeOf(_this, JSONParseError.prototype);
        return _this;
    }
    return JSONParseError;
}(Error));
exports.JSONParseError = JSONParseError;
var errEOF = new MichelineParseError(null, 'Unexpected EOF');
function isAnnotation(tok) {
    return tok.t === scan_1.Literal.Ident && (tok.v[0] === '@' || tok.v[0] === '%' || tok.v[0] === ':');
}
var intRe = new RegExp('^-?[0-9]+$');
var bytesRe = new RegExp('^([0-9a-fA-F]{2})*$');
/**
 * Converts and validates Michelson expressions between JSON-based Michelson and Micheline
 *
 * Pretty Print a Michelson Smart Contract:
 * ```
 * const contract = await Tezos.contract.at("KT1Vsw3kh9638gqWoHTjvHCoHLPKvCbMVbCg");
 * const p = new Parser();
 *
 * const michelsonCode = p.parseJSON(contract.script.code);
 * const storage = p.parseJSON(contract.script.storage);
 *
 * console.log("Pretty print Michelson smart contract:");
 * console.log(emitMicheline(michelsonCode, {indent:"    ", newline: "\n",}));
 *
 * console.log("Pretty print Storage:");
 * console.log(emitMicheline(storage, {indent:"    ", newline: "\n",}));
 * ```
 *
 * Encode a Michelson expression for inital storage of a smart contract
 * ```
 * const src = `(Pair (Pair { Elt 1
 *                (Pair (Pair "tz1gjaF81ZRRvdzjobyfVNsAeSC6PScjfQwN" "tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx")
 *                      0x0501000000026869) }
 *          10000000)
 *    (Pair 2 333))`;
 *
 * const p = new Parser();
 *
 * const exp = p.parseMichelineExpression(src);
 * console.log(JSON.stringify(exp));
 * ```
 */
var Parser = /** @class */ (function () {
    function Parser(opt) {
        this.opt = opt;
    }
    Parser.prototype.expand = function (ex) {
        var _a, _b;
        if (((_a = this.opt) === null || _a === void 0 ? void 0 : _a.expandMacros) !== undefined ? (_b = this.opt) === null || _b === void 0 ? void 0 : _b.expandMacros : true) {
            var ret = macros_1.expandMacros(ex, this.opt);
            if (ret !== ex) {
                ret[micheline_1.sourceReference] = __assign(__assign({}, (ex[micheline_1.sourceReference] || { first: 0, last: 0 })), { macro: ex });
            }
            return ret;
        }
        else {
            return ex;
        }
    };
    Parser.prototype.parseListExpr = function (scanner, start) {
        var _a;
        var _b;
        var ref = {
            first: start.first,
            last: start.last,
        };
        var expectBracket = start.t === "(";
        var tok;
        if (expectBracket) {
            tok = scanner.next();
            if (tok.done) {
                throw errEOF;
            }
            ref.last = tok.value.last;
        }
        else {
            tok = { value: start };
        }
        if (tok.value.t !== scan_1.Literal.Ident) {
            throw new MichelineParseError(tok.value, "not an identifier: " + tok.value.v);
        }
        var ret = (_a = {
                prim: tok.value.v
            },
            _a[micheline_1.sourceReference] = ref,
            _a);
        for (;;) {
            var tok_1 = scanner.next();
            if (tok_1.done) {
                if (expectBracket) {
                    throw errEOF;
                }
                break;
            }
            else if (tok_1.value.t === ')') {
                if (!expectBracket) {
                    throw new MichelineParseError(tok_1.value, "unexpected closing bracket");
                }
                ref.last = tok_1.value.last;
                break;
            }
            else if (isAnnotation(tok_1.value)) {
                ret.annots = ret.annots || [];
                ret.annots.push(tok_1.value.v);
                ref.last = tok_1.value.last;
            }
            else {
                ret.args = ret.args || [];
                var arg = this.parseExpr(scanner, tok_1.value);
                ref.last = ((_b = arg[micheline_1.sourceReference]) === null || _b === void 0 ? void 0 : _b.last) || ref.last;
                ret.args.push(arg);
            }
        }
        return this.expand(ret);
    };
    Parser.prototype.parseArgs = function (scanner, start) {
        var _a;
        var _b;
        // Identifier with arguments
        var ref = {
            first: start.first,
            last: start.last,
        };
        var p = (_a = {
                prim: start.v
            },
            _a[micheline_1.sourceReference] = ref,
            _a);
        for (;;) {
            var t = scanner.next();
            if (t.done || t.value.t === '}' || t.value.t === ';') {
                return [p, t];
            }
            if (isAnnotation(t.value)) {
                ref.last = t.value.last;
                p.annots = p.annots || [];
                p.annots.push(t.value.v);
            }
            else {
                var arg = this.parseExpr(scanner, t.value);
                ref.last = ((_b = arg[micheline_1.sourceReference]) === null || _b === void 0 ? void 0 : _b.last) || ref.last;
                p.args = p.args || [];
                p.args.push(arg);
            }
        }
    };
    Parser.prototype.parseSequenceExpr = function (scanner, start) {
        var _a, _b;
        var ref = {
            first: start.first,
            last: start.last,
        };
        var seq = [];
        seq[micheline_1.sourceReference] = ref;
        var expectBracket = start.t === "{";
        var tok = start.t === "{" ? null : { value: start };
        for (;;) {
            if (tok === null) {
                tok = scanner.next();
                if (!tok.done) {
                    ref.last = tok.value.last;
                }
            }
            if (tok.done) {
                if (expectBracket) {
                    throw errEOF;
                }
                else {
                    return seq;
                }
            }
            if (tok.value.t === "}") {
                if (!expectBracket) {
                    throw new MichelineParseError(tok.value, "unexpected closing bracket");
                }
                else {
                    return seq;
                }
            }
            else if (tok.value.t === scan_1.Literal.Ident) {
                // Identifier with arguments
                var _c = __read(this.parseArgs(scanner, tok.value), 2), itm = _c[0], n = _c[1];
                ref.last = ((_a = itm[micheline_1.sourceReference]) === null || _a === void 0 ? void 0 : _a.last) || ref.last;
                seq.push(this.expand(itm));
                tok = n;
            }
            else {
                // Other
                var ex = this.parseExpr(scanner, tok.value);
                ref.last = ((_b = ex[micheline_1.sourceReference]) === null || _b === void 0 ? void 0 : _b.last) || ref.last;
                seq.push(ex);
                tok = null;
            }
            if (tok === null) {
                tok = scanner.next();
                if (!tok.done) {
                    ref.last = tok.value.last;
                }
            }
            if (!tok.done && tok.value.t === ";") {
                tok = null;
            }
        }
    };
    Parser.prototype.parseExpr = function (scanner, tok) {
        var _a, _b, _c, _d;
        switch (tok.t) {
            case scan_1.Literal.Ident:
                return this.expand((_a = { prim: tok.v }, _a[micheline_1.sourceReference] = { first: tok.first, last: tok.last }, _a));
            case scan_1.Literal.Number:
                return _b = { int: tok.v }, _b[micheline_1.sourceReference] = { first: tok.first, last: tok.last }, _b;
            case scan_1.Literal.String:
                return _c = { string: JSON.parse(tok.v) }, _c[micheline_1.sourceReference] = { first: tok.first, last: tok.last }, _c;
            case scan_1.Literal.Bytes:
                return _d = { bytes: tok.v.slice(2) }, _d[micheline_1.sourceReference] = { first: tok.first, last: tok.last }, _d;
            case '{':
                return this.parseSequenceExpr(scanner, tok);
            default:
                return this.parseListExpr(scanner, tok);
        }
    };
    /**
     * Parses a Micheline sequence expression, such as smart contract source. Enclosing curly brackets may be omitted.
     * @param src A Micheline sequence `{parameter ...; storage int; code { DUP ; ...};}` or `parameter ...; storage int; code { DUP ; ...};`
     */
    Parser.prototype.parseSequence = function (src) {
        // tslint:disable-next-line: strict-type-predicates
        if (typeof src !== "string") {
            throw new TypeError("string type was expected, got " + typeof src + " instead");
        }
        var scanner = scan_1.scan(src);
        var tok = scanner.next();
        if (tok.done) {
            return null;
        }
        return this.parseSequenceExpr(scanner, tok.value);
    };
    /**
     * Parse a Micheline sequence expression. Enclosing curly brackets may be omitted.
     * @param src A Michelson list expression such as `(Pair {Elt "0" 0} 0)` or `Pair {Elt "0" 0} 0`
     * @returns An AST node or null for empty document.
     */
    Parser.prototype.parseList = function (src) {
        // tslint:disable-next-line: strict-type-predicates
        if (typeof src !== "string") {
            throw new TypeError("string type was expected, got " + typeof src + " instead");
        }
        var scanner = scan_1.scan(src);
        var tok = scanner.next();
        if (tok.done) {
            return null;
        }
        return this.parseListExpr(scanner, tok.value);
    };
    /**
     * Parse any Michelson expression
     * @param src A Michelson expression such as `(Pair {Elt "0" 0} 0)` or `{parameter ...; storage int; code { DUP ; ...};}`
     * @returns An AST node or null for empty document.
     */
    Parser.prototype.parseMichelineExpression = function (src) {
        // tslint:disable-next-line: strict-type-predicates
        if (typeof src !== "string") {
            throw new TypeError("string type was expected, got " + typeof src + " instead");
        }
        var scanner = scan_1.scan(src);
        var tok = scanner.next();
        if (tok.done) {
            return null;
        }
        return this.parseExpr(scanner, tok.value);
    };
    /**
     * Parse a Micheline sequence expression, such as smart contract source. Enclosing curly brackets may be omitted.
     * An alias for `parseSequence`
     * @param src A Micheline sequence `{parameter ...; storage int; code { DUP ; ...};}` or `parameter ...; storage int; code { DUP ; ...};`
     */
    Parser.prototype.parseScript = function (src) {
        return this.parseSequence(src);
    };
    /**
     * Parse a Micheline sequence expression. Enclosing curly brackets may be omitted.
     * An alias for `parseList`
     * @param src A Michelson list expression such as `(Pair {Elt "0" 0} 0)` or `Pair {Elt "0" 0} 0`
     * @returns An AST node or null for empty document.
     */
    Parser.prototype.parseData = function (src) {
        return this.parseList(src);
    };
    /**
     * Takes a JSON-encoded Michelson, validates it, strips away unneeded properties and optionally expands macros (See {@link ParserOptions}).
     * @param src An object containing JSON-encoded Michelson, usually returned by `JSON.parse()`
     */
    Parser.prototype.parseJSON = function (src) {
        var e_1, _a, e_2, _b, e_3, _c;
        // tslint:disable-next-line: strict-type-predicates
        if (typeof src !== "object") {
            throw new TypeError("object type was expected, got " + typeof src + " instead");
        }
        if (Array.isArray(src)) {
            var ret = [];
            try {
                for (var src_1 = __values(src), src_1_1 = src_1.next(); !src_1_1.done; src_1_1 = src_1.next()) {
                    var n = src_1_1.value;
                    if (n === null || typeof n !== 'object') {
                        throw new JSONParseError(n, "unexpected sequence element: " + n);
                    }
                    ret.push(this.parseJSON(n));
                }
            }
            catch (e_1_1) { e_1 = { error: e_1_1 }; }
            finally {
                try {
                    if (src_1_1 && !src_1_1.done && (_a = src_1.return)) _a.call(src_1);
                }
                finally { if (e_1) throw e_1.error; }
            }
            return ret;
        }
        else if ('prim' in src) {
            var p = src;
            if (typeof p.prim === 'string' &&
                (p.annots === undefined || Array.isArray(p.annots)) &&
                (p.args === undefined || Array.isArray(p.args))) {
                var ret = {
                    prim: p.prim,
                };
                if (p.annots !== undefined) {
                    try {
                        for (var _d = __values(p.annots), _e = _d.next(); !_e.done; _e = _d.next()) {
                            var a = _e.value;
                            if (typeof a !== 'string') {
                                throw new JSONParseError(a, "string expected: " + a);
                            }
                        }
                    }
                    catch (e_2_1) { e_2 = { error: e_2_1 }; }
                    finally {
                        try {
                            if (_e && !_e.done && (_b = _d.return)) _b.call(_d);
                        }
                        finally { if (e_2) throw e_2.error; }
                    }
                    ret.annots = p.annots;
                }
                if (p.args !== undefined) {
                    ret.args = [];
                    try {
                        for (var _f = __values(p.args), _g = _f.next(); !_g.done; _g = _f.next()) {
                            var a = _g.value;
                            if (a === null || typeof a !== 'object') {
                                throw new JSONParseError(a, "unexpected argument: " + a);
                            }
                            ret.args.push(this.parseJSON(a));
                        }
                    }
                    catch (e_3_1) { e_3 = { error: e_3_1 }; }
                    finally {
                        try {
                            if (_g && !_g.done && (_c = _f.return)) _c.call(_f);
                        }
                        finally { if (e_3) throw e_3.error; }
                    }
                }
                return this.expand(ret);
            }
            throw new JSONParseError(src, "malformed prim expression: " + src);
        }
        else if ('string' in src) {
            if (typeof src.string === 'string') {
                return { string: src.string };
            }
            throw new JSONParseError(src, "malformed string literal: " + src);
        }
        else if ('int' in src) {
            if (typeof src.int === 'string' && intRe.test(src.int)) {
                return { int: src.int };
            }
            throw new JSONParseError(src, "malformed int literal: " + src);
        }
        else if ('bytes' in src) {
            if (typeof src.bytes === 'string' &&
                bytesRe.test(src.bytes)) {
                return { bytes: src.bytes };
            }
            throw new JSONParseError(src, "malformed bytes literal: " + src);
        }
        else {
            throw new JSONParseError(src, "unexpected object: " + src);
        }
    };
    return Parser;
}());
exports.Parser = Parser;
//# sourceMappingURL=micheline-parser.js.map