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