Индивидуальный синтаксис 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 добавить постфиксные операторы? Если да, то как?

+5
источник поделиться
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;

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

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

+3
источник

Если вы хотите использовать промежуточную функцию, вы можете получить что-то, что 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__

Используйте его из script:

#!/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 и создания чего-то более быстрого из этих частей.

+2
источник

Исходные фильтры, как известно, хрупкие, но, вероятно, самый простой способ получить то, что вы хотите, не погружаясь глубоко в 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;

Я был анал об этом и изменил "С", поскольку, похоже, ты не имеешь в виду Кулона. Вероятно, вы могли бы пройти все 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 может на самом деле захотеть намного больше, чем разбор этой строки и перегрузка несколько операторов, поэтому вы можете рассчитывать с помощью единиц.

0
источник

Посмотрите другие вопросы по меткам или Задайте вопрос