|
| 1 | +# frozen_string_literal: true |
| 2 | + |
| 3 | +require "thor" |
| 4 | + |
| 5 | +module TRuby |
| 6 | + # Thor-based CLI for t-ruby command |
| 7 | + # Runs .trb files directly without generating intermediate files |
| 8 | + class RunnerCLI < Thor |
| 9 | + def self.exit_on_failure? |
| 10 | + true |
| 11 | + end |
| 12 | + |
| 13 | + # Override Thor's default behavior to treat unknown arguments as the file to run |
| 14 | + def self.start(given_args = ARGV, _config = {}) |
| 15 | + # Handle version flag |
| 16 | + if given_args.include?("--version") || given_args.include?("-v") |
| 17 | + new.version |
| 18 | + return |
| 19 | + end |
| 20 | + |
| 21 | + # Handle help flag or no arguments |
| 22 | + if given_args.empty? || given_args.include?("--help") || given_args.include?("-h") |
| 23 | + new.help |
| 24 | + return |
| 25 | + end |
| 26 | + |
| 27 | + # Treat first argument as file, rest as script arguments |
| 28 | + file = given_args.first |
| 29 | + args = given_args[1..] || [] |
| 30 | + |
| 31 | + runner = Runner.new |
| 32 | + runner.run_file(file, args) |
| 33 | + end |
| 34 | + |
| 35 | + desc "FILE [ARGS...]", "Run a .trb file directly without generating files" |
| 36 | + def run_file(file, *args) |
| 37 | + runner = Runner.new |
| 38 | + runner.run_file(file, args) |
| 39 | + end |
| 40 | + |
| 41 | + map %w[--version -v] => :version |
| 42 | + desc "--version, -v", "Show version" |
| 43 | + def version |
| 44 | + puts "t-ruby #{VERSION}" |
| 45 | + end |
| 46 | + |
| 47 | + desc "--help, -h", "Show help" |
| 48 | + def help |
| 49 | + puts <<~HELP |
| 50 | + t-ruby v#{VERSION} - Run T-Ruby files directly |
| 51 | +
|
| 52 | + Usage: |
| 53 | + t-ruby <file.trb> Run a .trb file directly |
| 54 | + t-ruby <file.trb> [args...] Run with arguments passed to the script |
| 55 | + t-ruby --version, -v Show version |
| 56 | + t-ruby --help, -h Show this help |
| 57 | +
|
| 58 | + Examples: |
| 59 | + t-ruby hello.trb Run hello.trb |
| 60 | + t-ruby server.trb 8080 Run with argument 8080 |
| 61 | + t-ruby script.trb foo bar Run with multiple arguments |
| 62 | +
|
| 63 | + Notes: |
| 64 | + - No .rb or .rbs files are generated |
| 65 | + - Type annotations are stripped at runtime |
| 66 | + - Arguments after the file are passed to ARGV |
| 67 | + HELP |
| 68 | + end |
| 69 | + end |
| 70 | + |
| 71 | + # Runner class - executes T-Ruby code directly |
| 72 | + # Can be used as a library or through RunnerCLI |
| 73 | + class Runner |
| 74 | + def initialize(config = nil) |
| 75 | + @config = config || Config.new |
| 76 | + @compiler = Compiler.new(@config) |
| 77 | + end |
| 78 | + |
| 79 | + # Run a .trb file directly |
| 80 | + # @param input_path [String] Path to the .trb file |
| 81 | + # @param argv [Array<String>] Arguments to pass to the script via ARGV |
| 82 | + def run_file(input_path, argv = []) |
| 83 | + unless File.exist?(input_path) |
| 84 | + warn "Error: File not found: #{input_path}" |
| 85 | + exit 1 |
| 86 | + end |
| 87 | + |
| 88 | + source = File.read(input_path) |
| 89 | + result = @compiler.compile_string(source) |
| 90 | + |
| 91 | + if result[:errors].any? |
| 92 | + result[:errors].each { |e| warn e } |
| 93 | + exit 1 |
| 94 | + end |
| 95 | + |
| 96 | + execute_ruby(result[:ruby], input_path, argv) |
| 97 | + end |
| 98 | + |
| 99 | + # Run T-Ruby source code from a string |
| 100 | + # @param source [String] T-Ruby source code |
| 101 | + # @param filename [String] Filename for error reporting |
| 102 | + # @param argv [Array<String>] Arguments to pass via ARGV |
| 103 | + # @return [Boolean] true if execution succeeded |
| 104 | + def run_string(source, filename: "(t-ruby)", argv: []) |
| 105 | + result = @compiler.compile_string(source) |
| 106 | + |
| 107 | + if result[:errors].any? |
| 108 | + result[:errors].each { |e| warn e } |
| 109 | + return false |
| 110 | + end |
| 111 | + |
| 112 | + execute_ruby(result[:ruby], filename, argv) |
| 113 | + true |
| 114 | + end |
| 115 | + |
| 116 | + private |
| 117 | + |
| 118 | + # Execute Ruby code with proper script environment |
| 119 | + # @param ruby_code [String] Ruby code to execute |
| 120 | + # @param filename [String] Script filename (for $0 and stack traces) |
| 121 | + # @param argv [Array<String>] Script arguments |
| 122 | + def execute_ruby(ruby_code, filename, argv) |
| 123 | + # Set up script environment |
| 124 | + ARGV.replace(argv) |
| 125 | + $0 = filename |
| 126 | + |
| 127 | + # Execute using eval with filename and line number preserved |
| 128 | + # This ensures stack traces point to the original .trb file |
| 129 | + TOPLEVEL_BINDING.eval(ruby_code, filename, 1) |
| 130 | + end |
| 131 | + end |
| 132 | +end |
0 commit comments