| 1 | # handle sqrt(-1) and log of negatives (make complexes) |
|
|
| 2 | # do division by zero and log of zero checks in compound functions |
|
|
| 3 | # add context flags for various reduction checks |
|
|
| 4 | # make context flag for reduction of constants |
|
|
| 5 | # make reduce have reduce patterns as parameters |
|
|
| 6 | # more reduce patterns |
|
|
| 7 | # make operator strings customizable (reduce, and other places they are used) |
|
|
| 8 | # add parens alternately as () and []? |
|
|
| 9 | |
|
|
| 10 | package Parser; |
1 | package Parser; |
| 11 | my $pkg = "Parser"; |
2 | my $pkg = "Parser"; |
| 12 | |
3 | |
| 13 | use strict; |
4 | use strict; |
| 14 | #use Carp; |
5 | #use Carp; |
| … | |
… | |
| 22 | sub new { |
13 | sub new { |
| 23 | my $self = shift; my $class = ref($self) || $self; |
14 | my $self = shift; my $class = ref($self) || $self; |
| 24 | my $string = shift; |
15 | my $string = shift; |
| 25 | my $math = bless { |
16 | my $math = bless { |
| 26 | string => undef, |
17 | string => undef, |
| 27 | tokens => [], |
18 | tokens => [], tree => undef, |
| 28 | tree => undef, |
|
|
| 29 | variables => {}, values => {}, |
19 | variables => {}, values => {}, |
| 30 | context => Parser::Context->current, |
20 | context => Parser::Context->current, |
| 31 | error => 0, errorPos => undef, |
|
|
| 32 | message => '', |
|
|
| 33 | }, $class; |
21 | }, $class; |
| 34 | if (ref($string) =~ m/^(Parser|Value::Formula)/) { |
22 | if (ref($string) =~ m/^(Parser|Value::Formula)/) { |
| 35 | my $tree = $string; $tree = $tree->{tree} if exists $tree->{tree}; |
23 | my $tree = $string; $tree = $tree->{tree} if exists $tree->{tree}; |
| 36 | $math->{tree} = $tree->copy($math); |
24 | $math->{tree} = $tree->copy($math); |
| 37 | } elsif (ref($string) =~ m/^Value/) { |
25 | } elsif (ref($string) =~ m/^Value/) { |
| … | |
… | |
| 68 | push(@{$tokens},['op',$5,$p0,$p1,$space]) if (defined($5)); |
56 | push(@{$tokens},['op',$5,$p0,$p1,$space]) if (defined($5)); |
| 69 | push(@{$tokens},['open',$6,$p0,$p1,$space]) if (defined($6)); |
57 | push(@{$tokens},['open',$6,$p0,$p1,$space]) if (defined($6)); |
| 70 | push(@{$tokens},['close',$7,$p0,$p1,$space]) if (defined($7)); |
58 | push(@{$tokens},['close',$7,$p0,$p1,$space]) if (defined($7)); |
| 71 | push(@{$tokens},['var',$8,$p0,$p1,$space]) if (defined($8)); |
59 | push(@{$tokens},['var',$8,$p0,$p1,$space]) if (defined($8)); |
| 72 | } else { |
60 | } else { |
| 73 | push(@{$tokens},['error',substr($string,$p0,3),$p0]); |
61 | push(@{$tokens},['error',substr($string,$p0,1),$p0,$p0+1]); |
| 74 | $self->{error} = 1; |
62 | $self->{error} = 1; |
| 75 | last; |
63 | last; |
| 76 | } |
64 | } |
| 77 | $space = ($string =~ m/\G\s+/gc); |
65 | $space = ($string =~ m/\G\s+/gc); |
| 78 | } |
66 | } |
| … | |
… | |
| 101 | /num/ and do {$self->Num($ref->[1]); last}; |
89 | /num/ and do {$self->Num($ref->[1]); last}; |
| 102 | /const/ and do {$self->Const($ref->[1]); last}; |
90 | /const/ and do {$self->Const($ref->[1]); last}; |
| 103 | /var/ and do {$self->Var($ref->[1]); last}; |
91 | /var/ and do {$self->Var($ref->[1]); last}; |
| 104 | /fn/ and do {$self->Fn($ref->[1]); last}; |
92 | /fn/ and do {$self->Fn($ref->[1]); last}; |
| 105 | /str/ and do {$self->Str($ref->[1]); last}; |
93 | /str/ and do {$self->Str($ref->[1]); last}; |
| 106 | /error/ and do {$self->Error("Unexpected characters '$ref->[1]'",$ref); last}; |
94 | /error/ and do {$self->Error("Unexpected character '$ref->[1]'",$ref); last}; |
| 107 | } |
95 | } |
| 108 | return if ($self->{error}); |
96 | return if ($self->{error}); |
| 109 | } |
97 | } |
| 110 | $self->Close('start'); return if ($self->{error}); |
98 | $self->Close('start'); return if ($self->{error}); |
| 111 | $self->{tree} = $self->{stack}->[0]->{value}; |
99 | $self->{tree} = $self->{stack}->[0]->{value}; |
| … | |
… | |
| 641 | my $self = shift; my ($value,$type); |
629 | my $self = shift; my ($value,$type); |
| 642 | my $variables = $self->{context}{variables}; |
630 | my $variables = $self->{context}{variables}; |
| 643 | $self->{values} = {@_}; |
631 | $self->{values} = {@_}; |
| 644 | foreach my $x (keys %{$self->{values}}) { |
632 | foreach my $x (keys %{$self->{values}}) { |
| 645 | $self->Error("Undeclared variable '$x'") unless defined $variables->{$x}; |
633 | $self->Error("Undeclared variable '$x'") unless defined $variables->{$x}; |
| 646 | $value = $self->{values}{$x}; |
634 | $value = Value::makeValue($self->{values}{$x}); |
| 647 | $value = Value::Formula->new($value) unless |
|
|
| 648 | Value::matchNumber($value) || Value::isFormula($value) || Value::isValue($value); |
|
|
| 649 | if (Value::isFormula($value)) {$type = $value->typeRef} |
635 | if (Value::isFormula($value)) {$type = $value->typeRef} |
| 650 | else {($value,$type) = Value::getValueType($self,$value)} |
636 | else {($value,$type) = Value::getValueType($self,$value)} |
| 651 | $self->Error("Variable '$x' should be of type $variables->{$x}{type}{name}") |
637 | $self->Error("Variable '$x' should be of type $variables->{$x}{type}{name}") |
| 652 | unless Parser::Item::typeMatch($type,$variables->{$x}{type}); |
638 | unless Parser::Item::typeMatch($type,$variables->{$x}{type}); |
| 653 | $self->{values}{$x} = $value; |
639 | $self->{values}{$x} = $value; |
| 654 | } |
640 | } |
|
|
641 | } |
|
|
642 | |
|
|
643 | |
|
|
644 | ################################################## |
|
|
645 | ################################################## |
|
|
646 | # |
|
|
647 | # Convert a student answer to a formula, with error trapping. |
|
|
648 | # If the result is undef, there was an error (message is in Context()->{error} object) |
|
|
649 | # |
|
|
650 | |
|
|
651 | sub Formula { |
|
|
652 | my $f = shift; |
|
|
653 | eval {Value::Formula->new($f)}; |
|
|
654 | } |
|
|
655 | |
|
|
656 | # |
|
|
657 | # Evaluate a formula, with error trapping. |
|
|
658 | # If the result is undef, there was an error (message is in Context()->{error} object) |
|
|
659 | # If the result was a real, make it a fuzzy one. |
|
|
660 | # |
|
|
661 | sub Evaluate { |
|
|
662 | my $f = shift; |
|
|
663 | return unless defined($f); |
|
|
664 | my $v = eval {$f->eval(@_)}; |
|
|
665 | $v = Value::makeValue($v) if defined($v); |
|
|
666 | return $v; |
|
|
667 | } |
|
|
668 | |
|
|
669 | |
|
|
670 | ################################################## |
|
|
671 | ################################################## |
|
|
672 | # |
|
|
673 | # Produce a vector in ijk form |
|
|
674 | # |
|
|
675 | sub ijk { |
|
|
676 | my $self = shift; |
|
|
677 | $self->{tree}->ijk; |
| 655 | } |
678 | } |
| 656 | |
679 | |
| 657 | ######################################################################### |
680 | ######################################################################### |
| 658 | ######################################################################### |
681 | ######################################################################### |
| 659 | # |
682 | # |
| … | |
… | |
| 662 | |
685 | |
| 663 | use Parser::Item; |
686 | use Parser::Item; |
| 664 | use Value; |
687 | use Value; |
| 665 | use Value::Formula; |
688 | use Value::Formula; |
| 666 | use Parser::Context; |
689 | use Parser::Context; |
|
|
690 | use Parser::Context::Default; |
|
|
691 | |
| 667 | # use Parser::Differentiation; |
692 | # use Parser::Differentiation; |
| 668 | |
693 | |
|
|
694 | ########################################################################### |
|
|
695 | |
|
|
696 | use vars qw($installed); |
|
|
697 | $Parser::installed = 1; |
|
|
698 | |
|
|
699 | ########################################################################### |
|
|
700 | ########################################################################### |
|
|
701 | # |
|
|
702 | # To Do: |
|
|
703 | # |
|
|
704 | # handle sqrt(-1) and log of negatives (make complexes) |
|
|
705 | # do division by zero and log of zero checks in compound functions |
|
|
706 | # add context flags for various reduction checks |
|
|
707 | # make context flag for reduction of constants |
|
|
708 | # make reduce have reduce patterns as parameters |
|
|
709 | # more reduce patterns |
|
|
710 | # make operator strings customizable (reduce, and other places they are used) |
|
|
711 | # add parens alternately as () and []? |
|
|
712 | # |
| 669 | ######################################################################### |
713 | ######################################################################### |
| 670 | |
714 | |
| 671 | 1; |
715 | 1; |
| 672 | |
716 | |