File:  [LON-CAPA] / loncom / html / adm / LC_math_editor / src / definitions.js
Revision 1.2: download - view: text, annotated - select for diffs
Tue Feb 24 15:20:44 2015 UTC (9 years, 6 months ago) by damieng
Branches: MAIN
CVS tags: version_2_12_X, HEAD
updated to git version (see git for log history), and changed param separator to ,

/*

Copyright (C) 2014  Michigan State University Board of Trustees

The JavaScript code in this page is free software: you can
redistribute it and/or modify it under the terms of the GNU
General Public License (GNU GPL) as published by the Free Software
Foundation, either version 3 of the License, or (at your option)
any later version.  The code is distributed WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.

As additional permission under GNU GPL version 3 section 7, you
may distribute non-source (e.g., minimized or compacted) forms of
that code without the copy of the GNU GPL normally required by
section 4, provided you include this license notice and a URL
through which recipients can access the Corresponding Source.

*/

"use strict";

/**
 * Operator definitions (see function define() at the end).
 * @constructor
 */
function Definitions() {
    this.operators = [];  /* Array of Operator */
}

Definitions.ARG_SEPARATOR = ",";
Definitions.DECIMAL_SIGN_1 = ".";
Definitions.DECIMAL_SIGN_2 = ".";
Definitions.INTERVAL_SEPARATOR = ":";

/**
 * Creates a new operator.
 * @param {string} id - Operator id (text used to recognize it)
 * @param {number} arity - Operator.UNARY, BINARY or TERNARY
 * @param {number} lbp - Left binding power
 * @param {number} rbp - Right binding power
 * @param {function} nud - Null denotation function
 * @param {function} led - Left denotation function
 */
Definitions.prototype.operator = function(id, arity, lbp, rbp, nud, led) {
    this.operators.push(new Operator(id, arity, lbp, rbp, nud, led));
};

/**
 * Creates a new separator operator.
 * @param {string} id - Operator id (text used to recognize it)
 */
Definitions.prototype.separator = function(id) {
    this.operator(id, Operator.BINARY, 0, 0, null, null);
};

/**
 * Creates a new infix operator.
 * @param {string} id - Operator id (text used to recognize it)
 * @param {number} lbp - Left binding power
 * @param {number} rbp - Right binding power
 * @param {ledFunction} [led] - Left denotation function
 */
Definitions.prototype.infix = function(id, lbp, rbp, led) {
    var arity, nud;
    arity = Operator.BINARY;
    nud = null;
    led = led || function(p, left) {
        var children = [left, p.expression(rbp)];
        return new ENode(ENode.OPERATOR, this, id, children);
    };
    this.operator(id, arity, lbp, rbp, nud, led);
};

/**
 * Creates a new prefix operator.
 * @param {string} id - Operator id (text used to recognize it)
 * @param {number} rbp - Right binding power
 * @param {nudFunction} [nud] - Null denotation function
 */
Definitions.prototype.prefix = function(id, rbp, nud) {
    var arity, lbp, led;
    arity = Operator.UNARY;
    lbp = 0;
    nud = nud || function(p) {
        var children = [p.expression(rbp)];
        return new ENode(ENode.OPERATOR, this, id, children);
    };
    led = null;
    this.operator(id, arity, lbp, rbp, nud, led);
};

/**
 * Creates a new suffix operator.
 * @param {string} id - Operator id (text used to recognize it)
 * @param {number} lbp - Left binding power
 * @param {ledFunction} [led] - Left denotation function
 */
Definitions.prototype.suffix = function(id, lbp, led) {
    var arity, rbp, nud;
    arity = Operator.UNARY;
    rbp = 0;
    nud = null;
    led = led || function(p, left) {
        var children = [left];
        return new ENode(ENode.OPERATOR, this, id, children);
    };
    this.operator(id, arity, lbp, rbp, nud, led);
};

/**
 * Returns the defined operator with the given id
 * @param {string} id - Operator id (text used to recognize it)
 * @returns {Operator}
 */
Definitions.prototype.findOperator = function(id) {
    for (var i=0; i<this.operators.length; i++) {
        if (this.operators[i].id == id) {
            return(this.operators[i]);
        }
    }
    return null;
}

/**
 * Returns the ENode for the interval, parsing starting just before the interval separator
 * @param {boolean} closed - was the first operator closed ?
 * @param {ENode} e1 - First argument (already parsed)
 * @param {Operator} op - The operator
 * @param {Parser} p - The parser
 * @returns {ENode}
 */
Definitions.prototype.buildInterval = function(closed, e1, op, p) {
    p.advance(Definitions.INTERVAL_SEPARATOR);
    var e2 = p.expression(0);
    if (p.current_token == null || p.current_token.op == null ||
            (p.current_token.op.id !== ")" && p.current_token.op.id !== "]")) {
        throw new ParseException("Wrong interval syntax.", p.tokens[p.token_nr - 1].from);
    }
    var interval_type;
    if (p.current_token.op.id == ")") {
        p.advance(")");
        if (closed)
            interval_type = ENode.CLOSED_OPEN;
        else
            interval_type = ENode.OPEN_OPEN;
    } else {
        p.advance("]");
        if (closed)
            interval_type = ENode.CLOSED_CLOSED;
        else
            interval_type = ENode.OPEN_CLOSED;
    }
    return new ENode(ENode.INTERVAL, op, null, [e1, e2], interval_type);
}

/**
 * Defines all the operators.
 */
