Overview
This project is a recreation of the infamous linux shell. its a minimal version that allows the user to navigate through different files and directories, use any linux command or initialize any applications, contains pipes and redirections and here-documents.
only C was used in this project. visit the project for more details.
What I Built
My part of the project:
My part of this project included parsing the line, parameter expansion and building the abstract syntax tree that's crucial for the execution part;
Implementation details:
This implementation was built completely from scratch, with the exception of readline. i had a choice between recursive descent which is the one mostly used or a bottom-up approach, but i ended up with my own implementation that utilizes left-to-right reduction customized one after implementing both. since c doesn't have polymorphism, i had to find/invent a way to keep the project extensible just in case i wanted to add more features to my shell, so for that purpose i ended up discovering and using tagged unions.
an example of a use case would be like this:
#include <stdio.h>
typedef enum {
EXPR_INT,
EXPR_BINARY,
EXPR_VARIABLE
} ExprType;
typedef enum {
OP_ADD,
OP_SUB,
OP_MUL,
OP_DIV
} BinaryOp;
typedef struct Expr Expr;
typedef struct {
int value;
} ExprInt;
typedef struct {
char *name;
} ExprVariable;
typedef struct {
BinaryOp op;
Expr *left;
Expr *right;
} ExprBinary;
struct Expr {
ExprType type;
union {
ExprInt integer;
ExprVariable variable;
ExprBinary binary;
} as;
};
this way you could add up as many types as possible as long as they exist within the union all of you have to do is define that type within and utilize it.
The overall parsing goes through 3 main steps: 1. parsing a single like and tokenizing each word, as it makes sure that everything is valid 2. variable expasion by replacing the corresponding value from the environment variables. 3. the construction of the abstract syntax tree. 4. scanning over the syntax and detecting any potential lexical errors within the ast.
then the second part of the project which is related to execution, in that part we go through the entire AST as we fork out processes, initiate pipes when we need to and redirect inputs and outputs to the right directions.
project rules and limitations:
we were not allowed to utilize most of the c standard libraries, we mostly worked with "unistd.h" for syscalls, stdlib but just for malloc and free, and a couple of functions from a previous project lib42 "libft.h"
those limitations make the project much harder, since you are expected to implement everything rather than relying on other libraries, previous implementations and external dependencies and libraries. the only exception for this being the gnu readLine because of its complexity.
overall its a really intersting project to work with and I've learned a lot from it, especially about how bash works and also how programming languages are parsed, especially interpreted languages.
Goals:
the goal was not just an interactive shell introduction, but it was a deep dive into interpretation as well building an AST was not my only option but I've decided to take the chance to learn as much as i can from this project it also includes a lot of details about signal handling, pipes and forking, how a shell communicates with the kernel (ex: fork, execve, wait/waitpid, read/write) and so on. this project also happened to be my first group project, so learning how to navigate the project with multiple people working on it is a plus too.
Notes
-
Limitations: Of course, re-implementing bash entirely within a single month is not feasible, that's why its limited to just single line commands used to communicate with mainly the linux kernel (or unix systems). but its good enough for most daily use cases, as you can start up applications, quite literally run any linux command etc..
-
future plans: i am planning on upgrading this later down the line or maybe doing a full re-write in some other language like rust or zig, to both make it cross-platform as those languages also have more features built in mind for low level projects.