Skip to content

mhdsbq/Toysh

Repository files navigation

Toysh - A POSIX Shell Implementation in C#

A lightweight, feature-rich shell implementation built in C# targeting .NET 9.0. Toysh provides a functional command-line interface with built-in commands, external command execution, and advanced shell features like history management, auto-completion, and line editing.

Features

Core Functionality

  • Command Execution: Execute both built-in and external commands from your system's PATH
  • Interactive Shell: Full-featured REPL with prompt and line editing capabilities
  • Command Parsing: Sophisticated lexer, expander, and parser for processing shell input
  • Error Handling: Graceful error management with proper exit codes

Built-in Commands

  • echo - Display text output
  • pwd - Print working directory
  • cd - Change directory
  • exit - Exit the shell
  • type - Show command type (builtin or external)
  • history - Access command history with read/write capabilities

Advanced Features

  • Auto-completion - Intelligent command and path auto-completion
  • Command History - Persistent history storage with load/save functionality
  • Line Editor - Rich line editing experience with history navigation
  • Argument Parsing - Sophisticated argument parser supporting flags and options
  • External Commands - Full PATH resolution and execution of system commands
  • Variable Expansion - Support for variable expansion in commands

Project Structure

src/
├── main.cs                 # Entry point and shell initialization
├── Commands/               # Built-in command implementations
│   ├── Echo.cs
│   ├── Exit.cs
│   ├── Pwd.cs
│   ├── Cd.cs
│   ├── Type.cs
│   ├── History.cs
│   └── ExternalCommand.cs
├── Core/                   # Core shell infrastructure
│   ├── CmdRegistry.cs      # Command registry and lookup
│   ├── Executer.cs         # Command execution engine
│   ├── LineEditor.cs       # Interactive line editing
│   ├── AutoCompleter.cs    # Auto-completion logic
│   ├── HistoryStore.cs     # Command history management
│   ├── Lexer.cs            # Tokenization
│   ├── Expander.cs         # Variable expansion
│   ├── Parser.cs           # AST generation
│   ├── Consts/             # Constants
│   ├── Enums/              # Enumerations
│   ├── Exceptions/         # Custom exceptions
│   ├── Interfaces/         # Core interfaces (ICommand, IOperator)
│   └── Types/              # AST types (Token, AstNode)
├── Parser/                 # Parsing components
├── Utils/                  # Utility functions
│   ├── ArgsParser.cs       # Command-line argument parsing
│   ├── FileUtils.cs        # File system utilities
│   ├── StreamUtils.cs      # Stream operations
│   ├── Logger.cs           # Logging utilities
│   └── AstLogger.cs        # AST debugging

Getting Started

Prerequisites

  • .NET 9.0 SDK
  • macOS, Linux, or Windows

Building

dotnet build

Running

dotnet run

The shell will start in interactive mode and display a prompt awaiting user input.

Running Built Executables

Compiled binaries are available in:

  • Debug mode: bin/Debug/net9.0/Toysh
  • Release mode: bin/Release/net9.0/Toysh

Architecture

Command Execution Flow

  1. Lexer tokenizes the user input
  2. Expander handles variable expansion
  3. Parser constructs an Abstract Syntax Tree (AST)
  4. Executer traverses the AST and executes commands
  5. Commands are resolved via the CommandRegistry and executed

Line Editing

The LineEditor provides:

  • Real-time input feedback
  • History navigation (up/down arrows)
  • Auto-completion suggestions
  • Standard editing keybindings

Command Registry

The CmdRegistry maintains two types of commands:

  • Built-in Commands: Implemented as ICommand instances (echo, pwd, cd, etc.)
  • External Commands: System executables discovered via PATH resolution

Configuration

Environment variables:

  • HISTFILE - Specifies the history file location (default: h)

Development

Adding New Built-in Commands

  1. Create a new class in src/Commands/ implementing ICommand
  2. Implement required members: Name, Type, ExecuteAsync()
  3. Register the command in Program.cs via RegisterBuiltinCommands()

Example:

class MyCommand : ICommand
{
    public string Name => "mycommand";
    public string Type => CommandType.Builtin;

    public async Task ExecuteAsync(string[] args, CommandContext context)
    {
        await context.Output.WriteStringAsync("Hello from mycommand!");
    }
}

Testing

Run unit tests with:

dotnet test

Logging & Debugging

The project includes logging utilities:

  • Logger.cs - General logging
  • AstLogger.cs - AST debugging and visualization

Codecrafters

This project is part of the CodeCrafters challenge platform. https://app.codecrafters.io/courses/shell/overview

About

A toy POSIX shell similer to bash

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors