<commit_before>const fs = require("fs");

const ohm = require("ohm-js");

const parserContents = fs.readFileSync("JCaml.ohm");
const Context = require("./context.js");

const JCamlGrammar = ohm.grammar(parserContents);

class Type {
    constructor(type) {
        this.type = type;
    }

    toString() {
        const typeString = `Type ${this.type}`;
        return typeString;
    }
}

Type.INT = new Type("int");
Type.FLOAT = new Type("float");
Type.STRING = new Type("string");
Type.BOOL = new Type("bool");
Type.CHAR = new Type("char");

class Program {
    constructor(block) {
        this.block = block;
    }

    analyze(context = Context.INITIAL) {
        this.block.analyze(context);
    }

    toString() {
        return `(Program ${this.block})`;
    }

    static gen() {
        // console.log("Hi this is the generator");
    }
}

class Block {
    constructor(statements) {
        this.statements = statements;
    }

    analyze(context) {
        this.statements.forEach((s) => {
            s.analyze(context);
        });
    }

    toString() {
        let stmtString;
        for (const s in this.statements) {
            stmtString += `\n (Block ${this.statements[s]})`;
        }
        return stmtString;
    }
}

class Stmt {
}

class StatementIfElse extends Stmt {
    constructor(expressions, blocks) {
        super();
        this.expressions = expressions;
        this.blocks = blocks;
    }

    analyze(context) {
        this.expressions.forEach((expression) => {
            expression.analyze(context);
            if (!(expression.type === Type.BOOL)) {
                throw new Error("Type Error: If statement conditional must be a bool.");
            }
        });

        this.blocks.forEach((block) => {
            block.analyze(context);
        });
    }
    toString() {
        let ifString = `(ifStatement if ${this.exp} ${this.block})`;
        for (const exps in this.exp2) {
            ifString += `\n (else if ${this.exp2[exps]})`;
        }
        for (const blocks in this.elseBlock) {
            ifString += `\n (${this.elseBlock[blocks]})`;
        }
        ifString += `\n (else ${this.finalBlock})`;
        return ifString;
    }
}

class Decl extends Stmt {
    constructor(declaredType, id, exp) {
        super();
        this.declaredType = declaredType;
        this.id = id;
        this.exp = exp;
    }

    analyze(context) {
        if (this.declaredType !== this.exp.type) {
            throw new Error("Declared type does not match the evaluated type.");
        }
        context.checkIfVariableIsAlreadyDeclared(this.id);
        context.addVariable(this.id, this.exp);
        this.exp.analyze(context);
        this.declaredType.analyze(context);
        this.id.analyze(context);
    }

    toString() {
        const declString = `(Decl let ${this.type} ${this.id} = ${this.exp})`;
        return declString;
    }
}

class FuncDec extends Stmt {
    constructor(id, params, returnType, body) {
        super();
        this.id = id;
        this.params = params;
        this.body = body;
        this.returnType = returnType;
    }

    analyze(context) {
        this.id.analyze(context);
        context.checkIfVariableIsAlreadyDeclared(this.id);

        const localContext = context.createChildContextForFunctionBody(this);
        this.params.forEach((param) => {
            // duplicate parameter check
            localContext.checkIfVariableIsAlreadyDeclared(param.id);
            param.analyze(localContext);
            localContext.addVariable(param.id, param);
        });

        this.returnType.analyze(context);
        this.body.analyze(localContext);
        this.type = this.body.type;
    }

    toString() {
        const funcDecString = `FuncDec let fun ${this.id} = ${this.params} => ${this.returnType} : ${this.body}`;
        return funcDecString;
    }
}

class Print extends Stmt {
    constructor(argument) {
        super();
        this.argument = argument;
    }

    toString() {
        return `(Print (${this.argument}))`;
    }
}

class Return extends Stmt {
    constructor(argument) {
        super();
        this.argument = argument;
    }

    toString() {
        return `(Return ${this.argument})`;
    }
}

class Addop {
    constructor(op) {
        this.op = op;
    }

    toString() {
        return `(addop ${this.op})`;
    }
}

class Relop {
    constructor(op) {
        this.op = op;
    }

    toString() {
        return `(relop ${this.op})`;
    }
}

class Mullop {
    constructor(op) {
        this.op = op;
    }

    toString() {
        return `(mullop ${this.op})`;
    }
}

class Expop {
    constructor(op) {
        this.op = op;
    }

    toString() {
        return `(expop ${this.op})`;
    }
}

class Binop {
    constructor(op) {
        this.op = op;
    }

    toString() {
        return `(binop ${this.op})`;
    }
}

class FuncCall extends Stmt {
    constructor(id, args) {
        super();
        this.id = id;
        this.args = args;
    }

    toString() {
        return `(funcCall ${this.id} ($this.args))`;
    }
}

class Args {
    constructor(args) {
        this.args = args;
    }

    toString() {
        return `(Args ${this.args})`;
    }
}

class Arg {
    constructor(id) {
        this.id = id;
    }

    toString() {
      return `(Arg ${this.id})`;
    }
}

class Params {
    constructor(params) {
        this.params = params;
    }

    toString() {
        let paramsString = "Params (";
        for (const params in this.params) {
            paramsString += `, ${params}`;
        }

        paramsString += ")";
        return paramsString;
    }
}

class Param {
    constructor(id) {
        this.id = id;
    }

    toString() {
        const paramString = `Param ${this.id}`;
        return paramString;
    }
}

class Body {
    constructor(block) {
        this.block = block;
    }

    toString() {
        const bodyString = `(Body :${this.block};;)`;
        return bodyString;
    }
}

class ExpBinary {
    constructor(op, exp, matchexp) {
        this.op = op;
        this.exp = exp;
        this.matchexp = matchexp;
    }

    toString() {
        return `(Exp_binary ${this.exp} ${this.op} ${this.matchexp})`;
    }
}

class ExpTernary {
    constructor(op, matchexp1, matchexp2, matchexp3) {
        this.matchexp1 = matchexp1;
        this.matchexp2 = matchexp2;
        this.matchexp3 = matchexp3;
    }

    toString() {
        return `(Exp_ternary ${this.matchexp1} ? ${this.matchexp2} : ${this.matchexp3})`;
    }
}

class MatchExp {
    constructor(id, matches) {
        this.id = id;
        this.matches = matches;
    }

    toString() {
        return `(MatchExp match ${this.id} with ${this.matches})`;
    }
}

class BinExp {
    constructor(op, binexp, addexp) {
        this.op = op;
        this.binexp = binexp;
        this.addexp = addexp;
    }

    analyze(context) {
        this.binexp.analyze(context);
        this.addexp.analyze(context);
        this.type = Type.INT;
    }

    toString() {
        return `(BinExp ${this.binexp} ${this.op} ${this.addexp})`;
    }
}

class AddExp {
    constructor(op, addexp, mullexp) {
        this.op = op;
        this.addexp = addexp;
        this.mullexp = mullexp;
    }

    toString() {
        return `(AddExp ${this.addexp} ${this.op} ${this.mullexp})`;
    }
}

class MullExp {
    constructor(op, mullexp, prefixexp) {
        this.op = op;
        this.mullexp = mullexp;
        this.prefixexp = prefixexp;
    }

    toString() {
        return `(Mullexp ${this.mullexp} ${this.op} ${this.prefixexp})`;
    }
}

class PrefixExp {
    constructor(op, expoexp) {
        this.op = op;
        this.expoexp = expoexp;
    }

    toString() {
        return `(Prefixexp ${this.op} ${this.expoexp})`;
    }
}

class ExpoExp {
    constructor(op, parenexp, expoexp) {
        this.op = op;
        this.parenexp = parenexp;
        this.expoexp = expoexp;
    }

    // analyze() {
    //     //  parenexp and expoexp must be the same type (float and int)
            // from there I can determine the ExpoExp type
    // }

    toString() {
        return `(Expoexp ${this.Parenexp} ${this.op} ${this.Expoexp})`;
    }
}

class ParenExp {
    constructor(parenexp) {
        this.parenexp = parenexp;
        this.type = this.parenexp.type;
    }

