Repository files navigation

MiniC

MiniC is a small C-like programming language implemented in Rust. It is designed as a teaching tool: the entire pipeline — parser, type checker, and interpreter — is intentionally small so you can read and understand every part of it. A complete MiniC program looks like this:

intfactorial(intn) {
ifn <= 1 { return1; }
returnn*factorial(n-1);
}
voidmain() {
intresult=factorial(10);
print(result);
}

Quick Start

cargo build # compile the project
cargo test# run the Rust library tests
shelltest tests/cli/ # run the CLI tests (requires cargo build first)

Nix Development Environment

This project ships a Nix flake that pins every tool to a reproducible version. If you have Nix installed you never need to manage Rust, Clippy, or shelltestrunner yourself — the flake does it for you.

What is Nix?

Nix is a package manager (and optionally a full OS) built around one idea: every package is a pure function of its inputs. The same inputs always produce the same output, on any machine. A flake is a self-contained Nix project that declares its own dependencies in flake.nix and locks them to exact hashes in flake.lock, giving you fully reproducible builds.

A devShell is a temporary shell that provides a curated set of tools without touching your normal environment. When you exit the shell those tools disappear.

Prerequisites

  1. Install Nix (single-user or multi-user).

  2. Enable flakes and the nix-command experimental feature. Add the following to ~/.config/nix/nix.conf (or /etc/nix/nix.conf):

    experimental-features = nix-command flakes
    
  3. (Optional but recommended) Install direnv and nix-direnv. The .envrc file at the root of this repo will then activate the dev shell automatically whenever you cd into the project directory.

Entering the dev shell

nix develop # enter the shell manually

Or, if you use direnv:

direnv allow # run once; the shell activates on every cd after that

The dev shell provides:

ToolPurpose
cargo / rustcCompile and run Rust code
rustfmtAuto-format source files
clippyLint Rust code
rust-analyzerLSP backend for editors
shelltestRun the CLI test suite

Building and testing inside the dev shell

Once inside nix develop (or with direnv active), the standard workflow is:

# 1. Compile
cargo build
# 2. Run unit and integration tests
cargo test# 3. Run the CLI (shelltestrunner) tests# These require the binary to be built first (step 1).
shelltest tests/cli/
# 4. Check formatting
cargo fmt --check
# 5. Run the linter
cargo clippy -- -D warnings

All commands above use the exact tool versions pinned in flake.lock, so results are identical regardless of what is installed on the host system.

The binary accepts two commands:

minic --check <file.minic> # parse + type-check only
minic --run <file.minic> # parse + type-check + interpret

CLI Usage

Running a valid program

Save the following as hello.minic:

voidmain() {
strname="Alice";
print(name)
}
$ minic --run hello.minic
Alice

Checking a program without running it

--check stops after the type checker. Useful for validating a program before committing to run it:

$ minic --check hello.minic
'hello.minic' is well-typed.

Malformed programs

The MiniC parser is lenient by design: it uses a many0 combinator that collects as many valid function declarations as it can and stops silently on anything it cannot recognise. This means most malformed programs do not produce a parse error — they produce an empty (or partial) function list, and the type checker then reports the problem.

Using an unknown keyword instead of a type:

defgreet(strname) {
print(name)
}

The parser skips the unrecognised def line and finds no functions. The type checker then rejects the result:

$ minic --check bad_keyword.minic
Type error: program must have a main function

Assigning the wrong type to a variable:

voidmain() {
intx="hello"
}

This parses successfully but is rejected by the type checker:

$ minic --check type_mismatch.minic
Type error: declaration of x: expected Int, got Str

Type errors

Type errors are caught after parsing, before any code runs.

Assigning a string to an int variable:

voidmain() {
intx="hello"
}
$ minic --check type_mismatch.minic
Type error: declaration of x: expected Int, got Str

Calling a function with the wrong argument type:

intdouble(intn) {
returnn*2
}
voidmain() {
intresult=double("hello")
}
$ minic --check wrong_arg.minic
Type error: argument 1 to double: expected Int, got Str

Using a boolean where an integer is expected:

voidmain() {
intx=10;
inty=x+ true
}
$ minic --check bool_in_arithmetic.minic
Type error: arithmetic operands must be Int or Float

Exit codes

SituationExit code
Success0
Parse error, type error, or runtime error1
Wrong arguments or missing file1

Documentation

Read the documents in order for a complete picture of the project, or jump directly to the topic you need.

#FileWhat you will learn
1Language referenceWhat you can write in MiniC: types, statements, operators
2Pipeline overviewHow source code travels from text to execution
3The ASTHow a MiniC program is represented in memory
4The parserHow source text is turned into an AST
5The type checkerHow type errors are caught before running
6The interpreterHow a type-checked program is executed
7The standard libraryBuilt-in functions and how to add new ones
8TestingHow the test suite is organised and how to add tests

Project Layout

src/
├── ir/ # AST node definitions
├── parser/ # Source text → unchecked AST
├── semantic/ # Type checker: unchecked AST → checked AST
├── environment/ # Shared symbol table used by type checker and interpreter
├── interpreter/ # Tree-walking interpreter
└── stdlib/ # Built-in functions (print, sqrt, pow, …)
tests/ # Integration tests (all tests live here)
docs/ # This documentation

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

