Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .github/workflows/jekyll-gh-pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Sample workflow for building and deploying a Jekyll site to GitHub Pages
name: Deploy Jekyll with GitHub Pages dependencies preinstalled

on:
# Runs on pushes targeting the default branch
push:
branches: ["main"]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false

jobs:
# Build job
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Build with Jekyll
uses: actions/jekyll-build-pages@v1
with:
source: ./
destination: ./_site
- name: Upload artifact
uses: actions/upload-pages-artifact@v3

# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Status

[![.github/workflows/ci.yml](https://github.com/clause/471c/actions/workflows/ci.yml/badge.svg)](https://github.com/clause/471c/actions/workflows/ci.yml)
[![Coverage](https://codecov.io/gh/clause/471c/branch/main/graph/badge.svg)](https://codecov.io/gh/clause/471c)
[![.github/workflows/ci.yml](https://github.com/clause/471c/actions/workflows/ci.yml/badge.svg)](https://github.com/Amena2026/471c/actions/workflows/ci.yml)
[![Coverage](https://codecov.io/gh/clause/471c/branch/main/graph/badge.svg)](https://app.codecov.io/gh/Amena2026/471c)

# Contributing

Expand Down
33 changes: 33 additions & 0 deletions packages/L0/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#copy
duplicate the value from a source identifier and insert it into a destination identifer

#immediate
A constant value like an integer that doesnt need to be loaded or stored

#primitave
a math operation containing one or more operands

#Branch
A sequence of instructions that you want to run only if a certain condition is met

#Allocate
give a certain amount of memory to a variable or data structure

#Load
get a value from memory or a data structure

#store
insert a value into memory or a data structure

#Address
a location of memory that can be used to store and retrieve data from

#Call
a sequence of instructions to be run, given an identifier

#halt
stop execution, return from a function

# difference between L2 & L1
Abstract has been removed, Address and call have been introducted. Address is an identifier to store and retrieve data from
, while a call allows for a sequence of instructions to be run given an identifier.
32 changes: 32 additions & 0 deletions packages/L1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#copy
duplicate the value from a source identifier and insert it into a destination identifer

#Abstract
assigns the value of a sequence into an identifier

#Apply
perform a squence of instructions

#immediate
A constant value like an integer that doesnt need to be loaded or stored

#primitave
a math operation containing one or more operands

#Branch
A sequence of instructions that you want to run only if a certain condition is met

#Allocate
give a certain amount of memory to a variable or data structure

#Load
get a value from memory or a data structure

#store
insert a value into memory or a data structure

#halt
stop execution, return from a function

# difference between L2 & L1
terms have been removed and are now replaced by statements. Let and reference have both been removed and is now replaced by copy, which allows for the duplication of data from one identifer to another. Halt is introduced to let you return from a function
36 changes: 36 additions & 0 deletions packages/L2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#Let
Assign a value to a identifier

#Reference
points to a memory location that contains a value

#Abstract
assigns the value of a sequence into an identifier

#Apply
perform a squence of instructions

#immediate
A constant value like an integer that doesnt need to be loaded or stored

#primitave
a math operation containing one or more operands

#Branch
A sequence of instructions that you want to run only if a certain condition is met

#Allocate
give a certain amount of memory to a variable or data structure

#Load
get a value from memory or a data structure

#store
insert a value into memory or a data structure

#begin
start a sequence of instructions


# difference between L3 & L2
no new features have been added. Letrec has been removed
178 changes: 173 additions & 5 deletions packages/L2/src/L2/optimize.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,175 @@
from .syntax import Program
from .syntax import (
Abstract,
Allocate,
Apply,
Begin,
Branch,
Identifier,
Immediate,
Let,
Load,
Primitive,
Program,
Reference,
Store,
Term,
)

type Env = dict[str, Term]

def optimize_program(
program: Program,
) -> Program:
return program

def free_variables(term: Term) -> set[str]:
match term:
case Reference(name=name):
return {name}
case Immediate() | Allocate():
return set()
case Let(bindings=bindings, body=body):
bound = {name for name, _ in bindings}
result = free_variables(body) - bound
for _, value in bindings:
result |= free_variables(value)
return result
case Abstract(parameters=parameters, body=body):
return free_variables(body) - set(parameters)
case Apply(target=target, arguments=arguments):
result = free_variables(target)
for arg in arguments:
result |= free_variables(arg)
return result
case Primitive(left=left, right=right):
return free_variables(left) | free_variables(right)
case Branch(left=left, right=right, consequent=consequent, otherwise=otherwise):
return free_variables(left) | free_variables(right) | free_variables(consequent) | free_variables(otherwise)
case Load(base=base):
return free_variables(base)
case Store(base=base, value=value):
return free_variables(base) | free_variables(value)
case Begin(effects=effects, value=value):
result = free_variables(value)
for effect in effects:
result |= free_variables(effect)
return result


def is_pure(term: Term) -> bool:
match term:
case Immediate() | Reference() | Abstract():
return True
case Primitive(left=left, right=right):
return is_pure(left) and is_pure(right)
case Branch(left=left, right=right, consequent=consequent, otherwise=otherwise):
return is_pure(left) and is_pure(right) and is_pure(consequent) and is_pure(otherwise)
case Let(bindings=bindings, body=body):
return all(is_pure(v) for _, v in bindings) and is_pure(body)
case Apply() | Allocate() | Load() | Store() | Begin():
return False


def optimize_term(term: Term, env: Env) -> Term:
match term:
case Reference(name=name):
return env.get(name, term)

case Immediate() | Allocate():
return term

case Let(bindings=bindings, body=body):
new_bindings: list[tuple[Identifier, Term]] = []
new_env = dict(env)
for name, value in bindings:
opt_value = optimize_term(value, new_env)
if isinstance(opt_value, (Immediate, Reference)):
# Constant: propagate into subsequent bindings and body
new_env[name] = opt_value
else:
# Non-constant: keep as binding; shadow any outer constant for this name
new_bindings.append((name, opt_value))
new_env.pop(name, None)

opt_body = optimize_term(body, new_env)

# Dead code elimination: drop unused bindings whose values are pure
final_bindings: list[tuple[Identifier, Term]] = []
for i, (name, value) in enumerate(new_bindings):
after_free = free_variables(opt_body)
for _, later_value in new_bindings[i + 1 :]:
after_free |= free_variables(later_value)
if name in after_free or not is_pure(value):
final_bindings.append((name, value))

if not final_bindings:
return opt_body
return Let(bindings=final_bindings, body=opt_body)

case Abstract(parameters=parameters, body=body):
inner_env = {k: v for k, v in env.items() if k not in set(parameters)}
return Abstract(
parameters=parameters,
body=optimize_term(body, inner_env),
)

case Apply(target=target, arguments=arguments):
return Apply(
target=optimize_term(target, env),
arguments=[optimize_term(arg, env) for arg in arguments],
)

case Primitive(operator=op, left=left, right=right):
opt_left = optimize_term(left, env)
opt_right = optimize_term(right, env)
if isinstance(opt_left, Immediate) and isinstance(opt_right, Immediate):
match op:
case "+":
return Immediate(value=opt_left.value + opt_right.value)
case "-":
return Immediate(value=opt_left.value - opt_right.value)
case "*":
return Immediate(value=opt_left.value * opt_right.value)
return Primitive(operator=op, left=opt_left, right=opt_right)

case Branch(operator=op, left=left, right=right, consequent=consequent, otherwise=otherwise):
opt_left = optimize_term(left, env)
opt_right = optimize_term(right, env)
opt_consequent = optimize_term(consequent, env)
opt_otherwise = optimize_term(otherwise, env)
if isinstance(opt_left, Immediate) and isinstance(opt_right, Immediate):
match op:
case "<":
condition = opt_left.value < opt_right.value
case "==":
condition = opt_left.value == opt_right.value
return opt_consequent if condition else opt_otherwise
return Branch(
operator=op,
left=opt_left,
right=opt_right,
consequent=opt_consequent,
otherwise=opt_otherwise,
)

case Load(base=base, index=index):
return Load(base=optimize_term(base, env), index=index)

case Store(base=base, index=index, value=value):
return Store(
base=optimize_term(base, env),
index=index,
value=optimize_term(value, env),
)

case Begin(effects=effects, value=value):
return Begin(
effects=[optimize_term(e, env) for e in effects],
value=optimize_term(value, env),
)


def optimize_program(program: Program) -> Program:
body = program.body
while True:
optimized = optimize_term(body, {})
if optimized == body:
break
body = optimized
return Program(parameters=program.parameters, body=body)
Loading