    toString() {
        return `(Parenexp (${this.parenexp}))`;
    }
}

class Matches {
    constructor(exp1, exp2) {
        this.exp1 = exp1;
        this.exp2 = exp2;
    }

    toString() {
        return `(Matches | ${this.exp1} -> ${this.exp2})`;
    }
}

class Tuplit {
    constructor(exp1, exp2) {
        this.exp1 = exp1;
        this.exp2 = exp2;
    }

    toString() {
        return `(Tuplit (${this.exp1}, ${this.exp2}))`;
    }
}

class List {
    constructor(arg, args) {
        this.arg = arg;
        this.args = args;
    }

  toString() {
    return `(List ${this.arg} ${this.args})`;
  }
}

class Numlit {
    constructor(value) {
      this.value = value;
    }
    toString() {
        let numberString = "(numlit ";
            for (const numbers in this.value) {
                numberString += `${this.value[numbers]}`;
            }
            numberString += ")";
        return numberString;
    }

    gen() {
        return `${this.value}`;
    }
}

class Charlit {
    constructor(value) {
        this.value = value;
    }
    toString() {
        return `(Charlit ${this.value})`;
    }
}

class Stringlit {
    constructor(value) {
        this.value = value;
    }
    toString() {
        return `(stringlit ${this.value})`;
    }
}

/* eslint-disable no-unused-vars */
const semantics = JCamlGrammar.createSemantics().addOperation("tree", {
    Program(block) { return new Program(block.tree()); },
    Block(stmt) { return new Block(stmt.tree()); },
    Stmt_if(_1, exp, block, _2, elseIfExprs, elseIfBlocks, _3, finalBlock) {
        return new StatementIfElse(exp.tree(), block.tree(), elseIfExprs.tree(), finalBlock.tree());
    },
    Decl_decl(_1, type, id, _2, exp) { return new Decl(type.tree(), id.sourceString, exp.tree()); },
    Print_print(_1, _2, binexp, _3) { return new Print(binexp.tree()); },
    Return(_1, arg) { return new Return(arg.tree()); },
    addop(op) { return new Addop(this.sourceString); },
    relop(op) { return new Relop(this.sourceString); },
    mullop(op) { return new Mullop(this.sourceString); },
    expop(op) { return new Expop(this.sourceString); },
    binop(op) { return new Binop(this.sourceString); },
    FuncDec(_1, id, _2, params, _3, type, body) {
      return new FuncDec(id.sourceString, params.tree(), type.tree(), body.tree());
    },
    FuncCall(id, _1, args, _2) { return new FuncCall(id.sourceString, args.tree()); },
    Args(arg) { return new Args(arg.tree()); },
    Arg(id) { return new Arg(id.sourceString); },
    Params(_1, firstParam, _2, moreParams, _3) {
      return new Params([firstParam.tree()].concat(moreParams.tree()));
    },
    Param(id) { return new Param(id.sourceString); },
    Type(type) { return new Type(type.sourceString); },
    Body(_1, block, _2) { return new Body(block.tree()); },
    Exp_binary(exp, op, matchexp) { return new ExpBinary(op.tree(), exp.tree(), matchexp.tree()); },
    Exp_ternary(matchexp, _1, matchexp2, _2, matchexp3) {
      return new ExpTernary(matchexp.tree(), matchexp2.tree(), matchexp3.tree());
    },
    BinExp_binary(binexp, op, addexp) {
        return new BinExp(op.tree(), binexp.tree(), addexp.tree());
    },
    MatchExp_matchexp(_1, id, _2, matches) {
        return new MatchExp(id.sourceString, matches.tree());
    },
    AddExp_binary(addexp, op, mullexp) {
      return new AddExp(op.tree(), addexp.tree(), mullexp.tree());
    },
    MullExp_binary(mullexp, op, prefixexp) {
      return new MullExp(op.tree(), mullexp.tree(), prefixexp.tree());
    },
    PrefixExp_binary(op, expoexp) { return new PrefixExp(op.tree(), expoexp.tree()); },
    ExpoExp_binary(parenexp, op, expoexp) {
      return new ExpoExp(op.tree(), parenexp.tree(), expoexp.tree());
    },
    ParenExp_parens(_1, parenexp, _2) { return new ParenExp(parenexp.tree()); },
    Matches(_1, exp1, _2, exp2) { return new Matches(exp1.tree(), exp2.tree()); },
    Tuplit(_1, exp1, _2, exp2, _3) { return new Tuplit(exp1.tree(), exp2.tree()); },
    List_list(_1, _2, _3, _4, args) { return new List(this.sourceString); },
    List(_1) { return new List(this.sourceString); },
    numlit(value) { return new Numlit(this.sourceString); },
    charlit(_1, value, _2) { return new Charlit(this.sourceString); },
    stringlit(_1, value, _2) { return new Stringlit(this.sourceString); },
});
/* eslint-enable no-unused-vars */

function parse(text) {
    const match = JCamlGrammar.match(text);
    return semantics(match).tree();
}
module.exports = parse;
<commit_msg>argument generator<commit_after>const fs = require("fs");

const ohm = require("ohm-js");

const parserContents = fs.readFileSync("JCaml.ohm");
const Context = require("./context.js");

const JCamlGrammar = ohm.grammar(parserContents);

class Type {
    constructor(type) {
        this.type = type;
    }

    toString() {
        const typeString = `Type ${this.type}`;
        return typeString;
    }
}

Type.INT = new Type("int");
Type.FLOAT = new Type("float");
Type.STRING = new Type("string");
Type.BOOL = new Type("bool");
Type.CHAR = new Type("char");

class Program {
    constructor(block) {
        this.block = block;
    }

    analyze(context = Context.INITIAL) {
        this.block.analyze(context);
    }

    toString() {
        return `(Program ${this.block})`;
    }

    static gen() {
        // console.log("Hi this is the generator");
    }
}

class Block {
    constructor(statements) {
        this.statements = statements;
    }

    analyze(context) {
        this.statements.forEach((s) => {
            s.analyze(context);
        });
    }

    toString() {
        let stmtString;
        for (const s in this.statements) {
            stmtString += `\n (Block ${this.statements[s]})`;
        }
        return stmtString;
    }
}

class Stmt {
}

class StatementIfElse extends Stmt {
    constructor(expressions, blocks) {
        super();
        this.expressions = expressions;
        this.blocks = blocks;
    }

    analyze(context) {
        this.expressions.forEach((expression) => {
            expression.analyze(context);
            if (!(expression.type === Type.BOOL)) {
                throw new Error("Type Error: If statement conditional must be a bool.");
            }
        });

        this.blocks.forEach((block) => {
            block.analyze(context);
        });
    }
    toString() {
        let ifString = `(ifStatement if ${this.exp} ${this.block})`;
        for (const exps in this.exp2) {
            ifString += `\n (else if ${this.exp2[exps]})`;
        }
        for (const blocks in this.elseBlock) {
            ifString += `\n (${this.elseBlock[blocks]})`;
        }
        ifString += `\n (else ${this.finalBlock})`;
        return ifString;
    }
}

class Decl extends Stmt {
    constructor(declaredType, id, exp) {
        super();
        this.declaredType = declaredType;
        this.id = id;
        this.exp = exp;
    }

    analyze(context) {
        if (this.declaredType !== this.exp.type) {
            throw new Error("Declared type does not match the evaluated type.");
        }
        context.checkIfVariableIsAlreadyDeclared(this.id);
        context.addVariable(this.id, this.exp);
        this.exp.analyze(context);
        this.declaredType.analyze(context);
        this.id.analyze(context);
    }

    toString() {
        const declString = `(Decl let ${this.type} ${this.id} = ${this.exp})`;
        return declString;
    }
}

class FuncDec extends Stmt {
    constructor(id, params, returnType, body) {
        super();
        this.id = id;
        this.params = params;
        this.body = body;
        this.returnType = returnType;
    }

    analyze(context) {
        this.id.analyze(context);
        context.checkIfVariableIsAlreadyDeclared(this.id);

        const localContext = context.createChildContextForFunctionBody(this);
        this.params.forEach((param) => {
            // duplicate parameter check
            localContext.checkIfVariableIsAlreadyDeclared(param.id);
            param.analyze(localContext);
            localContext.addVariable(param.id, param);
        });

        this.returnType.analyze(context);
        this.body.analyze(localContext);
        this.type = this.body.type;
    }

    toString() {
        const funcDecString = `FuncDec let fun ${this.id} = ${this.params} => ${this.returnType} : ${this.body}`;
        return funcDecString;
    }
}

class Print extends Stmt {
    constructor(argument) {
        super();
        this.argument = argument;
    }

    toString() {
        return `(Print (${this.argument}))`;
    }
}

class Return extends Stmt {
    constructor(argument) {
        super();
        this.argument = argument;
    }

    toString() {
        return `(Return ${this.argument})`;
    }
}

class Addop {
    constructor(op) {
        this.op = op;
    }

    toString() {
        return `(addop ${this.op})`;
    }
}

class Relop {
    constructor(op) {
        this.op = op;
    }

    toString() {
        return `(relop ${this.op})`;
    }
}

class Mullop {
    constructor(op) {
        this.op = op;
    }

    toString() {
        return `(mullop ${this.op})`;
    }
}

class Expop {
    constructor(op) {
        this.op = op;
    }

    toString() {
        return `(expop ${this.op})`;
    }
}

class Binop {
    constructor(op) {
        this.op = op;
    }

    toString() {
        return `(binop ${this.op})`;
    }
}

class FuncCall extends Stmt {
    constructor(id, args) {
        super();
        this.id = id;
        this.args = args;
    }

    toString() {
        return `(funcCall ${this.id} ($this.args))`;
    }
}

class Args {
    constructor(args) {
        this.args = args;
    }

    toString() {
        return `(Args ${this.args})`;
    }
}

class Arg {
    constructor(id) {
        this.id = id;
    }

    toString() {
      return `(Arg ${this.id})`;
    }

    gen() {
        return this.expression.gen();
    }
 }

class Params {
    constructor(params) {
        this.params = params;
    }

    toString() {
        let paramsString = "Params (";
        for (const params in this.params) {
            paramsString += `, ${params}`;
        }

        paramsString += ")";
        return paramsString;
    }
}

class Param {
    constructor(id) {
        this.id = id;
    }

    toString() {
        const paramString = `Param ${this.id}`;
        return paramString;
    }
}

class Body {
    constructor(block) {
        this.block = block;
    }

    toString() {
        const bodyString = `(Body :${this.block};;)`;
        return bodyString;
    }
}

class ExpBinary {
    constructor(op, exp, matchexp) {
        this.op = op;
        this.exp = exp;
        this.matchexp = matchexp;
    }

    toString() {
        return `(Exp_binary ${this.exp} ${this.op} ${this.matchexp})`;
    }
}

class ExpTernary {
    constructor(op, matchexp1, matchexp2, matchexp3) {
        this.matchexp1 = matchexp1;
        this.matchexp2 = matchexp2;
        this.matchexp3 = matchexp3;
    }

    toString() {
        return `(Exp_ternary ${this.matchexp1} ? ${this.matchexp2} : ${this.matchexp3})`;
    }
}

class MatchExp {
    constructor(id, matches) {
        this.id = id;
        this.matches = matches;
    }

    toString() {
        return `(MatchExp match ${this.id} with ${this.matches})`;
    }
}

class BinExp {
    constructor(op, binexp, addexp) {
        this.op = op;
        this.binexp = binexp;
        this.addexp = addexp;
    }

    analyze(context) {
        this.binexp.analyze(context);
        this.addexp.analyze(context);
        this.type = Type.INT;
    }

    toString() {
        return `(BinExp ${this.binexp} ${this.op} ${this.addexp})`;
    }
}

class AddExp {
    constructor(op, addexp, mullexp) {
        this.op = op;
        this.addexp = addexp;
        this.mullexp = mullexp;
    }

    toString() {
        return `(AddExp ${this.addexp} ${this.op} ${this.mullexp})`;
    }
}

class MullExp {
    constructor(op, mullexp, prefixexp) {
        this.op = op;
        this.mullexp = mullexp;
        this.prefixexp = prefixexp;
    }

    toString() {
        return `(Mullexp ${this.mullexp} ${this.op} ${this.prefixexp})`;
    }
}

class PrefixExp {
    constructor(op, expoexp) {
        this.op = op;
        this.expoexp = expoexp;
    }

    toString() {
        return `(Prefixexp ${this.op} ${this.expoexp})`;
    }
}

class ExpoExp {
    constructor(op, parenexp, expoexp) {
        this.op = op;
        this.parenexp = parenexp;
        this.expoexp = expoexp;
    }

    // analyze() {
    //     //  parenexp and expoexp must be the same type (float and int)
            // from there I can determine the ExpoExp type
    // }

    toString() {
        return `(Expoexp ${this.Parenexp} ${this.op} ${this.Expoexp})`;
    }
}

class ParenExp {
    constructor(parenexp) {
        this.parenexp = parenexp;
        this.type = this.parenexp.type;
    }

    toString() {
        return `(Parenexp (${this.parenexp}))`;
    }
}

class Matches {
    constructor(exp1, exp2) {
        this.exp1 = exp1;
        this.exp2 = exp2;
    }

    toString() {
        return `(Matches | ${this.exp1} -> ${this.exp2})`;
    }
}

class Tuplit {
    constructor(exp1, exp2) {
        this.exp1 = exp1;
        this.exp2 = exp2;
    }

    toString() {
        return `(Tuplit (${this.exp1}, ${this.exp2}))`;
    }
}

class List {
    constructor(arg, args) {
        this.arg = arg;
        this.args = args;
    }

  toString() {
    return `(List ${this.arg} ${this.args})`;
  }
}

class Numlit {
    constructor(value) {
      this.value = value;
    }

    toString() {
        let numberString = "(numlit ";
            for (const numbers in this.value) {
                numberString += `${this.value[numbers]}`;
            }
            numberString += ")";
        return numberString;
    }

    gen() {
        return `${this.value}`;
    }
}

class Charlit {
    constructor(value) {
        this.value = value;
    }
    toString() {
        return `(Charlit ${this.value})`;
    }
}

class Stringlit {
    constructor(value) {
        this.value = value;
    }
    toString() {
        return `(stringlit ${this.value})`;
    }
}

/* eslint-disable no-unused-vars */
const semantics = JCamlGrammar.createSemantics().addOperation("tree", {
    Program(block) { return new Program(block.tree()); },
    Block(stmt) { return new Block(stmt.tree()); },
    Stmt_if(_1, exp, block, _2, elseIfExprs, elseIfBlocks, _3, finalBlock) {
        return new StatementIfElse(exp.tree(), block.tree(), elseIfExprs.tree(), finalBlock.tree());
    },
    Decl_decl(_1, type, id, _2, exp) { return new Decl(type.tree(), id.sourceString, exp.tree()); },
    Print_print(_1, _2, binexp, _3) { return new Print(binexp.tree()); },
    Return(_1, arg) { return new Return(arg.tree()); },
    addop(op) { return new Addop(this.sourceString); },
    relop(op) { return new Relop(this.sourceString); },
    mullop(op) { return new Mullop(this.sourceString); },
    expop(op) { return new Expop(this.sourceString); },
    binop(op) { return new Binop(this.sourceString); },
    FuncDec(_1, id, _2, params, _3, type, body) {
      return new FuncDec(id.sourceString, params.tree(), type.tree(), body.tree());
    },
    FuncCall(id, _1, args, _2) { return new FuncCall(id.sourceString, args.tree()); },
    Args(arg) { return new Args(arg.tree()); },
    Arg(id) { return new Arg(id.sourceString); },
    Params(_1, firstParam, _2, moreParams, _3) {
      return new Params([firstParam.tree()].concat(moreParams.tree()));
    },
    Param(id) { return new Param(id.sourceString); },
    Type(type) { return new Type(type.sourceString); },
    Body(_1, block, _2) { return new Body(block.tree()); },
    Exp_binary(exp, op, matchexp) { return new ExpBinary(op.tree(), exp.tree(), matchexp.tree()); },
    Exp_ternary(matchexp, _1, matchexp2, _2, matchexp3) {
      return new ExpTernary(matchexp.tree(), matchexp2.tree(), matchexp3.tree());
    },
    BinExp_binary(binexp, op, addexp) {
        return new BinExp(op.tree(), binexp.tree(), addexp.tree());
    },
    MatchExp_matchexp(_1, id, _2, matches) {
        return new MatchExp(id.sourceString, matches.tree());
    },
    AddExp_binary(addexp, op, mullexp) {
      return new AddExp(op.tree(), addexp.tree(), mullexp.tree());
    },
    MullExp_binary(mullexp, op, prefixexp) {
      return new MullExp(op.tree(), mullexp.tree(), prefixexp.tree());
    },
    PrefixExp_binary(op, expoexp) { return new PrefixExp(op.tree(), expoexp.tree()); },
    ExpoExp_binary(parenexp, op, expoexp) {
      return new ExpoExp(op.tree(), parenexp.tree(), expoexp.tree());
    },
    ParenExp_parens(_1, parenexp, _2) { return new ParenExp(parenexp.tree()); },
    Matches(_1, exp1, _2, exp2) { return new Matches(exp1.tree(), exp2.tree()); },
    Tuplit(_1, exp1, _2, exp2, _3) { return new Tuplit(exp1.tree(), exp2.tree()); },
    List_list(_1, _2, _3, _4, args) { return new List(this.sourceString); },
    List(_1) { return new List(this.sourceString); },
    numlit(value) { return new Numlit(this.sourceString); },
    charlit(_1, value, _2) { return new Charlit(this.sourceString); },
    stringlit(_1, value, _2) { return new Stringlit(this.sourceString); },
});
/* eslint-enable no-unused-vars */

function parse(text) {
    const match = JCamlGrammar.match(text);
    return semantics(match).tree();
}
module.exports = parse;
<|endoftext|>


<commit_before>
var RE_COMMENT_START = /^\s*\/\*\*\s*$/m;
var RE_COMMENT_LINE  = /^\s*\*(?:\s(\s*)|$)/m;
var RE_COMMENT_END   = /^\s*\*\/\s*$/m;
var RE_COMMENT_1LINE = /^\s*\/\*\*\s*(.*)\s*\*\/\s*$/;

/* ------- util functions ------- */

function merge(/* ...objects */) {
  var k, obj, res = {}, objs = Array.prototype.slice.call(arguments);
  while (objs.length) {
    obj = objs.shift();
    for (k in obj) { if (obj.hasOwnProperty(k)) {
      res[k] = obj[k];
    }}
  }
  return res;
}

function find(list, filter) {
  var k, i = list.length, matchs = true;
  while (i--) {
    for (k in filter) { if (filter.hasOwnProperty(k)) {
        matchs = (filter[k] === list[i][k]) && matchs;
    }}
    if (matchs) { return list[i]; }
  }
  return null;
}

function skipws(str) {
  var i = 0;
  do {
    if (str[i] !== ' ') { return i; }
  } while (++i < str.length);
  return i;
}

/* ------- default parsers ------- */

var PARSERS = {};

PARSERS.parse_tag = function parse_tag(str) {
  var result = str.match(/^\s*@(\S+)/);

  if (!result) { throw new Error('Invalid `@tag`, missing @ symbol'); }

  return {
    source : result[0],
    data   : {tag: result[1]}
  };
};

PARSERS.parse_type = function parse_type(str, data) {
  if (data.errors && data.errors.length) { return null; }

  var pos = skipws(str);
  var res = '';
  var curlies = 0;

  if (str[pos] !== '{') { return null; }

  while (pos < str.length) {
    curlies += (str[pos] === '{' ? 1 : (str[pos] === '}' ? -1 : 0));
    res += str[pos];
    pos ++;
    if (curlies === 0) { break; }
  }

  if (curlies !== 0) { throw new Error('Invalid `{type}`, unpaired curlies'); }

  return {
    source : str.slice(0, pos),
    data   : {type: res.slice(1, -1)}
  };
};

