File: Calc.pm

package info (click to toggle)
tiarra 20100212-4
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 2,732 kB
  • ctags: 1,712
  • sloc: perl: 32,032; lisp: 193; sh: 109; makefile: 10
file content (283 lines) | stat: -rw-r--r-- 7,742 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# -*- cperl -*-
# -----------------------------------------------------------------------------
# $Id: Calc.pm 32571 2009-04-18 04:59:26Z hio $
# -----------------------------------------------------------------------------
# copyright (C) 2003-2004 Topia <topia@clovery.jp>. all rights reserved.
package Auto::Calc::Share;
use strict;
our $__export = [qw(pi pie e frac)];
sub export () { $__export }

sub pi () { 3.141592653589793238 }
sub pie () { pi }
sub e () { exp(1) }
sub frac ($) { $_[0] - int($_[0]) }

package Auto::Calc;
use strict;
use warnings;
use base qw(Module);
use Module::Use qw(Auto::Utils Auto::Calc::Share);
use Auto::Utils;
use Mask;

use Symbol ();
use Safe;

# 全角空白.
our $U_IDEOGRAPHIC_SPACE = "\xe3\x80\x80";

sub new {
    my $class = shift;
    my $this = $class->SUPER::new(@_);
    $this->{safe} = Safe->new(__PACKAGE__.'::Root');
    $this->{safe}->erase;
    $this->{safe}->permit_only(qw(:base_core :base_math :base_orig),
			       qw(pack unpack),
			       qw(atan2 sin cos exp log sqrt),
			      );
    if (!$this->config->permit_sub) {
	$this->{safe}->deny(qw(leavesub));
    }
    my $pkg = __PACKAGE__.'::Share';
    $this->{safe}->share_from($pkg, $pkg->export);

    return $this;
}

sub destruct {
    my ($this) = shift;

    Symbol::delete_package(__PACKAGE__.'::Root')
}

sub __message_io_hook
{
    my ($this,$msg,$io,$type) = @_;
    # 自分のWebClient からも使いたいけれど, どうもうまくいかない模様.

    # print "io_hook: $io $type ",$msg->command," ", $msg->param(1)," $msg\n";
    if( $type eq 'out' && $io->isa('IrcIO::Server') )
    {
      # print ">> action\n";
      my @ret = $this->_action($msg, $io);
      # print "ret: ".join(", ", @ret)."\n";
      return @ret;
    }
    return $msg;
}

sub message_arrived {
    my ($this,$msg,$sender) = @_;
    # print "arrived: $sender - ",$msg->command," ", $msg->param(1),"\n";
    # print ">> action\n";
    $this->_action($msg, $sender);
}

