Annotation of loncom/homework/math_parser/Definitions.pm, revision 1.1

1.1     ! damieng     1: # The LearningOnline Network with CAPA - LON-CAPA
        !             2: # Operator definitions
        !             3: #
        !             4: # Copyright (C) 2014 Michigan State University Board of Trustees
        !             5: #
        !             6: # This program is free software: you can redistribute it and/or modify
        !             7: # it under the terms of the GNU General Public License as published by
        !             8: # the Free Software Foundation, either version 3 of the License, or
        !             9: # (at your option) any later version.
        !            10: #
        !            11: # This program is distributed in the hope that it will be useful,
        !            12: # but WITHOUT ANY WARRANTY; without even the implied warranty of
        !            13: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
        !            14: # GNU General Public License for more details.
        !            15: #
        !            16: # You should have received a copy of the GNU General Public License
        !            17: # along with this program. If not, see <http://www.gnu.org/licenses/>.
        !            18: #
        !            19: 
        !            20: ##
        !            21: # Operator definitions (see function define() at the end).
        !            22: ##
        !            23: package Apache::math_parser::Definitions;
        !            24: 
        !            25: use strict;
        !            26: use warnings;
        !            27: use utf8;
        !            28: 
        !            29: use JSON::DWIW;
        !            30: use File::Slurp;
        !            31: 
        !            32: use aliased 'Apache::math_parser::ENode';
        !            33: use aliased 'Apache::math_parser::Operator';
        !            34: use aliased 'Apache::math_parser::ParseException';
        !            35: use aliased 'Apache::math_parser::Parser';
        !            36: use aliased 'Apache::math_parser::Token';
        !            37: 
        !            38: use constant ARG_SEPARATOR => ","; # ";" would be more international
        !            39: use constant DECIMAL_SIGN_1 => ".";
        !            40: use constant DECIMAL_SIGN_2 => "."; # with "," here
        !            41: use constant INTERVAL_SEPARATOR => ":";
        !            42: 
        !            43: use vars qw(%perlvar);
        !            44: 
        !            45: 
        !            46: ##
        !            47: # Constructor
        !            48: ##
        !            49: sub new {
        !            50:     my $class = shift;
        !            51:     my $self = {
        !            52:         _operators => [], # Array of Operator
        !            53:     };
        !            54:     bless $self, $class;
        !            55:     return $self;
        !            56: }
        !            57: 
        !            58: # Attribute helpers
        !            59: 
        !            60: ##
        !            61: # The operators.
        !            62: # @returns {Operator[]}
        !            63: ##
        !            64: sub operators {
        !            65:     my $self = shift;
        !            66:     return $self->{_operators};
        !            67: }
        !            68: 
        !            69: 
        !            70: ##
        !            71: # Creates a new operator.
        !            72: # @param {string} id - Operator id (text used to recognize it)
        !            73: # @param {integer} arity - Operator->UNARY, BINARY or TERNARY
        !            74: # @param {integer} lbp - Left binding power
        !            75: # @param {integer} rbp - Right binding power
        !            76: # @param {function} nud - Null denotation function. Parameters: Operator, Parser. Returns: ENode.
        !            77: # @param {function} led - Left denotation function. Parameters: Operator, Parser, ENode. Returns: ENode.
        !            78: ##
        !            79: sub operator {
        !            80:     my( $self, $id, $arity, $lbp, $rbp, $nud, $led ) = @_;
        !            81:     push(@{$self->{_operators}}, Operator->new($id, $arity, $lbp, $rbp, $nud, $led));
        !            82: }
        !            83: 
        !            84: ##
        !            85: # Creates a new separator operator.
        !            86: # @param {string} id - Operator id (text used to recognize it)
        !            87: ##
        !            88: sub separator {
        !            89:     my( $self, $id ) = @_;
        !            90:     $self->operator($id, Operator->BINARY, 0, 0);
        !            91: }
        !            92: 
        !            93: ##
        !            94: # Creates a new infix operator.
        !            95: # @param {string} id - Operator id (text used to recognize it)
        !            96: # @param {integer} lbp - Left binding power
        !            97: # @param {integer} rbp - Right binding power
        !            98: # @optional {function} led - Left denotation function
        !            99: ##
        !           100: sub infix {
        !           101:     my( $self, $id, $lbp, $rbp, $led ) = @_;
        !           102:     my $arity = Operator->BINARY;
        !           103:     my $nud = undef;
        !           104:     if (!defined $led) {
        !           105:         $led = sub {
        !           106:             my( $op, $p, $left ) = @_;
        !           107:             my @children = ($left, $p->expression($rbp));
        !           108:             return ENode->new(ENode->OPERATOR, $op, $id, \@children);
        !           109:         }
        !           110:     }
        !           111:     $self->operator($id, $arity, $lbp, $rbp, $nud, $led);
        !           112: }
        !           113: 
        !           114: ##
        !           115: # Creates a new prefix operator.
        !           116: # @param {string} id - Operator id (text used to recognize it)
        !           117: # @param {integer} rbp - Right binding power
        !           118: # @optional {function} nud - Null denotation function
        !           119: ##
        !           120: sub prefix {
        !           121:     my( $self, $id, $rbp, $nud ) = @_;
        !           122:     my $arity = Operator->UNARY;
        !           123:     my $lbp = 0;
        !           124:     if (!defined $nud) {
        !           125:         $nud = sub {
        !           126:             my( $op, $p ) = @_;
        !           127:             my @children = ($p->expression($rbp));
        !           128:             return ENode->new(ENode->OPERATOR, $op, $id, \@children);
        !           129:         }
        !           130:     }
        !           131:     my $led = undef;
        !           132:     $self->operator($id, $arity, $lbp, $rbp, $nud, $led);
        !           133: }
        !           134: 
        !           135: ##
        !           136: # Creates a new suffix operator.
        !           137: # @param {string} id - Operator id (text used to recognize it)
        !           138: # @param {integer} lbp - Left binding power
        !           139: # @optional {function} led - Left denotation function
        !           140: ##
        !           141: sub suffix {
        !           142:     my( $self, $id, $lbp, $led ) = @_;
        !           143:     my $arity = Operator->UNARY;
        !           144:     my $rbp = 0;
        !           145:     my $nud = undef;
        !           146:     if (!defined $led) {
        !           147:         $led = sub {
        !           148:             my( $op, $p, $left ) = @_;
        !           149:             my @children = ($left);
        !           150:             return ENode->new(ENode->OPERATOR, $op, $id, \@children);
        !           151:         }
        !           152:     }
        !           153:     $self->operator($id, $arity, $lbp, $rbp, $nud, $led);
        !           154: }
        !           155: 
        !           156: ##
        !           157: # Returns the defined operator with the given id
        !           158: # @param {string} id - Operator id (text used to recognize it)
        !           159: # @returns {Operator}
        !           160: ##
        !           161: sub findOperator {
        !           162:     my( $self, $id ) = @_;
        !           163:     for (my $i=0; $i<scalar(@{$self->operators}); $i++) {
        !           164:         if (@{$self->operators}[$i]->id eq $id) {
        !           165:             return(@{$self->operators}[$i]);
        !           166:         }
        !           167:     }
        !           168:     return undef;
        !           169: }
        !           170: 
        !           171: ##
        !           172: # Led function for the ` (units) operator
        !           173: # @param {Operator} op
        !           174: # @param {Parser} p
        !           175: # @param {ENode} left
        !           176: # @returns {ENode}
        !           177: ##
        !           178: sub unitsLed {
        !           179:     my( $op, $p, $left ) = @_;
        !           180:     # this led for units gathers all the units in an ENode
        !           181:     my $right = $p->expression(125);
        !           182:     while (defined $p->current_token && index("*/", $p->current_token->value) != -1) {
        !           183:         my $token2 = $p->tokens->[$p->token_nr];
        !           184:         if (!defined $token2) {
        !           185:             last;
        !           186:         }
        !           187:         if ($token2->type != Token->NAME && $token2->value ne "(") {
        !           188:             last;
        !           189:         }
        !           190:         my $token3 = $p->tokens->[$p->token_nr + 1];
        !           191:         if (defined $token3 && ($token3->value eq "(" || $token3->type == Token->NUMBER)) {
        !           192:             last;
        !           193:         }
        !           194:         # a check for constant names here is not needed because constant names are replaced in the tokenizer
        !           195:         my $t = $p->current_token;
        !           196:         $p->advance();
        !           197:         $right = $t->op->led->($t->op, $p, $right);
        !           198:     }
        !           199:     my @children = ($left, $right);
        !           200:     return ENode->new(ENode->OPERATOR, $op, $op->id, \@children);
        !           201: }
        !           202: 
        !           203: ##
        !           204: # nud function for the ( operator (used to parse mathematical sub-expressions and intervals)
        !           205: # @param {Operator} op
        !           206: # @param {Parser} p
        !           207: # @returns {ENode}
        !           208: ##
        !           209: sub parenthesisNud {
        !           210:     my( $op, $p ) = @_;
        !           211:     my $e = $p->expression(0);
        !           212:     if (defined $p->current_token && defined $p->current_token->op &&
        !           213:             $p->current_token->op->id eq INTERVAL_SEPARATOR) {
        !           214:         return buildInterval(0, $e, $op, $p);
        !           215:     }
        !           216:     $p->advance(")");
        !           217:     return $e;
        !           218: }
        !           219: 
        !           220: ##
        !           221: # Led function for the ( operator (used to parse function calls)
        !           222: # @param {Operator} op
        !           223: # @param {Parser} p
        !           224: # @param {ENode} left
        !           225: # @returns {ENode}
        !           226: ##
        !           227: sub parenthesisLed {
        !           228:     my( $op, $p, $left ) = @_;
        !           229:     if ($left->type != ENode->NAME && $left->type != ENode->SUBSCRIPT) {
        !           230:         die ParseException->new("Function name expected before a parenthesis.", $p->tokens->[$p->token_nr - 1]->from);
        !           231:     }
        !           232:     my @children = ($left);
        !           233:     if ((!defined $p->current_token) || (!defined $p->current_token->op) || ($p->current_token->op->id ne ")")) {
        !           234:         while (1) {
        !           235:             push(@children, $p->expression(0));
        !           236:             if (!defined $p->current_token || !defined $p->current_token->op || $p->current_token->op->id ne ARG_SEPARATOR) {
        !           237:                 last;
        !           238:             }
        !           239:             $p->advance(ARG_SEPARATOR);
        !           240:         }
        !           241:     }
        !           242:     $p->advance(")");
        !           243:     return ENode->new(ENode->FUNCTION, $op, $op->id, \@children);
        !           244: }
        !           245: 
        !           246: ##
        !           247: # nud function for the [ operator (used to parse vectors and intervals)
        !           248: # @param {Operator} op
        !           249: # @param {Parser} p
        !           250: # @returns {ENode}
        !           251: ##
        !           252: sub squareBracketNud {
        !           253:     my( $op, $p ) = @_;
        !           254:     my @children = ();
        !           255:     if (!defined $p->current_token || !defined $p->current_token->op || $p->current_token->op->id ne "]") {
        !           256:         my $e = $p->expression(0);
        !           257:         if (defined $p->current_token && defined $p->current_token->op &&
        !           258:                 $p->current_token->op->id eq INTERVAL_SEPARATOR) {
        !           259:             return buildInterval(1, $e, $op, $p);
        !           260:         }
        !           261:         while (1) {
        !           262:             push(@children, $e);
        !           263:             if (!defined $p->current_token || !defined $p->current_token->op || $p->current_token->op->id ne ARG_SEPARATOR) {
        !           264:                 last;
        !           265:             }
        !           266:             $p->advance(ARG_SEPARATOR);
        !           267:             $e = $p->expression(0);
        !           268:         }
        !           269:     }
        !           270:     $p->advance("]");
        !           271:     return ENode->new(ENode->VECTOR, $op, undef, \@children);
        !           272: }
        !           273: 
        !           274: ##
        !           275: # Led function for the [ operator (used to parse subscript)
        !           276: # @param {Operator} op
        !           277: # @param {Parser} p
        !           278: # @param {ENode} left
        !           279: # @returns {ENode}
        !           280: ##
        !           281: sub subscriptLed {
        !           282:     my( $op, $p, $left ) = @_;
        !           283:     if ($left->type != ENode->NAME && $left->type != ENode->SUBSCRIPT) {
        !           284:         die ParseException->new("Name expected before a square bracket.", $p->tokens->[$p->token_nr - 1]->from);
        !           285:     }
        !           286:     my @children = ($left);
        !           287:     if (!defined $p->current_token || !defined $p->current_token->op || $p->current_token->op->id != "]") {
        !           288:         while (1) {
        !           289:             push(@children, $p->expression(0));
        !           290:             if (!defined $p->current_token || !defined $p->current_token->op || $p->current_token->op->id ne ARG_SEPARATOR) {
        !           291:                 last;
        !           292:             }
        !           293:             $p->advance(ARG_SEPARATOR);
        !           294:         }
        !           295:     }
        !           296:     $p->advance("]");
        !           297:     return ENode->new(ENode->SUBSCRIPT, $op, "[", \@children);
        !           298: }
        !           299: 
        !           300: ##
        !           301: # Returns the ENode for the interval, parsing starting just before the interval separator
        !           302: # @param {boolean} closed - was the first operator closed ?
        !           303: # @param {ENode} e1 - First argument (already parsed)
        !           304: # @param {Operator} op - The operator
        !           305: # @param {Parser} p - The parser
        !           306: # @returns {ENode}
        !           307: ##
        !           308: sub buildInterval {
        !           309:     my ($closed, $e1, $op, $p) = @_;
        !           310:     $p->advance(INTERVAL_SEPARATOR);
        !           311:     my $e2 = $p->expression(0);
        !           312:     if (!defined $p->current_token || !defined $p->current_token->op ||
        !           313:             ($p->current_token->op->id ne ")" && $p->current_token->op->id ne "]")) {
        !           314:         die ParseException->new("Wrong interval syntax.", $p->tokens->[$p->token_nr - 1]->from);
        !           315:     }
        !           316:     my $interval_type;
        !           317:     if ($p->current_token->op->id eq ")") {
        !           318:         $p->advance(")");
        !           319:         if ($closed) {
        !           320:             $interval_type = ENode->CLOSED_OPEN;
        !           321:         } else {
        !           322:             $interval_type = ENode->OPEN_OPEN;
        !           323:         }
        !           324:     } else {
        !           325:         $p->advance("]");
        !           326:         if ($closed) {
        !           327:             $interval_type = ENode->CLOSED_CLOSED;
        !           328:         } else {
        !           329:             $interval_type = ENode->OPEN_CLOSED;
        !           330:         }
        !           331:     }
        !           332:     return ENode->new(ENode->INTERVAL, $op, undef, [$e1, $e2], $interval_type);
        !           333: }
        !           334: 
        !           335: ##
        !           336: # nud function for the { operator (used to parse sets)
        !           337: # @param {Operator} op
        !           338: # @param {Parser} p
        !           339: # @returns {ENode}
        !           340: ##
        !           341: sub curlyBracketNud {
        !           342:     my( $op, $p ) = @_;
        !           343:     my @children = ();
        !           344:     if (!defined $p->current_token || !defined $p->current_token->op || $p->current_token->op->id ne "}") {
        !           345:         while (1) {
        !           346:             push(@children, $p->expression(0));
        !           347:             if (!defined $p->current_token || !defined $p->current_token->op || $p->current_token->op->id ne ARG_SEPARATOR) {
        !           348:                 last;
        !           349:             }
        !           350:             $p->advance(ARG_SEPARATOR);
        !           351:         }
        !           352:     }
        !           353:     $p->advance("}");
        !           354:     return ENode->new(ENode->SET, $op, undef, \@children);
        !           355: }
        !           356: 
        !           357: ##
        !           358: # Defines all the operators.
        !           359: ##
        !           360: sub define {
        !           361:     my( $self ) = @_;
        !           362:     $self->suffix("!", 160);
        !           363:     $self->infix("^", 140, 139);
        !           364:     $self->infix(".", 130, 129);
        !           365:     $self->infix("`", 125, 125, \&unitsLed);
        !           366:     $self->infix("*", 120, 120);
        !           367:     $self->infix("/", 120, 120);
        !           368:     $self->infix("%", 120, 120);
        !           369:     $self->infix("+", 100, 100);
        !           370:     $self->operator("-", Operator->BINARY, 100, 134, sub {
        !           371:         my( $op, $p ) = @_;
        !           372:         my @children = ($p->expression($op->rbp));
        !           373:         return ENode->new(ENode->OPERATOR, $op, $op->id, \@children);
        !           374:     }, sub {
        !           375:         my( $op, $p, $left ) = @_;
        !           376:         my @children = ($left, $p->expression(100));
        !           377:         return ENode->new(ENode->OPERATOR, $op, $op->id, \@children);
        !           378:     });
        !           379:     $self->infix("=", 80, 80);
        !           380:     $self->infix("#", 80, 80);
        !           381:     $self->infix("<=", 80, 80);
        !           382:     $self->infix(">=", 80, 80);
        !           383:     $self->infix("<", 80, 80);
        !           384:     $self->infix(">", 80, 80);
        !           385:     
        !           386:     $self->separator(")");
        !           387:     $self->separator(ARG_SEPARATOR);
        !           388:     $self->separator(INTERVAL_SEPARATOR);
        !           389:     $self->operator("(", Operator->BINARY, 200, 200, \&parenthesisNud, \&parenthesisLed);
        !           390:     
        !           391:     $self->separator("]");
        !           392:     $self->operator("[", Operator->BINARY, 200, 70, \&squareBracketNud, \&subscriptLed);
        !           393:     
        !           394:     $self->separator("}");
        !           395:     $self->prefix("{", 200, \&curlyBracketNud);
        !           396: }
        !           397: 
        !           398: 
        !           399: 1;
        !           400: __END__

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