MiniC

MiniC is a small C-like programming language implemented in Rust. It is designed as a teaching tool: the entire pipeline — parser, type checker, and interpreter — is intentionally small so you can read and understand every part of it. A complete MiniC program looks like this:

intfactorial(intn) {
ifn <= 1 { return1; }
returnn*factorial(n-1);
}
voidmain() {
intresult=factorial(10);
print(result);
}

Quick Start

cargo build # compile the project
cargo test# run the Rust library tests
shelltest tests/cli/ # run the CLI tests (requires cargo build first)

Nix Development Environment

This project ships a Nix flake that pins every tool to a reproducible version. If you have Nix installed you never need to manage Rust, Clippy, or shelltestrunner yourself — the flake does it for you.

What is Nix?

Nix is a package manager (and optionally a full OS) built around one idea: every package is a pure function of its inputs. The same inputs always produce the same output, on any machine. A flake is a self-contained Nix project that declares its own dependencies in flake.nix and locks them to exact hashes in flake.lock, giving you fully reproducible builds.

A devShell is a temporary shell that provides a curated set of tools without touching your normal environment. When you exit the shell those tools disappear.

Prerequisites

  1. Install Nix (single-user or multi-user).

  2. Enable flakes and the nix-command experimental feature. Add the following to ~/.config/nix/nix.conf (or /etc/nix/nix.conf):

    experimental-features = nix-command flakes
    
  3. (Optional but recommended) Install direnv and nix-direnv. The .envrc file at the root of this repo will then activate the dev shell automatically whenever you cd into the project directory.

Entering the dev shell

nix develop # enter the shell manually

Or, if you use direnv:

direnv allow # run once; the shell activates on every cd after that

The dev shell provides:

ToolPurpose
cargo / rustcCompile and run Rust code
rustfmtAuto-format source files
clippyLint Rust code
rust-analyzerLSP backend for editors
shelltestRun the CLI test suite

Building and testing inside the dev shell

Once inside nix develop (or with direnv active), the standard workflow is:

# 1. Compile
cargo build
# 2. Run unit and integration tests
cargo test# 3. Run the CLI (shelltestrunner) tests# These require the binary to be built first (step 1).
shelltest tests/cli/
# 4. Check formatting
cargo fmt --check
# 5. Run the linter
cargo clippy -- -D warnings

All commands above use the exact tool versions pinned in flake.lock, so results are identical regardless of what is installed on the host system.

The binary accepts two commands:

minic --check <file.minic> # parse + type-check only
minic --run <file.minic> # parse + type-check + interpret

CLI Usage

Running a valid program

Save the following as hello.minic:

voidmain() {
strname="Alice";
print(name)
}
$ minic --run hello.minic
Alice

Checking a program without running it

--check stops after the type checker. Useful for validating a program before committing to run it:

$ minic --check hello.minic
'hello.minic' is well-typed.

Malformed programs

The MiniC parser is lenient by design: it uses a many0 combinator that collects as many valid function declarations as it can and stops silently on anything it cannot recognise. This means most malformed programs do not produce a parse error — they produce an empty (or partial) function list, and the type checker then reports the problem.

Using an unknown keyword instead of a type:

defgreet(strname) {
print(name)
}

The parser skips the unrecognised def line and finds no functions. The type checker then rejects the result:

$ minic --check bad_keyword.minic
Type error: program must have a main function

Assigning the wrong type to a variable:

voidmain() {
intx="hello"
}

This parses successfully but is rejected by the type checker:

$ minic --check type_mismatch.minic
Type error: declaration of x: expected Int, got Str

Type errors

Type errors are caught after parsing, before any code runs.

Assigning a string to an int variable:

voidmain() {
intx="hello"
}
$ minic --check type_mismatch.minic
Type error: declaration of x: expected Int, got Str

Calling a function with the wrong argument type:

intdouble(intn) {
returnn*2
}
voidmain() {
intresult=double("hello")
}
$ minic --check wrong_arg.minic
Type error: argument 1 to double: expected Int, got Str

Using a boolean where an integer is expected:

voidmain() {
intx=10;
inty=x+ true
}
$ minic --check bool_in_arithmetic.minic
Type error: arithmetic operands must be Int or Float

Exit codes

SituationExit code
Success0
Parse error, type error, or runtime error1
Wrong arguments or missing file1

Documentation

Read the documents in order for a complete picture of the project, or jump directly to the topic you need.

#FileWhat you will learn
1Language referenceWhat you can write in MiniC: types, statements, operators
2Pipeline overviewHow source code travels from text to execution
3The ASTHow a MiniC program is represented in memory
4The parserHow source text is turned into an AST
5The type checkerHow type errors are caught before running
6The interpreterHow a type-checked program is executed
7The standard libraryBuilt-in functions and how to add new ones
8TestingHow the test suite is organised and how to add tests

Project Layout

src/
├── ir/ # AST node definitions
├── parser/ # Source text → unchecked AST
├── semantic/ # Type checker: unchecked AST → checked AST
├── environment/ # Shared symbol table used by type checker and interpreter
├── interpreter/ # Tree-walking interpreter
└── stdlib/ # Built-in functions (print, sqrt, pow, …)
tests/ # Integration tests (all tests live here)
docs/ # This documentation

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

