crystal-lang / crystal

The Crystal Programming Language
https://crystal-lang.org
Apache License 2.0
19.46k stars 1.62k forks source link

Feature request: `--require` option #9364

Open simonhf opened 4 years ago

simonhf commented 4 years ago

It would be great to have the --prelude option working for eval as well as build. Why?

Crystal version:

$ crystal -v
Crystal 0.34.0 [4401e90f0] (2020-04-06)

LLVM: 8.0.0
Default target: x86_64-unknown-linux-gnu

--prelude option currently only for build but not eval:

$ crystal build -h | egrep prelude 
    --prelude                        Use given file as prelude
$ crystal eval -h | egrep prelude  
$
asterite commented 4 years ago

Just as a clarification, the intent here is to have --require file and that just results in require "file" being added to the evaluated code.

simonhf commented 4 years ago

Right, sort of the crystal equivalent of the gcc force include command line option [1].

[1] https://stackoverflow.com/questions/3387453/include-header-files-using-command-line-option

asterite commented 4 years ago

Ruby has this too by doing ruby -roptparse which does require "optparse" at the beginning of the script.

jhass commented 4 years ago

I don't think we really need this, Crystal doesn't have many features geared towards one off command line usage, so this would cater to a usecase that's otherwise poorly supported and which other tools just will always solve better than Crystal.

simonhf commented 4 years ago

Come to think of it, maybe the eval option could be merged with the build option? Instead of specifying the 'programfile' on the command line, then the 'source' could be optionally specified. Then all the same options could be used for eval as for build?

The proposed --require option would then be added to just build and would automatically work with eval which would just be part of build in the future?

For example, instead of:

$ crystal build -h | head -1
Usage: crystal build [options] [programfile] [--] [arguments]

$ crystal eval -h | head -1     
Usage: crystal eval [options] [source]

The merged functionality would be:

$ crystal build -h | head -1
Usage: crystal build [options] [programfile]|[source] [--] [arguments]

Why should it matter for the build option if the source code comes from a program file or from the command line?

This would be particularly useful if the crystal one liner via eval is using a module which relies on a build time --define ...

straight-shoota commented 4 years ago

What's the use case? For build I don't see how that would be useful. It makes sense to have all dependend code required from code, not from a CLI option. For eval, it's essentially

crystal eval --require "foo" 'Foo.hello_world'
# vs
crystal eval 'require "foo"; Foo.hello_world'

The latter is even shorter.

This would be particularly useful if the crystal one liner via eval is using a module which relies on a build time --define ...

What's a "build time --define"?

simonhf commented 4 years ago
crystal eval --require "foo" 'Foo.hello_world'
# vs
crystal eval 'require "foo"; Foo.hello_world'

The latter is even shorter.

Yeah, but just like shell users short cut ls -al to ll, the former can be short cut too but not the latter. Let's say the short cut is cre then the short cut would be the shortest, e.g.:

cre 'Foo.hello_world'

But I think require foo maybe not the best example. It would be more like --require generic-eval-environment.cr which would auto do boiler plate things that you might not want to type on the command line every time, e.g. STDOUT.flush_on_newline=true; is a bit of a handful to type often :-)

simonhf commented 4 years ago

This would be particularly useful if the crystal one liner via eval is using a module which relies on a build time --define ...

What's a "build time --define"?

Let's you define a flag which can be used at compile time to e.g. make a debug build versus a release build, etc:

$ crystal build -h | egrep define
    -D FLAG, --define FLAG           Define a compile-time flag
simonhf commented 4 years ago

... Crystal doesn't have many features geared towards one off command line usage, ...

Why do you say this, because AFAICT Crystal seems to have a really well functioning one liner capability via the eval command line option, or? And although I understand it might have been created more for Crystal testing purposes, it seems to work really well for general purpose one liners, too. Some Crystal users appear to use eval every day, while some virtually never use it. But if it works well for some people, why neglect it?

straight-shoota commented 4 years ago

You can already have such a short cut with the current CLI state:

$ cre () {
  crystal eval "require \"generic-eval-environment.cr\"; $0"
}
$ cre 'Foo.hello_world'

However, a benefit of external require would be that it does not mess up code locations in the user string.

Maybe as an alternative eval could treat each CLI argument as an individual expression. Currently it just concatenates them with whitespace in between, so you can build a single Crystal expression from separate arguments (such as crystal eval puts \"foo\"). I don't think that's particularly useful, though. This would then allow to easily inject arbitrary code with shell aliases. That includes custom requires. And it's much more versatile than a --require option. If you just want simple configuration settings for example, you can just inject STDOUT.flush_on_newline=true; and don't need to have that in a file available to the compiler instance.

simonhf commented 4 years ago

Thanks, and it works!

Even though I expected it not to work! I expected there to be some kind of conflict between the double quotes in the shell function wrapping $1 and the double quotes in the command line source code... but the shell seems to work its magic :-) The only thing that I don't know how to get is a verbatim copy of the Crystal command line actually run with all the correct quote escaping, etc.

$ echo 'puts %[- loaded |generic-eval-environment.cr| !]; STDOUT.flush_on_newline=true;' > generic-eval-environment.cr
$ cre () {
  echo crystal eval "require \"./generic-eval-environment.cr\"; $1"
  crystal eval "require \"./generic-eval-environment.cr\"; $1"
}
$ cre 'x = "abcdefg"; x =~ /(.*)(c.e).*/; _,a,b = $~; printf %[- a=%s b=%s // |$SHELL| !\n], a, b;'
crystal eval require "./generic-eval-environment.cr"; x = "abcdefg"; x =~ /(.*)(c.e).*/; _,a,b = $~; printf %[- a=%s b=%s // |$SHELL| !\n], a, b;
- loaded |generic-eval-environment.cr| !
- a=ab b=cde // |$SHELL| !
simonhf commented 4 years ago

I was fiddling around with a different way to cache the last 50 cre builds and came up with this:

$ cat cre
#!/usr/bin/perl
use strict;
use Digest::SHA;
my $body = sprintf qq[STDOUT.flush_on_newline=true; ];
foreach my $arg (@ARGV) {
    $body .= sprintf qq[%s], $arg;
}
my $path = sprintf qq[%s/.cre] , $ENV{HOME};
my $src  = sprintf qq[%s/%s.cr], $path, Digest::SHA::sha256_hex($body);
my $exe  = sprintf qq[%s.exe]  , $src;
my $cmd  = sprintf qq[crystal build -o %s %s 2>&1], $exe, $src;
if (not -e $exe) {
    mkdir $path  || die sprintf qq[ERROR: cannot create folder %s: $!], $path;
    open(my $fd, '>', $src) || die sprintf qq[ERROR: cannot open file %s: $!], $src;
    syswrite($fd, $body);
    close $fd;
    printf qq[- running: %s\n], $cmd if (exists $ENV{CRE_DEBUG});
    print `$cmd`;
}
my $sorted_files_cmd = sprintf qq[ls -1rt %s 2>&1], $path;
printf qq[- running: %s\n], $sorted_files_cmd if (exists $ENV{CRE_DEBUG});
my @sorted_files = `$sorted_files_cmd`;
while (scalar @sorted_files > 100) {
    my $temp_file_to_delete = sprintf qq[%s/%s], $path, shift @sorted_files;
    chomp $temp_file_to_delete;
    printf qq[- deleting oldest temp cache file: %s\n], $temp_file_to_delete if (exists $ENV{CRE_DEBUG});
    unlink $temp_file_to_delete || die sprintf qq[ERROR: cannot delete oldest temp cache file %s: $!], $temp_file_to_delete;
}
printf qq[- running: %s\n], $exe if (exists $ENV{CRE_DEBUG});
system $exe;

$ chmod +x cre

$ rm -rf ~/.cre/

$ CRE_DEBUG=1 /usr/bin/time ./cre 'x = "abcdefg"; x =~ /(.*)(c.e).*/; _,a,b = $~; printf %[- a=%s b=%s // |$SHELL| !\n], a, b;'
- running: crystal build -o /home/simon/.cre/9cddcf6d2334e104e06c4dd09514e61499e66b3c0bda5bdddb8e69ef322cc647.cr.exe /home/simon/.cre/9cddcf6d2334e104e06c4dd09514e61499e66b3c0bda5bdddb8e69ef322cc647.cr 2>&1
- running: /home/simon/.cre/9cddcf6d2334e104e06c4dd09514e61499e66b3c0bda5bdddb8e69ef322cc647.cr.exe
- a=ab b=cde // |$SHELL| !
1.11user 0.28system 0:00.77elapsed 179%CPU (0avgtext+0avgdata 144456maxresident)k
0inputs+3064outputs (0major+60646minor)pagefaults 0swaps

$ CRE_DEBUG=1 /usr/bin/time ./cre 'x = "abcdefg"; x =~ /(.*)(c.e).*/; _,a,b = $~; printf %[- a=%s b=%s // |$SHELL| !\n], a, b;'
- running: /home/simon/.cre/9cddcf6d2334e104e06c4dd09514e61499e66b3c0bda5bdddb8e69ef322cc647.cr.exe
- a=ab b=cde // |$SHELL| !
0.03user 0.00system 0:00.02elapsed 176%CPU (0avgtext+0avgdata 6184maxresident)k
0inputs+0outputs (0major+923minor)pagefaults 0swaps

$ /usr/bin/time ./cre 'x = "abcdefg"; x =~ /(.*)(c.e).*/; _,a,b = $~; printf %[- a=%s b=%s // |$SHELL| !\n], a, b;'
- a=ab b=cde // |$SHELL| !
0.00user 0.02system 0:00.01elapsed 172%CPU (0avgtext+0avgdata 6204maxresident)k
0inputs+0outputs (0major+920minor)pagefaults 0swaps

$ wc --bytes ~/.cre/*
    121 /home/simon/.cre/9cddcf6d2334e104e06c4dd09514e61499e66b3c0bda5bdddb8e69ef322cc647.cr
1559656 /home/simon/.cre/9cddcf6d2334e104e06c4dd09514e61499e66b3c0bda5bdddb8e69ef322cc647.cr.exe

$ cat ~/.cre/9cddcf6d2334e104e06c4dd09514e61499e66b3c0bda5bdddb8e69ef322cc647.cr
STDOUT.flush_on_newline=true; x = "abcdefg"; x =~ /(.*)(c.e).*/; _,a,b = $~; printf %[- a=%s b=%s // |$SHELL| !\n], a, b;
simonhf commented 4 years ago

And then I was thinking.... Why use Perl to create the Crystal one liner wrapper script? Why not use Crystal? This would end up being my first ever Crystal script... so please be gentle with the comments, but all comments welcome! :-)

$ cat cre.cr
require "openssl"
sha256 = OpenSSL::Digest.new("sha256")
body = sprintf %[STDOUT.flush_on_newline=true;]
ARGV.each {|a| body += sprintf %[ %s], a}
path = sprintf "%s/.cache/cre" , ENV["HOME"]
src  = sprintf "%s/%s.cr", path, sha256.update(body).hexdigest
exe  = sprintf "%s.exe"  , src
cmd  = sprintf "crystal build --stats --progress -o %s %s 2>&1 > %s.log", exe, src, src
printf "- body=%s\n- path=%s\n- src=%s\n- exe=%s\n- cmd=%s\n", body, path, src, exe, cmd if ENV.has_key?("CRE_DEBUG")
if ! File.file?(exe)
    Dir.mkdir(path) if ! File.directory?(path)
    File.write(src, body)
    printf "- running: %s\n", cmd if ENV.has_key?("CRE_DEBUG")
    print `#{cmd}`
end
%w(cr cr.log cr.exe).each { |s|
    sorted_files_cmd = sprintf "ls -1rt %s/*.%s 2>&1", path, s;
    printf "- running: %s\n", sorted_files_cmd if ENV.has_key?("CRE_DEBUG")
    sorted_files = `#{sorted_files_cmd}`.split
    while sorted_files.size > 50
        target_file = sorted_files.shift
        printf "- deleting oldest temp cache file: %s\n", target_file if ENV.has_key?("CRE_DEBUG")
        File.delete(target_file)
    end
}
printf "- running: %s\n", exe if ENV.has_key?("CRE_DEBUG")
system exe;
$ crystal build cre.cr

$ rm -rf ~/.cre/

$ CRE_DEBUG=1 /usr/bin/time ./cre 'x = "abcdefg"; x =~ /(.*)(c.e).*/; _,a,b = $~; printf %[- a=%s b=%s // |$SHELL| !\n], a, b;'
- body=STDOUT.flush_on_newline=true; x = "abcdefg"; x =~ /(.*)(c.e).*/; _,a,b = $~; printf %[- a=%s b=%s // |$SHELL| !\n], a, b;
- path=/home/simon/.cre
- src=/home/simon/.cre/9cddcf6d2334e104e06c4dd09514e61499e66b3c0bda5bdddb8e69ef322cc647.cr
- exe=/home/simon/.cre/9cddcf6d2334e104e06c4dd09514e61499e66b3c0bda5bdddb8e69ef322cc647.cr.exe
- cmd=crystal build -o /home/simon/.cre/9cddcf6d2334e104e06c4dd09514e61499e66b3c0bda5bdddb8e69ef322cc647.cr.exe /home/simon/.cre/9cddcf6d2334e104e06c4dd09514e61499e66b3c0bda5bdddb8e69ef322cc647.cr 2>&1
- running: crystal build -o /home/simon/.cre/9cddcf6d2334e104e06c4dd09514e61499e66b3c0bda5bdddb8e69ef322cc647.cr.exe /home/simon/.cre/9cddcf6d2334e104e06c4dd09514e61499e66b3c0bda5bdddb8e69ef322cc647.cr 2>&1
- running: ls -1rt /home/simon/.cre/*.cr 2>&1
- running: ls -1rt /home/simon/.cre/*.cr.exe 2>&1
- running: /home/simon/.cre/9cddcf6d2334e104e06c4dd09514e61499e66b3c0bda5bdddb8e69ef322cc647.cr.exe
- a=ab b=cde // |$SHELL| !
1.12user 0.32system 0:00.82elapsed 177%CPU (0avgtext+0avgdata 144544maxresident)k
0inputs+3064outputs (0major+61300minor)pagefaults 0swaps

$ CRE_DEBUG=1 /usr/bin/time ./cre 'x = "abcdefg"; x =~ /(.*)(c.e).*/; _,a,b = $~; printf %[- a=%s b=%s // |$SHELL| !\n], a, b;'
- body=STDOUT.flush_on_newline=true; x = "abcdefg"; x =~ /(.*)(c.e).*/; _,a,b = $~; printf %[- a=%s b=%s // |$SHELL| !\n], a, b;
- path=/home/simon/.cre
- src=/home/simon/.cre/9cddcf6d2334e104e06c4dd09514e61499e66b3c0bda5bdddb8e69ef322cc647.cr
- exe=/home/simon/.cre/9cddcf6d2334e104e06c4dd09514e61499e66b3c0bda5bdddb8e69ef322cc647.cr.exe
- cmd=crystal build -o /home/simon/.cre/9cddcf6d2334e104e06c4dd09514e61499e66b3c0bda5bdddb8e69ef322cc647.cr.exe /home/simon/.cre/9cddcf6d2334e104e06c4dd09514e61499e66b3c0bda5bdddb8e69ef322cc647.cr 2>&1
- running: ls -1rt /home/simon/.cre/*.cr 2>&1
- running: ls -1rt /home/simon/.cre/*.cr.exe 2>&1
- running: /home/simon/.cre/9cddcf6d2334e104e06c4dd09514e61499e66b3c0bda5bdddb8e69ef322cc647.cr.exe
- a=ab b=cde // |$SHELL| !
0.00user 0.04system 0:00.02elapsed 235%CPU (0avgtext+0avgdata 6736maxresident)k
0inputs+0outputs (0major+1568minor)pagefaults 0swaps

$ /usr/bin/time ./cre 'x = "abcdefg"; x =~ /(.*)(c.e).*/; _,a,b = $~; printf %[- a=%s b=%s // |$SHELL| !\n], a, b;'
- a=ab b=cde // |$SHELL| !
0.00user 0.04system 0:00.02elapsed 250%CPU (0avgtext+0avgdata 6804maxresident)k
0inputs+0outputs (0major+1561minor)pagefaults 0swaps

$ wc --bytes ~/.cre/*
    121 /home/simon/.cre/9cddcf6d2334e104e06c4dd09514e61499e66b3c0bda5bdddb8e69ef322cc647.cr
1559656 /home/simon/.cre/9cddcf6d2334e104e06c4dd09514e61499e66b3c0bda5bdddb8e69ef322cc647.cr.exe

$ cat ~/.cre/9cddcf6d2334e104e06c4dd09514e61499e66b3c0bda5bdddb8e69ef322cc647.cr
STDOUT.flush_on_newline=true; x = "abcdefg"; x =~ /(.*)(c.e).*/; _,a,b = $~; printf %[- a=%s b=%s // |$SHELL| !\n], a, b;
simonhf commented 4 years ago

And some Crystal gurus helped me refine the script a little with extra Crystal features:

$ cat cre.cr
require "file_utils"
require "openssl"
require "log"; Log.setup_from_env # e.g. enable with CRYSTAL_LOG_LEVEL=DEBUG

CACHE_SIZE   = 50
CACHE_PATH   = File.expand_path("~/.cache/cre", home: true)
BOILER_PLATE = "STDOUT.flush_on_newline=true;"

body = "#{BOILER_PLATE} #{ARGV.join(' ')}"
path = "#{CACHE_PATH}/#{OpenSSL::Digest.new("sha256").update(body).hexdigest}/"
name = "one-liner.cr"
src  = "#{path}#{name}"
log  = "#{src}.log"
exe  = "#{src}.exe"
cmd  = "cd #{path} ; crystal build --stats --progress -o #{name}.exe #{name} 2>&1 > #{name}.log"
aged = sprintf "ls -1drt %s/* 2>&1", CACHE_PATH;
aged = "ls -1drt #{CACHE_PATH}/* 2>&1"
{% for v in %w[body path name src log exe cmd aged] %}; Log.debug { "#{{{v}}}=#{{{v.id}}}" }; {% end %}

Log.debug { "#{File.executable?(exe) ? "Reusing" : "Compiling"} file #{exe}" }
if ! File.executable?(exe)
    Dir.mkdir_p(path)
    File.write src, body
    `#{cmd}`
    Log.fatal { "Failed to build #{exe}" } if ! File.executable?(exe)
end

sorted_folders = `#{aged}`.split
while sorted_folders.size > CACHE_SIZE
    target_folder = sorted_folders.shift
    Log.debug { "Pruning cache folder: #{target_folder}" }
    FileUtils.rm_rf(target_folder)
end

Log.debug { "Running #{exe}" }
Process.exec exe
$ crystal build cre.cr

$ rm -rf ~/.cache/cre/

$ CRYSTAL_LOG_LEVEL=DEBUG /usr/bin/time ./cre 'x = "abcdefg"; x =~ /(.*)(c.e).*/; _,a,b = $~; printf %[- a=%s b=%s // |$SHELL| !\n], a, b;'
D, [2020-05-28T23:47:27.444090000Z #66625]   DEBUG -- cre:: body=STDOUT.flush_on_newline=true;x = "abcdefg"; x =~ /(.*)(c.e).*/; _,a,b = $~; printf %[- a=%s b=%s // |$SHELL| !\n], a, b;
D, [2020-05-28T23:47:27.444172000Z #66625]   DEBUG -- cre:: path=/home/simon/.cache/cre/28fbe9a46fb80c1243764f41d92c8ba3862df2f8ac5bfa094a5fe586e3dd86d9/
D, [2020-05-28T23:47:27.444210000Z #66625]   DEBUG -- cre:: name=one-liner.cr
D, [2020-05-28T23:47:27.444256000Z #66625]   DEBUG -- cre:: src=/home/simon/.cache/cre/28fbe9a46fb80c1243764f41d92c8ba3862df2f8ac5bfa094a5fe586e3dd86d9/one-liner.cr
D, [2020-05-28T23:47:27.444295000Z #66625]   DEBUG -- cre:: exe=/home/simon/.cache/cre/28fbe9a46fb80c1243764f41d92c8ba3862df2f8ac5bfa094a5fe586e3dd86d9/one-liner.cr.exe
D, [2020-05-28T23:47:27.444358000Z #66625]   DEBUG -- cre:: log=/home/simon/.cache/cre/28fbe9a46fb80c1243764f41d92c8ba3862df2f8ac5bfa094a5fe586e3dd86d9/one-liner.cr.log
D, [2020-05-28T23:47:27.444403000Z #66625]   DEBUG -- cre:: cmd=cd /home/simon/.cache/cre/28fbe9a46fb80c1243764f41d92c8ba3862df2f8ac5bfa094a5fe586e3dd86d9/ ; crystal build --stats --progress -o one-liner.cr.exe one-liner.cr 2>&1 > one-liner.cr.log
D, [2020-05-28T23:47:27.444454000Z #66625]   DEBUG -- cre:: aged=ls -1drt /home/simon/.cache/cre/* 2>&1
D, [2020-05-28T23:47:27.444546000Z #66625]   DEBUG -- cre:: Compiling file /home/simon/.cache/cre/28fbe9a46fb80c1243764f41d92c8ba3862df2f8ac5bfa094a5fe586e3dd86d9/one-liner.cr.exe
D, [2020-05-28T23:47:28.712664000Z #66625]   DEBUG -- cre:: Running /home/simon/.cache/cre/28fbe9a46fb80c1243764f41d92c8ba3862df2f8ac5bfa094a5fe586e3dd86d9/one-liner.cr.exe
- a=ab b=cde // |$SHELL| !
2.28user 0.56system 0:01.28elapsed 220%CPU (0avgtext+0avgdata 144296maxresident)k
0inputs+13064outputs (0major+72963minor)pagefaults 0swaps

$ CRYSTAL_LOG_LEVEL=DEBUG /usr/bin/time ./cre 'x = "abcdefg"; x =~ /(.*)(c.e).*/; _,a,b = $~; printf %[- a=%s b=%s // |$SHELL| !\n], a, b;'
D, [2020-05-28T23:48:13.819790000Z #66718]   DEBUG -- cre:: body=STDOUT.flush_on_newline=true;x = "abcdefg"; x =~ /(.*)(c.e).*/; _,a,b = $~; printf %[- a=%s b=%s // |$SHELL| !\n], a, b;
D, [2020-05-28T23:48:13.819900000Z #66718]   DEBUG -- cre:: path=/home/simon/.cache/cre/28fbe9a46fb80c1243764f41d92c8ba3862df2f8ac5bfa094a5fe586e3dd86d9/
D, [2020-05-28T23:48:13.819988000Z #66718]   DEBUG -- cre:: name=one-liner.cr
D, [2020-05-28T23:48:13.820039000Z #66718]   DEBUG -- cre:: src=/home/simon/.cache/cre/28fbe9a46fb80c1243764f41d92c8ba3862df2f8ac5bfa094a5fe586e3dd86d9/one-liner.cr
D, [2020-05-28T23:48:13.820084000Z #66718]   DEBUG -- cre:: exe=/home/simon/.cache/cre/28fbe9a46fb80c1243764f41d92c8ba3862df2f8ac5bfa094a5fe586e3dd86d9/one-liner.cr.exe
D, [2020-05-28T23:48:13.820110000Z #66718]   DEBUG -- cre:: log=/home/simon/.cache/cre/28fbe9a46fb80c1243764f41d92c8ba3862df2f8ac5bfa094a5fe586e3dd86d9/one-liner.cr.log
D, [2020-05-28T23:48:13.820139000Z #66718]   DEBUG -- cre:: cmd=cd /home/simon/.cache/cre/28fbe9a46fb80c1243764f41d92c8ba3862df2f8ac5bfa094a5fe586e3dd86d9/ ; crystal build --stats --progress -o one-liner.cr.exe one-liner.cr 2>&1 > one-liner.cr.log
D, [2020-05-28T23:48:13.820176000Z #66718]   DEBUG -- cre:: aged=ls -1drt /home/simon/.cache/cre/* 2>&1
D, [2020-05-28T23:48:13.820201000Z #66718]   DEBUG -- cre:: Reusing file /home/simon/.cache/cre/28fbe9a46fb80c1243764f41d92c8ba3862df2f8ac5bfa094a5fe586e3dd86d9/one-liner.cr.exe
D, [2020-05-28T23:48:13.830203000Z #66718]   DEBUG -- cre:: Running /home/simon/.cache/cre/28fbe9a46fb80c1243764f41d92c8ba3862df2f8ac5bfa094a5fe586e3dd86d9/one-liner.cr.exe
- a=ab b=cde // |$SHELL| !
0.00user 0.03system 0:00.02elapsed 181%CPU (0avgtext+0avgdata 7108maxresident)k
0inputs+0outputs (0major+1130minor)pagefaults 0swaps

$ /usr/bin/time ./cre 'x = "abcdefg"; x =~ /(.*)(c.e).*/; _,a,b = $~; printf %[- a=%s b=%s // |$SHELL| !\n], a, b;'
- a=ab b=cde // |$SHELL| !
0.01user 0.01system 0:00.01elapsed 186%CPU (0avgtext+0avgdata 6996maxresident)k
0inputs+0outputs (0major+1118minor)pagefaults 0swaps

$ find ~/.cache/cre/* -type f | xargs wc --bytes
1559672 /home/simon/.cache/cre/28fbe9a46fb80c1243764f41d92c8ba3862df2f8ac5bfa094a5fe586e3dd86d9/one-liner.cr.exe
    120 /home/simon/.cache/cre/28fbe9a46fb80c1243764f41d92c8ba3862df2f8ac5bfa094a5fe586e3dd86d9/one-liner.cr
  15203 /home/simon/.cache/cre/28fbe9a46fb80c1243764f41d92c8ba3862df2f8ac5bfa094a5fe586e3dd86d9/one-liner.cr.log

$ cat /home/simon/.cache/cre/28fbe9a46fb80c1243764f41d92c8ba3862df2f8ac5bfa094a5fe586e3dd86d9/one-liner.cr
STDOUT.flush_on_newline=true;x = "abcdefg"; x =~ /(.*)(c.e).*/; _,a,b = $~; printf %[- a=%s b=%s // |$SHELL| !\n], a, b;
asterite commented 4 years ago

Instead of String.build I suggest you use string interpolation. Specially for a script.

simonhf commented 4 years ago

@asterite Thanks for the tip. I changed it in the above comment and it looks shorter and more intentional revealing now.

simonhf commented 4 years ago

Everything I learned so far about Crystal I dumped into some examples here: https://gist.github.com/simonhf/078f0874c622ef9b276fa58554fcd1fc

HertzDevil commented 3 years ago

As mentioned in https://github.com/crystal-lang/crystal/issues/10947#issuecomment-880535124, if we want this capability for tools other than eval then one way to do it is through a command line option:

$ crystal run -e 'require "big"' -e 'puts 10.to_big_r / 3'
10/3

$ crystal build -o a.out -e 'require "big"' -e 'puts 10.to_big_r / 3'
$ ./a.out
10/3

$ crystal i -e 'require "big"' -e 'puts 10.to_big_r / 3'
10/3