mirror of
https://github.com/samsonjs/csc360-a1-shell.git
synced 2026-03-25 08:45:52 +00:00
139 lines
3.2 KiB
Ruby
139 lines
3.2 KiB
Ruby
module Shell
|
|
class StringParser
|
|
class << self
|
|
def split_commands(line)
|
|
commands = []
|
|
command = +""
|
|
state = :unquoted
|
|
next_op = :always
|
|
i = 0
|
|
|
|
while i < line.length
|
|
c = line[i]
|
|
case state
|
|
when :unquoted
|
|
case c
|
|
when ";"
|
|
commands << {command: command, op: next_op}
|
|
command = +""
|
|
next_op = :always
|
|
i += 1
|
|
next
|
|
when "&"
|
|
if line[i + 1] == "&"
|
|
if command.strip.empty?
|
|
raise ArgumentError, "syntax error near unexpected token `&&`"
|
|
end
|
|
commands << {command: command, op: next_op}
|
|
command = +""
|
|
next_op = :and
|
|
i += 2
|
|
next
|
|
end
|
|
when "'"
|
|
state = :single_quoted
|
|
when "\""
|
|
state = :double_quoted
|
|
when "\\"
|
|
state = :escaped
|
|
end
|
|
command << c
|
|
|
|
when :single_quoted
|
|
command << c
|
|
state = :unquoted if c == "'"
|
|
|
|
when :double_quoted
|
|
command << c
|
|
if c == "\\"
|
|
state = :double_quoted_escape
|
|
elsif c == "\""
|
|
state = :unquoted
|
|
end
|
|
|
|
when :double_quoted_escape
|
|
command << c
|
|
state = :double_quoted
|
|
|
|
when :escaped
|
|
command << c
|
|
state = :unquoted
|
|
|
|
else
|
|
raise "Unknown state #{state}"
|
|
end
|
|
|
|
i += 1
|
|
end
|
|
|
|
if next_op == :and && command.strip.empty?
|
|
raise ArgumentError, "syntax error: expected command after `&&`"
|
|
end
|
|
|
|
commands << {command: command, op: next_op}
|
|
commands
|
|
end
|
|
|
|
def read_dollar_paren(line, start_index)
|
|
output = +""
|
|
i = start_index
|
|
depth = 1
|
|
state = :unquoted
|
|
|
|
while i < line.length
|
|
c = line[i]
|
|
|
|
case state
|
|
when :unquoted
|
|
case c
|
|
when "("
|
|
depth += 1
|
|
output << c
|
|
when ")"
|
|
depth -= 1
|
|
return [output, i + 1] if depth.zero?
|
|
output << c
|
|
when "'"
|
|
output << c
|
|
state = :single_quoted
|
|
when "\""
|
|
output << c
|
|
state = :double_quoted
|
|
when "\\"
|
|
output << c
|
|
if i + 1 < line.length
|
|
output << line[i + 1]
|
|
i += 1
|
|
end
|
|
else
|
|
output << c
|
|
end
|
|
|
|
when :single_quoted
|
|
output << c
|
|
state = :unquoted if c == "'"
|
|
|
|
when :double_quoted
|
|
if c == "\\"
|
|
output << c
|
|
if i + 1 < line.length
|
|
output << line[i + 1]
|
|
i += 1
|
|
end
|
|
else
|
|
output << c
|
|
state = :unquoted if c == "\""
|
|
end
|
|
|
|
else
|
|
raise "Unknown state #{state}"
|
|
end
|
|
|
|
i += 1
|
|
end
|
|
|
|
raise ArgumentError, "Unmatched $(...)"
|
|
end
|
|
end
|
|
end
|
|
end
|