aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/hello.sloth3
-rw-r--r--sloth/src/codegen.rs87
-rw-r--r--sloth/src/compiler.rs57
-rw-r--r--sloth/src/main.rs9
-rw-r--r--sloth/src/parser/ast.rs42
-rw-r--r--sloth/src/parser/expr.rs29
-rw-r--r--sloth/src/parser/graph.rs41
-rw-r--r--sloth/src/parser/stmt.rs313
8 files changed, 277 insertions, 304 deletions
diff --git a/examples/hello.sloth b/examples/hello.sloth
index 0da2fd0..fea5304 100644
--- a/examples/hello.sloth
+++ b/examples/hello.sloth
@@ -1,6 +1,7 @@
fn main() {
var i: Int = 10;
- while i > 0 {
+ var j: Int = int(1.0) - 1;
+ while i > j {
i = i - 1;
}
}
diff --git a/sloth/src/codegen.rs b/sloth/src/codegen.rs
deleted file mode 100644
index 8dec27e..0000000
--- a/sloth/src/codegen.rs
+++ /dev/null
@@ -1,87 +0,0 @@
-use inkwell::builder::Builder;
-use inkwell::context::Context;
-use inkwell::module::Module;
-use inkwell::types::{AnyTypeEnum, BasicMetadataTypeEnum, BasicType, BasicTypeEnum, FunctionType};
-use inkwell::values::FunctionValue;
-use itertools::Itertools;
-
-use crate::parser::ast::{FunctionInput, StmtKind};
-use crate::symbol::SymbolTableStack;
-
-/// A module codegen is a struct designated to compiling a single module
-pub struct ModuleCodegen<'ctx, 'a> {
- context: &'ctx Context,
- builder: Builder<'ctx>,
- module: Module<'ctx>,
-
- symbol_table: &'a mut SymbolTableStack,
-}
-
-impl<'ctx, 'a> ModuleCodegen<'ctx, 'a> {
- pub fn new(
- module: &str,
- context: &'ctx Context,
- symbol_table: &'a mut SymbolTableStack,
- ) -> Self {
- let builder = context.create_builder();
- let module = context.create_module(module);
-
- Self {
- context,
- builder,
- module,
-
- symbol_table,
- }
- }
-
- pub fn compile_function(
- &mut self,
- ident: String,
- args: Vec<FunctionInput>,
- return_type: Option<String>,
- body: &[StmtKind],
- ) {
- let llvm_function_type = self.compile_function_type(&args, return_type.as_deref());
- let llvm_function = self.module.add_function(&ident, llvm_function_type, None);
-
- let entry_block = self.context.append_basic_block(llvm_function, "entry");
-
- self.block(body);
- }
-
- fn compile_function_type(
- &self,
- args: &[FunctionInput],
- return_type: Option<&str>,
- ) -> FunctionType<'ctx> {
- let args = args
- .iter()
- .map(|it| self.compile_basic_metadata_type(&it.typ).unwrap())
- .collect_vec();
-
- match return_type {
- None => self.context.void_type().fn_type(&args, false),
- Some("int") => self.context.i64_type().fn_type(&args, false),
- Some("float") => self.context.f64_type().fn_type(&args, false),
- _ => panic!(),
- }
- }
-
- fn compile_basic_metadata_type(&self, typ: &str) -> Option<BasicMetadataTypeEnum<'ctx>> {
- match typ {
- "int" => Some(self.context.i64_type().into()),
- "float" => Some(self.context.f64_type().into()),
- _ => None,
- }
- }
-
- fn block(&mut self, body: &[StmtKind]) {
- self.symbol_table.push();
- self.symbol_table.pop();
- }
-
- // fn compile(symbol_table: &'a mut SymbolTableStack, code: Vec<Stmt>) {
- // //
- // }
-}
diff --git a/sloth/src/compiler.rs b/sloth/src/compiler.rs
deleted file mode 100644
index 3dedb49..0000000
--- a/sloth/src/compiler.rs
+++ /dev/null
@@ -1,57 +0,0 @@
-use inkwell::context::Context;
-use thiserror::Error;
-
-use crate::codegen::{self, ModuleCodegen};
-use crate::parser::ast::StmtKind;
-use crate::symbol::{Symbol, SymbolTable, SymbolTableStack, SymbolType};
-
-#[derive(Debug, Error)]
-pub enum CompilerError {
- #[error("Unknown compiler error")]
- Unknown,
-}
-
-pub struct Compiler {
- symbol_table: SymbolTableStack,
-}
-
-impl Compiler {
- /// Take in a AST in the form of a vector of statements and compile the
- /// program.
- pub fn compile(code: Vec<StmtKind>) -> Result<(), CompilerError> {
- let mut compiler = Self {
- symbol_table: SymbolTableStack::new(),
- };
-
- // Resolve names
- compiler.resolve_globals(&code);
-
- // Compile each function
- let context = Context::create();
- let codegen = ModuleCodegen::new("root", &context, &mut compiler.symbol_table);
-
- for stmt in code.iter() {
- if let StmtKind::DefineFunction { body, .. } = stmt {
- // compiler.compile_function(body);
- }
- }
-
- Ok(())
- }
-
- fn resolve_globals(&mut self, code: &[StmtKind]) {
- for stmt in code.iter() {
- // if let Stmt::DefineFunction { ident, .. } = stmt {
- // let symbol = Symbol {
- // typ: Some(SymbolType::Function),
- // };
- //
- // self.symbol_table.insert(ident, symbol);
- // }
- }
- }
-}
-
-// Step 1: Name resolution
-// Step 2: Type checking
-// Step 3: Code generation
diff --git a/sloth/src/main.rs b/sloth/src/main.rs
index 0cfeb09..6ae01ec 100644
--- a/sloth/src/main.rs
+++ b/sloth/src/main.rs
@@ -6,8 +6,6 @@
unused_lifetimes
)]
-pub mod codegen;
-pub mod compiler;
pub mod lexer;
pub mod parser;
pub mod sloth_std;
@@ -40,11 +38,4 @@ fn main() {
let graph = GraphBuilder::generate(&ast).unwrap();
println!("{graph}");
-
- // Compiler::compile(ast).unwrap();
-
- // let context = Context::create();
- // let compiler = Compiler::new(&context);
- //
- // compiler.compile(ast);
}
diff --git a/sloth/src/parser/ast.rs b/sloth/src/parser/ast.rs
index 749271c..1a994df 100644
--- a/sloth/src/parser/ast.rs
+++ b/sloth/src/parser/ast.rs
@@ -1,4 +1,4 @@
-use std::fmt::{format, Display};
+use std::fmt::Display;
use crate::lexer::{self, TokenType};
use crate::parser::ParsingError;
@@ -127,7 +127,7 @@ impl Display for Literal {
Literal::Boolean(b) => format!("{b}"),
Literal::Character(c) => format!("'{c}'"),
Literal::String(s) => format!("\"{s}\""),
- Literal::Array(a) => format!("<Array>"),
+ Literal::Array(..) => "<Array>".to_string(),
};
write!(f, "{value}")
@@ -187,6 +187,33 @@ impl TryFrom<TokenType> for BinaryOp {
}
}
+impl Display for BinaryOp {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let value = match self {
+ BinaryOp::Add => "+",
+ BinaryOp::Con => "++",
+ BinaryOp::Sub => "-",
+ BinaryOp::Mul => "*",
+ BinaryOp::Div => "/",
+ BinaryOp::Mod => "%",
+
+ BinaryOp::Lt => "<",
+ BinaryOp::Gt => ">",
+ BinaryOp::LtEq => "<=",
+ BinaryOp::GtEq => ">=",
+ BinaryOp::EqEq => "==",
+ BinaryOp::NotEq => "!=",
+
+ BinaryOp::LogicalAnd => "&&",
+ BinaryOp::LogicalOr => "||",
+
+ BinaryOp::Range => "..",
+ };
+
+ write!(f, "{value}")
+ }
+}
+
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum UnaryOp {
Not,
@@ -207,3 +234,14 @@ impl TryFrom<TokenType> for UnaryOp {
Ok(operation)
}
}
+
+impl Display for UnaryOp {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let value = match self {
+ UnaryOp::Not => "!",
+ UnaryOp::Neg => "-",
+ };
+
+ write!(f, "{value}")
+ }
+}
diff --git a/sloth/src/parser/expr.rs b/sloth/src/parser/expr.rs
index ff662cc..48ee038 100644
--- a/sloth/src/parser/expr.rs
+++ b/sloth/src/parser/expr.rs
@@ -10,7 +10,7 @@ impl<'a> AstParser<'a> {
}
fn unary(&mut self) -> Result<Expr, ParsingError> {
- if matches!(self.peek().tt, TokenType::Bang | TokenType::Minus) {
+ if !self.eof() && matches!(self.peek().tt, TokenType::Bang | TokenType::Minus) {
let oeprator_tt = self.advance().unwrap().tt.clone();
let operator = UnaryOp::try_from(oeprator_tt)?;
@@ -24,7 +24,32 @@ impl<'a> AstParser<'a> {
return Ok(Expr::new(self.reserve_id(), kind));
}
- self.primary()
+ self.call()
+ }
+
+ fn call(&mut self) -> Result<Expr, ParsingError> {
+ let mut expr = self.primary()?;
+
+ if !self.eof() && self.peek().tt == TokenType::OpeningParen {
+ self.consume(TokenType::OpeningParen, "Expected '('")?;
+
+ let mut arguments = Vec::new();
+ while !self.eof() && self.peek().tt != TokenType::ClosingParen {
+ arguments.push(self.expression()?);
+ if !self.advance_if_eq(&TokenType::Comma) {
+ break;
+ }
+ }
+
+ self.consume(TokenType::ClosingParen, "Expected ')'")?;
+
+ expr = Expr::new(self.reserve_id(), ExprKind::Call {
+ callee: Box::new(expr),
+ args: arguments,
+ });
+ }
+
+ Ok(expr)
}
fn primary(&mut self) -> Result<Expr, ParsingError> {
diff --git a/sloth/src/parser/graph.rs b/sloth/src/parser/graph.rs
index e8236be..da4bd71 100644
--- a/sloth/src/parser/graph.rs
+++ b/sloth/src/parser/graph.rs
@@ -120,7 +120,7 @@ impl GraphBuilder {
ExprKind::Grouping(child) => {
writeln!(
&mut self.graph,
- "N{} [shape=diamond label=\"Grouping\"];",
+ "N{} [shape=circle label=\"Grouping\"];",
expr.id
)?;
self.traverse_expr0(child)?;
@@ -140,13 +140,33 @@ impl GraphBuilder {
)?;
}
ExprKind::BinaryOp { op, lhs, rhs } => {
+ writeln!(
+ &mut self.graph,
+ "N{} [shape=circle label=\"{}\"];",
+ expr.id, op
+ )?;
self.traverse_expr0(lhs)?;
self.traverse_expr0(rhs)?;
}
ExprKind::UnaryOp { op, value } => {
+ writeln!(
+ &mut self.graph,
+ "N{} [shape=circle label=\"Unary {}\"];",
+ expr.id, op
+ )?;
self.traverse_expr0(value)?;
}
- ExprKind::Call { callee, args } => (),
+ ExprKind::Call { callee, args } => {
+ writeln!(
+ &mut self.graph,
+ "N{} [shape=circle label=\"Function Call\"];",
+ expr.id
+ )?;
+ self.traverse_expr0(callee)?;
+ for arg in args {
+ self.traverse_expr0(arg)?;
+ }
+ }
}
Ok(())
@@ -222,15 +242,26 @@ impl GraphBuilder {
match &expr.kind {
ExprKind::Grouping(children) => {
writeln!(&mut self.graph, "N{} -> N{};", expr.id, children.id)?;
+ self.traverse_expr(children)?;
}
ExprKind::BinaryOp { lhs, rhs, .. } => {
- writeln!(&mut self.graph, "N{} -> N{};", expr.id, lhs.id)?;
- writeln!(&mut self.graph, "N{} -> N{};", expr.id, rhs.id)?;
+ writeln!(&mut self.graph, "N{} -> N{} [label=lhs];", expr.id, lhs.id)?;
+ writeln!(&mut self.graph, "N{} -> N{} [label=rhs];", expr.id, rhs.id)?;
+ self.traverse_expr(lhs)?;
+ self.traverse_expr(rhs)?;
}
ExprKind::UnaryOp { value, .. } => {
writeln!(&mut self.graph, "N{} -> N{};", expr.id, value.id)?;
+ self.traverse_expr(value)?;
+ }
+ ExprKind::Call { callee, args } => {
+ writeln!(&mut self.graph, "N{} -> N{};", expr.id, callee.id)?;
+ self.traverse_expr(callee)?;
+ for arg in args {
+ writeln!(&mut self.graph, "N{} -> N{};", expr.id, arg.id)?;
+ self.traverse_expr(arg)?;
+ }
}
- ExprKind::Call { callee, args } => (),
_ => (),
}
diff --git a/sloth/src/parser/stmt.rs b/sloth/src/parser/stmt.rs
index c8913b4..2370311 100644
--- a/sloth/src/parser/stmt.rs
+++ b/sloth/src/parser/stmt.rs
@@ -178,145 +178,176 @@ mod tests {
use super::{AstParser, StmtKind};
use crate::lexer::Lexer;
- use crate::parser::ast::{BinaryOp, ExprKind, FunctionInput, Literal};
-
- // #[test]
- // fn standalone_blocks() {
- // let tokens = Lexer::new("{{{ 0; }}}").collect_vec();
- //
- // let expected_ast =
- // Ok(Stmt::Block(vec![Stmt::Block(vec![Stmt::Block(vec![
- // Stmt::ExprStmt(Literal::Integer(0).into()),
- // ])])]));
- //
- // let mut parser = AstParser::new(tokens);
- // let generated_ast = parser.statement();
- //
- // println!("Expected AST:\n{expected_ast:#?}\n\n");
- // println!("Generated AST:\n{generated_ast:#?}\n\n");
- //
- // assert_eq!(expected_ast, generated_ast);
- // }
- //
- // #[test]
- // fn basic_variable_definition() {
- // let tokens = Lexer::new("var foo: Int = 5 + 3;").collect_vec();
- //
- // let expected_ast = Ok(Stmt::DefineVariable {
- // identifier: "foo".to_string(),
- // value: ExprKind::BinaryOp {
- // op: BinaryOp::Add,
- // lhs: Box::new(ExprKind::Literal(Literal::Integer(5))),
- // rhs: Box::new(ExprKind::Literal(Literal::Integer(3))),
- // },
- // typ: "Int".to_string(),
- // });
- //
- // let mut parser = AstParser::new(tokens);
- // let generated_ast = parser.statement();
- //
- // println!("Expected AST:\n{expected_ast:#?}\n\n");
- // println!("Generated AST:\n{generated_ast:#?}\n\n");
- //
- // assert_eq!(expected_ast, generated_ast);
- // }
- //
- // #[test]
- // fn basic_function() {
- // let tokens = Lexer::new(
- // r#"
- // fn foo(bar: Int) Int {
- // var baz: Int = bar + 1;
- // baz = baz + 1;
- // return baz;
- // }
- // "#,
- // )
- // .collect_vec();
- //
- // let expected_ast = Ok(Stmt::DefineFunction {
- // identifier: "foo".to_owned(),
- // inputs: vec![FunctionInput {
- // identifier: "bar".to_owned(),
- // typ: "Int".to_owned(),
- // }],
- // output: Some("Int".to_owned()),
- // body: Box::new(Stmt::Block(vec![
- // Stmt::DefineVariable {
- // identifier: "baz".to_owned(),
- // value: ExprKind::BinaryOp {
- // op: BinaryOp::Add,
- // lhs:
- // Box::new(ExprKind::Identifier("bar".to_owned())),
- // rhs: Box::new(Literal::Integer(1).into()), },
- // typ: "Int".to_owned(),
- // },
- // Stmt::AssignVariable {
- // identifier: "baz".to_owned(),
- // value: ExprKind::BinaryOp {
- // op: BinaryOp::Add,
- // lhs:
- // Box::new(ExprKind::Identifier("baz".to_owned())),
- // rhs: Box::new(Literal::Integer(1).into()), },
- // },
- // Stmt::Return(ExprKind::Identifier("baz".to_owned())),
- // ])),
- // });
- //
- // let mut parser = AstParser::new(tokens);
- // let generated_ast = parser.statement();
- //
- // println!("Expected AST:\n{expected_ast:#?}\n\n");
- // println!("Generated AST:\n{generated_ast:#?}\n\n");
- //
- // assert_eq!(expected_ast, generated_ast);
- // }
- //
- // #[test]
- // fn basic_conditional() {
- // let tokens = Lexer::new(
- // r#"
- // if foo {
- // 0;
- // } else if bar {
- // 1;
- // } else if baz {
- // 2;
- // } else {
- // 3;
- // }
- // "#,
- // )
- // .collect_vec();
- //
- // let expected_ast = Ok(Stmt::IfStmt {
- // condition: ExprKind::Identifier("foo".to_owned()),
- // if_then: Box::new(Stmt::Block(vec![Stmt::ExprStmt(
- // Literal::Integer(0).into(),
- // )])),
- // else_then: Some(Box::new(Stmt::IfStmt {
- // condition: ExprKind::Identifier("bar".to_owned()),
- // if_then: Box::new(Stmt::Block(vec![Stmt::ExprStmt(
- // Literal::Integer(1).into(),
- // )])),
- // else_then: Some(Box::new(Stmt::IfStmt {
- // condition: ExprKind::Identifier("baz".to_owned()),
- // if_then: Box::new(Stmt::Block(vec![Stmt::ExprStmt(
- // Literal::Integer(2).into(),
- // )])),
- // else_then: Some(Box::new(Stmt::Block(vec![Stmt::ExprStmt(
- // Literal::Integer(3).into(),
- // )]))),
- // })),
- // })),
- // });
- //
- // let mut parser = AstParser::new(tokens);
- // let generated_ast = parser.statement();
- //
- // println!("Expected AST:\n{expected_ast:#?}\n\n");
- // println!("Generated AST:\n{generated_ast:#?}\n\n");
- //
- // assert_eq!(expected_ast, generated_ast);
- // }
+ use crate::parser::ast::{BinaryOp, Expr, ExprKind, FunctionInput, Literal, Stmt};
+
+ #[test]
+ fn standalone_blocks() {
+ let tokens = Lexer::new("{{{ 0; }}}").collect_vec();
+
+ let expected_ast = Ok(Stmt::new(
+ 4,
+ StmtKind::Block(vec![Stmt::new(
+ 3,
+ StmtKind::Block(vec![Stmt::new(
+ 2,
+ StmtKind::Block(vec![Stmt::new(
+ 1,
+ StmtKind::ExprStmt(Expr::new(0, Literal::Integer(0).into())),
+ )]),
+ )]),
+ )]),
+ ));
+
+ let mut parser = AstParser::new(tokens);
+ let generated_ast = parser.statement();
+
+ println!("Expected AST:\n{expected_ast:#?}\n\n");
+ println!("Generated AST:\n{generated_ast:#?}\n\n");
+
+ assert_eq!(expected_ast, generated_ast);
+ }
+
+ #[test]
+ fn basic_variable_definition() {
+ let tokens = Lexer::new("var foo: Int = 5 + 3;").collect_vec();
+
+ let expected_ast = Ok(Stmt::new(3, StmtKind::DefineVariable {
+ identifier: "foo".to_string(),
+ value: Expr::new(2, ExprKind::BinaryOp {
+ op: BinaryOp::Add,
+ lhs: Box::new(Expr::new(0, ExprKind::Literal(Literal::Integer(5)))),
+ rhs: Box::new(Expr::new(1, ExprKind::Literal(Literal::Integer(3)))),
+ }),
+ typ: "Int".to_string(),
+ }));
+
+ let mut parser = AstParser::new(tokens);
+ let generated_ast = parser.statement();
+
+ println!("Expected AST:\n{expected_ast:#?}\n\n");
+ println!("Generated AST:\n{generated_ast:#?}\n\n");
+
+ assert_eq!(expected_ast, generated_ast);
+ }
+
+ #[test]
+ fn basic_function() {
+ let tokens = Lexer::new(
+ r#"
+ fn foo(bar: Int) Int {
+ var baz: Int = bar + 1;
+ baz = baz + 1;
+ return baz;
+ }
+ "#,
+ )
+ .collect_vec();
+
+ let expected_ast = Ok(Stmt::new(11, StmtKind::DefineFunction {
+ identifier: "foo".to_owned(),
+ inputs: vec![FunctionInput {
+ identifier: "bar".to_owned(),
+ typ: "Int".to_owned(),
+ }],
+ output: Some("Int".to_owned()),
+ body: Box::new(Stmt::new(
+ 10,
+ StmtKind::Block(vec![
+ Stmt::new(3, StmtKind::DefineVariable {
+ identifier: "baz".to_owned(),
+ value: Expr::new(2, ExprKind::BinaryOp {
+ op: BinaryOp::Add,
+ lhs: Box::new(Expr::new(0, ExprKind::Identifier("bar".to_owned()))),
+ rhs: Box::new(Expr::new(1, Literal::Integer(1).into())),
+ }),
+ typ: "Int".to_owned(),
+ }),
+ Stmt::new(7, StmtKind::AssignVariable {
+ identifier: "baz".to_owned(),
+ value: Expr::new(6, ExprKind::BinaryOp {
+ op: BinaryOp::Add,
+ lhs: Box::new(Expr::new(4, ExprKind::Identifier("baz".to_owned()))),
+ rhs: Box::new(Expr::new(5, Literal::Integer(1).into())),
+ }),
+ }),
+ Stmt::new(
+ 9,
+ StmtKind::Return(Expr::new(8, ExprKind::Identifier("baz".to_owned()))),
+ ),
+ ]),
+ )),
+ }));
+
+ let mut parser = AstParser::new(tokens);
+ let generated_ast = parser.statement();
+
+ println!("Expected AST:\n{expected_ast:#?}\n\n");
+ println!("Generated AST:\n{generated_ast:#?}\n\n");
+
+ assert_eq!(expected_ast, generated_ast);
+ }
+
+ #[test]
+ fn basic_conditional() {
+ let tokens = Lexer::new(
+ r#"
+ if foo {
+ 0;
+ } else if bar {
+ 1;
+ } else if baz {
+ 2;
+ } else {
+ 3;
+ }
+ "#,
+ )
+ .collect_vec();
+
+ let expected_ast = Ok(Stmt::new(17, StmtKind::IfStmt {
+ condition: Expr::new(0, ExprKind::Identifier("foo".to_owned())),
+ if_then: Box::new(Stmt::new(
+ 3,
+ StmtKind::Block(vec![Stmt::new(
+ 2,
+ StmtKind::ExprStmt(Expr::new(1, Literal::Integer(0).into())),
+ )]),
+ )),
+ else_then: Some(Box::new(Stmt::new(16, StmtKind::IfStmt {
+ condition: Expr::new(4, ExprKind::Identifier("bar".to_owned())),
+ if_then: Box::new(Stmt::new(
+ 7,
+ StmtKind::Block(vec![Stmt::new(
+ 6,
+ StmtKind::ExprStmt(Expr::new(5, Literal::Integer(1).into())),
+ )]),
+ )),
+ else_then: Some(Box::new(Stmt::new(15, StmtKind::IfStmt {
+ condition: Expr::new(8, ExprKind::Identifier("baz".to_owned())),
+ if_then: Box::new(Stmt::new(
+ 11,
+ StmtKind::Block(vec![Stmt::new(
+ 10,
+ StmtKind::ExprStmt(Expr::new(9, Literal::Integer(2).into())),
+ )]),
+ )),
+ else_then: Some(Box::new(Stmt::new(
+ 14,
+ StmtKind::Block(vec![Stmt::new(
+ 13,
+ StmtKind::ExprStmt(Expr::new(12, Literal::Integer(3).into())),
+ )]),
+ ))),
+ }))),
+ }))),
+ }));
+
+ let mut parser = AstParser::new(tokens);
+ let generated_ast = parser.statement();
+
+ println!("Expected AST:\n{expected_ast:#?}\n\n");
+ println!("Generated AST:\n{generated_ast:#?}\n\n");
+
+ assert_eq!(expected_ast, generated_ast);
+ }
}