Definitions.prototype.define = function() {
    this.suffix("!", 160);
    this.infix("^", 140, 139);
    this.infix(".", 130, 129);
    this.infix("`", 125, 125, function(p, left) {
        // led (infix operator)
        // this led for units gathers all the units in an ENode
        var right = p.expression(125);
        while (p.current_token != null && "*/".indexOf(p.current_token.value) != -1) {
            var token2 = p.tokens[p.token_nr];
            if (token2 == null)
                break;
            if (token2.type != Token.NAME && token2.value != "(")
                break;
            var token3 = p.tokens[p.token_nr+1];
            if (token3 != null && (token3.value == "(" || token3.type == Token.NUMBER))
                break;
            if (p.unit_mode && p.tokens[p.token_nr].type == Token.NAME) {
                var nv = p.tokens[p.token_nr].value;
                var cst = false;
                for (var i=0; i<p.constants.length; i++) {
                    if (nv == p.constants[i]) {
                        cst = true;
                        break;
                    }
                }
                if (cst)
                    break;
            }
            var t = p.current_token;
            p.advance();
            right = t.op.led(p, right);
        }
        var children = [left, right];
        return new ENode(ENode.OPERATOR, this, "`", children);
    });
    this.infix("*", 120, 120);
    this.infix("/", 120, 120);
    this.infix("%", 120, 120);
    this.infix("+", 100, 100);
    this.operator("-", Operator.BINARY, 100, 134, function(p) {
        // nud (prefix operator)
        var children = [p.expression(134)];
        return new ENode(ENode.OPERATOR, this, "-", children);
    }, function(p, left) {
        // led (infix operator)
        var children = [left, p.expression(100)];
        return new ENode(ENode.OPERATOR, this, "-", children);
    });
    this.infix("=", 80, 80);
    this.infix("#", 80, 80);
    this.infix("<=", 80, 80);
    this.infix(">=", 80, 80);
    this.infix("<", 80, 80);
    this.infix(">", 80, 80);
    
    this.separator(")");
    this.separator(Definitions.ARG_SEPARATOR);
    this.separator(Definitions.INTERVAL_SEPARATOR);
    var defs = this;
    this.operator("(", Operator.BINARY, 200, 200, function(p) {
        // nud (for parenthesis and intervals)
        var e = p.expression(0);
        if (p.current_token != null && p.current_token.op != null &&
                p.current_token.op.id == Definitions.INTERVAL_SEPARATOR) {
            return defs.buildInterval(false, e, this, p);
        }
        p.advance(")");
        return e;
    }, function(p, left) {
        // led (for functions)
        if (left.type != ENode.NAME && left.type != ENode.SUBSCRIPT)
            throw new ParseException("Function name expected before a parenthesis.", p.tokens[p.token_nr - 1].from);
        var children = [left];
        if (p.current_token == null || p.current_token.op == null || p.current_token.op.id !== ")") {
            while (true) {
                children.push(p.expression(0));
                if (p.current_token == null || p.current_token.op == null || p.current_token.op.id !== Definitions.ARG_SEPARATOR) {
                    break;
                }
                p.advance(Definitions.ARG_SEPARATOR);
            }
        }
        p.advance(")");
        return new ENode(ENode.FUNCTION, this, "(", children);
    });
    
    this.separator("]");
    this.operator("[", Operator.BINARY, 200, 70, function(p) {
        // nud (for vectors and intervals)
        var children = [];
        if (p.current_token == null || p.current_token.op == null || p.current_token.op.id !== "]") {
            var e = p.expression(0);
            if (p.current_token != null && p.current_token.op != null &&
                    p.current_token.op.id == Definitions.INTERVAL_SEPARATOR) {
                return defs.buildInterval(true, e, this, p);
            }
            while (true) {
                children.push(e);
                if (p.current_token == null || p.current_token.op == null || p.current_token.op.id !== Definitions.ARG_SEPARATOR) {
                    break;
                }
                p.advance(Definitions.ARG_SEPARATOR);
                e = p.expression(0);
            }
        }
        p.advance("]");
        return new ENode(ENode.VECTOR, this, null, children);
    }, function(p, left) {
        // led (for subscript)
        if (left.type != ENode.NAME && left.type != ENode.SUBSCRIPT)
            throw new ParseException("Name expected before a square bracket.", p.tokens[p.token_nr - 1].from);
        var children = [left];
        if (p.current_token == null || p.current_token.op == null || p.current_token.op.id !== "]") {
            while (true) {
                children.push(p.expression(0));
                if (p.current_token == null || p.current_token.op == null || p.current_token.op.id !== Definitions.ARG_SEPARATOR) {
                    break;
                }
                p.advance(Definitions.ARG_SEPARATOR);
            }
        }
        p.advance("]");
        return new ENode(ENode.SUBSCRIPT, this, "[", children);
    });
    
    this.separator("}");
    this.prefix("{", 200, function(p) {
        // nud (for sets)
        var children = [];
        if (p.current_token == null || p.current_token.op == null || p.current_token.op.id !== "}") {
            while (true) {
                children.push(p.expression(0));
                if (p.current_token == null || p.current_token.op == null || p.current_token.op.id !== Definitions.ARG_SEPARATOR) {
                    break;
                }
                p.advance(Definitions.ARG_SEPARATOR);
            }
        }
        p.advance("}");
        return new ENode(ENode.SET, this, null, children);
    });
    this.prefix("$", 300, function(p) {
        // Perl variables
        var e = p.expression(300);
        if (e.type != ENode.NAME)
            throw new ParseException("Variable name expected after a $.", p.tokens[p.token_nr - 1].from);
        e.value = '$' + e.value;
        return e;
    });
};


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>