mirror of
https://github.com/samsonjs/csc360-a1-shell.git
synced 2026-04-27 14:57:43 +00:00
Modernize parser and shell code with Ruby 4 features
This commit is contained in:
parent
9630125a95
commit
850b2c23e2
4 changed files with 37 additions and 38 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue