This commit is contained in:
Jonas Maier 2024-07-06 11:35:38 +02:00
commit 43fcc1d394
7 changed files with 404 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

7
Cargo.lock generated Normal file
View File

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "the-comic-sans-of-programming-languages"
version = "0.1.0"

8
Cargo.toml Normal file
View File

@ -0,0 +1,8 @@
[package]
name = "the-comic-sans-of-programming-languages"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

114
src/ast.rs Normal file
View File

@ -0,0 +1,114 @@
type Ident = &'static str;
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Expr {
NeverLit,
UnitLit,
LValue(LValue),
Call(BExpr, Vec<Expr>),
Assign(LValue, BExpr),
While(While),
For(For),
Loop(Block),
Block(Block),
If(If),
}
pub type BExpr = Box<Expr>;
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct While {
pub condition: BExpr,
pub body: Block,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct For {
pub var: Ident,
pub iterator: BExpr,
pub body: Block,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct If {
pub condition: BExpr,
pub yes_branch: Block,
pub no_branch: Block,
}
#[derive(Clone, PartialEq, Eq, Debug)]
/// Assignable expression
pub enum LValue {
Var(Ident),
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Type {
Never,
Unit,
Int,
Function(Vec<Type>, BType),
Struct(StructDeclRef),
}
pub type BType = Box<Type>;
pub type Path = &'static [Ident];
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct StructDecl {
pub fields: Vec<Field>,
}
pub type StructDeclRef = &'static StructDecl;
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Field {
// TODO: visibility
pub name: Ident,
pub typ: Type,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct FunArg {
pub name: Ident,
pub typ: Type,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Stmt {
Expr(Expr),
Let(Let),
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Let {
pub ident: Ident,
// TODO: mutability control
pub typ: Option<Type>,
pub value: Expr,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Block {
pub stmts: Vec<Stmt>,
pub expr: BExpr,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct FunDecl {
pub name: Ident,
pub args: Vec<FunArg>,
pub ret_ty: Type,
pub body: Block,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Decl {
StructDecl(StructDecl),
FunDecl(FunDecl),
Module(Module),
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Module {
pub name: Ident,
pub decls: Vec<Decl>,
}

18
src/intern.rs Normal file
View File

@ -0,0 +1,18 @@
use std::collections::HashSet;
pub struct Interner<T: 'static> {
items: HashSet<&'static T>,
}
impl<T: std::hash::Hash + PartialEq + Eq> Interner<T> {
pub fn intern(&mut self, t: T) -> &'static T {
match self.items.get(&t) {
Some(r) => r,
None => {
let intern: &'static T = &*Box::leak(Box::new(t));
self.items.insert(intern);
intern
}
}
}
}

234
src/ir.rs Normal file
View File

@ -0,0 +1,234 @@
use crate::intern::Interner;
use core::panic;
use std::{collections::HashMap, hash::Hash};
pub type Typer = Interner<Type>;
impl<N> Expr<N, TypeRef> {
pub fn type_check(&self) {}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Expr<N, T> {
pub ty: T,
pub node: Box<ExprNode<N, T>>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ExprNode<N, T> {
Intrinsic(Intrinsic),
LitUnit,
LitInt(i128, IntegerType),
MakeTuple(MakeTuple<N, T>),
DestructTuple(DestructTuple<N, T>),
Let(Let<N, T>),
App(Expr<N, T>, Expr<N, T>),
Abs(Abs<N, T>),
Var(N),
}
/// `let var = expr in body`
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Let<N, T> {
pub var: N,
pub expr: Expr<N, T>,
pub body: Expr<N, T>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Abs<N, T> {
pub arg: N,
pub arg_ty: TypeRef,
pub body: Expr<N, T>,
pub ret_ty: TypeRef,
}
pub type MakeTuple<N, T> = Vec<Expr<N, T>>;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DestructTuple<N, T> {
pub tuple: Expr<N, T>,
pub bindings: Vec<(N, T)>,
pub body: Expr<N, T>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Intrinsic {
PrintNumber,
Panic,
}
type TypeRef = &'static Type;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Type {
Never,
Unit,
Tuple(Vec<TypeRef>),
Effect(Effect),
Primitive(IntegerType),
/// semantics: functions do *NOT* capture their environment
Fn(TypeRef, TypeRef),
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Effect {
IO,
Panic,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum IntegerType {
//U8,
//I8,
//U16,
//I16,
//U32,
I32,
//U64,
//I64,
}
impl Type {
fn is_linear(&self) -> bool {
match self {
Type::Never => true,
Type::Unit => false,
Type::Tuple(t) => t.iter().any(|t| t.is_linear()),
Type::Effect(_) => true,
Type::Primitive(_) => false,
Type::Fn(_, _) => false,
}
}
const fn io() -> Self {
Self::Effect(Effect::IO)
}
const fn panic() -> Self {
Self::Effect(Effect::Panic)
}
}
impl<N: Hash + Eq + Clone + std::fmt::Debug> Expr<N, ()> {
pub fn infer(self, t: &mut Typer, ctx: &HashMap<N, TypeRef>) -> Expr<N, TypeRef> {
let (ty, node) = match Box::into_inner(self.node) {
ExprNode::Intrinsic(i) => match i {
Intrinsic::PrintNumber => {
let io = t.intern(Type::io());
let int = t.intern(Type::Primitive(IntegerType::I32));
let tup = t.intern(Type::Tuple(vec![io, int]));
let ty = t.intern(Type::Fn(tup, io));
(ty, ExprNode::Intrinsic(i))
}
Intrinsic::Panic => {
let p = t.intern(Type::panic());
let ty = t.intern(Type::Fn(p, p));
(ty, ExprNode::Intrinsic(Intrinsic::Panic))
}
},
ExprNode::LitUnit => (t.intern(Type::Unit), ExprNode::LitUnit),
ExprNode::LitInt(val, ty) => (t.intern(Type::Primitive(ty)), ExprNode::LitInt(val, ty)),
ExprNode::MakeTuple(es) => {
let es: Vec<_> = es.into_iter().map(|e| e.infer(t, ctx)).collect();
let tys: Vec<_> = es.iter().map(|e| e.ty).collect();
let ty = t.intern(Type::Tuple(tys));
(ty, ExprNode::MakeTuple(es))
}
ExprNode::DestructTuple(d) => {
let tuple = d.tuple.infer(t, ctx);
match tuple.ty {
Type::Tuple(typs) => {
if typs.len() == d.bindings.len() {
let mut ctx2 = ctx.clone();
let bindings: Vec<_> = d
.bindings
.iter()
.zip(typs.iter())
.map(|((v, _), ty)| (v.clone(), *ty))
.collect();
for (v, ty) in bindings.iter() {
ctx2.insert(v.clone(), ty);
}
let body = d.body.infer(t, &ctx2);
(
body.ty,
ExprNode::DestructTuple(DestructTuple {
tuple,
bindings,
body,
}),
)
} else {
panic!(
"got tuple of length {} but got {} destructuring bindings.",
typs.len(),
d.bindings.len()
);
}
}
ty => panic!("cannot tuple-destruct type {ty:?}"),
}
}
ExprNode::App(fun, arg) => {
let fun = fun.infer(t, ctx);
let arg = arg.infer(t, ctx);
match fun.ty {
Type::Fn(arg_ty, ret_ty) => {
if arg_ty == &arg.ty {
(*ret_ty, ExprNode::App(fun, arg))
} else {
panic!(
"type error: function expects {arg_ty:?}, got {:?} instead.",
arg.ty
);
}
}
x => {
panic!("type error: LHS of function application is {x:?}");
}
}
}
ExprNode::Abs(a) => {
let ty = t.intern(Type::Fn(a.arg_ty, a.ret_ty));
let mut tys = HashMap::new();
tys.insert(a.arg.clone(), ty);
(
ty,
ExprNode::Abs(Abs {
arg: a.arg,
arg_ty: a.arg_ty,
body: a.body.infer(t, &tys),
ret_ty: a.ret_ty,
}),
)
}
ExprNode::Var(v) => {
if let Some(&ty) = ctx.get(&v) {
(ty, ExprNode::Var(v))
} else {
panic!("no variable {v:?} found.")
}
}
ExprNode::Let(l) => {
let expr = l.expr.infer(t, ctx);
let mut ctx2 = ctx.clone();
ctx2.insert(l.var.clone(), expr.ty);
let body = l.body.infer(t, &ctx2);
(
body.ty,
ExprNode::Let(Let {
var: l.var,
expr,
body,
}),
)
}
};
Expr {
ty,
node: Box::new(node),
}
}
}

22
src/main.rs Normal file
View File

@ -0,0 +1,22 @@
#![feature(box_into_inner)]
pub mod ast;
pub mod ir;
pub mod intern;
fn main() {
use ast::*;
let ast = Module {
name: "main",
decls: vec![Decl::FunDecl(FunDecl {
name: "main",
args: vec![],
ret_ty: Type::Unit,
body: Block {
stmts: vec![],
expr: Box::new(Expr::UnitLit),
},
})],
};
}