Skip to content

Commit

Permalink
adding workflows and README
Browse files Browse the repository at this point in the history
  • Loading branch information
nlsandler committed Jun 24, 2024
1 parent e9d6139 commit dd796f1
Show file tree
Hide file tree
Showing 5 changed files with 248 additions and 0 deletions.
28 changes: 28 additions & 0 deletions .github/run-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env bash

# sanity check
if (( CHAPTER < 1 | CHAPTER > 20)); then
echo "Bogus chapter number"
exit 1
fi

# define the stages we're going to test
declare -a STAGES
if ((CHAPTER == 1)); then
# no --tacky or --validate stage yet
STAGES=(lex parse codegen run)
elif ((CHAPTER < 5)); then
# --tacky but not --validate
STAGES=(lex parse tacky codegen run)
elif ((CHAPTER < 19)); then
# all the stages!
STAGES=(lex parse tacky validate codegen run)
else
# don't run intermediate stages for optimization tests
STAGES=(run)
fi
readonly STAGES

for stage in "${STAGES[@]}"; do
./test_compiler "${CC}" --chapter "${CHAPTER}" --stage "${stage}"
done
94 changes: 94 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Build tagged versions of the reference implementation compiler
name: Build the compiler

on:
workflow_dispatch:
inputs:
chapter:
required: true
type: string
os:
required: true
default: ubuntu-latest

workflow_call:
inputs:
chapter:
required: true
type: number
os:
type: string
required: true
default: ubuntu-latest
branch:
type: string
required: false
default: main

env:
EXE_DIR: _build/default/bin/
EXE_PATH: _build/default/bin/main.exe

jobs:

build:
runs-on: ${{ inputs.os }}
env:
CHAPTER: ${{ inputs.chapter }}

steps:

# first check out at specified branch so we can find commit hash for this chapter
- uses: actions/checkout@v4
with:
repository: nlsandler/nqcc2
ref: ${{ inputs.branch }}
fetch-depth: 0 # need this to get commit history

- name: Get commit hash
run: |
git log --grep "chapter $CHAPTER:" -i --format='%H'
commit=$(git log --grep "chapter $CHAPTER:" -i --format='%H')
echo "commit=$commit" >> $GITHUB_ENV
- name: Check out NQCC at chapter ${{ inputs.chapter }}
uses: actions/checkout@v4
with:
repository: nlsandler/nqcc2
ref: ${{ env.commit }}

- name: Construct cache key
id: make-key
env:
runner_os: ${{ runner.os }}
run: |
commit=$(git rev-parse --short "$commit")
echo "cache-key=${runner_os}-${commit}-nqcc" >> $GITHUB_OUTPUT
- name: Cache build result
id: cache-nqcc
uses: actions/cache@v4
with:
path: ${{ env.EXE_PATH }}
key: ${{ steps.make-key.outputs.cache-key }}

# skip building if we get a cache hit
- name: Set up OCaml
if: steps.cache-nqcc.outputs.cache-hit != 'true'
uses: ocaml/setup-ocaml@v2
with:
ocaml-compiler: 4.14.x
# necessary to avoid random errors, see https://github.com/ocaml/setup-ocaml/issues/400
dune-cache: false

- name: Build it
if: steps.cache-nqcc.outputs.cache-hit != 'true'
run: |
opam install . --deps-only
opam exec -- dune build
- name: Upload binary
uses: actions/upload-artifact@v4
with:
name: nqcc-${{inputs.os}}-${{ inputs.chapter }}
path: ${{ env.EXE_PATH }}
60 changes: 60 additions & 0 deletions .github/workflows/build_and_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Build and test the compiler

on:
workflow_call:
inputs:
os:
required: true
type: string
chapter:
required: true
type: number

jobs:
build:
uses: nlsandler/nqcc2/.github/workflows/build.yaml@main
with:
chapter: ${{ inputs.chapter }}
os: ${{ inputs.os }}

test:
runs-on: ${{ inputs.os }}
needs: [build]
env:
CHAPTER: ${{ inputs.chapter }}
steps:

- name: Check out tests
uses: actions/checkout@v4
with:
repository: nlsandler/writing-a-c-compiler-tests
ref: complete-test-suite # TODO use main once this is merged

- name: Check out test runner script
uses: actions/checkout@v4
with:
repository: nlsandler/nqcc2
sparse-checkout: |
.github/run-tests.sh
sparse-checkout-cone-mode: false

- name: Download the compiler
uses: actions/download-artifact@v4
with:
name: nqcc-${{inputs.os}}-${{ inputs.chapter }}
path: nqcc

# make NQCC executable
- run: chmod u+x nqcc/main.exe

# Invoke the run-tests script to test each intermediate stage

# use Rosetta on macOS since that's what most readers are doing
- name: Run the tests (Apple Silicon)
if: runner.arch == 'ARM64'
run: arch -x86_64 ./github/run-tests.sh

- name: Run the tests (x86-64)
if: runner.arch != 'ARM64'
run: ./github/run-tests.sh

18 changes: 18 additions & 0 deletions .github/workflows/test_each_chapter.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Build and test each chapter

on:
workflow_dispatch:

jobs:


build-and-test:
strategy:
fail-fast: true
matrix:
os: [macos-latest, ubuntu-latest]
chapter: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
uses: nlsandler/nqcc2/.github/workflows/build_and_test.yaml@main
with:
chapter: ${{ matrix.chapter }}
os: ${{ matrix.os }}
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# NQCC2, the Not-Quite-C Compiler

**CAUTION: FREQUENT FORCE PUSHES**

This is the reference implementation for the upcoming book [Writing a C Compiler](https://nostarch.com/writing-c-compiler), a hands-on guide to writing your own compiler for a big chunk of C.

Each commit corresponds to one chapter in the book, to illustrate what needs to change at each step. Because commits are structured this way, this repository sees frequent rebases and force pushes - fork with caution!

This implementation is still a work in progress - the functionality is complete, but needs more work on the readability front.

# Building the Compiler

This compiler is written in OCaml. Building it requires opam, the OCaml package manager (installation instructions [here](https://opam.ocaml.org/doc/Install.html)).

Then do:

```
git clone https://github.com/nlsandler/nqcc2.git
cd nqcc2
opam install . --deps-only
dune build
```

This puts the executable at `_build/default/bin/main.exe`.

# Usage Example

Assume we have this source file at ~/hello_world.c:
```c
int puts(char *c);

int main(void) {
puts("Hello, world!");
}
```
To compile and run it:
```
$ _build/default/bin/main.exe ~/hello_world.c
$ ~/hello_world
Hello, World!
```

0 comments on commit dd796f1

Please sign in to comment.