Skip to content

Commit b3cbef5

Browse files
committed
feat: add 4 new blog posts about Dush project and update project socials
1 parent ad3434c commit b3cbef5

File tree

8 files changed

+620
-93
lines changed

8 files changed

+620
-93
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Implementing Everything in Go: Built-ins, Pipes, and Jobs
2+
3+
When you write a shell in Go, you quickly realize that the standard library is your best friend. In **Dush**, we don't just call out to external binaries for everything—we implement as much as possible in pure Go to ensure cross-platform consistency and speed.
4+
5+
## The "Everything in Go" Philosophy
6+
7+
Many shells are just thin wrappers around `libc`. Dush aims to be different. By implementing core utilities as built-ins, we avoid the overhead of process spawning for common tasks.
8+
9+
### 1. The Modern `ls`
10+
One of the most visible parts of Dush is the built-in `ls`. Instead of relying on the system's `ls`, Dush uses `os.ReadDir` and a custom renderer. This allows us to:
11+
- Inject icons based on file extensions.
12+
- Provide consistent coloring across Windows, Linux, and macOS.
13+
- Build in human-readable sizes and grid layouts without extra flags.
14+
15+
### 2. The Pipeline Engine
16+
Implementing pipes (`|`) in Go is surprisingly elegant. We use `io.Pipe()` to create a connected reader and writer.
17+
18+
```go
19+
// Conceptual Go implementation
20+
reader, writer := io.Pipe()
21+
cmd1.Stdout = writer
22+
cmd2.Stdin = reader
23+
24+
go func() {
25+
cmd1.Run()
26+
writer.Close()
27+
}()
28+
cmd2.Run()
29+
```
30+
31+
This "pure Go" approach to plumbing ensures that Dush handles data streams efficiently without hitting OS-specific pipe buffer limits too early.
32+
33+
### 3. Job Control: The `JobManager`
34+
Handling background processes (`&`) and bringing them back to the foreground (`fg`) is one of the hardest parts of shell development. Dush uses a centralized `JobManager` that:
35+
- Wraps `os/exec.Cmd`.
36+
- Monitors process state via goroutines.
37+
- Handles `SIGINT` and `SIGTSTP` forwarding.
38+
- Provides a clean `jobs` command to see what's running.
39+
40+
### 4. Cross-Platform Built-ins
41+
Commands like `cd`, `pwd`, `mkdir`, and `rm` are all implemented using Go's `os` and `path/filepath` packages. This means a Dush script written on a Mac will run perfectly on Windows without needing a Mingw or Cygwin environment.
42+
43+
## Why it Matters
44+
45+
By implementing these features in Go, Dush achieves a level of "portability as a feature." You get the power of a Unix-like environment on any platform Go can compile to.
46+
47+
Dush is a testament to the power of the Go ecosystem for systems programming.
48+
49+
Check out the implementation: [https://github.com/fezcode/dush](https://github.com/fezcode/dush)
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Modernizing the Shell: The Dush Scripting Language
2+
3+
The biggest problem with shell scripting today is that it doesn't feel like *programming*. It feels like incantation. You're not writing logic; you're writing strings that happen to be interpreted as logic. **Dush** changes that.
4+
5+
## The Sigil: `@`
6+
7+
In Dush, variables are first-class citizens. We use the `@` sigil to denote any language-level entity. This solves the "is this a string or a variable?" problem that plagues Bash.
8+
9+
```bash
10+
# Bash
11+
VAR="hello"
12+
echo $VAR # Is it $VAR or ${VAR}?
13+
14+
# Dush
15+
@var = "hello"
16+
echo @var # Unambiguous.
17+
```
18+
19+
## Data Types that Matter
20+
21+
Dush isn't just strings. It supports integers, floats, booleans, arrays, and maps natively.
22+
23+
```bash
24+
@items = ["apple", "banana", "cherry"]
25+
@user = {"name": "fezcode", "role": "admin"}
26+
27+
loop (@x : @items) {
28+
echo "Item: @x"
29+
}
30+
```
31+
32+
## Method Syntax
33+
34+
Why should we have to pipe to `tr` or `awk` just to change a string's case? Dush brings modern method syntax to the shell:
35+
36+
```bash
37+
@path = "/usr/local/bin"
38+
@parts = @path.split("/")
39+
echo @parts.join("-")
40+
```
41+
42+
This keeps your logic inside the shell's memory space, making scripts faster and much easier to read.
43+
44+
## Pattern Matching with `match`
45+
46+
Forget the clunky `case` statements of old. Dush features a modern `match` block with wildcard support:
47+
48+
```bash
49+
match (@status) {
50+
case 0 { echo "Success!" }
51+
case 1 { echo "Warning..." }
52+
case _ { echo "Unknown error: @status" }
53+
}
54+
```
55+
56+
## Procedures and Scoping
57+
58+
Dush supports procedures with local scoping and parameters. They feel like Go functions but work like shell commands.
59+
60+
```bash
61+
proc greet(@name, @shout=false) {
62+
let @msg = "Hello, @name!"
63+
if (@shout) {
64+
echo @msg.upper()
65+
} else {
66+
echo @msg
67+
}
68+
}
69+
70+
greet("Dush", true)
71+
```
72+
73+
## Conclusion
74+
75+
Dush isn't trying to be Bash. It's trying to be what a shell would look like if it were designed today, with 50 years of programming language theory at its back.
76+
77+
It's time to stop scripting and start programming.
78+
79+
Join the revolution: [https://github.com/fezcode/dush](https://github.com/fezcode/dush)

public/posts/posts.json

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,78 @@
11
[
2+
{
3+
"slug": "why-i-built-dush",
4+
"title": "Why I Built Dush",
5+
"date": "2026-04-11",
6+
"updated": "2026-04-11",
7+
"description": "The story behind Dush, a 'Dumb Shell' that turned into a powerful Go-native scripting platform.",
8+
"tags": [
9+
"go",
10+
"golang",
11+
"shell",
12+
"dush",
13+
"dev"
14+
],
15+
"category": "dev",
16+
"filename": "why-i-built-dush.txt",
17+
"authors": [
18+
"fezcode"
19+
]
20+
},
21+
{
22+
"slug": "shell-architecture-in-go",
23+
"title": "Shell Architecture in Go: From Lexer to Execution",
24+
"date": "2026-04-11",
25+
"updated": "2026-04-11",
26+
"description": "A deep dive into the internal architecture of Dush, exploring its lexer, Pratt parser, and tree-walking evaluator.",
27+
"tags": [
28+
"go",
29+
"architecture",
30+
"parser",
31+
"lexer",
32+
"shell"
33+
],
34+
"category": "dev",
35+
"filename": "shell-architecture-in-go.txt",
36+
"authors": [
37+
"fezcode"
38+
]
39+
},
40+
{
41+
"slug": "implementing-everything-in-go",
42+
"title": "Implementing Everything in Go: Built-ins, Pipes, and Jobs",
43+
"date": "2026-04-11",
44+
"updated": "2026-04-11",
45+
"description": "How Dush implements core shell features like pipes, redirects, and job control using Go's standard library.",
46+
"tags": [
47+
"go",
48+
"golang",
49+
"shell",
50+
"systems-programming"
51+
],
52+
"category": "dev",
53+
"filename": "implementing-everything-in-go.txt",
54+
"authors": [
55+
"fezcode"
56+
]
57+
},
58+
{
59+
"slug": "modernizing-the-shell-dush-scripting-language",
60+
"title": "Modernizing the Shell: The Dush Scripting Language",
61+
"date": "2026-04-11",
62+
"updated": "2026-04-11",
63+
"description": "Exploring the @ sigil system and the clean, modern scripting syntax that makes Dush a joy to use.",
64+
"tags": [
65+
"dush",
66+
"scripting",
67+
"programming-languages",
68+
"go"
69+
],
70+
"category": "dev",
71+
"filename": "modernizing-the-shell-dush-scripting-language.txt",
72+
"authors": [
73+
"fezcode"
74+
]
75+
},
276
{
377
"slug": "quantum-physics-101",
478
"title": "Quantum Physics 101: A Beautiful Journey into the Microscopic",
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Shell Architecture in Go: From Lexer to Execution
2+
3+
Building a shell is often seen as a rite of passage for systems engineers. While most start with a simple loop and `execvp`, **Dush** takes a more architectural approach. In this post, we'll dive into the internal plumbing of Dush and see how a string of text becomes a running process.
4+
5+
## The High-Level Flow
6+
7+
Dush follows a classic compiler/interpreter pipeline, but optimized for the immediate nature of a shell:
8+
9+
1. **REPL / Script Reader**: Captures raw input.
10+
2. **Lexer**: Breaks the input into tokens (keywords, sigils, commands, arguments).
11+
3. **Parser (AST)**: Builds an Abstract Syntax Tree using a Pratt Parser.
12+
4. **Evaluator**: Walks the tree and executes Go code or spawns external processes.
13+
5. **Environment**: Manages scopes, variables, and exported environment variables.
14+
15+
## 1. The Lexer: Understanding `@`
16+
17+
The lexer is responsible for recognizing the difference between a shell command and a language expression. When it sees an `@`, it switches modes to handle variable names, numbers, or string literals. This unambiguous start is what makes Dush's syntax so much cleaner than Bash.
18+
19+
## 2. The Pratt Parser: Handling Complexity
20+
21+
For the parser, I chose a **Pratt Parser**. Unlike traditional recursive descent, Pratt parsers are exceptional at handling operator precedence and infix expressions. This allows Dush to support complex math and method chaining:
22+
23+
```bash
24+
@result = (10 + 5) * 2
25+
echo @text.trim().lower()
26+
```
27+
28+
Each token has an associated "parsing function" for both prefix and infix positions. It's elegant, fast, and very easy to extend with new language features.
29+
30+
## 3. The Evaluator: Tree Walking
31+
32+
The evaluator is a tree-walking interpreter. It takes an AST node and returns an `Object` (Dush's internal type system).
33+
34+
If the node is a `CommandExpression`, the evaluator:
35+
- Resolves all arguments (expanding globs like `*.go` and tildes like `~`).
36+
- Checks if the command is a **Built-in** (like `cd` or `exit`).
37+
- If not, it uses `os/exec` to spawn an external process.
38+
39+
## 4. The Environment: Scoped State
40+
41+
Dush uses a linked-list style environment for scoping. When you enter a `proc` (procedure) or an `if` block, a new environment is created that points back to its parent. This provides native support for closures and local variables without leaking state into the global shell.
42+
43+
## 5. Job Control and Pipes
44+
45+
The architecture is designed for concurrency. Pipelines (`cmd1 | cmd2`) are implemented by connecting the `Stdout` of one command's `exec.Cmd` to the `Stdin` of the next via `io.Pipe`. Background jobs (`cmd &`) are managed by a global `JobManager` that tracks PIDs and handles signal forwarding.
46+
47+
Building Dush has been a masterclass in Go's systems capabilities.
48+
49+
Explore the source code: [https://github.com/fezcode/dush](https://github.com/fezcode/dush)

public/posts/why-i-built-dush.txt

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Why I Built Dush
2+
3+
The terminal is my home, but Bash has always felt like a guest who stayed too long and forgot their manners. Don't get me wrong—Bash is legendary. It runs the world. But have you ever tried to remember the difference between `$@` and `$*`? Or spent an hour debugging a script only to realize you forgot a space inside a `[` bracket?
4+
5+
That's why I built **Dush** (Dumb Shell). It started as a weekend experiment to see if I could write a simple command runner in Go. It ended up becoming a full-blown shell with its own scripting language, a Pratt parser, and a philosophy of "explicit is better than cryptic."
6+
7+
## The Frustration
8+
9+
Bash syntax is rooted in the 1970s. It was designed for a world where every byte of memory was precious and every keystroke counted. But in 2026, we have the luxury of clarity.
10+
11+
In Bash:
12+
```bash
13+
if [ "$VAR" == "val" ]; then
14+
echo "equal"
15+
fi
16+
```
17+
18+
In Dush:
19+
```bash
20+
if (@VAR == "val") {
21+
echo "equal"
22+
}
23+
```
24+
25+
Dush replaces the `$VAR`, `${VAR}`, and `$(VAR)` madness with a single, unambiguous sigil: `@`. If you see an `@`, you know it's a Dush variable or expression.
26+
27+
## Why Go?
28+
29+
Go was the obvious choice for this project.
30+
1. **Static Binaries**: I wanted a shell I could drop onto any machine without worrying about dependencies.
31+
2. **Standard Library**: `os/exec` is powerful, and `io.Pipe` makes implementing pipelines almost trivial.
32+
3. **Concurrency**: Job control and background processes are much easier to manage with Go's goroutines and channels.
33+
34+
## From Wrapper to Language
35+
36+
What started as an `os.Stdin` scanner wrapping `os/exec.Command` quickly grew. I realized that to truly improve the shell experience, I needed a real language. I implemented a **Pratt Parser** (Top Down Operator Precedence) because it handles expressions beautifully and allowed me to add features like method syntax:
37+
38+
```bash
39+
@name = "fezcode"
40+
echo @name.upper() # FEZCODE
41+
```
42+
43+
## More Than a Scripting Language
44+
45+
Dush isn't just about scripts. It's about the interactive experience. I built a modern `ls` directly into the shell with icons and colors, implemented a customizable prompt system similar to Oh-My-Zsh (but faster), and added cross-platform job control.
46+
47+
Dush is still "Dumb" in name, but it's becoming the smartest tool in my kit.
48+
49+
Check out the project on GitHub: [https://github.com/fezcode/dush](https://github.com/fezcode/dush)

public/projects/dush/social.txt

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
1-
# Why I built Dush
1+
# Why I Built Dush
22
fezcode
3-
+10 -2 ~1
4-
link: https://github.com/fezcode/dush
3+
+15 -2 ~5
4+
link: /blog/why-i-built-dush
55
---
6-
# Shell Architecture in Go
6+
# Shell Architecture in Go: From Lexer to Execution
77
fezcode
8-
+5 -0 ~0
9-
link: https://github.com/fezcode/dush
8+
+12 -0 ~3
9+
link: /blog/shell-architecture-in-go
10+
---
11+
# Implementing Everything in Go: Built-ins, Pipes, and Jobs
12+
fezcode
13+
+10 -1 ~2
14+
link: /blog/implementing-everything-in-go
15+
---
16+
# Modernizing the Shell: The Dush Scripting Language
17+
fezcode
18+
+8 -0 ~4
19+
link: /blog/modernizing-the-shell-dush-scripting-language
1020
---
1121
# Implementing cd in Go
1222
fezcode

0 commit comments

Comments
 (0)