MiniC

MiniC is a small C-like programming language implemented in Rust. It is designed as a teaching tool: the entire pipeline — parser, type checker, and interpreter — is intentionally small so you can read and understand every part of it. A complete MiniC program looks like this:

intfactorial(intn) {
ifn <= 1 { return1; }
returnn*factorial(n-1);
}
voidmain() {
intresult=factorial(10);
print(result);
}

Quick Start

cargo build # compile the project
cargo test# run the Rust library tests
shelltest tests/cli/ # run the CLI tests (requires cargo build first)

Nix Development Environment

This project ships a Nix flake that pins every tool to a reproducible version. If you have Nix installed you never need to manage Rust, Clippy, or shelltestrunner yourself — the flake does it for you.

What is Nix?

Nix is a package manager (and optionally a full OS) built around one idea: every package is a pure function of its inputs. The same inputs always produce the same output, on any machine. A flake is a self-contained Nix project that declares its own dependencies in flake.nix and locks them to exact hashes in flake.lock, giving you fully reproducible builds.

A devShell is a temporary shell that provides a curated set of tools without touching your normal environment. When you exit the shell those tools disappear.

Prerequisites

  1. Install Nix (single-user or multi-user).

  2. Enable flakes and the nix-command experimental feature. Add the following to ~/.config/nix/nix.conf (or /etc/nix/nix.conf):

    experimental-features = nix-command flakes
    
  3. (Optional but recommended) Install direnv and nix-direnv. The .envrc file at the root of this repo will then activate the dev shell automatically whenever you cd into the project directory.

Entering the dev shell

nix develop # enter the shell manually

Or, if you use direnv:

direnv allow # run once; the shell activates on every cd after that

The dev shell provides:

ToolPurpose
cargo / rustcCompile and run Rust code
rustfmtAuto-format source files
clippyLint Rust code
rust-analyzerLSP backend for editors
shelltestRun the CLI test suite

Building and testing inside the dev shell

Once inside nix develop (or with direnv active), the standard workflow is:

# 1. Compile
cargo build
# 2. Run unit and integration tests
cargo test# 3. Run the CLI (shelltestrunner) tests# These require the binary to be built first (step 1).
shelltest tests/cli/
# 4. Check formatting
cargo fmt --check
# 5. Run the linter
cargo clippy -- -D warnings

All commands above use the exact tool versions pinned in flake.lock, so results are identical regardless of what is installed on the host system.

The binary accepts two commands:

minic --check <file.minic> # parse + type-check only
minic --run <file.minic> # parse + type-check + interpret

CLI Usage

Running a valid program

Save the following as hello.minic:

voidmain() {
strname="Alice";
print(name)
}
$ minic --run hello.minic
Alice

Checking a program without running it

--check stops after the type checker. Useful for validating a program before committing to run it:

$ minic --check hello.minic
'hello.minic' is well-typed.

Malformed programs

The MiniC parser is lenient by design: it uses a many0 combinator that collects as many valid function declarations as it can and stops silently on anything it cannot recognise. This means most malformed programs do not produce a parse error — they produce an empty (or partial) function list, and the type checker then reports the problem.

Using an unknown keyword instead of a type:

defgreet(strname) {
print(name)
}

The parser skips the unrecognised def line and finds no functions. The type checker then rejects the result:

$ minic --check bad_keyword.minic
Type error: program must have a main function

Assigning the wrong type to a variable:

voidmain() {
intx="hello"
}

This parses successfully but is rejected by the type checker:

$ minic --check type_mismatch.minic
Type error: declaration of x: expected Int, got Str

Type errors

Type errors are caught after parsing, before any code runs.

Assigning a string to an int variable:

voidmain() {
intx="hello"
}
$ minic --check type_mismatch.minic
Type error: declaration of x: expected Int, got Str

Calling a function with the wrong argument type:

intdouble(intn) {
returnn*2
}
voidmain() {
intresult=double("hello")
}
$ minic --check wrong_arg.minic
Type error: argument 1 to double: expected Int, got Str

Using a boolean where an integer is expected:

voidmain() {
intx=10;
inty=x+ true
}
$ minic --check bool_in_arithmetic.minic
Type error: arithmetic operands must be Int or Float

Exit codes

SituationExit code
Success0
Parse error, type error, or runtime error1
Wrong arguments or missing file1

Documentation

Read the documents in order for a complete picture of the project, or jump directly to the topic you need.

#FileWhat you will learn
1Language referenceWhat you can write in MiniC: types, statements, operators
2Pipeline overviewHow source code travels from text to execution
3The ASTHow a MiniC program is represented in memory
4The parserHow source text is turned into an AST
5The type checkerHow type errors are caught before running
6The interpreterHow a type-checked program is executed
7The standard libraryBuilt-in functions and how to add new ones
8TestingHow the test suite is organised and how to add tests

Project Layout

src/
├── ir/ # AST node definitions
├── parser/ # Source text → unchecked AST
├── semantic/ # Type checker: unchecked AST → checked AST
├── environment/ # Shared symbol table used by type checker and interpreter
├── interpreter/ # Tree-walking interpreter
└── stdlib/ # Built-in functions (print, sqrt, pow, …)
tests/ # Integration tests (all tests live here)
docs/ # This documentation

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

