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) def builtin_cd(args)
dir = args.first dir = args.first
oldpwd = Dir.pwd oldpwd = Dir.pwd
target = if dir.nil? target = case dir
in nil
Dir.home Dir.home
elsif dir == "-" in "-"
ENV["OLDPWD"] || oldpwd ENV["OLDPWD"] || oldpwd
else else
dir dir
@ -85,7 +86,7 @@ module Shell
logger.warn "#{red("[ERROR]")} Invalid export command" logger.warn "#{red("[ERROR]")} Invalid export command"
return -1 return -1
else 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 end
0 0
end end

View file

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

View file

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

View file

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