aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCody <cody@codyq.dev>2023-06-25 14:35:24 -0500
committerCody <cody@codyq.dev>2023-06-25 14:35:24 -0500
commit6a59bf6d5345fbe2487e1cc36c36aa6884fcc39d (patch)
treeb16908fb454553f8be3220af1890f5ecc452fd05
parentdef8adfcb9a9572f4e030990626a0d08377118bd (diff)
downloadsloth-6a59bf6d5345fbe2487e1cc36c36aa6884fcc39d.tar.gz
brain damage
-rw-r--r--examples/hello.sloth18
-rw-r--r--sloth/src/analysis/mod.rs8
-rw-r--r--sloth/src/analysis/setup.rs147
-rw-r--r--sloth/src/codegen/mod.rs204
-rw-r--r--sloth/src/main.rs9
-rw-r--r--sloth/src/parser/ast.rs30
6 files changed, 395 insertions, 21 deletions
diff --git a/examples/hello.sloth b/examples/hello.sloth
index a955e66..5deb0fc 100644
--- a/examples/hello.sloth
+++ b/examples/hello.sloth
@@ -1,11 +1,13 @@
-foreign fn example(arg: Int) Void;
+# foreign fn example();
-fn main() {
- var i: Int = 10;
- var j: Int = 1.0 - 1;
- while i > j {
- i = i - 1;
- }
- example(i);
+foreign fn foo() Int;
+foreign fn bar(y: Int, z: Int) Float;
+
+fn main() Int {
+ foo();
+ foo();
+ foo();
+ bar(5, 8 * 3 + foo());
+ return 7 + 5;
}
diff --git a/sloth/src/analysis/mod.rs b/sloth/src/analysis/mod.rs
index 8232e0e..29f1a91 100644
--- a/sloth/src/analysis/mod.rs
+++ b/sloth/src/analysis/mod.rs
@@ -8,8 +8,8 @@ pub enum AnalysisError {
TypeMismatch(u32),
#[error("Unknown identifier '{1}'")]
UnknownIdentifier(u32, String),
- #[error("Unknown error")]
- Unknown(u32),
+ #[error("Unknown error '{1}'")]
+ Unknown(u32, &'static str),
}
impl AnalysisError {
@@ -17,13 +17,15 @@ impl AnalysisError {
match self {
AnalysisError::TypeMismatch(line) => *line,
AnalysisError::UnknownIdentifier(line, ..) => *line,
- AnalysisError::Unknown(line) => *line,
+ AnalysisError::Unknown(line, ..) => *line,
}
}
}
pub fn analyze(root: &mut Stmt) -> Result<(), AnalysisError> {
setup::populate_symtable(&root.as_node())?;
+ setup::propagate_types_stmt(root)?;
+
check_usage(&root.as_node())?;
Ok(())
diff --git a/sloth/src/analysis/setup.rs b/sloth/src/analysis/setup.rs
index 0c4f228..e690131 100644
--- a/sloth/src/analysis/setup.rs
+++ b/sloth/src/analysis/setup.rs
@@ -1,5 +1,7 @@
use super::AnalysisError;
-use crate::parser::ast::{AstNode, Function, FunctionInput, FunctionKind, StmtKind};
+use crate::parser::ast::{
+ AstNode, Expr, ExprKind, Function, FunctionInput, FunctionKind, Literal, Stmt, StmtKind,
+};
use crate::symtable::{Symbol, SymbolTable, Type};
pub(super) fn populate_symtable(node: &AstNode) -> Result<(), AnalysisError> {
@@ -75,3 +77,146 @@ fn build_function_symbol(
output: output.into(),
}))
}
+
+pub(super) fn propagate_types_stmt(node: &mut Stmt) -> Result<(), AnalysisError> {
+ match &mut node.kind {
+ StmtKind::Block(children) => {
+ for child in children {
+ propagate_types_stmt(child)?;
+ }
+ }
+ StmtKind::ExprStmt(expr) => {
+ propagate_types(expr)?;
+ }
+ StmtKind::IfStmt {
+ condition,
+ if_then,
+ else_then,
+ } => {
+ propagate_types(condition)?;
+ propagate_types_stmt(if_then)?;
+ if let Some(else_then) = else_then {
+ propagate_types_stmt(else_then)?;
+ }
+ }
+ StmtKind::WhileStmt { condition, body } => {
+ propagate_types(condition)?;
+ propagate_types_stmt(body)?;
+ }
+ StmtKind::DefineVariable { value, .. } => {
+ propagate_types(value)?;
+ }
+ StmtKind::AssignVariable { value, .. } => {
+ propagate_types(value)?;
+ }
+ StmtKind::DefineFunction(function) => {
+ if let FunctionKind::Normal { body } = &mut function.kind {
+ propagate_types_stmt(body)?;
+ }
+ }
+ StmtKind::Return(expr) => {
+ propagate_types(expr)?;
+ }
+ }
+
+ Ok(())
+}
+
+pub(super) fn propagate_types(node: &mut Expr) -> Result<(), AnalysisError> {
+ let typ = match &mut node.kind {
+ ExprKind::Grouping(child) => {
+ propagate_types(child)?;
+ child
+ .typ
+ .clone()
+ .ok_or(AnalysisError::Unknown(node.line, "owo choco"))?
+ }
+ ExprKind::Literal(lit) => match lit {
+ Literal::Integer(_) => Type::Integer,
+ Literal::Float(_) => Type::Float,
+ Literal::Boolean(_) => Type::Boolean,
+ _ => todo!(),
+ },
+ ExprKind::Identifier(identifier) => {
+ let table = node.symtable.clone();
+ table
+ .get_value(identifier)
+ .ok_or(AnalysisError::UnknownIdentifier(
+ node.line,
+ identifier.to_owned(),
+ ))?
+ }
+ ExprKind::BinaryOp { lhs, rhs, .. } => {
+ // Propagating the types to the children
+ propagate_types(lhs)?;
+ propagate_types(rhs)?;
+
+ if lhs.typ != rhs.typ {
+ return Err(AnalysisError::TypeMismatch(node.line));
+ }
+
+ lhs.typ
+ .clone()
+ .ok_or(AnalysisError::Unknown(node.line, "owo?? choco???"))?
+ }
+ ExprKind::UnaryOp { value, .. } => {
+ propagate_types(value)?;
+
+ value.typ.clone().ok_or(AnalysisError::Unknown(
+ node.line,
+ "YOU'RE WRONG... SULFURIC ACID!",
+ ))?
+ }
+ ExprKind::Call { callee, args } => {
+ propagate_types(callee)?;
+ for arg in args {
+ propagate_types(arg)?;
+ }
+
+ let Some(Type::Function { ref output, .. }) = callee.typ else {
+ return Err(AnalysisError::TypeMismatch(node.line));
+ };
+
+ *output.clone()
+ }
+ };
+
+ node.typ = Some(typ);
+
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::analysis::setup::propagate_types;
+ use crate::parser::ast::{BinaryOp, Expr, ExprKind, Literal};
+ use crate::symtable::{Symbol, SymbolTable, Type};
+
+ #[test]
+ fn haiiiiiuwu() {
+ let mut table = SymbolTable::new();
+ table.insert("poggo".to_owned(), Symbol::Value(Type::Integer));
+ table.insert("poggu".to_owned(), Symbol::Value(Type::Float));
+
+ let mut x = Expr::new(
+ 0,
+ 0,
+ ExprKind::BinaryOp {
+ op: BinaryOp::Add,
+ lhs: Box::new(Expr::new(1, 0, Literal::Float(1.).into(), table.clone())),
+ rhs: Box::new(Expr::new(
+ 2,
+ 0,
+ ExprKind::Identifier("poggu".to_owned()),
+ table.clone(),
+ )),
+ },
+ table,
+ );
+
+ propagate_types(&mut x).expect("oh noes something went fucky wucky >~<");
+
+ println!("{x:#?}");
+ panic!()
+ }
+}
diff --git a/sloth/src/codegen/mod.rs b/sloth/src/codegen/mod.rs
new file mode 100644
index 0000000..fb83c99
--- /dev/null
+++ b/sloth/src/codegen/mod.rs
@@ -0,0 +1,204 @@
+use std::io::Write;
+
+use inkwell::builder::Builder;
+use inkwell::context::Context;
+use inkwell::module::Module;
+use inkwell::types::{BasicMetadataTypeEnum, BasicType};
+use inkwell::values::{BasicMetadataValueEnum, BasicValue, BasicValueEnum, FunctionValue};
+use itertools::Itertools;
+
+use crate::parser::ast::{
+ BinaryOp, Expr, ExprKind, Function, FunctionKind, Literal, Stmt, StmtKind,
+};
+use crate::symtable::{SymbolTable, Type};
+
+pub struct Compiler<'ctx> {
+ context: &'ctx Context,
+ builder: Builder<'ctx>,
+ module: Module<'ctx>,
+
+ current_func: Option<FunctionValue<'ctx>>,
+}
+
+impl<'ctx> Compiler<'ctx> {
+ pub fn codegen(context: &'ctx Context, module: &str, code: &Stmt) {
+ let builder = context.create_builder();
+ let module = context.create_module(module);
+
+ let mut this = Compiler {
+ context,
+ builder,
+ module,
+
+ current_func: None,
+ };
+
+ let StmtKind::Block(ref stmts) = &code.kind else {
+ panic!("Code root should be a block");
+ };
+
+ for stmt in stmts {
+ this.codegen_stmt(stmt);
+ this.current_func.unwrap().print_to_stderr();
+ }
+ }
+
+ fn codegen_stmt(&mut self, code: &Stmt) {
+ match &code.kind {
+ StmtKind::Block(stmts) => self.codegen_block(stmts),
+ StmtKind::ExprStmt(expr) => {
+ self.codegen_expr(expr);
+ }
+ StmtKind::Return(expr) => {
+ let res = self.codegen_expr(expr);
+ self.builder.build_return(Some(&res));
+ }
+ StmtKind::DefineFunction(function) => {
+ let table = code.symtable.clone();
+ self.codegen_function(table, function.clone());
+
+ // If the function is written in sloth (as opposed to an extern one) we generate
+ // the block contents
+ if let FunctionKind::Normal { body } = &function.kind {
+ if let StmtKind::Block(body) = &body.kind {
+ self.codegen_block(body);
+ }
+ };
+ }
+ _ => (),
+ }
+ }
+
+ fn codegen_function(&mut self, table: SymbolTable, function: Function) -> FunctionValue {
+ let inputs = function.inputs;
+ let inputs_typ = inputs
+ .iter()
+ .map(|it| table.get_type(&it.typ).unwrap())
+ .map(|it| self.type_as_metadata_type(it))
+ .collect_vec();
+
+ let output = function.output;
+ let output_typ = output
+ .map(|it| table.get_type(&it))
+ .unwrap_or(Some(Type::Void))
+ .unwrap();
+
+ let llvm_function_type = match output_typ {
+ Type::Void => self.context.void_type().fn_type(&inputs_typ, false),
+ Type::Integer => self.context.i64_type().fn_type(&inputs_typ, false),
+ Type::Float => self.context.f64_type().fn_type(&inputs_typ, false),
+ Type::Boolean => self.context.bool_type().fn_type(&inputs_typ, false),
+ _ => todo!(),
+ };
+
+ let llvm_function =
+ self.module
+ .add_function(&function.identifier, llvm_function_type, None);
+
+ self.current_func = Some(llvm_function);
+
+ llvm_function
+ }
+
+ fn codegen_block(&mut self, code: &[Stmt]) {
+ let Some(current_func) = self.current_func else {
+ panic!("Block codegen requires function");
+ };
+
+ let block = self.context.append_basic_block(current_func, "block");
+
+ self.builder.position_at_end(block);
+
+ for stmt in code {
+ self.codegen_stmt(stmt);
+ }
+ }
+
+ fn codegen_expr(&self, code: &Expr) -> BasicValueEnum<'ctx> {
+ // AnyValue
+ match &code.kind {
+ ExprKind::Literal(literal) => self.codegen_value(literal.clone()),
+ ExprKind::Grouping(inner) => self.codegen_expr(inner),
+ ExprKind::Identifier(ident) => {
+ // FIXME: Do thsi
+ todo!()
+ }
+ ExprKind::BinaryOp { op, lhs, rhs } => match lhs.typ {
+ Some(Type::Integer) => {
+ let lhs_gen = self.codegen_expr(lhs).into_int_value();
+ let rhs_gen = self.codegen_expr(rhs).into_int_value();
+
+ match op {
+ BinaryOp::Add => self.builder.build_int_add(lhs_gen, rhs_gen, "add"),
+ BinaryOp::Sub => self.builder.build_int_sub(lhs_gen, rhs_gen, "sub"),
+ BinaryOp::Mul => self.builder.build_int_mul(lhs_gen, rhs_gen, "mul"),
+ BinaryOp::Div => self.builder.build_int_signed_div(lhs_gen, rhs_gen, "div"),
+ _ => panic!(),
+ }
+ .into()
+ }
+ Some(Type::Float) => {
+ let lhs_gen = self.codegen_expr(lhs).into_float_value();
+ let rhs_gen = self.codegen_expr(rhs).into_float_value();
+
+ match op {
+ BinaryOp::Add => self.builder.build_float_add(lhs_gen, rhs_gen, "add"),
+ BinaryOp::Sub => self.builder.build_float_sub(lhs_gen, rhs_gen, "sub"),
+ BinaryOp::Mul => self.builder.build_float_mul(lhs_gen, rhs_gen, "mul"),
+ BinaryOp::Div => self.builder.build_float_div(lhs_gen, rhs_gen, "div"),
+ _ => panic!(),
+ }
+ .into()
+ }
+ None => unreachable!("Critical Error: Type should never be null by this point"),
+ _ => todo!(),
+ },
+ ExprKind::UnaryOp { op, value } => todo!(),
+ ExprKind::Call { callee, args } => {
+ // FIXME: Callee is an expression but for now were just
+ // extracting an identifier to it. Change this
+ // so you can do for example `fn(){}()`.
+ let ExprKind::Identifier(ident) = &callee.kind else { panic!() };
+ let function = self.module.get_function(ident).expect("oh nooos");
+
+ let args = args
+ .iter()
+ .map(|arg| self.codegen_expr(arg))
+ .map(|arg| arg.into())
+ .collect::<Vec<BasicMetadataValueEnum>>();
+
+ self.builder
+ .build_call(function, &args, "")
+ .try_as_basic_value()
+ .unwrap_left()
+ }
+ }
+ }
+
+ fn codegen_value(&self, value: Literal) -> BasicValueEnum<'ctx> {
+ match value {
+ Literal::Integer(value) => self
+ .context
+ .i64_type()
+ .const_int(value as u64, true)
+ .as_basic_value_enum(),
+ Literal::Float(value) => self
+ .context
+ .f64_type()
+ .const_float(value)
+ .as_basic_value_enum(),
+ _ => unimplemented!(),
+ }
+ }
+
+ fn type_as_metadata_type(&self, typ: Type) -> BasicMetadataTypeEnum<'ctx> {
+ match typ {
+ Type::Integer => self.context.i64_type().into(),
+ Type::Float => self.context.f64_type().into(),
+ Type::Boolean => self.context.bool_type().into(),
+ _ => todo!(), // Type::Function { inputs, output } => todo!(),
+ }
+ }
+
+ fn write_obj(&self, file: &mut impl Write) {}
+}
diff --git a/sloth/src/main.rs b/sloth/src/main.rs
index ec9eb7e..bdcb4cb 100644
--- a/sloth/src/main.rs
+++ b/sloth/src/main.rs
@@ -8,6 +8,7 @@
)]
pub mod analysis;
+pub mod codegen;
pub mod lexer;
pub mod parser;
pub mod sloth_std;
@@ -15,6 +16,8 @@ pub mod symtable;
use std::{env, fs};
+use codegen::Compiler;
+use inkwell::context::Context;
use itertools::Itertools;
use lexer::Lexer;
use parser::AstParser;
@@ -55,9 +58,11 @@ fn main() {
return;
}
- println!("Suces");
-
// println!("{ast:#?}");
+ // println!("Suces");
+
+ let context = Context::create();
+ Compiler::codegen(&context, "hi", &ast);
// let graph = GraphBuilder::generate(Some(&source), &ast).unwrap();
// println!("{graph}");
diff --git a/sloth/src/parser/ast.rs b/sloth/src/parser/ast.rs
index 8b58fcd..281cd86 100644
--- a/sloth/src/parser/ast.rs
+++ b/sloth/src/parser/ast.rs
@@ -2,7 +2,7 @@ use std::fmt::Display;
use crate::lexer::{self, TokenType};
use crate::parser::ParsingError;
-use crate::symtable::SymbolTable;
+use crate::symtable::{SymbolTable, Type};
#[derive(PartialEq, Clone, Debug)]
/// AstNode that is either an Expr or Stmt, typically used for iterating over an
@@ -36,6 +36,11 @@ pub struct Expr {
pub line: u32,
pub kind: ExprKind,
pub symtable: SymbolTable,
+
+ /// Type of the expression. If None it means the type hasn't yet been
+ /// checked.
+ pub typ: Option<Type>,
+ pub is_const: bool,
}
impl PartialEq for Expr {
@@ -46,22 +51,33 @@ impl PartialEq for Expr {
impl Expr {
pub fn new(id: i32, line: u32, kind: ExprKind, symtable: SymbolTable) -> Self {
+ /// Recursivly check if a expression is constant
+ fn is_const(kind: &ExprKind) -> bool {
+ match kind {
+ ExprKind::Literal(_) => true,
+ ExprKind::Grouping(child) => is_const(&child.kind),
+ ExprKind::BinaryOp { lhs, rhs, .. } => is_const(&lhs.kind) && is_const(&rhs.kind),
+ ExprKind::UnaryOp { value, .. } => is_const(&value.kind),
+ ExprKind::Call { .. } | ExprKind::Identifier(_) => false,
+ }
+ }
+
+ let is_const = is_const(&kind);
+
Self {
id,
line,
kind,
symtable,
+
+ typ: None,
+ is_const,
}
}
/// Useful for testing
pub fn without_table(id: i32, kind: ExprKind) -> Self {
- Self {
- id,
- line: 0,
- kind,
- symtable: SymbolTable::new(),
- }
+ Self::new(id, 0, kind, SymbolTable::new())
}
pub fn as_node(&self) -> AstNode {