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

Compare commits

..

No commits in common. "74adfe1f0f1cd8c18f7168e12a6fb61548eec8fd" and "fefffdd79e7e617173465d82d14fd6ae28517852" have entirely different histories.

12 changed files with 210 additions and 284 deletions

16
Cargo.lock generated
View file

@ -131,7 +131,6 @@ dependencies = [
"chumsky", "chumsky",
"hir", "hir",
"lexer", "lexer",
"typecheck",
] ]
[[package]] [[package]]
@ -164,7 +163,6 @@ dependencies = [
"serde", "serde",
"serde_derive", "serde_derive",
"toml", "toml",
"typecheck",
] ]
[[package]] [[package]]
@ -186,6 +184,7 @@ dependencies = [
name = "hir" name = "hir"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"levenshtein",
"parser", "parser",
] ]
@ -205,6 +204,12 @@ 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"
@ -356,13 +361,6 @@ 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,6 +5,5 @@ 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

@ -9,5 +9,4 @@ edition = "2021"
chumsky = "0.8.0" 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,7 +12,6 @@ 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 {
@ -38,10 +37,6 @@ 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
@ -125,11 +120,6 @@ 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| {
@ -148,27 +138,13 @@ impl Diagnostics {
.with_color(Color::Red) .with_color(Color::Red)
); );
report.finish().print(Source::from(&src)).unwrap(); 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

@ -7,4 +7,5 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
parser = { path = "../parser" } parser = { path = "../parser" }
levenshtein = "1.0.5" # Used for error reporting

View file

@ -29,62 +29,31 @@ 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> },
Unary { Binary { op: String, left: Box<Self>, right: Box<Self> },
op: String, Call { name: String, args: Vec<Self> },
right: Box<Self>, Intrinsic { name: String, args: Vec<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 { If { cond: Box<Self>, body: Box<Self>, else_body: Box<Self> },
cond: Box<Self>, Case { cond: Box<Self>, cases: Vec<(Box<Self>, Box<Self>)>, default: Box<Self> },
body: Box<Self>, Do { body: Vec<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>, span: Range<usize> }, Return { value: Box<Self> },
// Error { message: String, note: Option<String>, span: Range<usize> },
} }
#[derive(Debug)] #[derive(Debug)]
@ -93,10 +62,70 @@ 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>) {
@ -124,31 +153,13 @@ 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_ok!(IRKind::Unary { return (Some(IRKind::Unary { op: op.to_string(), right: Box::new(rhs_ir.0.unwrap()) }), None);
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 } => {
@ -158,19 +169,14 @@ 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_ok!(IRKind::Binary { return (Some(IRKind::Binary { op: op.to_string(), left: Box::new(lhs_ir.0.unwrap()), right: Box::new(rhs_ir.0.unwrap()) }), None)
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 lname = match &name.0 { let name = 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_err!(LoweringError { span: name.1.clone(), message: "Expected identifier".to_string()}) _ => return (None, Some(LoweringError { span: name.1.clone(), message: "Expected identifier".to_string(), note: None }))
}; };
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
@ -180,12 +186,8 @@ 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_ok!(IRKind::Call { return (Some(ir_kind), None);
name: lname,
args: largs,
span: name.1.start..args.1.end
});
}, },
Expr::Pipeline { lhs, rhs } => { Expr::Pipeline { lhs, rhs } => {
@ -198,7 +200,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_err!(LoweringError { span: name.1.clone(), message: "Expected identifier".to_string()}) _ => return (None, Some(LoweringError { span: name.1.clone(), message: "Expected identifier".to_string(), note: None }))
}; };
// Get all the `Hole` indexes // Get all the `Hole` indexes
@ -211,10 +213,11 @@ 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_err!(LoweringError { return (None, Some(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
@ -256,54 +259,50 @@ 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 { IRKind::Call { .. } => IRKind::Call { name: cname, args: largs },
name: cname, IRKind::Intrinsic { .. } => IRKind::Intrinsic { name: cname, args: largs },
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_ok!(ir_kind); return (Some(ir_kind), None);
}, },
_ => return_err!(LoweringError { _ => return (None, Some(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 lvalue = expr_to_ir(&value.0); let value = expr_to_ir(&value.0);
if_err_return!(lvalue.1); if_err_return!(value.1);
return_ok!(IRKind::Define { let value = value.0.unwrap();
let ir_kind = IRKind::Define {
public: *public, public: *public,
name: name.0.clone(), name: name.clone(),
type_hint: gen_type_hint(&type_hint.0), type_hint: gen_type_hint(type_hint),
value: Box::new(lvalue.0.unwrap()), value: Box::new(value),
mutable: *mutable, mutable: *mutable
span: value.1.clone() };
});
return (Some(ir_kind), None);
}, },
Expr::Intrinsic { name, args } => { Expr::Intrinsic { name, args } => {
let lname = match &name.0 { let name = 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_err!(LoweringError { return (None, Some(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_err!(LoweringError { span: name.1.clone(), message: "Expected identifier".to_string()}) _ => return (None, Some(LoweringError { span: name.1.clone(), message: "Expected identifier".to_string(), note: None }))
}; };
let mut largs = Vec::new(); let mut largs = Vec::new();
@ -315,80 +314,73 @@ 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_err!(LoweringError { span: arg.1.clone(), message: "Expected string".to_string()}); return (None, Some(LoweringError { span: arg.1.clone(), message: "Expected string".to_string(), note: None }))
} }
} }
return_ok!(IRKind::Intrinsic { let ir_kind = IRKind::Intrinsic { name, args: largs };
name: lname, return (Some(ir_kind), None);
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 largs = args.0.iter().map(|arg| (arg.0.0.clone(), gen_type_hint(&arg.1.0))).collect::<Vec<_>>(); let args = args.0.iter().map(|arg| (arg.0.0.clone(), gen_type_hint(&arg.1.0))).collect::<Vec<_>>();
let lbody = expr_to_ir(&body.0); let body = expr_to_ir(&body.0);
if_err_return!(lbody.1); if_err_return!(body.1);
return_ok!(IRKind::Fun { let body = body.0.unwrap();
let ir_kind = IRKind::Fun {
public: *public, public: *public,
name: name.0.clone(), name: name.clone(),
return_type_hint: gen_type_hint(&type_hint.0), return_type_hint: gen_type_hint(type_hint),
args: largs, args,
body: Box::new(lbody.0.unwrap()), body: Box::new(body)
span: name.1.start..body.1.end };
}); return (Some(ir_kind), None);
}, },
Expr::Return { expr } => { Expr::Return { expr } => {
let lexpr = expr_to_ir(&expr.0); let expr = expr_to_ir(&expr.0);
if_err_return!(lexpr.1); if_err_return!(expr.1);
return_ok!(IRKind::Return { let expr = expr.0.unwrap();
value: Box::new(lexpr.0.unwrap()), let ir_kind = IRKind::Return { value: Box::new(expr) };
span: expr.1.clone() return (Some(ir_kind), None);
});
}, },
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_ok!(IRKind::Do { return (Some(ir_kind), None);
body: lbody,
span: body.1.clone()
});
}, },
Expr::If { cond, body, else_body } => { Expr::If { cond, body, else_body } => {
let lcond = expr_to_ir(&cond.0); let cond = expr_to_ir(&cond.0);
if_err_return!(lcond.1); if_err_return!(cond.1);
let lbody = expr_to_ir(&body.0); let body = expr_to_ir(&body.0);
if_err_return!(lbody.1); if_err_return!(body.1);
let lelse_body = expr_to_ir(&else_body.0); let else_body = expr_to_ir(&else_body.0);
if_err_return!(lelse_body.1); if_err_return!(else_body.1);
return_ok!(IRKind::If { let ir_kind = IRKind::If {
cond: Box::new(lcond.0.unwrap()), cond: Box::new(cond.0.unwrap()),
body: Box::new(lbody.0.unwrap()), body: Box::new(body.0.unwrap()),
else_body: Box::new(lelse_body.0.unwrap()), else_body: Box::new(else_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 lexpr = expr_to_ir(&expr.0); let expr = expr_to_ir(&expr.0);
if_err_return!(lexpr.1); if_err_return!(expr.1);
let mut lcases = Vec::new(); let mut lcases = Vec::new();
for case in &cases.0 { for case in &cases.0 {
@ -403,23 +395,23 @@ pub fn expr_to_ir(expr: &Expr) -> (Option<IRKind>, Option<LoweringError>) {
); );
} }
let ldefault = expr_to_ir(&default.0); let default = expr_to_ir(&default.0);
if_err_return!(ldefault.1); if_err_return!(default.1);
return_ok!(IRKind::Case { let ir_kind = IRKind::Case {
cond: Box::new(lexpr.0.unwrap()), cond: Box::new(expr.0.unwrap()),
cases: lcases, cases: lcases,
default: Box::new(ldefault.0.unwrap()), default: Box::new(default.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) => return_ok!(IRKind::Value { value: Value::Int(*value) }), Expr::Int(value) => (Some(IRKind::Value { value: Value::Int(*value) }), None),
Expr::Boolean(value) => return_ok!(IRKind::Value { value: Value::Boolean(*value) }), Expr::Boolean(value) => (Some(IRKind::Value { value: Value::Boolean(*value) }), None),
Expr::String(value) => return_ok!(IRKind::Value { value: Value::String(value.clone()) }), Expr::String(value) => (Some(IRKind::Value { value: Value::String(value.clone()) }), None),
Expr::Identifier(value) => return_ok!(IRKind::Value { value: Value::Ident(value.clone()) }), Expr::Identifier(value) => (Some(IRKind::Value { value: Value::Ident(value.clone()) }), None),
Expr::Vector(values) => { Expr::Vector(values) => {
let mut lvalues = Vec::new(); let mut lvalues = Vec::new();
@ -435,7 +427,8 @@ 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!() }
} }
@ -453,4 +446,18 @@ fn gen_type_hint(type_hint: &str) -> String {
"vec_str" => "string[]".to_string(), "vec_str" => "string[]".to_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,7 +18,6 @@ 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,7 +6,6 @@ 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;
@ -53,24 +52,16 @@ 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)); } if print_ast { log(0, format!("IR\n{}", ir.iter().map(|x| format!("{}", x.kind)).collect::<Vec<String>>().join("\n\n"))); }
// 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: Spanned<String>, name: String,
type_hint: Spanned<String>, type_hint: String,
value: Box<Spanned<Self>>, value: Box<Spanned<Self>>,
mutable: bool, mutable: bool,
}, },
Fun { Fun {
public: bool, public: bool,
name: Spanned<String>, name: String,
type_hint: Spanned<String>, type_hint: 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: Spanned<Vec<Spanned<Self>>> body: 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.clone(), name: name.0.clone(),
type_hint, type_hint: type_hint.0,
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.clone(), name: name.0.clone(),
type_hint, type_hint: type_hint.0,
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(), span.clone()), body: body.clone(),
}, },
span, span,
) )

View file

@ -1,10 +0,0 @@
[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

@ -1,34 +0,0 @@
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!();
}