minishell
by bhagenlo
written for version 6
Wohoo, you've arrived at minishell. Well done!
Tokens, the environment and builtins await. Come in, come in :)
You'll be creating a working and day-to-day usable shell. It will be able to execute arbitrary commands, have its own environment, write to/read from files, and so on.
Disliking minishell?
I don't think the issue's the project, but rather how we handle it. Read on here, if you're interested.
For all of the following, there's one thing to keep in mind:
That being said, let's go.
Let us start by doing that: What exactly we're trying to do here? We are trying to implement our own programming language. That's a big task, and can make one a little bit anxious, but it's also a big opportunity for us. People have been doing that since the early 50s. There's a big body of knowledge to learn from. Condensing that: You probably want to structure your minishell like that: Input #Prerequisites
-lexer->
tokens
tokens -parser->
AST
AST -evaluator->
evaluated AST
evaluated AST -executor->
output
What could it look like in our case? Draft programs (+ their AST) on paper.
Can you think of an easier way than a tree? If so, make a draft of that, too.
Apart from sticking to the proposed general structure, another thing that buys you a lot of time is to write tests from the beginning. Say, you start out with writing your lexer (for that, it might be helpful to stick to For both of those: Beware of the Heredoc. (Look HERE.) If those both work, you're all set to implement the inner workings of a shell. I'll leave it at that. Except for those regularly occurring questions/issues:#During
bash
's tokenization rules). During that whole time, already think of test cases where your lexer might break. Collect them, and transfer them into tests.
Same for your parser (You can find the bash grammar here).
As for the parser's output, in case you're not doing the bonus, the way to go might be to use a Command Table as shown here.
Oof. I'm not really sure what's to clean up here. I hope you tested your shell thoroughly (probably even used it for a bit), and you found it to be generally usable. It does make sense to ask a peer that did minishell already for a test evaluation. And, since you don't know who'll evaluate you, probably also go through an Eval sheet once or twice :)#Cleaning Up
Well, after minishell, you should know about: In case you don't feel like you do, try to think it through again. I consider parsers and lexers to be generally very useful tools, and Concerning the last one, in case you're not convinced: So, no further work writing a parser by hand for you today. Instead, you might want to take a look on how to do it differently. Have fun and enjoy exploring!#Aftercare
microshell
awaits :)
It's quite probable a large part of all your errors were bugs related to the manual allocation of memory. And it's not only you.
Fortunately, it's 2023, and at least since May 15, 2015, there is only very little reason to still introduce them to our codebases. The two key terms here are Garbage Collection and Ownership.
A: As far as I know, you'd use yacc
.
A: How about Rust?
A: For that, take a look at the self-proclaimed Programming Language Programming Language: Racket.
Huh, what a project. Nasty sometimes, but full of learning. See you at the next one.
And may your grammars always be consistent.
#Pointers
bash
's tokenization rules
I've yet to meet someone (who finished the project) and hasn't been complaining about the project/all the annoying edge cases they needed to "fix". It goes like the following: I don't think that this is the point of the subject, but more importantly, it should not be. We're here to learn, remember? You can learn a lot from this subject. Heck, you're writing your own little programming language! But somehow, the joy this brings gets lost in all the edge case-fixing I've encountered. (Or, at least, it only rarely gets mentioned by the people who've completed minishell.) I think this project can be different, and it's up to us to make this happen. It can be more about understanding, and less about tedious replication. How can we achieve that? I think it all comes down to how minishell gets evaluated. Right now, the eval sheet is a practical joke. While the others also don't ask for looking at the code, they at least leave time for actually doing that. Minishell's doesn't. But how to make it better? I propose the following (/something along those lines): The point here is: In contrast to the normal eval sheet, the evaluator checks for understanding, and then tests whether the code is working in general. They do not try to break the shell's (or its builtins') parser at all costs, checking for every test case they know. Neither do they check only for the specific test cases of the eval sheet, which one can easily guard against. I'd love to see you join me on this. See you around. :)#Evaluating minishell
They all start out implementing their own shell, and then end up implementing every peculiarity of bash
.
Taking something as a reference is not the same as copying its behaviour to the tiniest detail.
(exchange the respective paragraphs from the subject with interrogations in a similar fashion.)echo
should print the text it's given to the screen.
Specifically: Check whether both echo test
and echo -n test lala
work the intended wayunset
should unset variables from the environment.
Ask the evaluatees how they did do it. What does their environment look like? How do they mutate it?
Then, check something like that:
$ export H=42
$ env
...
H=42
$ unset H
$ env
(no H anymore)
$ unset PATH
$ ls # or any other command in PATH
ls: command not found