sidef 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289
  1. #!/usr/bin/env perl
  2. use utf8;
  3. use 5.016;
  4. BEGIN { # support for running sidef locally from everywhere
  5. if (-w __FILE__) {
  6. require File::Spec;
  7. require File::Basename;
  8. unshift @INC,
  9. File::Spec->catdir(
  10. File::Basename::dirname(
  11. File::Spec->file_name_is_absolute(__FILE__)
  12. ? __FILE__
  13. : File::Spec->rel2abs(__FILE__)
  14. ),
  15. File::Spec->updir,
  16. 'lib'
  17. );
  18. }
  19. }
  20. binmode STDIN, ":utf8";
  21. binmode STDOUT, ":utf8";
  22. binmode STDERR, ":utf8" if $^P == 0; # to work under Devel::* modules
  23. use Sidef;
  24. my $name = 'Sidef';
  25. my $version = $Sidef::VERSION;
  26. my %args;
  27. if ($#ARGV != -1 and chr ord $ARGV[0] eq '-') {
  28. require Getopt::Std;
  29. $Getopt::Std::STANDARD_HELP_VERSION = 1;
  30. Getopt::Std::getopts('e:E:Dho:ivHWwbcrR:tCO:kP:M:sN:', \%args);
  31. }
  32. # Fix potential case mismatches for -R
  33. if (defined $args{R}) {
  34. if (lc($args{R}) eq 'perl') {
  35. $args{R} = 'Perl';
  36. }
  37. elsif (lc($args{R}) eq 'sidef') {
  38. $args{R} = 'Sidef';
  39. }
  40. }
  41. # Help
  42. if (defined $args{h}) {
  43. HELP_MESSAGE();
  44. exit 0;
  45. }
  46. # Version
  47. if (defined $args{v}) {
  48. VERSION_MESSAGE();
  49. exit 0;
  50. }
  51. # Warnings stack backtrace
  52. if (defined $args{w}) {
  53. $SIG{__WARN__} = sub {
  54. require Carp;
  55. Carp::cluck(@_);
  56. };
  57. }
  58. # Fatal warnings stack backtrace
  59. if (defined $args{W}) {
  60. $SIG{__DIE__} = $SIG{__WARN__} = sub {
  61. require Carp;
  62. Carp::confess(@_);
  63. };
  64. }
  65. # Interactive help
  66. if (defined $args{H}) {
  67. help_interactive();
  68. exit 0;
  69. }
  70. # Interactive coding
  71. if (defined $args{i}) {
  72. code_interactive();
  73. exit 0;
  74. }
  75. # Precision
  76. if (defined $args{P}) {
  77. require Sidef::Types::Number::Number;
  78. if ($args{P} <= 0) {
  79. die "Invalid precision: <<$args{P}>> (expected a positive integer)\n";
  80. }
  81. $Sidef::Types::Number::Number::PREC = $args{P} << 2;
  82. }
  83. # Other Number options
  84. if (defined $args{N}) {
  85. require Sidef::Types::Number::Number;
  86. my @options = split(/\s*;\s*/, $args{N});
  87. foreach my $option (@options) {
  88. if ($option =~ /^\s*(\w+)\s*=\s*(\S+)/) {
  89. my ($name, $value) = ($1, $2);
  90. if ($value eq 'true') {
  91. $value = 1;
  92. }
  93. elsif ($value eq 'false') {
  94. $value = 0;
  95. }
  96. no strict 'refs';
  97. ${'Sidef::Types::Number::Number::' . $name} = $value;
  98. }
  99. else {
  100. die "Invalid format: <<$option>>!\nExpected: 'NAME1=VALUE1; NAME2=VALUE2;'";
  101. }
  102. }
  103. }
  104. # Test mode
  105. if (defined $args{t}) {
  106. local $args{c} = 0;
  107. my @argv = splice(@ARGV);
  108. my @fails;
  109. require Encode;
  110. while (defined(my $script_name = shift @argv)) {
  111. my $script_name = Encode::decode_utf8($script_name);
  112. say "\n** Executing: $script_name";
  113. say "-" x 80;
  114. my $sidef = Sidef->new(opt => \%args,
  115. name => $script_name);
  116. my $code = read_script($script_name);
  117. my $deparsed = eval { $sidef->compile_code($code, 'Perl') };
  118. my $slept = 0;
  119. if ($@) {
  120. warn "[ERROR] Can't parse the script `$script_name`: $@";
  121. push @fails, $script_name;
  122. sleep 2;
  123. $slept = 1;
  124. }
  125. else {
  126. local $SIG{INT} = sub {
  127. die "Stopped by user...";
  128. };
  129. if (defined $args{C}) {
  130. say "$script_name syntax OK";
  131. next;
  132. }
  133. $sidef->execute_perl($deparsed);
  134. }
  135. if (not($slept) and $@) {
  136. warn "[ERROR] Error encountered on script `$script_name`: $@";
  137. push @fails, $script_name;
  138. sleep(2) if @argv;
  139. }
  140. }
  141. if (@fails) {
  142. say "\n";
  143. say "-" x 80;
  144. say ":: The following scripts failed";
  145. say "-" x 80;
  146. say "$_" for @fails;
  147. }
  148. }
  149. # Default
  150. else {
  151. my $script_name = '-';
  152. $args{E} = $args{e} if exists($args{e});
  153. my $code = exists($args{E})
  154. ? do {
  155. defined($args{E}) || die "No code specified for -E.\n";
  156. $script_name = '-E';
  157. require Encode;
  158. Encode::decode_utf8($args{E});
  159. }
  160. : defined($ARGV[0]) ? do {
  161. $script_name = shift @ARGV;
  162. if ($script_name eq '-') {
  163. local $/;
  164. <STDIN>;
  165. }
  166. else {
  167. read_script($script_name);
  168. }
  169. }
  170. : (-t STDIN) ? do { code_interactive(); exit 0; }
  171. : do { local $/; <STDIN> };
  172. $code // exit 2;
  173. my $sidef = Sidef->new(opt => \%args,
  174. name => $script_name);
  175. # Dump the AST
  176. if (defined $args{D}) {
  177. dump_ast($sidef->parse_code($code));
  178. }
  179. # Deparse code
  180. elsif (defined($args{r}) or defined($args{R})) {
  181. my $deparsed = $sidef->compile_code($code, $args{R});
  182. if (defined($args{R}) and $args{R} eq 'Perl') {
  183. require File::Basename;
  184. my $header =
  185. "\nuse lib (" . q{"}
  186. . quotemeta(File::Basename::dirname($INC{"Sidef.pm"})) . q{"}
  187. . ");\n\n"
  188. . "use Sidef;\n\n"
  189. . "binmode(STDIN, ':utf8');\n"
  190. . "binmode(STDOUT, ':utf8');\n"
  191. . "binmode(STDERR, ':utf8') if \$^P == 0;\n";
  192. $deparsed = $header . $deparsed;
  193. }
  194. output($deparsed);
  195. }
  196. # Compile the code to a Perl program
  197. elsif (defined $args{c}) {
  198. compile_to_perl(code => $sidef->compile_code($code, 'Perl'));
  199. }
  200. # Check the syntax
  201. elsif (defined $args{C}) {
  202. eval { $sidef->parse_code($code) };
  203. die $@ if $@;
  204. say "$script_name syntax OK";
  205. }
  206. # Execute the code
  207. else {
  208. $sidef->execute_code($code);
  209. die $@ if $@;
  210. }
  211. }
  212. #
  213. ## Subroutines
  214. #
  215. sub HELP_MESSAGE {
  216. #<<<
  217. my %switches = (
  218. '-i file' => 'execute a program in interactive mode',
  219. '-c' => 'compile the code into a Perl program',
  220. '-C' => 'check syntax only',
  221. '-D' => 'dump the syntax tree of a program',
  222. '-o file' => 'file where to dump the output',
  223. '-O level' => ['perform code optimizations before execution',
  224. 'valid levels: [0], 1, 2'],
  225. '-P int' => 'set the precision of floating-point numbers (default: ' . int($Sidef::Types::Number::Number::PREC / 4) . ')',
  226. '-M mode' => ['set the rounding mode of floating-point numbers',
  227. 'valid modes: [near], zero, inf, +inf, -inf, faith'],
  228. '-N options' => ['modify class-variables inside the Number class',
  229. "valid format: 'VERBOSE=1; USE_YAFU=1; USE_PRIMECOUNT=1'"],
  230. '-k' => 'keep track of potentially incorrect parser interpretations',
  231. '-E program' => 'one line of program',
  232. '-H' => 'interactive help',
  233. '-s' => 'save compiled code in a database to reduce boot-time',
  234. '-v' => 'print version number and exit',
  235. '-t' => 'treat all command-line arguments as scripts',
  236. '-r' => 'parse and deparse a Sidef program',
  237. '-R lang' => ['parse and deparse a Sidef program into a given language',
  238. 'valid values: sidef, perl'],
  239. '-w' => 'enable warnings with stack backtrace',
  240. '-W' => 'make warnings fatal (with stack backtrace)',
  241. );
  242. #>>>
  243. require File::Basename;
  244. my $basename = File::Basename::basename($0);
  245. print <<"USAGE";
  246. Usage: $basename [switches] [--] [programfile] [arguments]
  247. USAGE
  248. require List::Util;
  249. my $max_width = List::Util::max(map { length } keys %switches);
  250. $max_width += 4;
  251. foreach my $key (sort { lc($a) cmp lc($b) or lc($b) cmp lc($a) or $b cmp $a } keys %switches) {
  252. if (ref $switches{$key} eq 'ARRAY') {
  253. printf " %-${max_width}s%s\n", $key, $switches{$key}[0];
  254. foreach my $i (1 .. $#{$switches{$key}}) {
  255. printf " %-${max_width}s%s\n", '', $switches{$key}[$i];
  256. }
  257. }
  258. else {
  259. printf " %-${max_width}s%s\n", $key, $switches{$key};
  260. }
  261. }
  262. print <<"END";
  263. Run '$basename' for entering the interactive mode.
  264. END
  265. }
  266. sub VERSION_MESSAGE {
  267. print "$name $version\n";
  268. }
  269. sub read_script {
  270. my ($script_name) = @_;
  271. open my $fh, '<:utf8', $script_name
  272. or die qq{Can't open sidef script "$script_name": $!\n};
  273. local $/;
  274. <$fh>;
  275. }
  276. sub help_interactive {
  277. my ($term) = @_;
  278. require File::Spec;
  279. require File::Basename;
  280. require Encode;
  281. require Term::ReadLine;
  282. $term //= Term::ReadLine->new("$name $version -- help interactive mode");
  283. print <<"HELP";
  284. Welcome to $name $version! This is the interactive help utility.
  285. Enter the name of any object, keyword, or topic to get help on writing
  286. $name programs and using $name modules. To quit this help utility, just
  287. type "quit".
  288. HELP
  289. my $sidef = Sidef->new(
  290. name => '-H',
  291. opt => {i => 1, %args},
  292. parser_opt => {interactive => 1},
  293. );
  294. {
  295. my $line = Encode::decode_utf8(
  296. $term->readline('help> ')
  297. // do { print "\n"; return }
  298. );
  299. my $ccode = eval { $sidef->compile_code($line, 'Perl') };
  300. if ($@) {
  301. # Valid keywords for 'exit'
  302. if ($line eq 'quit' or $line eq 'q' or $line eq 'exit') {
  303. return;
  304. }
  305. # Otherwise, a syntax error
  306. warn $@;
  307. redo;
  308. }
  309. my @refs = (map { ref($_) } $sidef->execute_perl($ccode));
  310. foreach my $ref (@refs) {
  311. $ref eq '' && do { warn "Not an object!\n"; next };
  312. my $name = $ref =~ s{::}{/}gr;
  313. my $file = $INC{$name . '.pm'};
  314. my $pod;
  315. foreach my $dir (@INC) {
  316. if (-e (my $f = File::Spec->catfile($dir, $name . '.pod'))) {
  317. $pod = $f;
  318. last;
  319. }
  320. }
  321. if (defined($pod)) {
  322. system 'perldoc', $pod;
  323. $? && system 'man', $ref;
  324. }
  325. else {
  326. system 'man', $ref;
  327. $? && system 'perldoc', $ref;
  328. }
  329. }
  330. redo;
  331. }
  332. }
  333. sub create_completion_tree {
  334. scalar {
  335. table => {},
  336. special_key => "\0",
  337. };
  338. }
  339. sub add_tree_entry {
  340. my ($tree, $key, $value) = @_;
  341. my $ref = $tree->{table};
  342. foreach my $item (@$key) {
  343. $ref = $ref->{$item} //= {};
  344. undef $ref->{$tree->{special_key}}{$value};
  345. }
  346. $tree;
  347. }
  348. sub search_tree {
  349. my ($tree, $prefix) = @_;
  350. my $ref = $tree->{table};
  351. foreach my $item (@$prefix) {
  352. if (exists $ref->{$item}) {
  353. $ref = $ref->{$item};
  354. }
  355. else {
  356. return;
  357. }
  358. }
  359. sort keys %{$ref->{$tree->{special_key}} // {}};
  360. }
  361. sub add_class_methods_to_completion {
  362. my ($tree) = @_;
  363. my $modules_count = scalar(keys %INC);
  364. state %seen;
  365. state $included_modules = $modules_count - 1;
  366. if ($modules_count == $included_modules) {
  367. return 1;
  368. }
  369. foreach my $module (keys %INC) {
  370. next if $seen{$module}++;
  371. my $class = $module =~ s{\.pm\z}{}r =~ s{\W+}{::}gr;
  372. $class =~ /^Sidef::Types::/ or next;
  373. foreach my $method_name (keys %{(eval { $class->methods }) // {}}) {
  374. add_tree_entry($tree, [split(//, $method_name)], $method_name);
  375. }
  376. }
  377. $included_modules = $modules_count;
  378. return 1;
  379. }
  380. sub add_words_to_completion {
  381. my ($tree, $string) = @_;
  382. while ($string =~ /(\w+)/g) {
  383. my $word = $1;
  384. if (length($word) <= 50) {
  385. add_tree_entry($tree, [split(//, $word)], $word);
  386. }
  387. }
  388. return 1;
  389. }
  390. sub code_interactive {
  391. require Encode;
  392. require File::Spec;
  393. require Term::ReadLine;
  394. my $term = Term::ReadLine->new("$name $version -- interactive mode");
  395. my $sidef;
  396. my $init_sidef = sub {
  397. $sidef = Sidef->new(
  398. name => '-i',
  399. opt => {i => 1, %args},
  400. parser_opt => {interactive => 1},
  401. );
  402. $sidef->execute_code(''); # warm-up
  403. };
  404. $init_sidef->();
  405. my ($copy_array, $copy_hash);
  406. $copy_array = sub {
  407. my ($array) = @_;
  408. my @copy;
  409. foreach my $item (@$array) {
  410. if (ref($item) eq 'ARRAY') {
  411. push @copy, __SUB__->($item);
  412. }
  413. elsif (ref($item) eq 'HASH') {
  414. push @copy, $copy_hash->($item);
  415. }
  416. else {
  417. push @copy, $item;
  418. }
  419. }
  420. \@copy;
  421. };
  422. $copy_hash = sub {
  423. my ($hash) = @_;
  424. my %copy;
  425. foreach my $key (keys %$hash) {
  426. my $value = $hash->{$key};
  427. if (ref($value) eq 'ARRAY') {
  428. $copy{$key} = $copy_array->($value);
  429. }
  430. elsif (ref($value) eq 'HASH') {
  431. $copy{$key} = __SUB__->($value);
  432. }
  433. else {
  434. $copy{$key} = $value;
  435. }
  436. }
  437. \%copy;
  438. };
  439. require Time::HiRes;
  440. print <<"EOT" if 0;
  441. ** ** **** * ********* *********
  442. * * ** * * **** ** ** ** ** ** ** **
  443. ** ** **** *** ********* * * *
  444. ** ** ** **** * * ****** ******
  445. * * * * * * * * * **** ** ** ** ** ** **
  446. ** ** ** **** ****** ****** * *
  447. ** ** **** * * * ********* ***
  448. * * ** * * **** ** ** ** ** ** ** **
  449. ** ** **** ********* ********* *
  450. EOT
  451. print <<"EOT";
  452. Sidef $version, running on \u$^O, using Perl $^V.
  453. Type "help", "copyright" or "license" for more information.
  454. EOT
  455. my $valid_lines = '';
  456. my ($vars, $ref_vars_refs);
  457. my $completion_tree;
  458. my $history_support = $term->can('ReadHistory') && $term->can('Attribs');
  459. my $history_file = File::Spec->catfile($sidef->get_sidef_config_dir(), 'sidef_history.txt');
  460. if ($history_support) {
  461. if (not -e $history_file) {
  462. open my $fh, '>', $history_file;
  463. }
  464. $completion_tree = create_completion_tree();
  465. my $attr = $term->Attribs;
  466. $attr->{basic_quote_characters} = q{};
  467. add_class_methods_to_completion($completion_tree);
  468. my @results;
  469. $attr->{completion_entry_function} = sub {
  470. my ($prefix, $state) = @_;
  471. my $root = '';
  472. if ($prefix !~ /^\w+\z/ and $prefix =~ /^(.*)\b(\w+)\z/) {
  473. $root = $1;
  474. $prefix = $2;
  475. }
  476. if ($state == 0) {
  477. @results = search_tree($completion_tree, [split(//, $prefix)]);
  478. }
  479. @results || return undef;
  480. $root . shift(@results);
  481. };
  482. $term->ReadHistory($history_file);
  483. }
  484. my $tΔ = 0;
  485. my @values;
  486. my $FH = undef;
  487. if (@ARGV) {
  488. my $file = shift(@ARGV);
  489. open $FH, '<:utf8', $file
  490. or die "Can't open file <<$file>> for reading: $!\n";
  491. }
  492. MAINLOOP: {
  493. my $line = '';
  494. LINE: {
  495. if (defined($FH) and !eof($FH)) {
  496. chomp(my $curr_line = <$FH>);
  497. if ($line eq '' and $curr_line =~ /^\s*__(?:END|DATA)__\s*\z/) {
  498. $curr_line .= "\n" . do { local $/; <$FH> };
  499. }
  500. if ($history_support and $curr_line ne '' and $line eq '') {
  501. $term->addhistory($curr_line =~ s/\R/\r/gr);
  502. }
  503. $line .= $curr_line;
  504. }
  505. else {
  506. $line .= Encode::decode_utf8($term->readline($line eq '' ? '> ' : ' ') // return);
  507. }
  508. if ($line eq 'help') {
  509. help_interactive($term);
  510. redo MAINLOOP;
  511. }
  512. elsif ($line eq '##') {
  513. say " *** last result computed in $tΔ seconds";
  514. redo MAINLOOP;
  515. }
  516. elsif ($line =~ /^#+\h*load\h+(.+)/) {
  517. my $file = unpack('A*', $1);
  518. open $FH, '<:utf8', $file or do {
  519. warn "Can't open file <<$file>> for reading: $!\n";
  520. redo MAINLOOP;
  521. };
  522. redo MAINLOOP;
  523. }
  524. elsif ($line =~ /^#+\h*exec\h+(.+)/) {
  525. my $file = unpack('A*', $1);
  526. $init_sidef->();
  527. open my $fh, '<:utf8', $file or do {
  528. warn "Can't open file <<$file>> for reading: $!\n";
  529. redo MAINLOOP;
  530. };
  531. $line = do { local $/; <$fh> };
  532. close $fh;
  533. }
  534. elsif ($line =~ /^#+\h*save\h+(.+)/) {
  535. my $file = unpack('A*', $1);
  536. open my $fh, '>:utf8', $file or do {
  537. warn "Can't open file <<$file>> for writing: $!\n";
  538. redo MAINLOOP;
  539. };
  540. print $fh $valid_lines;
  541. close $fh;
  542. say "** Created file: $file";
  543. }
  544. elsif ($line eq 'copyright') {
  545. print <<'EOT';
  546. Copyright © 2013-2024 Daniel Șuteu, Ioana Fălcușan
  547. All Rights Reserved.
  548. EOT
  549. redo MAINLOOP;
  550. }
  551. elsif ($line eq 'license') {
  552. print <<'EOT';
  553. This program is free software; you can redistribute it
  554. and/or modify it under the terms of the Artistic License (2.0).
  555. For more details, see the full text in the LICENSE file.
  556. This program is distributed in the hope that it will be
  557. useful, but without any warranty; without even the implied
  558. warranty of merchantability or fitness for a particular purpose.
  559. For more information, see:
  560. https://github.com/trizen/sidef
  561. https://www.perlfoundation.org/artistic-license-20.html
  562. EOT
  563. redo MAINLOOP;
  564. }
  565. }
  566. # Replace top-level variables and constants with globals
  567. if (not defined($args{r}) and not defined($args{R})) {
  568. $line =~ s/^\h*(?:var|define|const|static)\b/global/;
  569. }
  570. $vars = $copy_hash->($sidef->{parser}{vars});
  571. $ref_vars_refs = $copy_hash->($sidef->{parser}{ref_vars_refs});
  572. $line =~ s{#(-?[1-9][0-9]*)\b}{(abs($1) <= scalar(@values)) ? ('(' . $values[($1 < 0) ? $1 : $1-1]->{value} . ')') : "#$1"}ge;
  573. # Last character was '\': read the next line
  574. if ($line =~ /\\\s*\z/) {
  575. $line .= "\n";
  576. goto LINE;
  577. }
  578. my $ccode = eval { $sidef->compile_code($line, $args{r} ? 'Sidef' : ($args{R} || 'Perl')) };
  579. if ($@) {
  580. # Valid keywords for 'exit'
  581. if ($line eq 'q' or $line eq 'exit' or $line eq 'quit') {
  582. return;
  583. }
  584. # Reset the parser
  585. if ($line eq 'reset') {
  586. $init_sidef->();
  587. undef $vars;
  588. undef $ref_vars_refs;
  589. @values = ();
  590. redo;
  591. }
  592. # Restore parser variables
  593. if (defined($vars) and defined($ref_vars_refs)) {
  594. %{$sidef->{parser}{vars}} = %$vars;
  595. %{$sidef->{parser}{ref_vars_refs}} = %$ref_vars_refs;
  596. }
  597. # Give up if the previous line is blank,
  598. # or when it's impossible to recover from an error
  599. if (
  600. $@ =~ /is not declared in the current scope/i
  601. or $@ =~ /invalid \S+ declaration/i
  602. or $@ =~ /attempt to (?:use|call|delete) /i
  603. or $@ =~ /not declared in the current scope/i
  604. or $@ =~ /expected a block after/i
  605. or $@ =~ /unexpected end-of-statement/i
  606. or (
  607. $@ =~ /unbalanced|string terminator|delimiter/
  608. ? $line =~ /\R\R\z/
  609. : $line =~ /\R\z/
  610. )
  611. ) {
  612. warn $@;
  613. redo;
  614. }
  615. $line .= "\n";
  616. goto LINE;
  617. }
  618. else {
  619. $valid_lines .= "$line\n"; # store valid lines
  620. }
  621. if ($history_support) {
  622. if ($line =~ /\R/) {
  623. $term->addhistory($line =~ s/\R/\r/gr);
  624. }
  625. $term->append_history(1, $history_file);
  626. }
  627. if (defined($args{r}) or defined($args{R})) {
  628. output($ccode);
  629. }
  630. elsif ($line =~ /\S/ and not $line =~ /^\s*#.*$/) {
  631. my $t0 = eval { [Time::HiRes::gettimeofday()] };
  632. my @results = $sidef->execute_perl($ccode);
  633. if ($@) {
  634. print $@;
  635. }
  636. elsif ($history_support) {
  637. add_words_to_completion($completion_tree, $line);
  638. }
  639. $tΔ = eval { Time::HiRes::tv_interval($t0) };
  640. # use overload;
  641. # overload::StrVal($_) ? "$_" : $_->dump;
  642. my $dump = join(
  643. ', ',
  644. map {
  645. (ref($_) ? UNIVERSAL::can($_, 'dump') ? $_->dump : $_ : ($_ // 'nil'))
  646. . ((ref($_) eq 'Sidef::Types::Number::Number' and ref($$_) eq 'Math::MPFR' and Math::MPFR::Rmpfr_number_p($$_)) ? 'f' : '')
  647. } @results
  648. );
  649. $dump = "($dump)" if @results > 1;
  650. push @values,
  651. {
  652. type => ((scalar(@results) == 1) ? 'scalar' : 'list'),
  653. value => $dump,
  654. };
  655. say "#" . scalar(@values) . " = $dump";
  656. if ($history_support) {
  657. add_class_methods_to_completion($completion_tree);
  658. }
  659. }
  660. redo;
  661. }
  662. }
  663. sub _get_loaded_modules {
  664. my @modules;
  665. foreach my $key (sort { length($a) <=> length($b) || $a cmp $b } keys %INC) {
  666. if ($key =~ /^(Sidef\b.*)\.pm\z/) {
  667. push @modules, $1 =~ s{/}{::}gr;
  668. }
  669. }
  670. return @modules;
  671. }
  672. sub output {
  673. my ($content) = @_;
  674. my $out_fh = \*STDOUT;
  675. if (defined $args{o}) {
  676. open $out_fh, '>:utf8', $args{o}
  677. or die "Can't open file '$args{o}' for write: $!\n";
  678. }
  679. print {$out_fh} $content;
  680. return $out_fh;
  681. }
  682. sub dump_ast {
  683. my ($ast) = @_;
  684. eval { require Data::Dump };
  685. if ($@) {
  686. die qq{** "Data::Dump" is not installed!\n};
  687. }
  688. else {
  689. my $out_fh = output('');
  690. my $requirify = sub {
  691. join('', map { "require '" . (s{::}{/}gr) . ".pm';\n" } @_);
  692. };
  693. print {$out_fh} $requirify->(_get_loaded_modules());
  694. print {$out_fh} Data::Dump::pp($ast) . "\n";
  695. }
  696. }
  697. sub compile_to_perl {
  698. my (%opt) = @_;
  699. require File::Spec;
  700. require File::Basename;
  701. my $path = File::Spec->catdir(File::Basename::dirname($INC{'Sidef.pm'}), 'Sidef');
  702. my $package_content = <<"HEAD";
  703. #!$^X
  704. eval 'exec $^X -S \$0 \${1+"\$@"}'
  705. if 0; # not running under some shell
  706. use utf8;
  707. binmode STDIN, ":utf8";
  708. binmode STDOUT, ":utf8";
  709. binmode STDERR, ":utf8" if \$^P == 0; # to work under Devel::* modules
  710. my %REQ;
  711. my %MODULE;
  712. HEAD
  713. $package_content .= "BEGIN { %MODULE = (\n";
  714. require File::Find;
  715. File::Find::find(
  716. {
  717. no_chdir => 1,
  718. wanted => sub {
  719. if (/\.pm\z/ and -f) {
  720. local $/;
  721. open my $fh, '<:utf8', $_
  722. or die "Can't open file `$_` for reading: $!";
  723. my $token = tr/A-Za-z0-9/_/cr;
  724. my $content = <$fh>;
  725. if ($content =~ /^package\h+([\w:]+)/) {
  726. $package_content .= qq{'${1}' => };
  727. }
  728. else {
  729. die qq{ERROR: can't get the package name from file `$_`};
  730. }
  731. $package_content .= qq{<<'${token}',\n};
  732. $package_content .= $content;
  733. $package_content .= "\n$token\n";
  734. close $fh;
  735. }
  736. }
  737. } => ($path, $INC{'Sidef.pm'})
  738. );
  739. $package_content .= <<'FOOT';
  740. );
  741. sub __load_sidef_module__ {
  742. my ($name) = @_;
  743. if (not exists $REQ{$name}) {
  744. my $module = $name =~ s{::}{/}gr . '.pm';
  745. if (exists $MODULE{$name} and not exists $INC{$module}) {
  746. # Load the Sidef used modules
  747. $MODULE{$name} =~ s{^\h*
  748. use \h+ (?:
  749. parent \s+ qw\((.*?)\)
  750. | (Sidef::[\w:]+)
  751. )
  752. }{
  753. join(
  754. ";\n" => map{
  755. exists($REQ{$_})
  756. ? ()
  757. : "BEGIN{ main::__load_sidef_module__('${_}') }" } split(' ', $+)
  758. ) . (defined($1) ? "\nuse parent qw(-norequire $1);\n" : '')
  759. }gxmse;
  760. $INC{$module} = 1;
  761. eval($MODULE{$name});
  762. die "[FATAL ERROR] Can't load `$module`: $@" if $@;
  763. }
  764. else {
  765. require $module;
  766. }
  767. $REQ{$name} = 1;
  768. }
  769. return 1;
  770. }
  771. FOOT
  772. my $requirify = sub {
  773. join('', map { "__load_sidef_module__('${_}');\n" } grep { $_ ne 'Sidef::Optimizer' } @_);
  774. };
  775. $package_content .= $requirify->(_get_loaded_modules(), 'Sidef::Module::OO', 'Sidef::Module::Func');
  776. my @used_pkgs;
  777. while ($opt{code} =~ /^use (Sidef::\S+);$/gm) {
  778. push @used_pkgs, $1;
  779. }
  780. $package_content .= $requirify->(@used_pkgs) if @used_pkgs;
  781. $package_content .= "}\n\n";
  782. my $out_fh = output('');
  783. print {$out_fh} $package_content;
  784. print {$out_fh} $opt{code};
  785. }
  786. __END__
  787. =encoding utf8
  788. =head1 NAME
  789. ** ** **** * ********* *********
  790. * * ** * * **** ** ** ** ** ** ** **
  791. ** ** **** *** ********* * * *
  792. ** ** ** **** * * ****** ******
  793. * * * * * * * * * **** ** ** ** ** ** **
  794. ** ** ** **** ****** ****** * *
  795. ** ** **** * * * ********* ***
  796. * * ** * * **** ** ** ** ** ** ** **
  797. ** ** **** ********* ********* *
  798. =cut
  799. =head1 SYNOPSIS
  800. Usage: sidef [switches] [--] [programfile] [arguments]
  801. -c compile the code into a Perl program
  802. -C check syntax only
  803. -D dump the syntax tree of a program
  804. -E program one line of program
  805. -H interactive help
  806. -i file execute a program in interactive mode
  807. -k keep track of potentially incorrect parser interpretations
  808. -M mode set the rounding mode of floating-point numbers
  809. valid modes: [near], zero, inf, +inf, -inf, faith
  810. -N options modify class-variables inside the Number class
  811. valid format: 'VERBOSE=1; USE_YAFU=1; USE_PRIMECOUNT=1'
  812. -o file file where to dump the output
  813. -O level perform code optimizations before execution
  814. valid levels: [0], 1, 2
  815. -P int set the precision of floating-point numbers (default: 48)
  816. -r parse and deparse a Sidef program
  817. -R lang parse and deparse a Sidef program into a given language
  818. valid values: sidef, perl
  819. -s save compiled code in a database to reduce boot-time
  820. -t treat all command-line arguments as scripts
  821. -v print version number and exit
  822. -w enable warnings with stack backtrace
  823. -W make warnings fatal (with stack backtrace)
  824. Run 'sidef' for entering the interactive mode.
  825. =head1 HELLO WORLD
  826. A Sidef script can be written in any text editor and, by convention, it has the C<.sf> extension.
  827. The content of a simple I<Hello World> program looks like this:
  828. say "Hello, 世界"
  829. If we save the content in a new file called C<hello.sf>, we can execute the code by running:
  830. sidef hello.sf
  831. =head1 ONE LINE OF PROGRAM
  832. The C<-E code> command will execute the code specified as a command-line argument:
  833. sidef -E "say 'hello world'"
  834. Outputs:
  835. hello world
  836. =head1 ITERACTIVE MODE
  837. The interactive mode (a.k.a. REPL) is available by simply executing the C<sidef> command, or by specifying the C<-i> command-line switch:
  838. $ sidef -i
  839. Sidef 24.05, running on Linux, using Perl v5.38.2.
  840. Type "help", "copyright" or "license" for more information.
  841. > n = 41
  842. #1 = 41
  843. > n**2 + n - 1
  844. #2 = 1721
  845. > is_prime(#2)
  846. #3 = true
  847. >
  848. =head1 SPECIAL REPL COMMANDS
  849. The REPL supports the following special commands:
  850. =over 4
  851. =item * Display the duration it took to execute the previous command:
  852. > ##
  853. =item * Refer to a previous output value, using the C<#n> syntax (a negative value for C<n> is also supported):
  854. > 3+4
  855. #1 = 7
  856. > sqrt(#1)
  857. =item * Load a Sidef file inside the REPL, line by line:
  858. > # load filename.sf
  859. =item * Execute a Sidef file inside the REPL:
  860. > # exec filename.sf
  861. =item * Save the code from the REPL inside a file:
  862. > # save filename.sf
  863. =item * Reset the REPL:
  864. > reset
  865. =item * Close the REPL:
  866. > quit
  867. =back
  868. =head1 OPTIMIZATION
  869. The C<-O level> command-line option controls the level of optimization before the execution begins.
  870. Currently, there are three levels of optimization available:
  871. 0 -- Does nothing. (default)
  872. 1 -- Does constant folding on the AST. (recommended)
  873. 2 -- Does constant folding, after which it deparses the AST into Sidef code, parses the code again and does more constant folding on the new AST.
  874. In the end, the code is translated to Perl and is ready to be executed. In the translation process, several other optimizations are also performed.
  875. =head1 NUMBER OPTIONS
  876. The C<-N> option can be used for changing the class-variables in the Number class:
  877. sidef -N 'PREC = 192' # precision for floating-point numbers
  878. sidef -N 'ROUND = 0' # rounding mode for floating-point numbers
  879. sidef -N 'VERBOSE = false' # true to enable verbose/debug mode
  880. sidef -N 'USE_YAFU = false' # true to use YAFU for factoring large integers
  881. sidef -N 'USE_PFGW = false' # true to use PFGW64 as a primality pretest for large enough n
  882. sidef -N 'USE_PARI_GP = false' # true to use PARI/GP in several methods
  883. sidef -N 'USE_FACTORDB = false' # true to use factordb.com for factoring large integers
  884. sidef -N 'USE_PRIMESUM = false' # true to use Kim Walisch's primesum in prime_sum(n)
  885. sidef -N 'USE_PRIMECOUNT = false' # true to use Kim Walisch's primecount in prime_count(n)
  886. sidef -N 'USE_CONJECTURES = false' # true to use conjectured methods for better performance
  887. sidef -N 'SPECIAL_FACTORS = true' # true to try to find factors of special form in factor(n)
  888. Multiple options can be separated with C<;>, as in:
  889. sidef -N 'VERBOSE = true; USE_FACTORDB = true' -E 'say factor(2**256 + 1)'
  890. The C<-P> option can be used for changing the precision of floating-point numbers:
  891. sidef -P 1024 -E 'say sqrt(2)'
  892. The C<-M> option can be used for changing the rounding-mode for floating-point numbers:
  893. sidef -M 'near' # round to nearest (default)
  894. sidef -M 'zero' # round towards zero
  895. sidef -M 'inf' # round away from zero
  896. sidef -M '+inf' # round towards +Infinity
  897. sidef -M '-inf' # round towards -Infinity
  898. sidef -M 'faith' # faithful rounding
  899. =head1 PARSER WARNINGS
  900. Sidef provides the C<-k> option which will keep track of all the possible incorrect parser interpretations.
  901. For example, if we declare the following function, but we misspell its name when we call it, Sidef will interpret it as a method call, which is probably not what we want:
  902. func foo(n) { say n }
  903. fo(42) # will get interpreted as `42.fo`
  904. When the command-line option C<-k> is specified, the following warning is produced:
  905. [INFO] `fo` is parsed as a prefix method-call at script.sf line 2
  906. =head1 DEPARSING
  907. Deparsing is the reverse process of parsing, which translates the AST back into code. Currently, Sidef supports deparsing into two languages with the C<-R lang> command-line switch:
  908. =over 4
  909. =item -R perl
  910. Deparses the AST into valid Perl code.
  911. =item -R sidef
  912. Deparses the AST into valid Sidef code.
  913. =back
  914. Example:
  915. sidef -Rperl script.sf | perl
  916. The C<-Rsidef> switch (or simply C<-r>) is useful for verifying how the code is parsed:
  917. sidef -r -E '1 + 2/3'
  918. outputs:
  919. (1)->+((2)->/(3));
  920. =head1 DUMPING THE AST
  921. The C<-D> command-line option dumps the abstract syntax tree (AST) of a given Sidef program:
  922. sidef -D script.sf # will dump the AST of script.sf
  923. =head1 PRECOMPILATION
  924. Sidef supports experimental precompilation by saving compiled code inside a database, which is updated automatically and sanitized periodically.
  925. This method reduces significantly the boot-time of very large Sidef scripts, and it works as following:
  926. =over 4
  927. =item * it checks the database with the MD5 of the code
  928. =item * if the MD5 exists inside the database, it returns the executable code
  929. =back
  930. otherwise:
  931. =over 4
  932. =item * parses the code and generates the executable code
  933. =item * stores the executable code inside the database with the MD5 of the code
  934. =back
  935. Next time when the same code is executed, Sidef will simply retrieve the executable code from the database, without generating it again:
  936. sidef -s script.sf # may load slow the first time
  937. sidef -s script.sf # will load much faster the second time
  938. =head1 COMPILATION
  939. A Sidef script can be compiled to a stand-alone Perl program by using the C<-c> command-line option:
  940. sidef -o out.pl -c script.sf
  941. The above command will compile the file C<script.sf> into the Perl script C<out.pl>, which will include the entire implementation code of Sidef.
  942. Currently, Sidef code that contains C<eval()> cannot be compiled correctly to Perl, as it requires some parse-time information for run-time evaluation, which is lost in the compilation process.
  943. =head1 WWW
  944. You can find more info about Sidef, by clicking on the following links:
  945. =over 2
  946. =item * GitHub: L<https://github.com/trizen/sidef>
  947. =item * Gitbook: L<https://trizen.gitbook.io/sidef-lang/>
  948. =item * Tutorial: L<https://notabug.org/trizen/sidef/wiki>
  949. =item * RosettaCode: L<https://rosettacode.org/wiki/Sidef>
  950. =back
  951. =head1 LICENSE AND COPYRIGHT
  952. Copyright (C) 2013-2024 Daniel Șuteu, Ioana Fălcușan
  953. This program is free software; you can redistribute it and/or modify it
  954. under the terms of the B<Artistic License (2.0)>. You may obtain a copy
  955. of the full license at:
  956. L<https://www.perlfoundation.org/artistic-license-20.html>
  957. Any use, modification, and distribution of the Standard or Modified
  958. Versions is governed by this Artistic License. By using, modifying or
  959. distributing the Package, you accept this license. Do not use, modify,
  960. or distribute the Package, if you do not accept this license.
  961. If your Modified Version has been derived from a Modified Version made
  962. by someone other than you, you are nevertheless required to ensure that
  963. your Modified Version complies with the requirements of this license.
  964. This license does not grant you the right to use any trademark, service
  965. mark, tradename, or logo of the Copyright Holder.
  966. This license includes the non-exclusive, worldwide, free-of-charge
  967. patent license to make, have made, use, offer to sell, sell, import and
  968. otherwise transfer the Package with respect to any patent claims
  969. licensable by the Copyright Holder that are necessarily infringed by the
  970. Package. If you institute patent litigation (including a cross-claim or
  971. counterclaim) against any party alleging that the Package constitutes
  972. direct or contributory patent infringement, then this Artistic License
  973. to you shall terminate on the date that such litigation is filed.
  974. Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
  975. AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
  976. THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  977. PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
  978. YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
  979. CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
  980. CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
  981. EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  982. =cut