Modernize parser and shell code with Ruby 4 features

This commit is contained in:
Sami Samhuri 2026-02-07 15:09:24 -08:00
parent 9630125a95
commit 850b2c23e2
No known key found for this signature in database
4 changed files with 37 additions and 38 deletions

View file

@ -60,9 +60,10 @@ module Shell
def builtin_cd(args)
dir = args.first
oldpwd = Dir.pwd
target = if dir.nil?
target = case dir
in nil
Dir.home
elsif dir == "-"
in "-"
ENV["OLDPWD"] || oldpwd
else
dir
@ -85,7 +86,7 @@ module Shell
logger.warn "#{red("[ERROR]")} Invalid export command"
return -1
else
ENV[name] = value_parts.join("=").gsub(EXPORT_VARIABLE_PATTERN) { |m| ENV[m[1..]] || "" }
ENV[name] = value_parts.join("=").gsub(EXPORT_VARIABLE_PATTERN) { ENV[it[1..]] || "" }
end
0
end

View file

@ -54,19 +54,24 @@ module Shell
commands = parse_line(line)
result = 0
commands.each do |entry|
command = entry[:command]
next if command.strip.empty?
next if entry[:op] == :and && result != 0
case entry
in StringParser::Command[text:, op:]
command = text
next if command.strip.empty?
next if op == :and && result != 0
args = word_expander.expand(command)
program = args.shift
logger.verbose "Parsed command: #{program} #{args.inspect}"
if builtins.builtin?(program)
logger.verbose "Executing builtin #{program}"
result = builtins.exec(program, args)
args = word_expander.expand(command)
program = args.shift
logger.verbose "Parsed command: #{program} #{args.inspect}"
if builtins.builtin?(program)
logger.verbose "Executing builtin #{program}"
result = builtins.exec(program, args)
else
logger.verbose "Shelling out for #{program}"
result = job_control.exec_command(program, args)
end
else
logger.verbose "Shelling out for #{program}"
result = job_control.exec_command(program, args)
raise ArgumentError, "Unknown parsed command node: #{entry.inspect}"
end
end
result
@ -76,12 +81,8 @@ module Shell
end
# Looks like this: /path/to/somewhere%
def prompt(pwd)
"#{blue(pwd)}#{white("%")} #{CLEAR}"
end
def prompt(pwd) = "#{blue(pwd)}#{white("%")} #{CLEAR}"
def parse_line(line)
StringParser.split_commands(line)
end
def parse_line(line) = StringParser.split_commands(line)
end
end

View file

@ -1,5 +1,6 @@
module Shell
class StringParser
Command = Data.define(:text, :op)
Token = Data.define(:type, :value)
class Scanner
@ -336,22 +337,20 @@ module Shell
tokens = Scanner.new(line).tokenize_command_list
tokens.each do |token|
case token.type
when :text
commands << {command: token.value, op: next_op}
if next_op == :and && token.value.strip.empty?
case token
in Token[type: :text, value:]
if next_op == :and && value.strip.empty?
raise ArgumentError, "syntax error: expected command after `&&`"
end
commands << Command.new(text: value, op: next_op)
next_op = :always
when :separator
if token.value == :and
if commands.empty? || commands.last[:command].strip.empty?
raise ArgumentError, "syntax error near unexpected token `&&`"
end
next_op = :and
else
next_op = :always
in Token[type: :separator, value: :and]
if commands.empty? || commands.last.text.strip.empty?
raise ArgumentError, "syntax error near unexpected token `&&`"
end
next_op = :and
in Token[type: :separator, value: :always]
next_op = :always
else
raise ArgumentError, "Unknown token type: #{token.type}"
end
@ -360,9 +359,7 @@ module Shell
commands
end
def read_dollar_paren(line, start_index)
Scanner.new(line, index: start_index).read_dollar_paren_body
end
def read_dollar_paren(line, start_index) = Scanner.new(line, index: start_index).read_dollar_paren_body
end
end
end

View file

@ -39,7 +39,7 @@ module Shell
expanded = expand_variables(word.text)
.tr(ESCAPED_DOLLAR, "$")
.tr(ESCAPED_BACKTICK, "`")
expand_braces(expanded).map { |part| SplitWord.new(text: part, globbed: word.globbed) }
expand_braces(expanded).map { SplitWord.new(text: it, globbed: word.globbed) }
end
.flat_map do |word|
if word.globbed
@ -116,7 +116,7 @@ module Shell
if glob_words.empty?
words << SplitWord.new(text: field, globbed: false)
else
glob_words.each { |glob_word| words << SplitWord.new(text: glob_word, globbed: true) }
glob_words.each { words << SplitWord.new(text: it, globbed: true) }
end
else
words << SplitWord.new(text: field, globbed: false)
@ -535,7 +535,7 @@ module Shell
return [word] unless body.include?(",")
parts = body.split(",", -1)
parts.flat_map { |part| expand_braces(prefix + part + suffix) }
parts.flat_map { expand_braces(prefix + it + suffix) }
end
def escaped_replacement(char)