initial
This commit is contained in:
commit
43fcc1d394
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/target
|
||||
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal 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
8
Cargo.toml
Normal 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
114
src/ast.rs
Normal 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
18
src/intern.rs
Normal 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
234
src/ir.rs
Normal 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
22
src/main.rs
Normal 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),
|
||||
},
|
||||
})],
|
||||
};
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user