1
1
Fork 0
mirror of https://github.com/azur1s/bobbylisp.git synced 2024-10-16 02:37:40 -05:00

Compare commits

..

2 commits

Author SHA1 Message Date
Natapat Samutpong 74adfe1f0f cleanup 2022-03-23 13:03:08 +07:00
Natapat Samutpong 8021a0e31d prepare for typechecking 2022-03-23 12:45:35 +07:00
12 changed files with 284 additions and 210 deletions

16
Cargo.lock generated
View file

@ -131,6 +131,7 @@ dependencies = [
"chumsky",
"hir",
"lexer",
"typecheck",
]
[[package]]
@ -163,6 +164,7 @@ dependencies = [
"serde",
"serde_derive",
"toml",
"typecheck",
]
[[package]]
@ -184,7 +186,6 @@ dependencies = [
name = "hir"
version = "0.1.0"
dependencies = [
"levenshtein",
"parser",
]
@ -204,12 +205,6 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "levenshtein"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760"
[[package]]
name = "lexer"
version = "0.1.0"
@ -361,6 +356,13 @@ dependencies = [
"serde",
]
[[package]]
name = "typecheck"
version = "0.1.0"
dependencies = [
"hir",
]
[[package]]
name = "unicode-xid"
version = "0.2.2"

View file

@ -5,5 +5,6 @@ members = [
"crates/parser",
"crates/diagnostic",
"crates/hir",
"crates/typecheck",
"crates/codegen",
]

View file

@ -31,7 +31,7 @@ impl Codegen {
macro_rules! semicolon { () => { if should_gen_semicolon { ";" } else { "" } }; }
match ir {
IRKind::Define { public, name, type_hint, value, mutable } => {
IRKind::Define { public, name, type_hint, value, mutable, .. } => {
format!(
"{} {} v_{}: {} = {}{}\n",
if *public { "export" } else { "" },
@ -43,7 +43,7 @@ impl Codegen {
)
},
IRKind::Call { name, args } => {
IRKind::Call { name, args, .. } => {
format!(
"f_{}({}){}",
name,
@ -57,7 +57,7 @@ impl Codegen {
)
},
IRKind::Intrinsic { name, args } => {
IRKind::Intrinsic { name, args, .. } => {
match name.as_str() {
"write" => { format!("write({}){}\n" , self.gen_ir(&args[0], false), semicolon!()) },
"write_file" => { format!("writeFile({}, {}){}\n", self.gen_ir(&args[0], false), self.gen_ir(&args[1], false), semicolon!()) },
@ -71,7 +71,7 @@ impl Codegen {
}
},
IRKind::Fun { public, name, return_type_hint, args, body } => {
IRKind::Fun { public, name, return_type_hint, args, body, .. } => {
let args = args
.iter()
.map(|arg| format!("v_{}: {}", arg.0, arg.1))
@ -87,14 +87,14 @@ impl Codegen {
)
},
IRKind::Return { value } => {
IRKind::Return { value, .. } => {
format!(
"return {};\n",
self.gen_ir(value, false)
)
},
IRKind::Do { body } => {
IRKind::Do { body, .. } => {
let mut out = "{\n".to_string();
for expr in body {
out.push_str(&self.gen_ir(&expr, true));
@ -103,7 +103,7 @@ impl Codegen {
out
},
IRKind::If { cond, body, else_body } => {
IRKind::If { cond, body, else_body, .. } => {
format!(
"if ({}) {{\n{}}} else {{\n{}}}\n",
self.gen_ir(cond, true),
@ -112,7 +112,7 @@ impl Codegen {
)
},
IRKind::Case { cond, cases, default } => {
IRKind::Case { cond, cases, default, .. } => {
format!(
"switch ({}) {{\n{}{}\n}}\n",
self.gen_ir(cond, true),
@ -131,11 +131,11 @@ impl Codegen {
)
},
IRKind::Unary { op, right } => {
IRKind::Unary { op, right, .. } => {
format!("{}{}", op, self.gen_ir(right, false))
},
IRKind::Binary { left, op, right } => {
IRKind::Binary { left, op, right, .. } => {
format!("{} {} {}", self.gen_ir(left, false), op, self.gen_ir(right, false))
},

View file

@ -10,3 +10,4 @@ chumsky = "0.8.0"
ariadne = "0.1.5"
lexer = { path = "../lexer" }
hir = { path = "../hir" }
typecheck = { path = "../typecheck" }

View file

@ -12,6 +12,7 @@ pub enum Kind {
LexError(Simple<char>),
ParseError(Simple<Token>),
LoweringError(hir::LoweringError),
TypecheckError(typecheck::TypecheckError),
}
impl Diagnostics {
@ -37,6 +38,10 @@ impl Diagnostics {
self.errors.push(Kind::LoweringError(error));
}
pub fn add_typecheck_error(&mut self, error: typecheck::TypecheckError) {
self.errors.push(Kind::TypecheckError(error));
}
pub fn display(&self, src: String) {
let lex_error = self.errors.iter().filter_map(|kind| match kind {
Kind::LexError(error) => Some(error.clone()), // Using clone() to remove reference
@ -120,6 +125,11 @@ impl Diagnostics {
Kind::LoweringError(error) => Some(error.clone()),
_ => None,
});
let typecheck_error = self.errors.iter().filter_map(|kind| match kind {
Kind::TypecheckError(error) => Some(error.clone()),
_ => None,
});
// TODO: so many .iter(), maybe collapse them into one?
lower_error.into_iter()
.for_each(|e| {
@ -138,13 +148,27 @@ impl Diagnostics {
.with_color(Color::Red)
);
if let Some(note) = &e.note {
report
.with_note(note)
.finish().print(Source::from(&src)).unwrap();
} else {
report.finish().print(Source::from(&src)).unwrap();
}
});
typecheck_error.into_iter()
.for_each(|e| {
let span = &e.span;
let message = &e.kind;
let report = Report::build(ReportKind::Error, (), span.start)
.with_message(
format!("{}", message)
)
.with_label(
Label::new(span.clone())
.with_message(
format!("{}", message)
)
.with_color(Color::Red)
);
report.finish().print(Source::from(&src)).unwrap();
})
}
}

View file

@ -8,4 +8,3 @@ edition = "2021"
[dependencies]
parser = { path = "../parser" }
levenshtein = "1.0.5" # Used for error reporting

View file

@ -29,31 +29,62 @@ impl std::fmt::Display for Value {
pub enum IRKind {
Value { value: Value },
Vector { values: Vec<Self> },
Unary { op: String, right: Box<Self> },
Binary { op: String, left: Box<Self>, right: Box<Self> },
Call { name: String, args: Vec<Self> },
Intrinsic { name: String, args: Vec<Self> },
Unary {
op: String,
right: Box<Self>,
span: Range<usize>
},
Binary {
op: String,
left: Box<Self>,
right: Box<Self>,
span: Range<usize>
},
Call {
name: String,
args: Vec<Self>,
span: Range<usize>
},
Intrinsic {
name: String,
args: Vec<Self>,
span: Range<usize>
},
Define {
public: bool,
name: String,
type_hint: String,
value: Box<Self>,
mutable: bool
mutable: bool,
span: Range<usize>,
},
Fun {
public: bool,
name: String,
return_type_hint: String,
args: Vec<(String, String)>,
body: Box<Self>
body: Box<Self>,
span: Range<usize>,
},
If { cond: Box<Self>, body: Box<Self>, else_body: Box<Self> },
Case { cond: Box<Self>, cases: Vec<(Box<Self>, Box<Self>)>, default: Box<Self> },
Do { body: Vec<Self> },
If {
cond: Box<Self>,
body: Box<Self>,
else_body: Box<Self>,
span: Range<usize>,
},
Case {
cond: Box<Self>,
cases: Vec<(Box<Self>, Box<Self>)>,
default: Box<Self>,
span: Range<usize>,
},
Do { body: Vec<Self>, span: Range<usize> },
Return { value: Box<Self> },
Return { value: Box<Self>, span: Range<usize> },
// Error { message: String, note: Option<String>, span: Range<usize> },
}
#[derive(Debug)]
@ -62,70 +93,10 @@ pub struct IR {
pub span: Range<usize>
}
// S-Expression displaying for IR
// Used for debugging (run `compile` with `-a`)
impl std::fmt::Display for IRKind {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
IRKind::Define { ref public, ref name, ref type_hint, ref value, ref mutable } => {
write!(f, "(let {} {} {} {} {})",
if *public { "export" } else { "" },
name,
type_hint,
value,
if *mutable { "mut" } else { "" },
)
},
IRKind::Fun { ref public, ref name, ref return_type_hint, ref args, ref body } => {
write!(f, "(fun{} {} :{} [{}] {})",
if *public { " export" } else { "" },
name,
return_type_hint,
args.iter().map(|(name, type_hint)| format!(":{} {}", name, type_hint)).collect::<Vec<_>>().join(" "),
body,
)
},
IRKind::Call { ref name, ref args } => {
write!(f, "({} {})", name, args.iter().map(|arg| arg.to_string()).collect::<Vec<_>>().join(" "))
},
IRKind::Intrinsic { ref name, ref args } => {
write!(f, "(@{} {})", name, args.iter().map(|arg| arg.to_string()).collect::<Vec<_>>().join(" "))
},
IRKind::Do { ref body } => {
write!(f, "(do {})", body.iter().map(|expr| expr.to_string()).collect::<Vec<_>>().join(" "))
},
IRKind::If { ref cond, ref body, ref else_body } => {
write!(f, "(if {} {} {})", cond, body, else_body)
},
IRKind::Case { ref cond, ref cases, ref default } => {
write!(f, "(case {} {} {})", cond, cases.iter().map(|(cond, body)| format!("({} {})", cond, body)).collect::<Vec<_>>().join(" "), default)
},
IRKind::Unary { ref op, ref right } => {
write!(f, "({} {})", op, right)
},
IRKind::Binary { ref op, ref left, ref right } => {
write!(f, "({} {} {})", op, left, right)
},
IRKind::Value { ref value } => {
write!(f, "{}", value)
},
IRKind::Vector { ref values } => {
write!(f, "[{}]", values.iter().map(|expr| expr.to_string()).collect::<Vec<_>>().join(" "))
},
IRKind::Return { ref value } => {
write!(f, "(return {})", value)
}
#[allow(unreachable_patterns)]
_ => { dbg!(self); unreachable!() }
}
}
}
#[derive(Debug)]
pub struct LoweringError {
pub span: Range<usize>,
pub message: String,
pub note: Option<String>,
}
pub fn ast_to_ir(ast: Vec<(Expr, Range<usize>)>) -> (Vec<IR>, Vec<LoweringError>) {
@ -153,13 +124,31 @@ macro_rules! if_err_return {
};
}
#[macro_export]
macro_rules! return_ok {
($value:expr) => {
return (Some($value), None)
};
}
#[macro_export]
macro_rules! return_err {
($value:expr) => {
return (None, Some($value))
};
}
pub fn expr_to_ir(expr: &Expr) -> (Option<IRKind>, Option<LoweringError>) {
match expr {
Expr::Unary { op, rhs } => {
let rhs_ir = expr_to_ir(&rhs.0);
if_err_return!(rhs_ir.1);
return (Some(IRKind::Unary { op: op.to_string(), right: Box::new(rhs_ir.0.unwrap()) }), None);
return_ok!(IRKind::Unary {
op: op.to_string(),
right: Box::new(rhs_ir.0.unwrap()),
span: rhs.1.clone()
});
}
Expr::Binary { lhs, op, rhs } => {
@ -169,14 +158,19 @@ pub fn expr_to_ir(expr: &Expr) -> (Option<IRKind>, Option<LoweringError>) {
let rhs_ir = expr_to_ir(&rhs.0);
if_err_return!(rhs_ir.1);
return (Some(IRKind::Binary { op: op.to_string(), left: Box::new(lhs_ir.0.unwrap()), right: Box::new(rhs_ir.0.unwrap()) }), None)
return_ok!(IRKind::Binary {
op: op.to_string(),
left: Box::new(lhs_ir.0.unwrap()),
right: Box::new(rhs_ir.0.unwrap()),
span: lhs.1.start..rhs.1.end
});
},
Expr::Call { name, args } => {
let name = match &name.0 {
let lname = match &name.0 {
Expr::Identifier(s) => s.clone(),
// Should never happen because the parser should have caught this
_ => return (None, Some(LoweringError { span: name.1.clone(), message: "Expected identifier".to_string(), note: None }))
_ => return_err!(LoweringError { span: name.1.clone(), message: "Expected identifier".to_string()})
};
let mut largs = Vec::new(); // `largs` stand for lowered args
// Iterate over args
@ -186,8 +180,12 @@ pub fn expr_to_ir(expr: &Expr) -> (Option<IRKind>, Option<LoweringError>) {
if_err_return!(arg.1);
largs.push(arg.0.unwrap());
}
let ir_kind = IRKind::Call { name, args: largs };
return (Some(ir_kind), None);
return_ok!(IRKind::Call {
name: lname,
args: largs,
span: name.1.start..args.1.end
});
},
Expr::Pipeline { lhs, rhs } => {
@ -200,7 +198,7 @@ pub fn expr_to_ir(expr: &Expr) -> (Option<IRKind>, Option<LoweringError>) {
let cname = match &name.0 {
Expr::Identifier(s) => s.clone(),
// Should never happen because the parser should have caught this
_ => return (None, Some(LoweringError { span: name.1.clone(), message: "Expected identifier".to_string(), note: None }))
_ => return_err!(LoweringError { span: name.1.clone(), message: "Expected identifier".to_string()})
};
// Get all the `Hole` indexes
@ -213,11 +211,10 @@ pub fn expr_to_ir(expr: &Expr) -> (Option<IRKind>, Option<LoweringError>) {
// If there is no `Hole` in the args then return early
if indexes.is_empty() {
return (None, Some(LoweringError {
return_err!(LoweringError {
span: rhs.1.clone(),
message: "Expected hole in piping".to_string(),
note: None
}));
});
}
// Remove the `Hole` from the args
@ -259,50 +256,54 @@ pub fn expr_to_ir(expr: &Expr) -> (Option<IRKind>, Option<LoweringError>) {
// Match the call to the right IRKind
let ir_kind = match new_call.0.unwrap() {
IRKind::Call { .. } => IRKind::Call { name: cname, args: largs },
IRKind::Intrinsic { .. } => IRKind::Intrinsic { name: cname, args: largs },
IRKind::Call { .. } => IRKind::Call {
name: cname,
args: largs,
span: name.1.start..args.1.end
},
IRKind::Intrinsic { .. } => IRKind::Intrinsic {
name: cname,
args: largs,
span: name.1.start..args.1.end
},
_ => unreachable!()
};
return (Some(ir_kind), None);
return_ok!(ir_kind);
},
_ => return (None, Some(LoweringError {
_ => return_err!(LoweringError {
span: rhs.1.clone(),
message: "Expected call".to_string(),
note: None
})),
message: "Expected call".to_string()
}),
};
},
Expr::Let { public, name, type_hint, value, mutable } => {
let value = expr_to_ir(&value.0);
if_err_return!(value.1);
let lvalue = expr_to_ir(&value.0);
if_err_return!(lvalue.1);
let value = value.0.unwrap();
let ir_kind = IRKind::Define {
return_ok!(IRKind::Define {
public: *public,
name: name.clone(),
type_hint: gen_type_hint(type_hint),
value: Box::new(value),
mutable: *mutable
};
return (Some(ir_kind), None);
name: name.0.clone(),
type_hint: gen_type_hint(&type_hint.0),
value: Box::new(lvalue.0.unwrap()),
mutable: *mutable,
span: value.1.clone()
});
},
Expr::Intrinsic { name, args } => {
let name = match &name.0 {
let lname = match &name.0 {
Expr::Identifier(s) => {
if INTRINSICS.contains(&s.as_str()) { s.clone() }
else {
return (None, Some(LoweringError {
return_err!(LoweringError {
span: name.1.clone(),
message: format!("Unknown intrinsic: `{}`", s),
note: Some(format!("Did you mean: {}?", closet_intrinsic(s.to_string())))
}));
});
}
}
_ => return (None, Some(LoweringError { span: name.1.clone(), message: "Expected identifier".to_string(), note: None }))
_ => return_err!(LoweringError { span: name.1.clone(), message: "Expected identifier".to_string()})
};
let mut largs = Vec::new();
@ -314,73 +315,80 @@ pub fn expr_to_ir(expr: &Expr) -> (Option<IRKind>, Option<LoweringError>) {
if let IRKind::Value{ .. } = larg.0.clone().unwrap() {
largs.push(larg.0.clone().unwrap());
} else {
return (None, Some(LoweringError { span: arg.1.clone(), message: "Expected string".to_string(), note: None }))
return_err!(LoweringError { span: arg.1.clone(), message: "Expected string".to_string()});
}
}
let ir_kind = IRKind::Intrinsic { name, args: largs };
return (Some(ir_kind), None);
return_ok!(IRKind::Intrinsic {
name: lname,
args: largs,
span: name.1.start..args.1.end
});
},
Expr::Fun { public, name, type_hint, args, body } => {
// Iterate each argument and give it a type hint
let args = args.0.iter().map(|arg| (arg.0.0.clone(), gen_type_hint(&arg.1.0))).collect::<Vec<_>>();
let largs = args.0.iter().map(|arg| (arg.0.0.clone(), gen_type_hint(&arg.1.0))).collect::<Vec<_>>();
let body = expr_to_ir(&body.0);
if_err_return!(body.1);
let lbody = expr_to_ir(&body.0);
if_err_return!(lbody.1);
let body = body.0.unwrap();
let ir_kind = IRKind::Fun {
return_ok!(IRKind::Fun {
public: *public,
name: name.clone(),
return_type_hint: gen_type_hint(type_hint),
args,
body: Box::new(body)
};
return (Some(ir_kind), None);
name: name.0.clone(),
return_type_hint: gen_type_hint(&type_hint.0),
args: largs,
body: Box::new(lbody.0.unwrap()),
span: name.1.start..body.1.end
});
},
Expr::Return { expr } => {
let expr = expr_to_ir(&expr.0);
if_err_return!(expr.1);
let lexpr = expr_to_ir(&expr.0);
if_err_return!(lexpr.1);
let expr = expr.0.unwrap();
let ir_kind = IRKind::Return { value: Box::new(expr) };
return (Some(ir_kind), None);
return_ok!(IRKind::Return {
value: Box::new(lexpr.0.unwrap()),
span: expr.1.clone()
});
},
Expr::Do { body } => {
let mut lbody = Vec::new();
for expr in body {
for expr in &body.0 {
let expr = expr_to_ir(&expr.0);
if_err_return!(expr.1);
lbody.push(expr.0.unwrap());
}
let ir_kind = IRKind::Do { body: lbody };
return (Some(ir_kind), None);
};
return_ok!(IRKind::Do {
body: lbody,
span: body.1.clone()
});
},
Expr::If { cond, body, else_body } => {
let cond = expr_to_ir(&cond.0);
if_err_return!(cond.1);
let lcond = expr_to_ir(&cond.0);
if_err_return!(lcond.1);
let body = expr_to_ir(&body.0);
if_err_return!(body.1);
let lbody = expr_to_ir(&body.0);
if_err_return!(lbody.1);
let else_body = expr_to_ir(&else_body.0);
if_err_return!(else_body.1);
let lelse_body = expr_to_ir(&else_body.0);
if_err_return!(lelse_body.1);
let ir_kind = IRKind::If {
cond: Box::new(cond.0.unwrap()),
body: Box::new(body.0.unwrap()),
else_body: Box::new(else_body.0.unwrap())
};
return (Some(ir_kind), None);
return_ok!(IRKind::If {
cond: Box::new(lcond.0.unwrap()),
body: Box::new(lbody.0.unwrap()),
else_body: Box::new(lelse_body.0.unwrap()),
span: cond.1.start..else_body.1.end
});
},
Expr::Case { expr, cases, default } => {
let expr = expr_to_ir(&expr.0);
if_err_return!(expr.1);
let lexpr = expr_to_ir(&expr.0);
if_err_return!(lexpr.1);
let mut lcases = Vec::new();
for case in &cases.0 {
@ -395,23 +403,23 @@ pub fn expr_to_ir(expr: &Expr) -> (Option<IRKind>, Option<LoweringError>) {
);
}
let default = expr_to_ir(&default.0);
if_err_return!(default.1);
let ldefault = expr_to_ir(&default.0);
if_err_return!(ldefault.1);
let ir_kind = IRKind::Case {
cond: Box::new(expr.0.unwrap()),
return_ok!(IRKind::Case {
cond: Box::new(lexpr.0.unwrap()),
cases: lcases,
default: Box::new(default.0.unwrap())
};
return (Some(ir_kind), None);
default: Box::new(ldefault.0.unwrap()),
span: expr.1.start..default.1.end
});
},
// TODO: Handle primitive types error (e.g. overflow)
// For now it just leaves the value as is and let the target compiler handle it
Expr::Int(value) => (Some(IRKind::Value { value: Value::Int(*value) }), None),
Expr::Boolean(value) => (Some(IRKind::Value { value: Value::Boolean(*value) }), None),
Expr::String(value) => (Some(IRKind::Value { value: Value::String(value.clone()) }), None),
Expr::Identifier(value) => (Some(IRKind::Value { value: Value::Ident(value.clone()) }), None),
Expr::Int(value) => return_ok!(IRKind::Value { value: Value::Int(*value) }),
Expr::Boolean(value) => return_ok!(IRKind::Value { value: Value::Boolean(*value) }),
Expr::String(value) => return_ok!(IRKind::Value { value: Value::String(value.clone()) }),
Expr::Identifier(value) => return_ok!(IRKind::Value { value: Value::Ident(value.clone()) }),
Expr::Vector(values) => {
let mut lvalues = Vec::new();
@ -427,8 +435,7 @@ pub fn expr_to_ir(expr: &Expr) -> (Option<IRKind>, Option<LoweringError>) {
// Probably will never happen because it is catched in parser
Expr::Hole(start, end) => (None, Some(LoweringError {
span: *start..*end,
message: "Hole can only be used in piping, it is not allowed here.".to_string(),
note: None
message: "Hole can only be used in piping, it is not allowed here.".to_string()
})),
_ => { dbg!(expr); todo!() }
}
@ -447,17 +454,3 @@ fn gen_type_hint(type_hint: &str) -> String {
_ => { dbg!(type_hint); todo!() }
}
}
// Get the closet intrinsic name to the given name
fn closet_intrinsic(got: String) -> String {
let mut closest = String::new();
let mut closest_dist = std::usize::MAX;
for intrinsic in INTRINSICS.iter() {
let dist = levenshtein::levenshtein(got.as_str(), intrinsic);
if dist < closest_dist {
closest = intrinsic.to_string();
closest_dist = dist;
}
}
closest
}

View file

@ -18,6 +18,7 @@ lexer = { path = "../lexer" }
parser = { path = "../parser" }
# Diagnostics
typecheck = { path = "../typecheck" }
diagnostic = { path = "../diagnostic" }
# Codegen

View file

@ -6,6 +6,7 @@ use lexer::lex;
use parser::parse;
use diagnostic::Diagnostics;
use hir::ast_to_ir;
use typecheck::check;
use codegen::ts;
pub mod args;
@ -52,16 +53,24 @@ fn main() {
logif!(0, format!("Parsing took {}ms", start.elapsed().as_millis()));
}
// TODO: S-Expr syntax for AST
// if print_ast { log(0, format!("{:#?}", ast)); }
match ast {
Some(ast) => {
// Convert the AST to HIR
let (ir, lowering_error) = ast_to_ir(ast);
for err in lowering_error { diagnostics.add_lowering_error(err); }
if print_ast { log(0, format!("IR\n{}", ir.iter().map(|x| format!("{}", x.kind)).collect::<Vec<String>>().join("\n\n"))); }
if print_ast { log(0, format!("IR\n{:#?}", ir)); }
// Typecheck the HIR
match check(&ir) {
Ok(_) => {},
Err(err) => {
diagnostics.add_typecheck_error(err);
diagnostics.display(src);
logif!(0, "Typechecking failed");
std::process::exit(1);
}
}
// Report lowering errors if any
if diagnostics.has_error() {

View file

@ -18,15 +18,15 @@ pub enum Expr {
Let {
public: bool,
name: String,
type_hint: String,
name: Spanned<String>,
type_hint: Spanned<String>,
value: Box<Spanned<Self>>,
mutable: bool,
},
Fun {
public: bool,
name: String,
type_hint: String,
name: Spanned<String>,
type_hint: Spanned<String>,
args: Spanned<Vec<(Spanned<String>, Spanned<String>)>>,
body: Box<Spanned<Self>>
},
@ -43,7 +43,7 @@ pub enum Expr {
default: Box<Spanned<Self>>
},
Do {
body: Vec<Spanned<Self>>
body: Spanned<Vec<Spanned<Self>>>
},
// Hole for positional argument(s) in piping
@ -226,8 +226,8 @@ fn expr_parser() -> impl Parser<Token, Vec<Spanned<Expr>>, Error = Simple<Token>
(
Expr::Let {
public: public.is_some(),
name: name.0.clone(),
type_hint: type_hint.0,
name: name.clone(),
type_hint,
value: Box::new(value.clone()),
mutable: mutable.is_some(),
},
@ -256,8 +256,8 @@ fn expr_parser() -> impl Parser<Token, Vec<Spanned<Expr>>, Error = Simple<Token>
(
Expr::Fun {
public: public.is_some(),
name: name.0.clone(),
type_hint: type_hint.0,
name: name.clone(),
type_hint,
args: (args, name.1.clone()),
body: Box::new(body.clone()),
},
@ -286,7 +286,7 @@ fn expr_parser() -> impl Parser<Token, Vec<Spanned<Expr>>, Error = Simple<Token>
.map_with_span(|body, span| {
(
Expr::Do {
body: body.clone(),
body: (body.clone(), span.clone()),
},
span,
)

View file

@ -0,0 +1,10 @@
[package]
name = "typecheck"
license = "MIT OR Apache-2.0"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
hir = { path = "../hir" }

View file

@ -0,0 +1,34 @@
use hir::{IR, IRKind};
#[derive(Debug)]
pub enum TypecheckErrorKind {
DefinitionTypeMismatch {
type_specified: String,
type_found: String,
},
}
impl std::fmt::Display for TypecheckErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
TypecheckErrorKind::DefinitionTypeMismatch {
type_specified,
type_found,
} => write!(
f,
"expected type `{}`, found type `{}`",
type_specified, type_found
),
}
}
}
#[derive(Debug)]
pub struct TypecheckError {
pub kind: TypecheckErrorKind,
pub span: std::ops::Range<usize>,
}
pub fn check(ir: &Vec<IR>) -> Result<(), TypecheckError> {
todo!();
}