package main import ( "bufio" "fmt" "io" "os" ) type Context struct { stdout io.Writer stderr io.Writer stdin io.Reader } type GetCommandResult int const ( GetCommandResultOk GetCommandResult = iota GetCommandResultError ) type Command struct { command_type CommandType tokens []string } func makeCommandNone() Command { return Command{ command_type: CommandTypeNone, tokens: nil, } } func makeCommand(command_type CommandType, tokens []string) Command { if command_type == CommandTypeNone { return Command{ command_type: command_type, tokens: nil, } } else { return Command{ command_type: command_type, tokens: tokens, } } } type CommandType int const ( CommandTypeNone CommandType = iota CommandTypeExternal CommandTypeBuiltinExit ) func getCommand(ctx *Context) (Command, error) { fmt.Print("$ ") command_line, err := bufio.NewReader(ctx.stdin).ReadString('\n') if err != nil { return makeCommandNone(), err } command, err := parseCommand(command_line) if err != nil { fmt.Fprintln(ctx.stderr, err) return makeCommandNone(), nil } return command, nil } func parseTokens(str string) []string { tokens := make([]string, 2) tokens[0] = str[:len(str)-1] return tokens } func parseCommand(command_line string) (Command, error) { tokens := parseTokens(command_line) if tokens[0] == "exit" { return makeCommand(CommandTypeBuiltinExit, tokens), nil } return makeCommandNone(), fmt.Errorf("%s: command not found", tokens[0]) } func handleCommand(ctx Context, command Command) { _ = ctx switch command.command_type { case CommandTypeNone: return case CommandTypeBuiltinExit: os.Exit(0) } } func main() { ctx := Context{ stdout: os.Stdout, stderr: os.Stderr, stdin: os.Stdin, } for { command, err := getCommand(&ctx) if err != nil { fmt.Fprintln(os.Stderr, "Error reading input: ", err) os.Exit(1) } handleCommand(ctx, command) } }