MiniC

MiniC is a small C-like programming language implemented in Rust. It is designed as a teaching tool: the entire pipeline — parser, type checker, and interpreter — is intentionally small so you can read and understand every part of it. A complete MiniC program looks like this:

intfactorial(intn) {
ifn <= 1 { return1; }
returnn*factorial(n-1);
}
voidmain() {
intresult=factorial(10);
print(result);
}

Quick Start

cargo build # compile the project
cargo test# run the Rust library tests
shelltest tests/cli/ # run the CLI tests (requires cargo build first)

Nix Development Environment

This project ships a Nix flake that pins every tool to a reproducible version. If you have Nix installed you never need to manage Rust, Clippy, or shelltestrunner yourself — the flake does it for you.

What is Nix?

Nix is a package manager (and optionally a full OS) built around one idea: every package is a pure function of its inputs. The same inputs always produce the same output, on any machine. A flake is a self-contained Nix project that declares its own dependencies in flake.nix and locks them to exact hashes in flake.lock, giving you fully reproducible builds.

A devShell is a temporary shell that provides a curated set of tools without touching your normal environment. When you exit the shell those tools disappear.

Prerequisites

  1. Install Nix (single-user or multi-user).

  2. Enable flakes and the nix-command experimental feature. Add the following to ~/.config/nix/nix.conf (or /etc/nix/nix.conf):

    experimental-features = nix-command flakes
    
  3. (Optional but recommended) Install direnv and nix-direnv. The .envrc file at the root of this repo will then activate the dev shell automatically whenever you cd into the project directory.

Entering the dev shell

nix develop # enter the shell manually

Or, if you use direnv:

direnv allow # run once; the shell activates on every cd after that

The dev shell provides:

ToolPurpose
cargo / rustcCompile and run Rust code
rustfmtAuto-format source files
clippyLint Rust code
rust-analyzerLSP backend for editors
shelltestRun the CLI test suite

Building and testing inside the dev shell

Once inside nix develop (or with direnv active), the standard workflow is:

# 1. Compile
cargo build
# 2. Run unit and integration tests
cargo test# 3. Run the CLI (shelltestrunner) tests# These require the binary to be built first (step 1).
shelltest tests/cli/
# 4. Check formatting
cargo fmt --check
# 5. Run the linter
cargo clippy -- -D warnings

All commands above use the exact tool versions pinned in flake.lock, so results are identical regardless of what is installed on the host system.

The binary accepts two commands:

minic --check <file.minic> # parse + type-check only
minic --run <file.minic> # parse + type-check + interpret

CLI Usage

Running a valid program

Save the following as hello.minic:

voidmain() {
strname="Alice";
print(name)
}
$ minic --run hello.minic
Alice

Checking a program without running it

--check stops after the type checker. Useful for validating a program before committing to run it:

$ minic --check hello.minic
'hello.minic' is well-typed.

Malformed programs

The MiniC parser is lenient by design: it uses a many0 combinator that collects as many valid function declarations as it can and stops silently on anything it cannot recognise. This means most malformed programs do not produce a parse error — they produce an empty (or partial) function list, and the type checker then reports the problem.

Using an unknown keyword instead of a type:

defgreet(strname) {
print(name)
}

The parser skips the unrecognised def line and finds no functions. The type checker then rejects the result:

$ minic --check bad_keyword.minic
Type error: program must have a main function

Assigning the wrong type to a variable:

voidmain() {
intx="hello"
}

This parses successfully but is rejected by the type checker:

$ minic --check type_mismatch.minic
Type error: declaration of x: expected Int, got Str

Type errors

Type errors are caught after parsing, before any code runs.

Assigning a string to an int variable:

voidmain() {
intx="hello"
}
$ minic --check type_mismatch.minic
Type error: declaration of x: expected Int, got Str

Calling a function with the wrong argument type:

intdouble(intn) {
returnn*2
}
voidmain() {
intresult=double("hello")
}
$ minic --check wrong_arg.minic
Type error: argument 1 to double: expected Int, got Str

Using a boolean where an integer is expected:

voidmain() {
intx=10;
inty=x+ true
}
$ minic --check bool_in_arithmetic.minic
Type error: arithmetic operands must be Int or Float

Exit codes

SituationExit code
Success0
Parse error, type error, or runtime error1
Wrong arguments or missing file1

Documentation

Read the documents in order for a complete picture of the project, or jump directly to the topic you need.

#FileWhat you will learn
1Language referenceWhat you can write in MiniC: types, statements, operators
2Pipeline overviewHow source code travels from text to execution
3The ASTHow a MiniC program is represented in memory
4The parserHow source text is turned into an AST
5The type checkerHow type errors are caught before running
6The interpreterHow a type-checked program is executed
7The standard libraryBuilt-in functions and how to add new ones
8TestingHow the test suite is organised and how to add tests

Project Layout

src/
├── ir/ # AST node definitions
├── parser/ # Source text → unchecked AST
├── semantic/ # Type checker: unchecked AST → checked AST
├── environment/ # Shared symbol table used by type checker and interpreter
├── interpreter/ # Tree-walking interpreter
└── stdlib/ # Built-in functions (print, sqrt, pow, …)
tests/ # Integration tests (all tests live here)
docs/ # This documentation

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

