123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. package main
  2. import (
  3. "bufio"
  4. "fmt"
  5. "io"
  6. "os"
  7. "strings"
  8. )
  9. type Context struct {
  10. stdout io.Writer
  11. stderr io.Writer
  12. stdin io.Reader
  13. }
  14. type GetCommandResult int
  15. const (
  16. GetCommandResultOk GetCommandResult = iota
  17. GetCommandResultError
  18. )
  19. type Command struct {
  20. command_type CommandType
  21. tokens []string
  22. }
  23. func makeCommandNone() Command {
  24. return Command{
  25. command_type: CommandTypeNone,
  26. tokens: nil,
  27. }
  28. }
  29. func makeCommand(command_type CommandType, tokens []string) Command {
  30. if command_type == CommandTypeNone {
  31. return Command{
  32. command_type: command_type,
  33. tokens: nil,
  34. }
  35. } else {
  36. return Command{
  37. command_type: command_type,
  38. tokens: tokens,
  39. }
  40. }
  41. }
  42. type CommandType int
  43. const (
  44. CommandTypeNone CommandType = iota
  45. CommandTypeExternal
  46. CommandTypeBuiltinExit
  47. CommandTypeBuiltinEcho
  48. )
  49. func getCommand(ctx *Context) (Command, error) {
  50. fmt.Print("$ ")
  51. command_line, err := bufio.NewReader(ctx.stdin).ReadString('\n')
  52. if err != nil {
  53. return makeCommandNone(), err
  54. }
  55. command, err := parseCommand(command_line)
  56. if err != nil {
  57. fmt.Fprintln(ctx.stderr, err)
  58. return makeCommandNone(), nil
  59. }
  60. return command, nil
  61. }
  62. func tokenize(str string) []string {
  63. tokens := make([]string, 2)
  64. tokens = strings.Fields(str)
  65. return tokens
  66. }
  67. func parseCommand(command_line string) (Command, error) {
  68. tokens := tokenize(command_line)
  69. if tokens[0] == "exit" {
  70. return makeCommand(CommandTypeBuiltinExit, tokens), nil
  71. }
  72. if tokens[0] == "echo" {
  73. return makeCommand(CommandTypeBuiltinEcho, tokens), nil
  74. }
  75. return makeCommandNone(), fmt.Errorf("%s: command not found", tokens[0])
  76. }
  77. func handleCommand(ctx Context, command Command) {
  78. _ = ctx
  79. switch command.command_type {
  80. case CommandTypeNone:
  81. return
  82. case CommandTypeBuiltinExit:
  83. os.Exit(0)
  84. case CommandTypeBuiltinEcho:
  85. fmt.Fprintln(ctx.stdout, strings.Join(command.tokens[1:], " "))
  86. }
  87. }
  88. func main() {
  89. ctx := Context{
  90. stdout: os.Stdout,
  91. stderr: os.Stderr,
  92. stdin: os.Stdin,
  93. }
  94. for {
  95. command, err := getCommand(&ctx)
  96. if err != nil {
  97. fmt.Fprintln(os.Stderr, "Error reading input: ", err)
  98. os.Exit(1)
  99. }
  100. handleCommand(ctx, command)
  101. }
  102. }