2016-08-02 8 views
5

я хотел бы знать, как я могу запустить операции, как этотPerl пользовательский синтаксис для суффиксов и пользовательских операторов постфиксных

$T = 25 C; 
@specs = (273.15 K, 23 bar, 2.0 mol/s); 

и заставить их компилировать. Я не придирчив к тому, что их результат, или как он реализован. Моя цель состоит в том, чтобы выражения для физических величин с условными аннотациями постфикса, компилируемыми для выражений perl для этих единиц.

Я думаю, что мне нужно использовать специальные методы парсинга, но я бы предпочел использовать любые существующие функции или модули синтаксического анализа только при применении регулярных фильтров к моему исходному исходному коду.

Parse::Keyword представляется многообещающим, но я не могу понять, может ли он анализировать постфиксные операции и утверждает, что он устарел.

Редактировать: Я хотел бы избежать фильтров источников, если это возможно, потому что я предпочитаю не писать регулярные выражения для синтаксических угловых случаев Perl (например, «25 (J/K)»).

Ошибка Perl производит здесь говорит:

perl -E "25 C" 
Bareword found where operator expected at -e line 1, near "25 C" 
(Missing operator before C?) 

Похоже, мне нужно подключить в Perl, где обнаруживает операторы после числовых литералов.

Может ли Devel::Declare добавить постфиксные операторы? Если да, то как?

+3

Звучит как работа для [исходного фильтра] (http://perldoc.perl.org/perlfilter.html). Поймите, однако, что [исходные фильтры - почти всегда плохая идея] (http://stackoverflow.com/questions/1785852/why-are-perl-source-filters-bad-and-when-is-it-ok- к потребительным их). Я бы рекомендовал подход, который не требует привинчивания с правилами синтаксического анализа perl; это не так красиво, но я бы предпочел нечто вроде '$ T = My :: Measurement-> new (value => 25, units => 'C');'. – ThisSuitIsBlackNot

+0

Цель этого модуля - явно разрешить краткую форму, такую ​​как язык, специфичный для домена. – alexchandel

+0

Просьба описать более точно результат, который вам нужен. Является ли '$ T = '25 C'' приемлемым? – Borodin

ответ

3

Вы можете злоупотреблять перегрузкой, чтобы получить что-то близкое к тому, что вы хотите.

#!/usr/bin/perl 

use strict; 
use warnings; 
use 5.010; 

use Data::Dumper; 
use MyUnits; 

my $T = '25 C'; 

say Dumper $T; 

my @specs = ('273.15 K', '23 bar', '2.0 mol/s'); 

say Dumper \@specs; 

Как вы увидите, вы возвращаете объекты с атрибутами «значение» и «тип».

MyUnits.pm выглядит следующим образом:

package MyUnits; 

use strict; 
use warnings; 

use overload 
    '""' => \&to_string; 

my %_const_handlers = (
    q => \&string_parser, 
); 

sub string_parser { 
    my $c = eval { __PACKAGE__->new($_[0]) }; 
    return $_[1] if [email protected]; 
    return $c; 
} 

sub import { 
    overload::constant %_const_handlers; 
} 

sub new { 
    my $class = shift; 

    # ->new(type => ..., value => ...) 
    if (@_ == 4) { 
    return bless { @_ }, $class; 
    } 
    # ->new({ type => ..., value => ...) 
    if (@_ == 1 and ref $_[0] eq 'HASH') { 
    return bless $_[0], $class; 
    } 
    # -> new('999xxx') 
    if (@_ == 1 and ! ref $_[0]) { 
    my ($val, $type) = $_[0] =~ /(\d+\.?\d*)\s*(.+)/; 
    return bless({ 
     value => $val, type => $type, 
    }); 
    } 
} 

sub to_string { 
    return "$_[0]->{value}$_[0]->{type}"; 
} 

1; 

Вы хотели бы добавить больше методов, чтобы дать ему возможность сделать что-то полезное.

В большинстве случаев перегрузка не является чем-то вроде партийного трюка, чем исходные фильтры. Это почти наверняка сделает вашу программу намного медленнее.

0

Исходные фильтры, как известно, хрупкие, но, вероятно, самый простой способ получить то, что вы хотите, не погружаясь глубоко в непереносимые кишечники perl. Нечто подобное возможно:

package U; 
use strict; 
use warnings; 
use Filter::Simple; 

my @UNITS = qw(degC degK bar mol s); 

FILTER { 
    my $unit_re = '(?:' . join('|', @UNITS) . ')'; 
    s#(\d+(?:\.\d\+)?)\s?((?:${unit_re}[*/])*$unit_re)\b#Units->new({value => $1, unit => '$2'})#g; 
}; 

package Units; 
use Class::Accessor 'antlers'; 
has value => (is => "ro", isa => "Num"); 
has unit => (is => "ro", isa => "Str"); 

1; 

Я был анальным об этом и изменил «C», как это выглядит не значит Кулон. Вы могли бы пойти все use utf8 и писать °C хотя;)

Тест:

perl -I. -MU -e'my $val = 23 degK/s; printf "Value: %g, Unit: %s\n", $val->value, $val->unit' 
Value: 23, Unit: degK/s 

Конечно простое регулярное выражение оставляет желать лучшего, как скобки и такие, для которых вы, вероятно, понадобится Text::Balanced, и класс Units может на самом деле захотеть больше походить на парсинг этой строки и перегрузку нескольких операторов, чтобы вы могли рассчитывать с помощью единиц.

2

Если вы хотите использовать посредническую функцию, вы можете получить что-то, что sorta будет выглядеть так, как вам хочется, если вы достаточно сильно пощупаете.Я не можно писать Haskell в Perl ;-)

package My::Units; 

use strict; 
use warnings; 

use Importer 'Math::Units::PhysicalValue', 'PV'; 

our @EXPORT = qw(); 
our @EXPORT_OK = qw(with_units); 

sub with_units(\[email protected]) { 
    my (undef, $value, $units) = @_; 
    ${ $_[0] } = PV "$value $units"; 
    return; 
} 

__PACKAGE__; 
__END__ 

Используйте его из сценария:

#!/usr/bin/env perl 

use feature 'say'; 
use strict; 
use warnings; 

use lib '.'; 
use Importer 'My::Units', 'with_units'; 

with_units my $x => 25 => 'C'; 
with_units my $y => 20 => 'F'; 
with_units my $z => 0 => 'C'; 

say $x + $y; 
say $y + $z; 

Выход:

C:\...\t> perl t.pl 
97 F 
-6.67 C

Теперь Math::Units::PhysicalValue безоговорочно использует Math::BigFloat поэтому арифметика должна быть медленной, но точной. Если вам действительно нужны такие вещи, вы можете захотеть заглянуть в убирающие части Math::Units и Math::Units::PhysicalValue и создать что-то более быстрое от этих частей.