mirror of
https://github.com/azur1s/bobbylisp.git
synced 2024-10-16 02:37:40 -05:00
Compare commits
2 commits
fefffdd79e
...
74adfe1f0f
Author | SHA1 | Date | |
---|---|---|---|
Natapat Samutpong | 74adfe1f0f | ||
Natapat Samutpong | 8021a0e31d |
16
Cargo.lock
generated
16
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -5,5 +5,6 @@ members = [
|
|||
"crates/parser",
|
||||
"crates/diagnostic",
|
||||
"crates/hir",
|
||||
"crates/typecheck",
|
||||
"crates/codegen",
|
||||
]
|
|
@ -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))
|
||||
},
|
||||
|
||||
|
|
|
@ -10,3 +10,4 @@ chumsky = "0.8.0"
|
|||
ariadne = "0.1.5"
|
||||
lexer = { path = "../lexer" }
|
||||
hir = { path = "../hir" }
|
||||
typecheck = { path = "../typecheck" }
|
|
@ -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();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,4 +8,3 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
parser = { path = "../parser" }
|
||||
levenshtein = "1.0.5" # Used for error reporting
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ lexer = { path = "../lexer" }
|
|||
parser = { path = "../parser" }
|
||||
|
||||
# Diagnostics
|
||||
typecheck = { path = "../typecheck" }
|
||||
diagnostic = { path = "../diagnostic" }
|
||||
|
||||
# Codegen
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
10
crates/typecheck/Cargo.toml
Normal file
10
crates/typecheck/Cargo.toml
Normal 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" }
|
34
crates/typecheck/src/lib.rs
Normal file
34
crates/typecheck/src/lib.rs
Normal 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!();
|
||||
}
|
Loading…
Reference in a new issue