PARSERS.parse_name = function parse_name(str, data) {
  if (data.errors && data.errors.length) { return null; }

  var pos      = skipws(str);
  var name     = '';
  var brackets = 0;

  while (pos < str.length) {
    brackets += (str[pos] === '[' ? 1 : (str[pos] === ']' ? -1 : 0));
    name += str[pos];
    pos ++;
    if (brackets === 0 && /\s/.test(str[pos])) { break; }
  }

  if (brackets !== 0) { throw new Error('Invalid `name`, unpaired brackets'); }

  var res = {name: name, optional: false};

  if (name[0] === '[' && name[name.length - 1] === ']') {
    res.optional = true;
    name = name.slice(1, -1);

    if (name.indexOf('=') !== -1) {
      var parts = name.split('=');
      name = parts[0];
      res.default = parts[1].replace(/^(["'])(.+)(\1)$/, '$2');
    }
  }

  res.name = name;

  return {
    source : str.slice(0, pos),
    data   : res
  };
};

PARSERS.parse_description = function parse_description(str, data) {
  if (data.errors && data.errors.length) { return null; }

  var result = str.match(/^\s+([^$]+)?/);

  if (result) {
    return {
      source : result[0],
      data   : {description: result[1] === undefined ? '' : result[1]}
    };
  }

  return null;
};

/* ------- parsing ------- */

/**
 * Parses "@tag {type} name description"
 * @param {string} str Raw doc string
 * @param {Array[function]} parsers Array of parsers to be applied to the source
 * @returns {object} parsed tag node
 */
function parse_tag(str, parsers) {
  if (typeof str !== 'string' || str[0] !== '@') { return null; }

  var data = parsers.reduce(function(state, parser) {
    var result;

    try {
      result = parser(state.source, merge({}, state.data));
    } catch (err) {
      state.data.errors = (state.data.errors || [])
        .concat(parser.name + ': ' + err.message);
    }

    if (result) {
      state.source = state.source.slice(result.source.length);
      state.data   = merge(state.data, result.data);
    }

    return state;
  }, {
    source : str,
    data   : {}
  }).data;

  data.optional    = !!data.optional;
  data.type        = data.type === undefined        ? '' : data.type;
  data.name        = data.name === undefined        ? '' : data.name;
  data.description = data.description === undefined ? '' : data.description;

  return data;
}

/**
 * Parses comment block (array of String lines)
 */
function parse_block(source, opts) {

  function trim(s) {
    return opts.trim ? s.trim() : s;
  }

  var source_str = source
      .map(function(line) { return trim(line.source); })
      .join('\n');

  source_str = trim(source_str);

  var start = source[0].number;

  // merge source lines into tags
  // we assume tag starts with "@"
  source = source
    .reduce(function(tags, line) {
      line.source = trim(line.source);

      if (line.source.match(/^\s*@(\w+)/)) {
        tags.push({source: [line.source], line: line.number});
      } else {
        var tag = tags[tags.length - 1];
        tag.source.push(line.source);
      }

      return tags;
    }, [{source: []}])
    .map(function(tag) {
      tag.source = trim(tag.source.join('\n'));
      return tag;
    });

  // Block description
  var description = source.shift();

  // skip if no descriptions and no tags
  if (description.source === '' && source.length === 0) {
    return null;
  }

  var tags = source.reduce(function(tags, tag) {
    var tag_node = parse_tag(tag.source, opts.parsers);

    if (!tag_node) { return tags; }

    tag_node.line   = tag.line;
    tag_node.source = tag.source;

    if (opts.dotted_names && tag_node.name.indexOf('.') !== -1) {
      var parent_name;
      var parent_tag;
      var parent_tags = tags;
      var parts = tag_node.name.split('.');

      while (parts.length > 1) {
        parent_name = parts.shift();
        parent_tag  = find(parent_tags, {
          tag  : tag_node.tag,
          name : parent_name
        });

        if (!parent_tag) {
          parent_tag = {
            tag         : tag_node.tag,
            line        : Number(tag_node.line),
            name        : parent_name,
            type        : '',
            description : ''
          };
          parent_tags.push(parent_tag);
        }

        parent_tag.tags = parent_tag.tags || [];
        parent_tags = parent_tag.tags;
      }

      tag_node.name = parts[0];
      parent_tags.push(tag_node);
      return tags;
    }

    return tags.concat(tag_node);
  }, []);

  return {
    tags        : tags,
    line        : start,
    description : description.source,
    source      : source_str
  };
}

/**
 * Produces `extract` function with internal state initialized
 */
function mkextract(opts) {
  var chunk = null;
  var number = 0;

  opts = merge({}, {
    trim         : true,
    dotted_names : false,
    parsers      : [
      PARSERS.parse_tag,
      PARSERS.parse_type,
      PARSERS.parse_name,
      PARSERS.parse_description
    ]
  }, opts || {});

  /**
   * Cumulatively reading lines until they make one comment block
   * Returns block object or null.
   */
  return function extract(line) {

    // if oneliner
    // then parse it immediately
    if (line.match(RE_COMMENT_1LINE)) {
      return parse_block([{
        source: line.replace(RE_COMMENT_1LINE, '$1'),
        number: number}], opts);
    }

    number += 1;

    // if start of comment
    // then init the chunk
    if (line.match(RE_COMMENT_START)) {
      chunk = [{source: line.replace(RE_COMMENT_START, ''), number: number - 1}];
      return null;
    }

    // if comment line and chunk started
    // then append
    if (chunk && line.match(RE_COMMENT_LINE)) {
      chunk.push({
        number: number - 1,
        source: line.replace(RE_COMMENT_LINE, opts.trim ? '' : '$1')
      });
      return null;
    }

    // if comment end and chunk started
    // then parse the chunk and push
    if (chunk && line.match(RE_COMMENT_END)) {
      chunk.push({source: line.replace(RE_COMMENT_END, ''), number: number - 1});
      return parse_block(chunk, opts);
    }

    // if non-comment line
    // then reset the chunk
    chunk = null;
  };
}

/* ------- Public API ------- */

module.exports = function parse(source, opts) {
  var block;
  var blocks  = [];
  var extract = mkextract(opts);
  var lines   = source.split(/\n/);

  while (lines.length) {
    block = extract(lines.shift());
    if (block) {
      blocks.push(block);
    }
  }

  return blocks;
};

module.exports.PARSERS = PARSERS;
module.exports.mkextract = mkextract;<commit_msg>Fix parser regex for $ character<commit_after>
var RE_COMMENT_START = /^\s*\/\*\*\s*$/m;
var RE_COMMENT_LINE  = /^\s*\*(?:\s(\s*)|$)/m;
var RE_COMMENT_END   = /^\s*\*\/\s*$/m;
var RE_COMMENT_1LINE = /^\s*\/\*\*\s*(.*)\s*\*\/\s*$/;

/* ------- util functions ------- */

function merge(/* ...objects */) {
  var k, obj, res = {}, objs = Array.prototype.slice.call(arguments);
  while (objs.length) {
    obj = objs.shift();
    for (k in obj) { if (obj.hasOwnProperty(k)) {
      res[k] = obj[k];
    }}
  }
  return res;
}

function find(list, filter) {
  var k, i = list.length, matchs = true;
  while (i--) {
    for (k in filter) { if (filter.hasOwnProperty(k)) {
        matchs = (filter[k] === list[i][k]) && matchs;
    }}
    if (matchs) { return list[i]; }
  }
  return null;
}

function skipws(str) {
  var i = 0;
  do {
    if (str[i] !== ' ') { return i; }
  } while (++i < str.length);
  return i;
}

/* ------- default parsers ------- */

var PARSERS = {};

PARSERS.parse_tag = function parse_tag(str) {
  var result = str.match(/^\s*@(\S+)/);

  if (!result) { throw new Error('Invalid `@tag`, missing @ symbol'); }

  return {
    source : result[0],
    data   : {tag: result[1]}
  };
};

PARSERS.parse_type = function parse_type(str, data) {
  if (data.errors && data.errors.length) { return null; }

  var pos = skipws(str);
  var res = '';
  var curlies = 0;

  if (str[pos] !== '{') { return null; }

  while (pos < str.length) {
    curlies += (str[pos] === '{' ? 1 : (str[pos] === '}' ? -1 : 0));
    res += str[pos];
    pos ++;
    if (curlies === 0) { break; }
  }

  if (curlies !== 0) { throw new Error('Invalid `{type}`, unpaired curlies'); }

  return {
    source : str.slice(0, pos),
    data   : {type: res.slice(1, -1)}
  };
};

PARSERS.parse_name = function parse_name(str, data) {
  if (data.errors && data.errors.length) { return null; }

  var pos      = skipws(str);
  var name     = '';
  var brackets = 0;

  while (pos < str.length) {
    brackets += (str[pos] === '[' ? 1 : (str[pos] === ']' ? -1 : 0));
    name += str[pos];
    pos ++;
    if (brackets === 0 && /\s/.test(str[pos])) { break; }
  }

  if (brackets !== 0) { throw new Error('Invalid `name`, unpaired brackets'); }

  var res = {name: name, optional: false};

  if (name[0] === '[' && name[name.length - 1] === ']') {
    res.optional = true;
    name = name.slice(1, -1);

    if (name.indexOf('=') !== -1) {
      var parts = name.split('=');
      name = parts[0];
      res.default = parts[1].replace(/^(["'])(.+)(\1)$/, '$2');
    }
  }

  res.name = name;

  return {
    source : str.slice(0, pos),
    data   : res
  };
};

PARSERS.parse_description = function parse_description(str, data) {
  if (data.errors && data.errors.length) { return null; }

  var result = str.match(/^\s+((.|\s)+)?/);

  if (result) {
    return {
      source : result[0],
      data   : {description: result[1] === undefined ? '' : result[1]}
    };
  }

  return null;
};

/* ------- parsing ------- */

/**
 * Parses "@tag {type} name description"
 * @param {string} str Raw doc string
 * @param {Array[function]} parsers Array of parsers to be applied to the source
 * @returns {object} parsed tag node
 */
function parse_tag(str, parsers) {
  if (typeof str !== 'string' || str[0] !== '@') { return null; }

  var data = parsers.reduce(function(state, parser) {
    var result;

    try {
      result = parser(state.source, merge({}, state.data));
    } catch (err) {
      state.data.errors = (state.data.errors || [])
        .concat(parser.name + ': ' + err.message);
    }

    if (result) {
      state.source = state.source.slice(result.source.length);
      state.data   = merge(state.data, result.data);
    }

    return state;
  }, {
    source : str,
    data   : {}
  }).data;

  data.optional    = !!data.optional;
  data.type        = data.type === undefined        ? '' : data.type;
  data.name        = data.name === undefined        ? '' : data.name;
  data.description = data.description === undefined ? '' : data.description;

  return data;
}

/**
 * Parses comment block (array of String lines)
 */
function parse_block(source, opts) {

  function trim(s) {
    return opts.trim ? s.trim() : s;
  }

  var source_str = source
      .map(function(line) { return trim(line.source); })
      .join('\n');

  source_str = trim(source_str);

  var start = source[0].number;

  // merge source lines into tags
  // we assume tag starts with "@"
  source = source
    .reduce(function(tags, line) {
      line.source = trim(line.source);

      if (line.source.match(/^\s*@(\w+)/)) {
        tags.push({source: [line.source], line: line.number});
      } else {
        var tag = tags[tags.length - 1];
        tag.source.push(line.source);
      }

      return tags;
    }, [{source: []}])
    .map(function(tag) {
      tag.source = trim(tag.source.join('\n'));
      return tag;
    });

  // Block description
  var description = source.shift();

  // skip if no descriptions and no tags
  if (description.source === '' && source.length === 0) {
    return null;
  }

  var tags = source.reduce(function(tags, tag) {
    var tag_node = parse_tag(tag.source, opts.parsers);

    if (!tag_node) { return tags; }

    tag_node.line   = tag.line;
    tag_node.source = tag.source;

    if (opts.dotted_names && tag_node.name.indexOf('.') !== -1) {
      var parent_name;
      var parent_tag;
      var parent_tags = tags;
      var parts = tag_node.name.split('.');

      while (parts.length > 1) {
        parent_name = parts.shift();
        parent_tag  = find(parent_tags, {
          tag  : tag_node.tag,
          name : parent_name
        });

        if (!parent_tag) {
          parent_tag = {
            tag         : tag_node.tag,
            line        : Number(tag_node.line),
            name        : parent_name,
            type        : '',
            description : ''
          };
          parent_tags.push(parent_tag);
        }

        parent_tag.tags = parent_tag.tags || [];
        parent_tags = parent_tag.tags;
      }

      tag_node.name = parts[0];
      parent_tags.push(tag_node);
      return tags;
    }

    return tags.concat(tag_node);
  }, []);

  return {
    tags        : tags,
    line        : start,
    description : description.source,
    source      : source_str
  };
}

/**
 * Produces `extract` function with internal state initialized
 */
function mkextract(opts) {
  var chunk = null;
  var number = 0;

  opts = merge({}, {
    trim         : true,
    dotted_names : false,
    parsers      : [
      PARSERS.parse_tag,
      PARSERS.parse_type,
      PARSERS.parse_name,
      PARSERS.parse_description
    ]
  }, opts || {});

  /**
   * Cumulatively reading lines until they make one comment block
   * Returns block object or null.
   */
  return function extract(line) {

    // if oneliner
    // then parse it immediately
    if (line.match(RE_COMMENT_1LINE)) {
      return parse_block([{
        source: line.replace(RE_COMMENT_1LINE, '$1'),
        number: number}], opts);
    }

    number += 1;

    // if start of comment
    // then init the chunk
    if (line.match(RE_COMMENT_START)) {
      chunk = [{source: line.replace(RE_COMMENT_START, ''), number: number - 1}];
      return null;
    }

    // if comment line and chunk started
    // then append
    if (chunk && line.match(RE_COMMENT_LINE)) {
      chunk.push({
        number: number - 1,
        source: line.replace(RE_COMMENT_LINE, opts.trim ? '' : '$1')
      });
      return null;
    }

    // if comment end and chunk started
    // then parse the chunk and push
    if (chunk && line.match(RE_COMMENT_END)) {
      chunk.push({source: line.replace(RE_COMMENT_END, ''), number: number - 1});
      return parse_block(chunk, opts);
    }

    // if non-comment line
    // then reset the chunk
    chunk = null;
  };
}

/* ------- Public API ------- */

module.exports = function parse(source, opts) {
  var block;
  var blocks  = [];
  var extract = mkextract(opts);
  var lines   = source.split(/\n/);

  while (lines.length) {
    block = extract(lines.shift());
    if (block) {
      blocks.push(block);
    }
  }

  return blocks;
};

module.exports.PARSERS = PARSERS;
module.exports.mkextract = mkextract;
<|endoftext|>


<commit_before>//= require ./array

var
    Iterable;

(function($) {
    "use strict";

    var
        areEquivalents = function(object1, object2) {
            var
                attributesCount,
                objectAttributes,
                comparatorAttributes,
                objectMethods,
                comparatorMethods,
                objectAttributeValue,
                comparatorAttributeValue,
                iterator,
                count = 0,
                primitiveTypes =
                    /(number|string|boolean|null|undefined|symbol)/;

            if (object1 == object2) {
                return true;
            } else if ($.hasSameClass(object1, object2)) {
                if (object1 instanceof $ && !(object1 instanceof Function)) {
                    if (object1 instanceof window.Node) {
                        if (object1.isEqualNode(object2)) {
                            return true;
                        }
                    } else {
                        objectAttributes = $.attributes(object1);
                        comparatorAttributes = $.attributes(object2);
                        objectMethods = $.methods(object1);
                        comparatorMethods = $.methods(object2);

                        // if doesn't have attributes
                        if (!objectAttributes.length) {
                            if (objectMethods.length) {
                                if (objectMethods.length == comparatorMethods
                                        .length &&
                                        $.areEquivalents(objectMethods,
                                            comparatorMethods)) {
                                    return true;
                                }
                            } else if (objectMethods.length ==
                                       comparatorMethods.length) {
                                return true;
                            }
                        }

                        if (objectAttributes.length ==
                            comparatorAttributes.length) {

                            iterator = Iterable.Proxy.new(objectAttributes);

                            iterator.each(function(attribute) {
                                objectAttributeValue = object1[attribute];
                                comparatorAttributeValue = object2[attribute];

                                if (typeof objectAttributeValue == "boolean" &&
                                    typeof comparatorAttributeValue ==
                                    "boolean") {
                                    objectAttributeValue = objectAttributeValue
                                        .toString();
                                    comparatorAttributeValue =
                                        comparatorAttributeValue.toString();
                                }

                                if (objectAttributeValue &&
                                    comparatorAttributeValue) {
                                    if ($.hasSameClass(
                                        objectAttributeValue,
                                        comparatorAttributeValue)) {
                                        if (objectAttributeValue instanceof $) {

                                            // recursivity
                                            if ($.areEquivalents(
                                                objectAttributeValue,
                                                comparatorAttributeValue)) {
                                                count += 1;
                                            }
                                        } else
                                            if (objectAttributeValue ==
                                            comparatorAttributeValue) {
                                            count += 1;
                                        }
                                    }

                                } else if (objectAttributeValue ==
                                           comparatorAttributeValue) {
                                    return true;
                                }
                            });

                            attributesCount = objectAttributes.length;

                            return count && count == attributesCount;
                        }
                    }
                } else {
                    if (primitiveTypes.test(typeof object1)) {
                        return object1 == object2;
                    }
                }
            }

            return false;
        };

    try {
        $.defineProperties($, {

            clone: {
                value: function(object, includeNonEnumerable) {
                    if (typeof includeNonEnumerable != "boolean") {
                        includeNonEnumerable = true;
                    }

                    var
                        clone = {},
                        properties = $.getOwnPropertyNames(object);

                    if (!includeNonEnumerable) {
                        return Object.assign(clone, object);
                    }

                    try{
                        properties.forEach(function(p) {
                            clone[p] = object[p];
                        });
                    } catch(e) {}

                    return clone;
                }
            },

            classForName: {
                value: function(name) {
                    return window[name];
                }
            },

            hasSameClass: {
                value: function(objects) {
                    var
                        argumentsAsArray = Array.from(arguments),
                        compared = objects,
                        count = 0;

                    argumentsAsArray = argumentsAsArray.slice(1,
                        argumentsAsArray.length);
                    argumentsAsArray.forEach(function(object) {
                        if ($.getPrototypeOf(compared) ==
                            $.getPrototypeOf(object)) {
                            count++;
                        }
                    });

                    return count == argumentsAsArray.length;
                }
            },

            isFromClass: {
                value: function(object, classFunction) {
                    return $.classForName($.className(object)) == classFunction;
                }
            },

            firstKey: {
                value: function(object) {
                    var
                        // Object: javascript literal object
                        keys = Object.keys(object);

                    return keys[0];
                }
            },

            lastKey: {
                value: function(object) {
                    var
                        keys = Object.keys(object);

                    return keys[keys.length - 1];
                }
            },

            normalizedAsLowerCase: {
                value: function(object) {
                    var
                        iterator,
                        attributes = Object.attributes(object),
                        clone = {};

                    Object.assign(clone, object);
                    object.length = attributes.length;
                    iterator = Iterable.Proxy.new(clone);

                    iterator.each(function(v, k) {
                        if (typeof v == "string") {
                            clone[k] = v.toLowerCase();
                        }
                    });

                    return clone;
                }
            },

            firstFromASlice: {
                value: function(object, slice, startingIndex, caseSensitive) {
                    var
                        iterator,
                        key,
                        regexp,
                        attributes = Object.attributes(object);

                    if (!startingIndex || typeof startingIndex != "number") {
                        if (typeof startingIndex == "boolean") {
                            caseSensitive = startingIndex;
                        } else if (!caseSensitive || typeof caseSensitive !=
                                   "boolean") {
                            caseSensitive = false;
                        }

                        startingIndex = 0;
                    }

                    slice = slice.trim();

                    if (!caseSensitive) {
                        regexp = new RegExp(slice, "i");
                    } else {
                        regexp = new RegExp(slice);
                    }

                    iterator = Iterable.Proxy.new(attributes);
                    iterator.each(function(k, i) {
                        if (!isNaN(i)) {
                            if (typeof object[k] == "string" && i
                                >= startingIndex) {
                                if (object[k].search(regexp) != -1) {
                                    key = k;
                                    this.finalize();
                                }
                            }
                        } else if (typeof object[k] == "string") {
                            if (object[k].search(regexp) != -1) {
                                key = k;
                                this.finalize();
                            }
                        }
                    });

                    if (key) {
                        return key;
                    }

                    return undefined;
                }
            },

            asCountableLiteralObject: {
                value: function(collection) {
                    return Iterable.parse(collection);
                }
            },

            // The keys of associative arrays in JS only can be of integer or string kind. The associations that keys are of Integer kind, the items is sorted according the keys (works as HashSet of the Java); the associations that keys are String kind, works as LinkedHashMap, it implements a associative array and maintains the order of entrance on iterate.
            // TreeMap implements a structure based red-black tree (binary search).
            indexOf: {
                value: function(object, value) {
                    var
                        key,
                        iterator,
                        properties = $.keys(object);

                    iterator = Iterable.Proxy.new(properties);
                    iterator.each(function(p) {
                        if ($.areEquivalents(object[p], value)) {
                            key = p;
                            this.finalize();
                        }
                    });

                    if (key) {
                        return key;
                    }

                    return undefined;
                }
            },

            count: {
                value: function(object, item) {
                    var
                        count;

                    if (item) {
                        count = $.values(object).count(item);
                    } else {
                        count = $.keys(object).length;
                    }

                    return count;
                }
            },

            some: {
                value: function(object) {
                    return $.count(object) > 0;
                }
            },

            isEmpty: {
                value: function(object) {
                    return !$.some(object);
                }
            },

            delete: {
                value: function(index, object) {
                    if (delete object[index]) {
                        if (object.hasOwnProperty("length")) {
                            object.length -= 1;
                        }

                        return true;
                    }

                    return false;
                }
            },

            reverse: {
                value: function(object) {
                    var
                        newObject = {};

                    $.keys(object).reverse().forEach(function(k) {
                        newObject[k] = object[k];
                    });

                    return newObject;
                }
            },

            attributes: {
                value: function(object, includeNonEnumerable) {
                    if (typeof includeNonEnumerable != "boolean") {
                        includeNonEnumerable = false;
                    }

                    var
                        clone,
                        properties,
                        attributes = [];

                    if (!object) {
                        return [];
                    }

                    clone = Object.clone(object, includeNonEnumerable);

                    if (typeof object != "object" &&
                        typeof object != "function" &&
                        Object.getPrototypeOf(object)) {
                        clone = Object.getPrototypeOf(object);
                    }

                    properties = $.keys(clone);

                    if (includeNonEnumerable) {
                        properties = $.getOwnPropertyNames(clone);
                    }

                    try {
                        properties.forEach(function(p) {
                            if (typeof object[p] != "function") {
                                attributes.push(p);
                            }
                        });
                    } catch(e) {}

                    return attributes;
                }
            },

            methods: {
                value: function(object, includeNonEnumerable) {
                    if (typeof includeNonEnumerable != "boolean") {
                        includeNonEnumerable = true;
                    }

                    var
                        clone,
                        properties;

                    // otherwise it will give a cascade error
                    if (!object) {
                        return [];
                    }

                    clone = Object.clone(object, includeNonEnumerable);

                    if (typeof object != "object" &&
                        typeof object != "function" &&
                        Object.getPrototypeOf(object)) {
                        clone = Object.getPrototypeOf(object);
                    }

                    properties = $.getOwnPropertyNames(clone);

                    if (!includeNonEnumerable) {
                        properties = $.keys(clone);
                    }

                    return properties.difference($.attributes(object, true));
                }
            },

            hasAttributes: {
                value: function(object, propertyNames) {
                    var
                        argumentsAsArray = Array.from(arguments);

                    propertyNames = argumentsAsArray.slice(1, argumentsAsArray
                                                           .length);

                    return $.attributes(object, true).includes(propertyNames);
                }
            },

            implementsMethods: {
                value: function(object, propertyNames) {
                    var
                        argumentsAsArray = Array.from(arguments);

                    propertyNames = argumentsAsArray.slice(1, argumentsAsArray
                                                           .length);

                    return $.methods(object, true).includes(propertyNames);
                }
            },

            className: {
                value: function(object) {
                    // /[^ ]+(?=\])/g

                    if (typeof object == "object" &&
                        $.implementsMethods(object, "toString")) {
                        // if implements, then calls it
                        return /[a-zA-Z_]+[^\]]+/g.exec(object.toString())[0]
                        .split(" ")[1];
                    }

                    return /[A-Z_]+[^\]]+/g.exec($.prototype.toString
                                                 .call(object))[0]
                                                 .replace(/ /g, "");
                }
            },

            areEquivalents: {
                value: areEquivalents
            }

        });
    } catch(e) {}

})(Object);
<commit_msg>adequations<commit_after>//= require ./array

var
    Iterable;

(function($) {
    "use strict";

    var
        areEquivalents = function(object1, object2) {
            var
                attributesCount,
                objectAttributes,
                comparatorAttributes,
                objectMethods,
                comparatorMethods,
                objectAttributeValue,
                comparatorAttributeValue,
                iterator,
                count = 0,
                primitiveTypes =
                    /(number|string|boolean|null|undefined|symbol)/;

            if (object1 == object2) {
                return true;
            } else if ($.hasSameClass(object1, object2)) {
                if (object1 instanceof $ && !(object1 instanceof Function)) {
                    if (object1 instanceof window.Node) {
                        if (object1.isEqualNode(object2)) {
                            return true;
                        }
                    } else {
                        objectAttributes = $.attributes(object1);
                        comparatorAttributes = $.attributes(object2);
                        objectMethods = $.methods(object1);
                        comparatorMethods = $.methods(object2);

                        // if doesn't have attributes
                        if (!objectAttributes.length) {
                            if (objectMethods.length) {
                                if (objectMethods.length == comparatorMethods
                                        .length &&
                                        $.areEquivalents(objectMethods,
                                            comparatorMethods)) {
                                    return true;
                                }
                            } else if (objectMethods.length ==
                                       comparatorMethods.length) {
                                return true;
                            }
                        }

                        if (objectAttributes.length ==
                            comparatorAttributes.length) {

                            iterator = Iterable.Proxy.new(objectAttributes);

                            iterator.each(function(attribute) {
                                objectAttributeValue = object1[attribute];
                                comparatorAttributeValue = object2[attribute];

                                if (typeof objectAttributeValue == "boolean" &&
                                    typeof comparatorAttributeValue ==
                                    "boolean") {
                                    objectAttributeValue = objectAttributeValue
                                        .toString();
                                    comparatorAttributeValue =
                                        comparatorAttributeValue.toString();
                                }

                                if (objectAttributeValue &&
                                    comparatorAttributeValue) {
                                    if ($.hasSameClass(objectAttributeValue,
                                        comparatorAttributeValue)) {
                                        if (objectAttributeValue instanceof $) {

                                            // recursivity
                                            if ($.areEquivalents(
                                                objectAttributeValue,
                                                comparatorAttributeValue)) {
                                                count += 1;
                                            }
                                        } else
                                            if (objectAttributeValue ==
                                            comparatorAttributeValue) {
                                            count += 1;
                                        }
                                    }

                                } else if (objectAttributeValue ==
                                           comparatorAttributeValue) {
                                    return true;
                                }
                            });

                            attributesCount = objectAttributes.length;

                            return count && count == attributesCount;
                        }
                    }
                } else {
                    if (primitiveTypes.test(typeof object1)) {
                        return object1 == object2;
                    }
                }
            }

            return false;
        };

    try {
        $.defineProperties($, {

            clone: {
                value: function(object, includeNonEnumerable) {
                    if (typeof includeNonEnumerable != "boolean") {
                        includeNonEnumerable = true;
                    }

                    var
                        clone = {},
                        properties = $.getOwnPropertyNames(object);

                    if (!includeNonEnumerable) {
                        return Object.assign(clone, object);
                    }

                    try{
                        properties.forEach(function(p) {
                            clone[p] = object[p];
                        });
                    } catch(e) {}

                    return clone;
                }
            },

            classForName: {
                value: function(name) {
                    return window[name];
                }
            },

            hasSameClass: {
                value: function(objects) {
                    var
                        argumentsAsArray = Array.from(arguments),
                        compared = objects,
                        count = 0;

                    argumentsAsArray = argumentsAsArray.slice(1,
                        argumentsAsArray.length);
                    argumentsAsArray.forEach(function(object) {
                        if ($.getPrototypeOf(compared) ==
                            $.getPrototypeOf(object)) {
                            count++;
                        }
                    });

                    return count == argumentsAsArray.length;
                }
            },

            isFromClass: {
                value: function(object, classFunction) {
                    return $.classForName($.className(object)) == classFunction;
                }
            },

            firstKey: {
                value: function(object) {
                    var
                        // Object: javascript literal object
                        keys = Object.keys(object);

                    return keys[0];
                }
            },

            lastKey: {
                value: function(object) {
                    var
                        keys = Object.keys(object);

                    return keys[keys.length - 1];
                }
            },

            normalizedAsLowerCase: {
                value: function(object) {
                    var
                        iterator,
                        attributes = Object.attributes(object),
                        clone = {};

                    Object.assign(clone, object);
                    object.length = attributes.length;
                    iterator = Iterable.Proxy.new(clone);

                    iterator.each(function(v, k) {
                        if (typeof v == "string") {
                            clone[k] = v.toLowerCase();
                        }
                    });

                    return clone;
                }
            },

            firstFromASlice: {
                value: function(object, slice, startingIndex, caseSensitive) {
                    var
                        iterator,
                        key,
                        regexp,
                        attributes = Object.attributes(object);

                    if (!startingIndex || typeof startingIndex != "number") {
                        if (typeof startingIndex == "boolean") {
                            caseSensitive = startingIndex;
                        } else if (!caseSensitive || typeof caseSensitive !=
                                   "boolean") {
                            caseSensitive = false;
                        }

                        startingIndex = 0;
                    }

                    slice = slice.trim();

                    if (!caseSensitive) {
                        regexp = new RegExp(slice, "i");
                    } else {
                        regexp = new RegExp(slice);
                    }

                    iterator = Iterable.Proxy.new(attributes);
                    iterator.each(function(k, i) {
                        if (!isNaN(i)) {
                            if (typeof object[k] == "string" && i
                                >= startingIndex) {
                                if (object[k].search(regexp) != -1) {
                                    key = k;
                                    this.finalize();
                                }
                            }
                        } else if (typeof object[k] == "string") {
                            if (object[k].search(regexp) != -1) {
                                key = k;
                                this.finalize();
                            }
                        }
                    });

                    if (key) {
                        return key;
                    }

                    return undefined;
                }
            },

            asCountableLiteralObject: {
                value: function(collection) {
                    return Iterable.parse(collection);
                }
            },

            // The keys of associative arrays in JS only can be of integer or string kind. The associations that keys are of Integer kind, the items is sorted according the keys (works as HashSet of the Java); the associations that keys are String kind, works as LinkedHashMap, it implements a associative array and maintains the order of entrance on iterate.
            // TreeMap implements a structure based red-black tree (binary search).
            indexOf: {
                value: function(object, value) {
                    var
                        key,
                        iterator,
                        properties = $.keys(object);

                    iterator = Iterable.Proxy.new(properties);
                    iterator.each(function(p) {
                        if ($.areEquivalents(object[p], value)) {
                            key = p;
                            this.finalize();
                        }
                    });

                    if (key) {
                        return key;
                    }

                    return undefined;
                }
            },

            count: {
                value: function(object, item) {
                    var
                        count;

                    if (item) {
                        count = $.values(object).count(item);
                    } else {
                        count = $.keys(object).length;
                    }

                    return count;
                }
            },

            some: {
                value: function(object) {
                    return $.count(object) > 0;
                }
            },

            isEmpty: {
                value: function(object) {
                    return !$.some(object);
                }
            },

            delete: {
                value: function(index, object) {
                    if (delete object[index]) {
                        if (object.hasOwnProperty("length")) {
                            object.length -= 1;
                        }

                        return true;
                    }

                    return false;
                }
            },

            reverse: {
                value: function(object) {
                    var
                        newObject = {};

                    $.keys(object).reverse().forEach(function(k) {
                        newObject[k] = object[k];
                    });

                    return newObject;
                }
            },

            attributes: {
                value: function(object, includeNonEnumerable) {
                    if (typeof includeNonEnumerable != "boolean") {
                        includeNonEnumerable = false;
                    }

                    var
                        clone,
                        properties,
                        attributes = [];

                    if (!object) {
                        return [];
                    }

                    clone = Object.clone(object, includeNonEnumerable);

                    if (typeof object != "object" &&
                        typeof object != "function" &&
                        Object.getPrototypeOf(object)) {
                        clone = Object.getPrototypeOf(object);
                    }

                    properties = $.keys(clone);

                    if (includeNonEnumerable) {
                        properties = $.getOwnPropertyNames(clone);
                    }

                    try {
                        properties.forEach(function(p) {
                            if (typeof object[p] != "function") {
                                attributes.push(p);
                            }
                        });
                    } catch(e) {}

                    return attributes;
                }
            },

            methods: {
                value: function(object, includeNonEnumerable) {
                    if (typeof includeNonEnumerable != "boolean") {
                        includeNonEnumerable = true;
                    }

                    var
                        clone,
                        properties;

                    // otherwise it will give a cascade error
                    if (!object) {
                        return [];
                    }

                    clone = Object.clone(object, includeNonEnumerable);

                    if (typeof object != "object" &&
                        typeof object != "function" &&
                        Object.getPrototypeOf(object)) {
                        clone = Object.getPrototypeOf(object);
                    }

                    properties = $.getOwnPropertyNames(clone);

                    if (!includeNonEnumerable) {
                        properties = $.keys(clone);
                    }

                    return properties.difference($.attributes(object, true));
                }
            },

            hasAttributes: {
                value: function(object, propertyNames) {
                    var
                        argumentsAsArray = Array.from(arguments);

                    propertyNames = argumentsAsArray.slice(1, argumentsAsArray
                                                           .length);

                    return $.attributes(object, true).includes(propertyNames);
                }
            },

            implementsMethods: {
                value: function(object, propertyNames) {
                    var
                        argumentsAsArray = Array.from(arguments);

                    propertyNames = argumentsAsArray.slice(1, argumentsAsArray
                                                           .length);

                    return $.methods(object, true).includes(propertyNames);
                }
            },

            className: {
                value: function(object) {
                    // /[^ ]+(?=\])/g

                    if (typeof object == "object" &&
                        $.implementsMethods(object, "toString")) {
                        // if implements, then calls it
                        return /[a-zA-Z_]+[^\]]+/g.exec(object.toString())[0]
                        .split(" ")[1];
                    }

                    return /[A-Z_]+[^\]]+/g.exec($.prototype.toString
                                                 .call(object))[0]
                                                 .replace(/ /g, "");
                }
            },

            areEquivalents: {
                value: areEquivalents
            }

        });
    } catch(e) {}

})(Object);
<|endoftext|>