MiniC

MiniC is a small C-like programming language implemented in Rust. It is designed as a teaching tool: the entire pipeline — parser, type checker, and interpreter — is intentionally small so you can read and understand every part of it. A complete MiniC program looks like this:

intfactorial(intn) {
ifn <= 1 { return1; }
returnn*factorial(n-1);
}
voidmain() {
intresult=factorial(10);
print(result);
}

Quick Start

cargo build # compile the project
cargo test# run the Rust library tests
shelltest tests/cli/ # run the CLI tests (requires cargo build first)

Nix Development Environment

This project ships a Nix flake that pins every tool to a reproducible version. If you have Nix installed you never need to manage Rust, Clippy, or shelltestrunner yourself — the flake does it for you.

What is Nix?

Nix is a package manager (and optionally a full OS) built around one idea: every package is a pure function of its inputs. The same inputs always produce the same output, on any machine. A flake is a self-contained Nix project that declares its own dependencies in flake.nix and locks them to exact hashes in flake.lock, giving you fully reproducible builds.

A devShell is a temporary shell that provides a curated set of tools without touching your normal environment. When you exit the shell those tools disappear.

Prerequisites

  1. Install Nix (single-user or multi-user).

  2. Enable flakes and the nix-command experimental feature. Add the following to ~/.config/nix/nix.conf (or /etc/nix/nix.conf):

    experimental-features = nix-command flakes
    
  3. (Optional but recommended) Install direnv and nix-direnv. The .envrc file at the root of this repo will then activate the dev shell automatically whenever you cd into the project directory.

Entering the dev shell

nix develop # enter the shell manually

Or, if you use direnv:

direnv allow # run once; the shell activates on every cd after that

The dev shell provides:

ToolPurpose
cargo / rustcCompile and run Rust code
rustfmtAuto-format source files
clippyLint Rust code
rust-analyzerLSP backend for editors
shelltestRun the CLI test suite

Building and testing inside the dev shell

Once inside nix develop (or with direnv active), the standard workflow is:

# 1. Compile
cargo build
# 2. Run unit and integration tests
cargo test# 3. Run the CLI (shelltestrunner) tests# These require the binary to be built first (step 1).
shelltest tests/cli/
# 4. Check formatting
cargo fmt --check
# 5. Run the linter
cargo clippy -- -D warnings

All commands above use the exact tool versions pinned in flake.lock, so results are identical regardless of what is installed on the host system.

The binary accepts two commands:

minic --check <file.minic> # parse + type-check only
minic --run <file.minic> # parse + type-check + interpret

CLI Usage

Running a valid program

Save the following as hello.minic:

voidmain() {
strname="Alice";
print(name)
}
$ minic --run hello.minic
Alice

Checking a program without running it

--check stops after the type checker. Useful for validating a program before committing to run it:

$ minic --check hello.minic
'hello.minic' is well-typed.

Malformed programs

The MiniC parser is lenient by design: it uses a many0 combinator that collects as many valid function declarations as it can and stops silently on anything it cannot recognise. This means most malformed programs do not produce a parse error — they produce an empty (or partial) function list, and the type checker then reports the problem.

Using an unknown keyword instead of a type:

defgreet(strname) {
print(name)
}

The parser skips the unrecognised def line and finds no functions. The type checker then rejects the result:

$ minic --check bad_keyword.minic
Type error: program must have a main function

Assigning the wrong type to a variable:

voidmain() {
intx="hello"
}

This parses successfully but is rejected by the type checker:

$ minic --check type_mismatch.minic
Type error: declaration of x: expected Int, got Str

Type errors

Type errors are caught after parsing, before any code runs.

Assigning a string to an int variable:

voidmain() {
intx="hello"
}
$ minic --check type_mismatch.minic
Type error: declaration of x: expected Int, got Str

Calling a function with the wrong argument type:

intdouble(intn) {
returnn*2
}
voidmain() {
intresult=double("hello")
}
$ minic --check wrong_arg.minic
Type error: argument 1 to double: expected Int, got Str

Using a boolean where an integer is expected:

voidmain() {
intx=10;
inty=x+ true
}
$ minic --check bool_in_arithmetic.minic
Type error: arithmetic operands must be Int or Float

Exit codes

SituationExit code
Success0
Parse error, type error, or runtime error1
Wrong arguments or missing file1

Documentation

Read the documents in order for a complete picture of the project, or jump directly to the topic you need.

#FileWhat you will learn
1Language referenceWhat you can write in MiniC: types, statements, operators
2Pipeline overviewHow source code travels from text to execution
3The ASTHow a MiniC program is represented in memory
4The parserHow source text is turned into an AST
5The type checkerHow type errors are caught before running
6The interpreterHow a type-checked program is executed
7The standard libraryBuilt-in functions and how to add new ones
8TestingHow the test suite is organised and how to add tests

Project Layout

src/
├── ir/ # AST node definitions
├── parser/ # Source text → unchecked AST
├── semantic/ # Type checker: unchecked AST → checked AST
├── environment/ # Shared symbol table used by type checker and interpreter
├── interpreter/ # Tree-walking interpreter
└── stdlib/ # Built-in functions (print, sqrt, pow, …)
tests/ # Integration tests (all tests live here)
docs/ # This documentation

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

