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

View file

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

View file

@ -31,7 +31,7 @@ impl Codegen {
macro_rules! semicolon { () => { if should_gen_semicolon { ";" } else { "" } }; } macro_rules! semicolon { () => { if should_gen_semicolon { ";" } else { "" } }; }
match ir { match ir {
IRKind::Define { public, name, type_hint, value, mutable } => { IRKind::Define { public, name, type_hint, value, mutable, .. } => {
format!( format!(
"{} {} v_{}: {} = {}{}\n", "{} {} v_{}: {} = {}{}\n",
if *public { "export" } else { "" }, if *public { "export" } else { "" },
@ -43,7 +43,7 @@ impl Codegen {
) )
}, },
IRKind::Call { name, args } => { IRKind::Call { name, args, .. } => {
format!( format!(
"f_{}({}){}", "f_{}({}){}",
name, name,
@ -57,7 +57,7 @@ impl Codegen {
) )
}, },
IRKind::Intrinsic { name, args } => { IRKind::Intrinsic { name, args, .. } => {
match name.as_str() { match name.as_str() {
"write" => { format!("write({}){}\n" , self.gen_ir(&args[0], false), semicolon!()) }, "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!()) }, "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 let args = args
.iter() .iter()
.map(|arg| format!("v_{}: {}", arg.0, arg.1)) .map(|arg| format!("v_{}: {}", arg.0, arg.1))
@ -87,14 +87,14 @@ impl Codegen {
) )
}, },
IRKind::Return { value } => { IRKind::Return { value, .. } => {
format!( format!(
"return {};\n", "return {};\n",
self.gen_ir(value, false) self.gen_ir(value, false)
) )
}, },
IRKind::Do { body } => { IRKind::Do { body, .. } => {
let mut out = "{\n".to_string(); let mut out = "{\n".to_string();
for expr in body { for expr in body {
out.push_str(&self.gen_ir(&expr, true)); out.push_str(&self.gen_ir(&expr, true));
@ -103,7 +103,7 @@ impl Codegen {
out out
}, },
IRKind::If { cond, body, else_body } => { IRKind::If { cond, body, else_body, .. } => {
format!( format!(
"if ({}) {{\n{}}} else {{\n{}}}\n", "if ({}) {{\n{}}} else {{\n{}}}\n",
self.gen_ir(cond, true), self.gen_ir(cond, true),
@ -112,7 +112,7 @@ impl Codegen {
) )
}, },
IRKind::Case { cond, cases, default } => { IRKind::Case { cond, cases, default, .. } => {
format!( format!(
"switch ({}) {{\n{}{}\n}}\n", "switch ({}) {{\n{}{}\n}}\n",
self.gen_ir(cond, true), 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)) 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)) 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" ariadne = "0.1.5"
lexer = { path = "../lexer" } lexer = { path = "../lexer" }
hir = { path = "../hir" } hir = { path = "../hir" }
typecheck = { path = "../typecheck" }

View file

@ -12,6 +12,7 @@ pub enum Kind {
LexError(Simple<char>), LexError(Simple<char>),
ParseError(Simple<Token>), ParseError(Simple<Token>),
LoweringError(hir::LoweringError), LoweringError(hir::LoweringError),
TypecheckError(typecheck::TypecheckError),
} }
impl Diagnostics { impl Diagnostics {
@ -37,6 +38,10 @@ impl Diagnostics {
self.errors.push(Kind::LoweringError(error)); 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) { pub fn display(&self, src: String) {
let lex_error = self.errors.iter().filter_map(|kind| match kind { let lex_error = self.errors.iter().filter_map(|kind| match kind {
Kind::LexError(error) => Some(error.clone()), // Using clone() to remove reference Kind::LexError(error) => Some(error.clone()), // Using clone() to remove reference
@ -120,6 +125,11 @@ impl Diagnostics {
Kind::LoweringError(error) => Some(error.clone()), Kind::LoweringError(error) => Some(error.clone()),
_ => None, _ => 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() lower_error.into_iter()
.for_each(|e| { .for_each(|e| {
@ -138,13 +148,27 @@ impl Diagnostics {
.with_color(Color::Red) .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(); 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] [dependencies]
parser = { path = "../parser" } 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 { pub enum IRKind {
Value { value: Value }, Value { value: Value },
Vector { values: Vec<Self> }, Vector { values: Vec<Self> },
Unary { op: String, right: Box<Self> },
Binary { op: String, left: Box<Self>, right: Box<Self> }, Unary {
Call { name: String, args: Vec<Self> }, op: String,
Intrinsic { name: String, args: Vec<Self> }, 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 { Define {
public: bool, public: bool,
name: String, name: String,
type_hint: String, type_hint: String,
value: Box<Self>, value: Box<Self>,
mutable: bool mutable: bool,
span: Range<usize>,
}, },
Fun { Fun {
public: bool, public: bool,
name: String, name: String,
return_type_hint: String, return_type_hint: String,
args: Vec<(String, 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> }, If {
Case { cond: Box<Self>, cases: Vec<(Box<Self>, Box<Self>)>, default: Box<Self> }, cond: Box<Self>,
Do { body: Vec<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)] #[derive(Debug)]
@ -62,70 +93,10 @@ pub struct IR {
pub span: Range<usize> 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)] #[derive(Debug)]
pub struct LoweringError { pub struct LoweringError {
pub span: Range<usize>, pub span: Range<usize>,
pub message: String, pub message: String,
pub note: Option<String>,
} }
pub fn ast_to_ir(ast: Vec<(Expr, Range<usize>)>) -> (Vec<IR>, Vec<LoweringError>) { 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>) { pub fn expr_to_ir(expr: &Expr) -> (Option<IRKind>, Option<LoweringError>) {
match expr { match expr {
Expr::Unary { op, rhs } => { Expr::Unary { op, rhs } => {
let rhs_ir = expr_to_ir(&rhs.0); let rhs_ir = expr_to_ir(&rhs.0);
if_err_return!(rhs_ir.1); 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 } => { 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); let rhs_ir = expr_to_ir(&rhs.0);
if_err_return!(rhs_ir.1); 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 } => { Expr::Call { name, args } => {
let name = match &name.0 { let lname = match &name.0 {
Expr::Identifier(s) => s.clone(), Expr::Identifier(s) => s.clone(),
// Should never happen because the parser should have caught this // 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 let mut largs = Vec::new(); // `largs` stand for lowered args
// Iterate over args // Iterate over args
@ -186,8 +180,12 @@ pub fn expr_to_ir(expr: &Expr) -> (Option<IRKind>, Option<LoweringError>) {
if_err_return!(arg.1); if_err_return!(arg.1);
largs.push(arg.0.unwrap()); 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 } => { Expr::Pipeline { lhs, rhs } => {
@ -200,7 +198,7 @@ pub fn expr_to_ir(expr: &Expr) -> (Option<IRKind>, Option<LoweringError>) {
let cname = match &name.0 { let cname = match &name.0 {
Expr::Identifier(s) => s.clone(), Expr::Identifier(s) => s.clone(),
// Should never happen because the parser should have caught this // 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 // 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 there is no `Hole` in the args then return early
if indexes.is_empty() { if indexes.is_empty() {
return (None, Some(LoweringError { return_err!(LoweringError {
span: rhs.1.clone(), span: rhs.1.clone(),
message: "Expected hole in piping".to_string(), message: "Expected hole in piping".to_string(),
note: None });
}));
} }
// Remove the `Hole` from the args // 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 // Match the call to the right IRKind
let ir_kind = match new_call.0.unwrap() { let ir_kind = match new_call.0.unwrap() {
IRKind::Call { .. } => IRKind::Call { name: cname, args: largs }, IRKind::Call { .. } => IRKind::Call {
IRKind::Intrinsic { .. } => IRKind::Intrinsic { name: cname, args: largs }, 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!() _ => unreachable!()
}; };
return (Some(ir_kind), None); return_ok!(ir_kind);
}, },
_ => return (None, Some(LoweringError { _ => return_err!(LoweringError {
span: rhs.1.clone(), span: rhs.1.clone(),
message: "Expected call".to_string(), message: "Expected call".to_string()
note: None }),
})),
}; };
}, },
Expr::Let { public, name, type_hint, value, mutable } => { Expr::Let { public, name, type_hint, value, mutable } => {
let value = expr_to_ir(&value.0); let lvalue = expr_to_ir(&value.0);
if_err_return!(value.1); if_err_return!(lvalue.1);
let value = value.0.unwrap(); return_ok!(IRKind::Define {
let ir_kind = IRKind::Define {
public: *public, public: *public,
name: name.clone(), name: name.0.clone(),
type_hint: gen_type_hint(type_hint), type_hint: gen_type_hint(&type_hint.0),
value: Box::new(value), value: Box::new(lvalue.0.unwrap()),
mutable: *mutable mutable: *mutable,
}; span: value.1.clone()
});
return (Some(ir_kind), None);
}, },
Expr::Intrinsic { name, args } => { Expr::Intrinsic { name, args } => {
let name = match &name.0 { let lname = match &name.0 {
Expr::Identifier(s) => { Expr::Identifier(s) => {
if INTRINSICS.contains(&s.as_str()) { s.clone() } if INTRINSICS.contains(&s.as_str()) { s.clone() }
else { else {
return (None, Some(LoweringError { return_err!(LoweringError {
span: name.1.clone(), span: name.1.clone(),
message: format!("Unknown intrinsic: `{}`", s), 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(); 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() { if let IRKind::Value{ .. } = larg.0.clone().unwrap() {
largs.push(larg.0.clone().unwrap()); largs.push(larg.0.clone().unwrap());
} else { } 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_ok!(IRKind::Intrinsic {
return (Some(ir_kind), None); name: lname,
args: largs,
span: name.1.start..args.1.end
});
}, },
Expr::Fun { public, name, type_hint, args, body } => { Expr::Fun { public, name, type_hint, args, body } => {
// Iterate each argument and give it a type hint // 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); let lbody = expr_to_ir(&body.0);
if_err_return!(body.1); if_err_return!(lbody.1);
let body = body.0.unwrap(); return_ok!(IRKind::Fun {
let ir_kind = IRKind::Fun {
public: *public, public: *public,
name: name.clone(), name: name.0.clone(),
return_type_hint: gen_type_hint(type_hint), return_type_hint: gen_type_hint(&type_hint.0),
args, args: largs,
body: Box::new(body) body: Box::new(lbody.0.unwrap()),
}; span: name.1.start..body.1.end
return (Some(ir_kind), None); });
}, },
Expr::Return { expr } => { Expr::Return { expr } => {
let expr = expr_to_ir(&expr.0); let lexpr = expr_to_ir(&expr.0);
if_err_return!(expr.1); if_err_return!(lexpr.1);
let expr = expr.0.unwrap(); return_ok!(IRKind::Return {
let ir_kind = IRKind::Return { value: Box::new(expr) }; value: Box::new(lexpr.0.unwrap()),
return (Some(ir_kind), None); span: expr.1.clone()
});
}, },
Expr::Do { body } => { Expr::Do { body } => {
let mut lbody = Vec::new(); let mut lbody = Vec::new();
for expr in body {
for expr in &body.0 {
let expr = expr_to_ir(&expr.0); let expr = expr_to_ir(&expr.0);
if_err_return!(expr.1); if_err_return!(expr.1);
lbody.push(expr.0.unwrap()); 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 } => { Expr::If { cond, body, else_body } => {
let cond = expr_to_ir(&cond.0); let lcond = expr_to_ir(&cond.0);
if_err_return!(cond.1); if_err_return!(lcond.1);
let body = expr_to_ir(&body.0); let lbody = expr_to_ir(&body.0);
if_err_return!(body.1); if_err_return!(lbody.1);
let else_body = expr_to_ir(&else_body.0); let lelse_body = expr_to_ir(&else_body.0);
if_err_return!(else_body.1); if_err_return!(lelse_body.1);
let ir_kind = IRKind::If { return_ok!(IRKind::If {
cond: Box::new(cond.0.unwrap()), cond: Box::new(lcond.0.unwrap()),
body: Box::new(body.0.unwrap()), body: Box::new(lbody.0.unwrap()),
else_body: Box::new(else_body.0.unwrap()) else_body: Box::new(lelse_body.0.unwrap()),
}; span: cond.1.start..else_body.1.end
return (Some(ir_kind), None); });
}, },
Expr::Case { expr, cases, default } => { Expr::Case { expr, cases, default } => {
let expr = expr_to_ir(&expr.0); let lexpr = expr_to_ir(&expr.0);
if_err_return!(expr.1); if_err_return!(lexpr.1);
let mut lcases = Vec::new(); let mut lcases = Vec::new();
for case in &cases.0 { 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); let ldefault = expr_to_ir(&default.0);
if_err_return!(default.1); if_err_return!(ldefault.1);
let ir_kind = IRKind::Case { return_ok!(IRKind::Case {
cond: Box::new(expr.0.unwrap()), cond: Box::new(lexpr.0.unwrap()),
cases: lcases, cases: lcases,
default: Box::new(default.0.unwrap()) default: Box::new(ldefault.0.unwrap()),
}; span: expr.1.start..default.1.end
return (Some(ir_kind), None); });
}, },
// TODO: Handle primitive types error (e.g. overflow) // TODO: Handle primitive types error (e.g. overflow)
// For now it just leaves the value as is and let the target compiler handle it // 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::Int(value) => return_ok!(IRKind::Value { value: Value::Int(*value) }),
Expr::Boolean(value) => (Some(IRKind::Value { value: Value::Boolean(*value) }), None), Expr::Boolean(value) => return_ok!(IRKind::Value { value: Value::Boolean(*value) }),
Expr::String(value) => (Some(IRKind::Value { value: Value::String(value.clone()) }), None), Expr::String(value) => return_ok!(IRKind::Value { value: Value::String(value.clone()) }),
Expr::Identifier(value) => (Some(IRKind::Value { value: Value::Ident(value.clone()) }), None), Expr::Identifier(value) => return_ok!(IRKind::Value { value: Value::Ident(value.clone()) }),
Expr::Vector(values) => { Expr::Vector(values) => {
let mut lvalues = Vec::new(); 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 // Probably will never happen because it is catched in parser
Expr::Hole(start, end) => (None, Some(LoweringError { Expr::Hole(start, end) => (None, Some(LoweringError {
span: *start..*end, span: *start..*end,
message: "Hole can only be used in piping, it is not allowed here.".to_string(), message: "Hole can only be used in piping, it is not allowed here.".to_string()
note: None
})), })),
_ => { dbg!(expr); todo!() } _ => { dbg!(expr); todo!() }
} }
@ -447,17 +454,3 @@ fn gen_type_hint(type_hint: &str) -> String {
_ => { dbg!(type_hint); todo!() } _ => { 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" } parser = { path = "../parser" }
# Diagnostics # Diagnostics
typecheck = { path = "../typecheck" }
diagnostic = { path = "../diagnostic" } diagnostic = { path = "../diagnostic" }
# Codegen # Codegen

View file

@ -6,6 +6,7 @@ use lexer::lex;
use parser::parse; use parser::parse;
use diagnostic::Diagnostics; use diagnostic::Diagnostics;
use hir::ast_to_ir; use hir::ast_to_ir;
use typecheck::check;
use codegen::ts; use codegen::ts;
pub mod args; pub mod args;
@ -52,16 +53,24 @@ fn main() {
logif!(0, format!("Parsing took {}ms", start.elapsed().as_millis())); 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 { match ast {
Some(ast) => { Some(ast) => {
// Convert the AST to HIR // Convert the AST to HIR
let (ir, lowering_error) = ast_to_ir(ast); let (ir, lowering_error) = ast_to_ir(ast);
for err in lowering_error { diagnostics.add_lowering_error(err); } 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 // Report lowering errors if any
if diagnostics.has_error() { if diagnostics.has_error() {

View file

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