#!/usr/bin/perl # http://perlmonks.org/?node_id=1124684 use strict; # hand rolled recursive descent parser using /\G/gc use Data::Dump qw(pp); # anon arrays are [x^0, x^1, x^2, ...] my $variable; @ARGV or @ARGV = split /\n/, < 2 && $answer->[-1] == 0; # trim trailing 0's $answer->[1] or die "variable $variable cancels out"; @$answer > 2 and die "quadratic or higher equation"; print "\n $variable = ", -$answer->[0] / $answer->[1], "\n"; }; $@ and print $@; } sub expr # left associative => term ([+-] term)* { my $left = term(); while( /\G\s*((\+)|-)/gc ) { my $add = $2; my $right = term(); if($add) { $left->[$_] += $right->[$_] for 0..$#$right; } else { $left->[$_] -= $right->[$_] for 0..$#$right; } } return $left; } sub term # left associative => factor (* factor)* { my $left = item(); while( /\G\s*\*/gc ) { my $right = item(); my @result = (0) x (@$left + @$right - 1); my $n = 0; for my $vl (@$left) # cross multiply { my $pos = $n; $result[$pos++] += $vl * $_ for @$right; $n++; } pop @result while @result > 2 && $result[-1] == 0; # trim trailing 0's $left = [ @result ]; } return $left; } sub item # parens or number or variable or unary minus { if( /\G\s*\(/gc ) { my $val = expr(); return /\G\s*\)/gc ? $val : error(); # must have closing paren } /\G\s*((\d+(\.\d*)?|\.\d+))/gc and return [ $1, 0 ]; if( /\G\s*(\w+)/gc ) { defined $variable && $variable ne $1 and die "more than one variable used '$variable' and '$1'"; $variable = $1; return [ 0, 1 ]; } if( /\G\s*-/gc ) # unary minus { my $right = item(); return [ map -$_, @$right ]; } error(); } sub error { s/\G/ SYNTAX ERROR->/; die "$_\n"; }