MiniC

MiniC is a small C-like programming language implemented in Rust. It is designed as a teaching tool: the entire pipeline — parser, type checker, and interpreter — is intentionally small so you can read and understand every part of it. A complete MiniC program looks like this:

intfactorial(intn) {
ifn <= 1 { return1; }
returnn*factorial(n-1);
}
voidmain() {
intresult=factorial(10);
print(result);
}

Quick Start

cargo build # compile the project
cargo test# run the Rust library tests
shelltest tests/cli/ # run the CLI tests (requires cargo build first)

Nix Development Environment

This project ships a Nix flake that pins every tool to a reproducible version. If you have Nix installed you never need to manage Rust, Clippy, or shelltestrunner yourself — the flake does it for you.

What is Nix?

Nix is a package manager (and optionally a full OS) built around one idea: every package is a pure function of its inputs. The same inputs always produce the same output, on any machine. A flake is a self-contained Nix project that declares its own dependencies in flake.nix and locks them to exact hashes in flake.lock, giving you fully reproducible builds.

A devShell is a temporary shell that provides a curated set of tools without touching your normal environment. When you exit the shell those tools disappear.

Prerequisites

  1. Install Nix (single-user or multi-user).

  2. Enable flakes and the nix-command experimental feature. Add the following to ~/.config/nix/nix.conf (or /etc/nix/nix.conf):

    experimental-features = nix-command flakes
    
  3. (Optional but recommended) Install direnv and nix-direnv. The .envrc file at the root of this repo will then activate the dev shell automatically whenever you cd into the project directory.

Entering the dev shell

nix develop # enter the shell manually

Or, if you use direnv:

direnv allow # run once; the shell activates on every cd after that

The dev shell provides:

ToolPurpose
cargo / rustcCompile and run Rust code
rustfmtAuto-format source files
clippyLint Rust code
rust-analyzerLSP backend for editors
shelltestRun the CLI test suite

Building and testing inside the dev shell

Once inside nix develop (or with direnv active), the standard workflow is:

# 1. Compile
cargo build
# 2. Run unit and integration tests
cargo test# 3. Run the CLI (shelltestrunner) tests# These require the binary to be built first (step 1).
shelltest tests/cli/
# 4. Check formatting
cargo fmt --check
# 5. Run the linter
cargo clippy -- -D warnings

All commands above use the exact tool versions pinned in flake.lock, so results are identical regardless of what is installed on the host system.

The binary accepts two commands:

minic --check <file.minic> # parse + type-check only
minic --run <file.minic> # parse + type-check + interpret

CLI Usage

Running a valid program

Save the following as hello.minic:

voidmain() {
strname="Alice";
print(name)
}
$ minic --run hello.minic
Alice

Checking a program without running it

--check stops after the type checker. Useful for validating a program before committing to run it:

$ minic --check hello.minic
'hello.minic' is well-typed.

Malformed programs

The MiniC parser is lenient by design: it uses a many0 combinator that collects as many valid function declarations as it can and stops silently on anything it cannot recognise. This means most malformed programs do not produce a parse error — they produce an empty (or partial) function list, and the type checker then reports the problem.

Using an unknown keyword instead of a type:

defgreet(strname) {
print(name)
}

The parser skips the unrecognised def line and finds no functions. The type checker then rejects the result:

$ minic --check bad_keyword.minic
Type error: program must have a main function

Assigning the wrong type to a variable:

voidmain() {
intx="hello"
}

This parses successfully but is rejected by the type checker:

$ minic --check type_mismatch.minic
Type error: declaration of x: expected Int, got Str

Type errors

Type errors are caught after parsing, before any code runs.

Assigning a string to an int variable:

voidmain() {
intx="hello"
}
$ minic --check type_mismatch.minic
Type error: declaration of x: expected Int, got Str

Calling a function with the wrong argument type:

intdouble(intn) {
returnn*2
}
voidmain() {
intresult=double("hello")
}
$ minic --check wrong_arg.minic
Type error: argument 1 to double: expected Int, got Str

Using a boolean where an integer is expected:

voidmain() {
intx=10;
inty=x+ true
}
$ minic --check bool_in_arithmetic.minic
Type error: arithmetic operands must be Int or Float

Exit codes

SituationExit code
Success0
Parse error, type error, or runtime error1
Wrong arguments or missing file1

Documentation

Read the documents in order for a complete picture of the project, or jump directly to the topic you need.

#FileWhat you will learn
1Language referenceWhat you can write in MiniC: types, statements, operators
2Pipeline overviewHow source code travels from text to execution
3The ASTHow a MiniC program is represented in memory
4The parserHow source text is turned into an AST
5The type checkerHow type errors are caught before running
6The interpreterHow a type-checked program is executed
7The standard libraryBuilt-in functions and how to add new ones
8TestingHow the test suite is organised and how to add tests

Project Layout

src/
├── ir/ # AST node definitions
├── parser/ # Source text → unchecked AST
├── semantic/ # Type checker: unchecked AST → checked AST
├── environment/ # Shared symbol table used by type checker and interpreter
├── interpreter/ # Tree-walking interpreter
└── stdlib/ # Built-in functions (print, sqrt, pow, …)
tests/ # Integration tests (all tests live here)
docs/ # This documentation

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