sub _action
{
    my ($this, $msg, $sender) = @_;
    my @result = ($msg);

    my $return_value = sub {
	return @result;
    };

    my (undef,undef,undef,$reply_anywhere,$get_full_ch_name)
	= Auto::Utils::generate_reply_closures($msg,$sender,\@result);

    if ($msg->command eq 'PRIVMSG') {
	my $method = $msg->param(1);
	$method =~ s/^\s*(.*)\s*$/$1/;

	if( my $val = $this->config->support_shared_webclient )
	{
		# no や false は除外的.
		if( $val !~ /^[nf]/i )
		{
			$method =~ s/^[^\s>]+>\s*//;
		}
	}

	# init
	if (Mask::match_deep([$this->config->init('all')], $method)) {
	    if (Mask::match_deep_chan([$this->config->init_mask('all')],
				      $msg->prefix, $get_full_ch_name->())) {
		$this->{safe}->reinit;
		$reply_anywhere->([$this->config->init_format('all')]);
		return $return_value->();
	    }
	}

	my $keyword;
	($keyword, $method) = split(/(?:\s|$U_IDEOGRAPHIC_SPACE)+/o, $method, 2);

	# request
	if (Mask::match_deep([$this->config->request('all')], $keyword)) {
	    my $prefix = $msg->prefix->clone->prefix || '*!*@*';
	    if (Mask::match_deep_chan([$this->config->mask('all')],
				      $prefix, $get_full_ch_name->())) {
		my ($ret, $err, $signal);
		do {
		    # disable warning
		    local $SIG{__WARN__} = sub { };
		    #
		    my $signal_handler = sub {
			$signal = shift;
			die "$signal called";
		    };
		    # floating point exceptions
		    local $SIG{FPE} = sub { $signal_handler->('SIGFPE'); }
			if exists $SIG{FPE};
		    # alarm
		    local $SIG{ALRM} = sub { $signal_handler->('ALARM'); }
			if exists $SIG{ALRM};
		    my $timeout = $this->config->timeout;
		    $timeout = 1 unless defined $timeout;
		    # die handler
		    local $SIG{__DIE__} = sub {
			$err = shift;
			die '';
		    };

		    alarm $timeout if ($timeout);
		    no strict;
		    $ret = $this->{safe}->reval($method);
		    $err ||= $@;
		    alarm 0 if ($timeout);
		};

		my $reply = sub {
		    my $array = shift;

		    map {
			if (defined($$_)) {
			    # 汚染の除去
			    $$_ =~ tr/\t\x0a\x0d/ /;
			    $$_ =~ tr/\x00-\x19//d;
			    $$_ =~ s/^\s+//;
			    $$_ =~ s/\s+$//;
			    $$_ =~ s/\s{2,}/ /;
			} else {
			    $$_ = $this->config->undef || 'undef';
			}
		    } (\$ret, \$err);

		    if ($err) {
			$err =~ s/ +at \(eval \d+\) line \d+//;
			$err =~ s/, <DATA> line \d+//;
		    }

		    map {
			$reply_anywhere->(
			    $_,
			    method => $method,
			    result => $ret,
			    error => $err,
			    signal => $signal,
			   );
		    } @$array;
		};

		my @format_names;
		if ($signal) {
		    push(@format_names, 'signal-'.lc($signal).'-format');
		    push(@format_names, 'signal-format');
		}
		if ($err) {
		    my $format = undef;
		    # format の個別化
		    my $error_name = $err;
		    if ($this->config->error_name_formatter) {

		    }
		    $error_name =~ s/'.+' (trapped by operation mask)/$1/;
		    $error_name =~ s/(Undefined subroutine) \&.+ (called)/$1 $2/;
		    $error_name =~ tr/ _/-/;
		    $error_name =~ tr/'`//d;
		    $error_name =~ s/\.$//;
		    $error_name =~ s/-+$//;
		    $error_name = lc($error_name);
		    #::debug_printmsg("error_name: $error_name");

		    push(@format_names, 'error-format');
		} else {
		    push(@format_names, 'reply-format');
		}
		foreach my $format_name (@format_names) {
		    my @formats = $this->config->get($format_name, 'all');
		    next if $#formats != 0;
		    $reply->(\@formats);
		    last;
		}
	    }
	}
    }

    return $return_value->();
}

1;

=pod
info: Perlの式を計算させるモジュール。
default: off

# 反応する発言を指定します。
request: 計算

# 使用を許可する人&チャンネルのマスク。
# 例はTiarraモード時。 [default: なし]
mask: * +*!*@*
# [plum-mode] mask: +*!*@*

# 結果が未定義だったときに置き換えられる文字列。省略されると undef 。
-undef: (未定義)

# 正常に計算できたときのフォーマット
# method: 計算式, result: 結果, error: エラー, signal: シグナル
reply-format: #(method): #(result)

# エラーが起きたときのフォーマット
# method: 計算式, result: 結果, error: エラー, signal: シグナル
error-format: #(method): エラーです。(#(error))

# シグナルが発生したときのフォーマット
-signal-format: #(method): シグナルです。(#(signal))

# signal-$SIGNALNAME-format 形式。
# $SIGNALNAME には現状 alarm/sigfpe があります。
# 該当がなければ signal-format にフォールバックします。

# いくつかの例を挙げます。
-signal-alarm-format: #(method): 時間切れです。
-signal-sigfpe-format: #(method): 浮動小数点計算例外です。

# タイムアウトする秒数を指定します。 alarm に渡されます。
# 再帰を止めるのに使えますが、どうもメモリリークしていそうな雰囲気です。
timeout: 1

# サブルーチン定義を許可するかどうかを指定する。
# 再帰定義が可能なので、許可する場合はこのモジュール専用の
# Tiarra を動かすことをお勧めします。
permit-sub: 0

# 初期化する発言を指定します。
# このモジュールでは現状変数や関数定義などを行えます。
# このコマンドが発行されるとそれらをクリアします。
init: 計算初期化

# 初期化を許可する人&チャンネルのマスク。
# 例はTiarraモード時。 [default: なし]
init-mask: * +*!*@*
# [plum-mode] mask: +*!*@*

# 再初期化したときの発言を指定します。
init-format: 初期化しました。

# 別の shared-mode な System::WebClient からの発言に対応(yes/no).
# 自分自身の発言は未対応.
# [default: no]
-support-shared-webclient: no

=cut