#!/usr/bin/perl # # C - a pseudo-interpreter of the C programming language # http://labs.cybozu.co.jp/blog/kazuhoatwork/ # # Copyright (C) 2006 Cybozu Labs, Inc. # # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. # # use strict; use warnings; use File::Temp qw(tempdir); our $VERSION = 0.03; sub usage { print <<"EOF"; C - a pseudo interpreter of the C programming language (version $VERSION) http://labs.cybozu.co.jp/blog/kazuhoatwork/ Usage: $0 [options] [source_file] [argv] Example: C -cWall -e 'printf("hello world\\n")' Options: -c pass a compiler option to gcc -i add an include file -d use debugger -e expression executes the expression -k keep temporary files -l pass a linker option to gcc -m use main function -p use C++ (implies -m) -h, --help displays this help message EOF ; exit 0; } my $src_text; my $work_dir = tempdir(); my $out_file = "$work_dir/a.out"; my $src_file = "$out_file.c"; my $includes = "#include \n#include \n"; my $use_main = 0; my $use_plusplus = 0; my $use_debugger = 0; my $keep_files = 0; my @copts; my @lopts; my @line_pragma = ('', ''); # cleanup sub cleanup { return if $keep_files; unlink($src_file); unlink($out_file); rmdir($work_dir); } # show error if used in file sub assert_cmdline { my ($opt, $file, $line) = @_; die "$file:$line: $opt cannot be used in file\n" if defined($file); } # parse args sub parse_args { my ($args, $file, $line) = @_; while (@$args && $args->[0] =~ /^-/) { my $arg = shift(@$args); if ($arg =~ /^-c/) { push(@copts, "-$'"); } elsif ($arg eq '-d') { push(@copts, '-g'); $use_debugger = 1; } elsif ($arg eq '-e') { assert_cmdline($arg, $file, $line); if ($#$args == -1) { print STDERR "$file:$line: -e cannot be used in file\n"; exit(255); } $src_text = shift(@$args); } elsif ($arg =~ /^-i/) { assert_cmdline($arg, $file, $line); $includes .= "#include \"$'\"\n"; } elsif ($arg eq '-k') { $keep_files = 1; } elsif ($arg =~ /^-l/) { push(@lopts, "-$'"); } elsif ($arg eq '-m') { $use_main = 1; } elsif ($arg eq '-p') { $includes .= "#include \n"; $use_main = 1; $use_plusplus = 1; } elsif ($arg eq '-h' || $arg eq '--help') { assert_cmdline($arg, $file, $line); usage(); } else { print STDERR "unknown option: $arg\n"; exit(255); } } } parse_args(\@ARGV); if (! defined($src_text)) { my $file; my $fh; if (@ARGV) { $file = shift(@ARGV); open($fh, '<', $file) or die "cannot open file: $file"; } else { $fh = \*STDIN; } my $lineno = 0; while (my $l = <$fh>) { $lineno++; my $comment_out = 1; if ($l =~ /^\#!/) { } elsif ($l =~ /^\#option\s+/) { my @args = split(/\s+/, $'); parse_args(\@args, defined($file) ? $file : 'stdin', $lineno); } else { $comment_out = 0; } $l = "// $l" if $comment_out; $src_text .= $l; } if (defined($file)) { close($fh); my $at = (($includes . $src_text) =~ tr/\n/\n/) + 6; @line_pragma = ("# 1 \"$file\" 1", "# $at \"$src_file\" 2"); } } else { if ($use_plusplus) { $src_text = "using namespace std;\n".$src_text; } } # build source if ($use_main) { $src_text = "$line_pragma[0]\n$src_text\n$line_pragma[1]\n"; } else { $src_text =<<"EOF"; int main(int argc, char** argv) { $line_pragma[0] $src_text; $line_pragma[1] return 0; } EOF ; } # save source file open(my $fh, '>', $src_file) or die "cannot create source file: $src_file : $!"; print $fh $includes.$src_text; close($fh); # compile if ((my $err = system($use_plusplus ? 'g++' : 'gcc', @copts, '-o', $out_file, $src_file, @lopts)) != 0) { cleanup(); die "failed to execute gcc: $!" if $err == -1; exit($err >> 8); } # execute my @args = ($out_file, @ARGV); unshift(@args, "gdb") if $use_debugger; system(@args); cleanup(); die "failed to execute the generated binary : $!" if $? == -1; exit($? >> 8) if $? != 0; __END__ =head1 NAME C - a pseudo-interpreter of the C programming language =head1 SYNOPSIS /usr/bin/C [options] [source_file] [arguments...] C hello.C # executes hello.C C -cWall -e 'printf("hello world\n")' # one-liner C -m uuencode.c encoded.txt < data.bin # run an ordinally C source C -cWall -cO2 hello.C # gcc -Wall -O2 C -d hello.C # debug hello.C =head1 DESCRIPTION C (pronounced large-C) is a pseudo-interpreter of the C programming language. Without the need of manual compilation, developers can rapidly create scripts or write one-liners using the C programming language that runs at native code speed. =head1 OPTIONS -c pass a compiler option to GCC -d use debugger -e expression executes the expression -i add an include file -k keep temporary files -l pass a linker option to GCC -m use main function -p use C++ (implies -m) -h, --help displays this help message =head1 NOTES Several options can be defined within the source code using the ``#option'' directive. Example: #!/usr/bin/C #option -cWall -cO2 printf("hello world\n"); =head1 AUTHOR Kazuho Oku E kazuho ___at___ labs.cybozu.co.jpE =head1 COPYRIGHT Coypright(C) 2006 Cybozu Labs, Inc. =cut