MiniC

MiniC is a small C-like programming language implemented in Rust. It is designed as a teaching tool: the entire pipeline — parser, type checker, and interpreter — is intentionally small so you can read and understand every part of it. A complete MiniC program looks like this:

intfactorial(intn) {
ifn <= 1 { return1; }
returnn*factorial(n-1);
}
voidmain() {
intresult=factorial(10);
print(result);
}

Quick Start

cargo build # compile the project
cargo test# run the Rust library tests
shelltest tests/cli/ # run the CLI tests (requires cargo build first)

Nix Development Environment

This project ships a Nix flake that pins every tool to a reproducible version. If you have Nix installed you never need to manage Rust, Clippy, or shelltestrunner yourself — the flake does it for you.

What is Nix?

Nix is a package manager (and optionally a full OS) built around one idea: every package is a pure function of its inputs. The same inputs always produce the same output, on any machine. A flake is a self-contained Nix project that declares its own dependencies in flake.nix and locks them to exact hashes in flake.lock, giving you fully reproducible builds.

A devShell is a temporary shell that provides a curated set of tools without touching your normal environment. When you exit the shell those tools disappear.

Prerequisites

  1. Install Nix (single-user or multi-user).

  2. Enable flakes and the nix-command experimental feature. Add the following to ~/.config/nix/nix.conf (or /etc/nix/nix.conf):

    experimental-features = nix-command flakes
    
  3. (Optional but recommended) Install direnv and nix-direnv. The .envrc file at the root of this repo will then activate the dev shell automatically whenever you cd into the project directory.

Entering the dev shell

nix develop # enter the shell manually

Or, if you use direnv:

direnv allow # run once; the shell activates on every cd after that

The dev shell provides:

ToolPurpose
cargo / rustcCompile and run Rust code
rustfmtAuto-format source files
clippyLint Rust code
rust-analyzerLSP backend for editors
shelltestRun the CLI test suite

Building and testing inside the dev shell

Once inside nix develop (or with direnv active), the standard workflow is:

# 1. Compile
cargo build
# 2. Run unit and integration tests
cargo test# 3. Run the CLI (shelltestrunner) tests# These require the binary to be built first (step 1).
shelltest tests/cli/
# 4. Check formatting
cargo fmt --check
# 5. Run the linter
cargo clippy -- -D warnings

All commands above use the exact tool versions pinned in flake.lock, so results are identical regardless of what is installed on the host system.

The binary accepts two commands:

minic --check <file.minic> # parse + type-check only
minic --run <file.minic> # parse + type-check + interpret

CLI Usage

Running a valid program

Save the following as hello.minic:

voidmain() {
strname="Alice";
print(name)
}
$ minic --run hello.minic
Alice

Checking a program without running it

--check stops after the type checker. Useful for validating a program before committing to run it:

$ minic --check hello.minic
'hello.minic' is well-typed.

Malformed programs

The MiniC parser is lenient by design: it uses a many0 combinator that collects as many valid function declarations as it can and stops silently on anything it cannot recognise. This means most malformed programs do not produce a parse error — they produce an empty (or partial) function list, and the type checker then reports the problem.

Using an unknown keyword instead of a type:

defgreet(strname) {
print(name)
}

The parser skips the unrecognised def line and finds no functions. The type checker then rejects the result:

$ minic --check bad_keyword.minic
Type error: program must have a main function

Assigning the wrong type to a variable:

voidmain() {
intx="hello"
}

This parses successfully but is rejected by the type checker:

$ minic --check type_mismatch.minic
Type error: declaration of x: expected Int, got Str

Type errors

Type errors are caught after parsing, before any code runs.

Assigning a string to an int variable:

voidmain() {
intx="hello"
}
$ minic --check type_mismatch.minic
Type error: declaration of x: expected Int, got Str

Calling a function with the wrong argument type:

intdouble(intn) {
returnn*2
}
voidmain() {
intresult=double("hello")
}
$ minic --check wrong_arg.minic
Type error: argument 1 to double: expected Int, got Str

Using a boolean where an integer is expected:

voidmain() {
intx=10;
inty=x+ true
}
$ minic --check bool_in_arithmetic.minic
Type error: arithmetic operands must be Int or Float

Exit codes

SituationExit code
Success0
Parse error, type error, or runtime error1
Wrong arguments or missing file1

Documentation

Read the documents in order for a complete picture of the project, or jump directly to the topic you need.

#FileWhat you will learn
1Language referenceWhat you can write in MiniC: types, statements, operators
2Pipeline overviewHow source code travels from text to execution
3The ASTHow a MiniC program is represented in memory
4The parserHow source text is turned into an AST
5The type checkerHow type errors are caught before running
6The interpreterHow a type-checked program is executed
7The standard libraryBuilt-in functions and how to add new ones
8TestingHow the test suite is organised and how to add tests

Project Layout

src/
├── ir/ # AST node definitions
├── parser/ # Source text → unchecked AST
├── semantic/ # Type checker: unchecked AST → checked AST
├── environment/ # Shared symbol table used by type checker and interpreter
├── interpreter/ # Tree-walking interpreter
└── stdlib/ # Built-in functions (print, sqrt, pow, …)
tests/ # Integration tests (all tests live here)
docs/ # This documentation

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

