From 152b4067a7c3bc9e1133271aae5064fbb214824c Mon Sep 17 00:00:00 2001 From: Lucas Date: Fri, 15 Nov 2024 08:52:19 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=A7=20WIP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit semver: chore --- Cargo.lock | 64 ++++++-------- Cargo.toml | 2 +- src/generator.rs | 175 +++++++++++++++++++++++++++++++++++++++ src/main.rs | 27 ++---- src/parser/ast.rs | 5 +- src/passer/mod.rs | 133 ----------------------------- src/passer/typing/mod.rs | 158 ----------------------------------- src/passer/unused.rs | 75 ----------------- src/typing/error.rs | 19 +++++ src/typing/mod.rs | 94 +++++++++++++++++++++ 10 files changed, 325 insertions(+), 427 deletions(-) create mode 100644 src/generator.rs delete mode 100644 src/passer/mod.rs delete mode 100644 src/passer/typing/mod.rs delete mode 100644 src/passer/unused.rs create mode 100644 src/typing/error.rs create mode 100644 src/typing/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 8fa2740..574b4f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,15 +23,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - [[package]] name = "backtrace" version = "0.3.73" @@ -229,7 +220,7 @@ dependencies = [ "syntect", "terminal_size", "textwrap", - "thiserror", + "thiserror 1.0.65", "unicode-width", ] @@ -373,29 +364,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "regex" -version = "1.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - [[package]] name = "regex-syntax" version = "0.8.3" @@ -487,7 +455,7 @@ dependencies = [ "miette", "owo-colors", "pretty_assertions", - "regex", + "thiserror 2.0.3", ] [[package]] @@ -513,9 +481,9 @@ checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" [[package]] name = "syn" -version = "2.0.85" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -539,7 +507,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "thiserror", + "thiserror 1.0.65", "walkdir", "yaml-rust", ] @@ -571,7 +539,16 @@ version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.65", +] + +[[package]] +name = "thiserror" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +dependencies = [ + "thiserror-impl 2.0.3", ] [[package]] @@ -585,6 +562,17 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "time" version = "0.3.36" diff --git a/Cargo.toml b/Cargo.toml index 793738d..6be3239 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,6 @@ description = "The 'som' programming language" [dependencies] miette = { version = "7.2.0", features = ["fancy", "syntect-highlighter"] } +thiserror = "2.0.3" owo-colors = "4.1.0" pretty_assertions = "1.4.1" -regex = "1.10" diff --git a/src/generator.rs b/src/generator.rs new file mode 100644 index 0000000..e5daba2 --- /dev/null +++ b/src/generator.rs @@ -0,0 +1,175 @@ +use std::fmt::format; + +use crate::parser::ast::{ + Expression, ExpressionValue, Primitive, Statement, StatementValue, Symbol, +}; + +pub struct CodeGenerator<'de> { + indent_level: usize, + symbol: &'de Symbol<'de>, +} + +impl<'de> CodeGenerator<'de> { + // create a new builder with no indentation + pub fn new(symbol: &'de Symbol<'de>) -> Self { + Self { + indent_level: 0, + symbol, + } + } + + // compile the entire symbol + pub fn compile(&mut self) -> String { + match self.symbol { + Symbol::Statement(statement) => self.compile_statement(statement), + Symbol::Expression(expression) => self.compile_expression(expression), + } + } + + // increase the indentation level + fn increase_indent(&mut self) { + self.indent_level += 1; + } + + // decrease the indentation level + fn decrease_indent(&mut self) { + if self.indent_level > 0 { + self.indent_level -= 1; + } + } + + // get the current indentation as a string of spaces + fn current_indent(&self) -> String { + " ".repeat(self.indent_level) // adjust spaces per level as needed + } + + // compile a block of statements, maintaining indentation + fn compile_block(&mut self, statements: &[Statement]) -> String { + let mut code = String::new(); + self.increase_indent(); + for stmt in statements { + code.push_str(&format!( + "{}{}\n", + self.current_indent(), + self.compile_statement(stmt) + )); + } + self.decrease_indent(); + code + } + + // compile an individual statement + fn compile_statement(&mut self, stmt: &Statement) -> String { + match &stmt.value { + StatementValue::Assignment { name, value } => { + format!("var {} = {};", name, self.compile_expression(value)) + } + StatementValue::Return(expression) => { + format!("return {};", self.compile_expression(expression)) + } + StatementValue::Block(block_statements) => { + let mut block_code = String::from("{\n"); + block_code.push_str(&self.compile_block(block_statements)); + block_code.push_str(&format!("{}}}", self.current_indent())); + block_code + } + StatementValue::Function { header, body } => { + let mut code = format!("function {}(", header.name); + for (i, parameter) in header.parameters.iter().enumerate() { + if i > 0 { + code.push_str(", "); + } + code.push_str(¶meter.name); + } + + code.push_str(") {\n"); + code.push_str(&self.compile_expression(body)); + code.push_str(&format!("\n{}}}", self.current_indent())); + + code + } + StatementValue::Expression(expression) => { + format!("{}", self.compile_expression(expression)) + } + StatementValue::Conditional { + condition, + truthy, + falsy, + } => { + let mut code = format!("if {} {{\n", self.compile_expression(condition)); + self.increase_indent(); + code.push_str(&self.compile_statement(truthy)); + self.decrease_indent(); + code.push_str(&format!("\n{}}}", self.current_indent())); + if let Some(falsy) = falsy { + code.push_str(" else {\n"); + self.increase_indent(); + code.push_str(&self.compile_statement(falsy)); + self.decrease_indent(); + code.push_str(&format!("\n{}}}", self.current_indent())); + } + code + } + _ => { + println!("{:?}", stmt); + todo!("handle more statement types") + } + } + } + + // compile an expression (placeholder for your full expression handling) + fn compile_expression(&mut self, expr: &Expression) -> String { + match &expr.value { + ExpressionValue::Primitive(primitive) => primitive.to_string(), + ExpressionValue::Binary { + operator, + left, + right, + } => { + format!( + "({} {} {})", + self.compile_expression(left), + operator, + self.compile_expression(right) + ) + } + ExpressionValue::Group(inner) => self.compile_expression(inner).to_string(), + ExpressionValue::Block { + statements, + return_value, + } => { + let mut code = String::new(); + code.push_str(&self.compile_block(statements)); + self.increase_indent(); + + if let ExpressionValue::Primitive(Primitive::Unit) = return_value.value { + } else { + code.push_str(&format!( + "{}return {};", + self.current_indent(), + self.compile_expression(return_value) + )); + } + + self.decrease_indent(); + code + } + ExpressionValue::Conditional { + condition, + truthy, + falsy, + } => { + format!( + "{} ? {} : {}", + self.compile_expression(condition), + self.compile_expression(truthy), + self.compile_expression(falsy) + ) + } + _ => { + println!("{:?}", expr); + todo!("handle more expression types") + } + } + } +} diff --git a/src/main.rs b/src/main.rs index 1b9dd53..82d24f8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,27 +1,22 @@ +use generator::CodeGenerator; use lexer::{Lexer, TokenKind}; use owo_colors::{Style, Styled}; use parser::{ ast::{Expression, ExpressionValue, Statement}, Parser, }; -use passer::{typing::Typing, Passer}; use std::vec; +pub mod generator; pub mod lexer; pub mod parser; -pub mod passer; +pub mod typing; const INPUT: &str = " fn main() { - let string = \"Hello, world!\"; - return 12; + let cum = 12; - { - let string = \"Hello, world!\"; - return 12; - }; - - let abc = 12; + 1 + 1 if cum > 12 else 2 } "; @@ -47,16 +42,10 @@ fn main() { } }; - let typing_pass = passer::typing::TypingPasser::pass(&symbol).unwrap(); - let typing_pass = typing_pass.combine(passer::unused::UnusedPass::pass(&symbol).unwrap()); + let mut generator = CodeGenerator::new(&symbol); + let code = generator.compile(); - for note in typing_pass.non_critical { - println!("{:?}", note.with_source_code(INPUT)); - } - - for note in typing_pass.critical { - println!("{:?}", note.with_source_code(INPUT)); - } + println!("{}", code); } struct SomHighlighter {} diff --git a/src/parser/ast.rs b/src/parser/ast.rs index fc11862..8fc809c 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -1,6 +1,5 @@ -use std::{borrow::Cow, fmt::Display}; - use miette::SourceSpan; +use std::{borrow::Cow, fmt::Display}; #[derive(Debug, Clone)] pub enum Symbol<'de> { @@ -108,7 +107,7 @@ impl Display for Primitive<'_> { Primitive::Identifier(value) => write!(f, "{}", value), Primitive::Character(value) => write!(f, "{}", value), Primitive::Boolean(value) => write!(f, "{}", value), - Primitive::Unit => write!(f, "nothing"), + Primitive::Unit => write!(f, "ยง"), } } } diff --git a/src/passer/mod.rs b/src/passer/mod.rs deleted file mode 100644 index 5addf84..0000000 --- a/src/passer/mod.rs +++ /dev/null @@ -1,133 +0,0 @@ -use std::collections::HashSet; - -use crate::parser::ast::{Expression, ExpressionValue, Statement, StatementValue, Symbol}; -use miette::{Report, Result, SourceSpan}; - -pub mod typing; -pub mod unused; - -pub trait Passer { - fn pass(ast: &Symbol<'_>) -> Result; -} - -#[derive(Default, Debug)] -pub struct PasserResult { - pub non_critical: Vec, - pub critical: Vec, -} - -impl PasserResult { - pub fn combine(mut self, other: PasserResult) -> Self { - self.non_critical.extend(other.non_critical); - self.critical.extend(other.critical); - - self - } -} - -pub fn walk<'de>( - symbol: &Symbol<'de>, - statement_fn: fn(&Statement<'de>) -> Result, - expression_fn: fn(&Expression<'de>) -> Result, -) -> Result { - match symbol { - Symbol::Statement(statement) => walk_statement(statement, statement_fn, expression_fn), - Symbol::Expression(expression) => walk_expression(expression, statement_fn, expression_fn), - } -} - -fn walk_statement<'de>( - statement: &Statement<'de>, - statement_fn: fn(&Statement<'de>) -> Result, - expression_fn: fn(&Expression<'de>) -> Result, -) -> Result { - let mut result = statement_fn(statement)?; - - match &statement.value { - StatementValue::Block(statements) => { - for statement in statements { - result = result.combine(walk_statement(statement, statement_fn, expression_fn)?); - } - } - StatementValue::Expression(expression) => { - result = result.combine(walk_expression(expression, statement_fn, expression_fn)?); - } - StatementValue::Assignment { name, value } => { - result = result.combine(walk_expression(value, statement_fn, expression_fn)?); - } - StatementValue::Struct { name, fields } => {} - StatementValue::Enum { name, variants } => {} - StatementValue::Function { header, body } => { - result = result.combine(walk_expression(body, statement_fn, expression_fn)?); - } - StatementValue::Trait { name, functions } => {} - StatementValue::Return(expression) => { - result = result.combine(walk_expression(expression, statement_fn, expression_fn)?); - } - StatementValue::Conditional { - condition, - truthy, - falsy, - } => { - result = result.combine(walk_expression(condition, statement_fn, expression_fn)?); - result = result.combine(walk_statement(truthy, statement_fn, expression_fn)?); - if let Some(falsy) = falsy { - result = result.combine(walk_statement(falsy, statement_fn, expression_fn)?); - } - } - } - - Ok(result) -} - -fn walk_expression<'de>( - expression: &Expression<'de>, - statement_fn: fn(&Statement<'de>) -> Result, - expression_fn: fn(&Expression<'de>) -> Result, -) -> Result { - let mut result = expression_fn(expression)?; - - match &expression.value { - ExpressionValue::Binary { - operator, - left, - right, - } => { - result = result.combine(walk_expression(left, statement_fn, expression_fn)?); - result = result.combine(walk_expression(right, statement_fn, expression_fn)?); - } - ExpressionValue::Unary { operator, operand } => { - result = result.combine(walk_expression(operand, statement_fn, expression_fn)?); - } - ExpressionValue::Group(expression) => { - result = result.combine(walk_expression(expression, statement_fn, expression_fn)?); - } - ExpressionValue::Block { - statements, - return_value, - } => { - for statement in statements { - result = result.combine(walk_statement(statement, statement_fn, expression_fn)?); - } - result = result.combine(walk_expression(return_value, statement_fn, expression_fn)?); - } - ExpressionValue::Conditional { - condition, - truthy, - falsy, - } => { - result = result.combine(walk_expression(condition, statement_fn, expression_fn)?); - result = result.combine(walk_expression(truthy, statement_fn, expression_fn)?); - result = result.combine(walk_expression(falsy, statement_fn, expression_fn)?); - } - ExpressionValue::Call { callee, arguments } => { - result = result.combine(walk_expression(callee, statement_fn, expression_fn)?); - for argument in arguments { - result = result.combine(walk_expression(argument, statement_fn, expression_fn)?); - } - } - ExpressionValue::Primitive(_) => {} - } - - Ok(result) -} diff --git a/src/passer/typing/mod.rs b/src/passer/typing/mod.rs deleted file mode 100644 index 7cf8548..0000000 --- a/src/passer/typing/mod.rs +++ /dev/null @@ -1,158 +0,0 @@ -use core::net; - -use super::{Passer, PasserResult}; -use crate::{ - parser::{ - ast::{ - Expression, ExpressionValue, Spannable, Statement, StatementValue, Symbol, Type, - TypeValue, - }, - expression, - }, - passer::walk, -}; -use miette::{Error, LabeledSpan, Report, Result}; - -pub struct TypingPasser; - -impl Passer for TypingPasser { - fn pass(ast: &Symbol<'_>) -> Result { - fn check_expression(expression: &Expression<'_>) -> Result { - Ok(PasserResult::default()) - } - - fn check_statement(statement: &Statement<'_>) -> Result { - let mut critical = vec![]; - - let types = statement.possible_types(); - - let distinct_types = types.clone().into_iter().fold(vec![], |mut acc, ty| { - if !acc.iter().any(|t: &Type<'_>| t.value == ty.value) { - acc.push(ty); - } - acc - }); - - if distinct_types.is_empty() || distinct_types.len() == 1 { - return Ok(PasserResult::default()); - } - - let labels = types - .iter() - .map(|ty| LabeledSpan::at(ty.span, format!("{}", ty))) - .collect::>(); - - critical.push(miette::miette! { - labels = labels, - help = "statement has multiple possible types", - "{} has multiple possible types", statement.value - }); - - Ok(PasserResult { - critical, - non_critical: vec![], - }) - } - - walk(ast, check_statement, check_expression) - } -} - -pub trait Typing<'de> { - fn possible_types(&self) -> Vec>; -} - -impl<'de> Typing<'de> for Expression<'de> { - fn possible_types(&self) -> Vec> { - match &self.value { - ExpressionValue::Primitive(primitive) => vec![match primitive { - crate::parser::ast::Primitive::Integer(_) => { - Type::at(self.span, TypeValue::Integer) - } - crate::parser::ast::Primitive::Decimal(_) => { - Type::at(self.span, TypeValue::Decimal) - } - crate::parser::ast::Primitive::String(_) => Type::at(self.span, TypeValue::String), - crate::parser::ast::Primitive::Identifier(value) => { - Type::at(self.span, TypeValue::Symbol(value.clone())) - } - crate::parser::ast::Primitive::Character(_) => { - Type::at(self.span, TypeValue::Character) - } - crate::parser::ast::Primitive::Boolean(_) => { - Type::at(self.span, TypeValue::Boolean) - } - crate::parser::ast::Primitive::Unit => Type::at(self.span, TypeValue::Unit), - }], - ExpressionValue::Binary { - operator: _, - left, - right, - } => { - let mut types = left.possible_types(); - types.extend(right.possible_types()); - types - } - ExpressionValue::Unary { - operator: _, - operand, - } => operand.possible_types(), - ExpressionValue::Group(expression) => expression.possible_types(), - ExpressionValue::Block { - statements: _, - return_value, - } => return_value.possible_types(), - ExpressionValue::Conditional { - condition: _, - truthy, - falsy, - } => { - let mut types = truthy.possible_types(); - types.extend(falsy.possible_types()); - types - } - ExpressionValue::Call { - callee, - arguments: _, - } => callee.possible_types(), - } - } -} - -impl<'de> Typing<'de> for Statement<'de> { - fn possible_types(&self) -> Vec> { - match &self.value { - StatementValue::Block(statements) => vec![], - StatementValue::Expression(expression) => expression.possible_types(), - StatementValue::Assignment { name: _, value } => value.possible_types(), - StatementValue::Struct { name, fields } => { - vec![Type::at(self.span, TypeValue::Symbol(name.clone()))] - } - StatementValue::Enum { name, variants } => { - vec![Type::at(self.span, TypeValue::Symbol(name.clone()))] - } - StatementValue::Function { header, body } => { - let mut types = body.possible_types(); - if let Some(explicit_return_type) = &header.explicit_return_type { - types.push(explicit_return_type.clone()); - } - types - } - StatementValue::Trait { name, functions } => { - vec![Type::at(self.span, TypeValue::Symbol(name.clone()))] - } - StatementValue::Return(expression) => expression.possible_types(), - StatementValue::Conditional { - condition, - truthy, - falsy, - } => { - let mut types = truthy.possible_types(); - if let Some(falsy) = falsy { - types.extend(falsy.possible_types()); - } - types - } - } - } -} diff --git a/src/passer/unused.rs b/src/passer/unused.rs deleted file mode 100644 index 3353e5f..0000000 --- a/src/passer/unused.rs +++ /dev/null @@ -1,75 +0,0 @@ -use crate::parser::ast::CombineSpan; -use crate::parser::ast::{Expression, ExpressionValue, Statement, StatementValue}; -use miette::{LabeledSpan, SourceSpan}; - -use super::{walk, Passer, PasserResult}; - -pub struct UnusedPass; - -impl Passer for UnusedPass { - fn pass(ast: &crate::parser::ast::Symbol<'_>) -> miette::Result { - walk( - ast, - |s: &Statement<'_>| match &s.value { - StatementValue::Block(statements) => { - let mut result = super::PasserResult::default(); - let mut has_passed_return = false; - for statement in statements { - if let StatementValue::Return(_) = statement.value { - has_passed_return = true; - println!("has passed return"); - continue; - } - - if has_passed_return { - println!("unreachable code"); - - result.non_critical.push(miette::miette! { - help = "unreachable code", - "unreachable code" - }); - } - } - Ok(result) - } - _ => Ok(PasserResult::default()), - }, - |e: &Expression<'_>| match &e.value { - ExpressionValue::Block { - statements, - return_value, - } => { - let mut result = super::PasserResult::default(); - let mut has_passed_return = false; - let mut unreachable_spans = vec![]; - for statement in statements { - if let StatementValue::Return(_) = statement.value { - has_passed_return = true; - println!("has passed return"); - continue; - } - - if has_passed_return { - unreachable_spans.push(statement.span); - } - } - - if has_passed_return { - unreachable_spans.push(return_value.span); - } - - if !unreachable_spans.is_empty() { - result.non_critical.push(miette::miette! { - labels = vec![LabeledSpan::at(SourceSpan::combine(unreachable_spans), "unreachable code")], - help = "unreachable code", - "unreachable code" - }); - } - - Ok(result) - } - _ => Ok(PasserResult::default()), - }, - ) - } -} diff --git a/src/typing/error.rs b/src/typing/error.rs new file mode 100644 index 0000000..701b198 --- /dev/null +++ b/src/typing/error.rs @@ -0,0 +1,19 @@ +use std::borrow::Cow; + +#[derive(thiserror::Error, miette::Diagnostic, Debug)] +pub enum Error<'de> { + #[error("type error")] + TypeError(TypeError<'de>), +} + +#[derive(thiserror::Error, miette::Diagnostic, Debug)] +pub enum TypeError<'de> { + #[error("unknown identifier")] + #[diagnostic(help("try doing this instead"))] + UnknownIdentifier { + identifier: Cow<'de, str>, + + #[label("this identifier is not in scope")] + span: miette::SourceSpan, + }, +} diff --git a/src/typing/mod.rs b/src/typing/mod.rs new file mode 100644 index 0000000..cfd5ac2 --- /dev/null +++ b/src/typing/mod.rs @@ -0,0 +1,94 @@ +use crate::parser::ast::{Expression, ExpressionValue, Primitive}; +use miette::Result; +use std::collections::HashMap; + +pub mod error; +use error::*; + +#[derive(Debug, Clone, Copy)] +pub struct Value; +pub struct Use; + +pub struct TypeChecker {} + +impl TypeChecker { + pub fn var(&mut self) -> (Value, Use) { + todo!() + } + + pub fn bool(&mut self) -> Value { + todo!() + } + pub fn bool_use(&mut self) -> Use { + todo!() + } + + pub fn func(&mut self, arg: Use, ret: Value) -> Value { + todo!() + } + pub fn func_use(&mut self, arg: Value, ret: Use) -> Use { + todo!() + } + + pub fn obj(&mut self, fields: Vec<(String, Value)>) -> Value { + todo!() + } + pub fn obj_use(&mut self, field: (String, Use)) -> Use { + todo!() + } + + pub fn case(&mut self, case: (String, Value)) -> Value { + todo!() + } + pub fn case_use(&mut self, cases: Vec<(String, Use)>) -> Use { + todo!() + } + + pub fn flow(&mut self, lhs: Value, rhs: Use) -> Result<(), TypeError> { + todo!() + } +} + +struct Bindings { + m: HashMap, +} + +impl Bindings { + fn new() -> Self { + Self { m: HashMap::new() } + } + + fn get(&self, k: &str) -> Option { + self.m.get(k).copied() + } + + fn insert(&mut self, k: String, v: Value) { + self.m.insert(k.clone(), v); + } + + fn in_child_scope(&mut self, callback: impl FnOnce(&mut Self) -> T) -> T { + let mut child_scope = Bindings { m: self.m.clone() }; + callback(&mut child_scope) + } +} + +fn check_expression<'de>( + checker: &mut TypeChecker, + bindings: &mut Bindings, + expression: Expression<'de>, +) -> Result> { + match &expression.value { + ExpressionValue::Primitive(primitive) => match &primitive { + Primitive::Boolean(_) => Ok(checker.bool()), + Primitive::Identifier(ident) => match bindings.get(&ident) { + Some(value) => Ok(value), + None => Err(Error::TypeError(TypeError::UnknownIdentifier { + identifier: ident.clone(), + span: expression.span, + })), + }, + _ => todo!(), + }, + _ => todo!(), + } +}