MiniC

MiniC is a small C-like programming language implemented in Rust. It is designed as a teaching tool: the entire pipeline — parser, type checker, and interpreter — is intentionally small so you can read and understand every part of it. A complete MiniC program looks like this:

intfactorial(intn) {
ifn <= 1 { return1; }
returnn*factorial(n-1);
}
voidmain() {
intresult=factorial(10);
print(result);
}

Quick Start

cargo build # compile the project
cargo test# run the Rust library tests
shelltest tests/cli/ # run the CLI tests (requires cargo build first)

Nix Development Environment

This project ships a Nix flake that pins every tool to a reproducible version. If you have Nix installed you never need to manage Rust, Clippy, or shelltestrunner yourself — the flake does it for you.

What is Nix?

Nix is a package manager (and optionally a full OS) built around one idea: every package is a pure function of its inputs. The same inputs always produce the same output, on any machine. A flake is a self-contained Nix project that declares its own dependencies in flake.nix and locks them to exact hashes in flake.lock, giving you fully reproducible builds.

A devShell is a temporary shell that provides a curated set of tools without touching your normal environment. When you exit the shell those tools disappear.

Prerequisites

  1. Install Nix (single-user or multi-user).

  2. Enable flakes and the nix-command experimental feature. Add the following to ~/.config/nix/nix.conf (or /etc/nix/nix.conf):

    experimental-features = nix-command flakes
    
  3. (Optional but recommended) Install direnv and nix-direnv. The .envrc file at the root of this repo will then activate the dev shell automatically whenever you cd into the project directory.

Entering the dev shell

nix develop # enter the shell manually

Or, if you use direnv:

direnv allow # run once; the shell activates on every cd after that

The dev shell provides:

ToolPurpose
cargo / rustcCompile and run Rust code
rustfmtAuto-format source files
clippyLint Rust code
rust-analyzerLSP backend for editors
shelltestRun the CLI test suite

Building and testing inside the dev shell

Once inside nix develop (or with direnv active), the standard workflow is:

# 1. Compile
cargo build
# 2. Run unit and integration tests
cargo test# 3. Run the CLI (shelltestrunner) tests# These require the binary to be built first (step 1).
shelltest tests/cli/
# 4. Check formatting
cargo fmt --check
# 5. Run the linter
cargo clippy -- -D warnings

All commands above use the exact tool versions pinned in flake.lock, so results are identical regardless of what is installed on the host system.

The binary accepts two commands:

minic --check <file.minic> # parse + type-check only
minic --run <file.minic> # parse + type-check + interpret

CLI Usage

Running a valid program

Save the following as hello.minic:

voidmain() {
strname="Alice";
print(name)
}
$ minic --run hello.minic
Alice

Checking a program without running it

--check stops after the type checker. Useful for validating a program before committing to run it:

$ minic --check hello.minic
'hello.minic' is well-typed.

Malformed programs

The MiniC parser is lenient by design: it uses a many0 combinator that collects as many valid function declarations as it can and stops silently on anything it cannot recognise. This means most malformed programs do not produce a parse error — they produce an empty (or partial) function list, and the type checker then reports the problem.

Using an unknown keyword instead of a type:

defgreet(strname) {
print(name)
}

The parser skips the unrecognised def line and finds no functions. The type checker then rejects the result:

$ minic --check bad_keyword.minic
Type error: program must have a main function

Assigning the wrong type to a variable:

voidmain() {
intx="hello"
}

This parses successfully but is rejected by the type checker:

$ minic --check type_mismatch.minic
Type error: declaration of x: expected Int, got Str

Type errors

Type errors are caught after parsing, before any code runs.

Assigning a string to an int variable:

voidmain() {
intx="hello"
}
$ minic --check type_mismatch.minic
Type error: declaration of x: expected Int, got Str

Calling a function with the wrong argument type:

intdouble(intn) {
returnn*2
}
voidmain() {
intresult=double("hello")
}
$ minic --check wrong_arg.minic
Type error: argument 1 to double: expected Int, got Str

Using a boolean where an integer is expected:

voidmain() {
intx=10;
inty=x+ true
}
$ minic --check bool_in_arithmetic.minic
Type error: arithmetic operands must be Int or Float

Exit codes

SituationExit code
Success0
Parse error, type error, or runtime error1
Wrong arguments or missing file1

Documentation

Read the documents in order for a complete picture of the project, or jump directly to the topic you need.

#FileWhat you will learn
1Language referenceWhat you can write in MiniC: types, statements, operators
2Pipeline overviewHow source code travels from text to execution
3The ASTHow a MiniC program is represented in memory
4The parserHow source text is turned into an AST
5The type checkerHow type errors are caught before running
6The interpreterHow a type-checked program is executed
7The standard libraryBuilt-in functions and how to add new ones
8TestingHow the test suite is organised and how to add tests

Project Layout

src/
├── ir/ # AST node definitions
├── parser/ # Source text → unchecked AST
├── semantic/ # Type checker: unchecked AST → checked AST
├── environment/ # Shared symbol table used by type checker and interpreter
├── interpreter/ # Tree-walking interpreter
└── stdlib/ # Built-in functions (print, sqrt, pow, …)
tests/ # Integration tests (all tests live here)
docs/ # This documentation

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages