diff --git a/examples/hello.sloth b/examples/hello.sloth index 4b99943..7378cb1 100644 --- a/examples/hello.sloth +++ b/examples/hello.sloth @@ -1,12 +1,11 @@ -foreign fn addz(lhs: Int, rhs: Int) Int; +fn test() [Int 3] { + var list: [Int 3] = [9, 5, 7]; -fn haiiii() Void {} + vpushi(list, 3); + vpushi(list, 3); + vpushi(list, 3); + vpushi(list, 5); -fn hehehaha() Int { - var x: Int = 0; - while x < 10 { - x = x + 1; - } - return x; + return list; } diff --git a/sloth/src/analysis/setup.rs b/sloth/src/analysis/setup.rs index 577fe46..c405977 100644 --- a/sloth/src/analysis/setup.rs +++ b/sloth/src/analysis/setup.rs @@ -1,6 +1,7 @@ use super::AnalysisError; use crate::parser::ast::{ AstNode, Expr, ExprKind, Function, FunctionInput, FunctionKind, Literal, Stmt, StmtKind, + TypeIdentifier, }; use crate::symtable::{Symbol, SymbolTable, Type, ValueSymbol}; @@ -40,7 +41,7 @@ impl Populator { // table of the current scope, and add the inputs to the child // (body) scope. let function_symbol = - self.build_function_symbol(node.line(), &table, inputs, output.as_deref())?; + self.build_function_symbol(node.line(), &table, inputs, output.as_ref())?; table.insert(identifier.to_owned(), function_symbol); if let FunctionKind::Normal { body } = kind { @@ -68,11 +69,11 @@ impl Populator { &mut self, line: u32, table: &SymbolTable, - typ: &str, + typ: &TypeIdentifier, ) -> Result { let typ = table .get_type(typ) - .ok_or(AnalysisError::UnknownIdentifier(line, typ.to_owned()))?; + .ok_or(AnalysisError::UnknownIdentifier(line, typ.to_string()))?; Ok(Symbol::Value(ValueSymbol { typ, @@ -85,7 +86,7 @@ impl Populator { line: u32, table: &SymbolTable, inputs: &[FunctionInput], - output: Option<&str>, + output: Option<&TypeIdentifier>, ) -> Result { let inputs = inputs .iter() @@ -166,6 +167,20 @@ pub(super) fn propagate_types(node: &mut Expr) -> Result<(), AnalysisError> { Literal::Integer(_) => Type::Integer, Literal::Float(_) => Type::Float, Literal::Boolean(_) => Type::Boolean, + Literal::Array(members) => { + let mut last = None; + for member in members { + propagate_types(member)?; + if let Some(ref last) = last { + if member.typ.as_ref().unwrap() != last { + return Err(AnalysisError::TypeMismatch(node.line)); + } + } + last = Some(member.typ.clone().unwrap()); + } + + last.expect("need 1 element in literal im sozzy") + } _ => todo!(), }, ExprKind::Identifier(identifier) => { diff --git a/sloth/src/codegen/mod.rs b/sloth/src/codegen/mod.rs index a3a1239..de19116 100644 --- a/sloth/src/codegen/mod.rs +++ b/sloth/src/codegen/mod.rs @@ -7,11 +7,11 @@ use inkwell::module::Module; use inkwell::targets::{ CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, }; -use inkwell::types::{BasicMetadataTypeEnum, BasicTypeEnum}; +use inkwell::types::{BasicMetadataTypeEnum, BasicType, BasicTypeEnum, PointerType}; use inkwell::values::{ BasicMetadataValueEnum, BasicValue, BasicValueEnum, FunctionValue, PointerValue, }; -use inkwell::{FloatPredicate, IntPredicate, OptimizationLevel}; +use inkwell::{AddressSpace, FloatPredicate, IntPredicate, OptimizationLevel}; use itertools::{Either, Itertools}; use crate::parser::ast::{ @@ -35,7 +35,7 @@ impl<'ctx> Codegen<'ctx> { let builder = context.create_builder(); let module = context.create_module(module); - Codegen { + let mut this = Codegen { context, builder, module, @@ -44,7 +44,13 @@ impl<'ctx> Codegen<'ctx> { current_func_void: false, references: Default::default(), - } + }; + + this.INTRINSIC_vpush("i", Type::Integer); + this.INTRINSIC_vpush("f", Type::Float); + this.INTRINSIC_vpush("b", Type::Boolean); + + this } pub fn codegen(&mut self, code: &Stmt) { @@ -68,7 +74,7 @@ impl<'ctx> Codegen<'ctx> { None => builder.position_at_end(entry), } - builder.build_alloca(typ, &format!("alloca {name}")) + builder.build_alloca(typ, name) } fn codegen_stmt(&mut self, code: &Stmt) { @@ -207,6 +213,25 @@ impl<'ctx> Codegen<'ctx> { 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), + Type::Array { ref typ, .. } => { + let i32_type = self.context.i32_type().as_basic_type_enum(); + + let typ = self + .type_as_basic_type(*typ.clone()) + .ptr_type(AddressSpace::default()) + .as_basic_type_enum(); + + let vector_type = self.context.struct_type(&[i32_type, i32_type, typ], false); + + let ptr_to_that = vector_type.ptr_type(AddressSpace::default()); + + ptr_to_that.fn_type(&inputs_typ, false) + + // killll me + // self.type_as_basic_type(*typ.clone()) + // .ptr_type(AddressSpace::default()) + // .fn_type(&inputs_typ, false) + } _ => todo!(), }; @@ -333,15 +358,89 @@ impl<'ctx> Codegen<'ctx> { .bool_type() .const_int(if value { 1 } else { 0 }, false) .as_basic_value_enum(), + Literal::Array(values) => { + // FIXME: Allocating a new dynamic array for constants is really inefficient + let element_type = self.type_as_basic_type(values[0].typ.clone().unwrap()); + let i32_type = self.context.i32_type(); + + let inner_ptr = self + .builder + .build_array_malloc(element_type, i32_type.const_int(100, false), "vecinnerptr") + .unwrap(); + + for (idx, value) in values.iter().enumerate() { + let value = self.codegen_expr(value).unwrap(); + let value_ptr = unsafe { + self.builder.build_gep( + i32_type, + inner_ptr, + &[i32_type.const_int(idx as u64, false)], + "", + ) + }; + + self.builder.build_store(value_ptr, value); + } + + let vector_type = self.context.struct_type( + &[ + i32_type.as_basic_type_enum(), + i32_type.as_basic_type_enum(), + inner_ptr.get_type().as_basic_type_enum(), + ], + false, + ); + + let vector_ptr = self.builder.build_malloc(vector_type, "vecptr").unwrap(); + + // Set the size and capacity values + let size = self + .builder + .build_struct_gep(vector_type, vector_ptr, 0, "gepvec") + .unwrap(); + self.builder + .build_store(size, i32_type.const_int(values.len() as u64, false)); + + let cap = self + .builder + .build_struct_gep(vector_type, vector_ptr, 1, "gepvec") + .unwrap(); + self.builder + .build_store(cap, i32_type.const_int(100, false)); + + let inner = self + .builder + .build_struct_gep(vector_type, vector_ptr, 2, "gepvec") + .unwrap(); + self.builder.build_store(inner, inner_ptr); + + vector_ptr.as_basic_value_enum() + } _ => unimplemented!(), } } fn type_as_basic_type(&self, typ: Type) -> BasicTypeEnum<'ctx> { + // self.context.i64_type().ptr_type(Address) match typ { Type::Integer => self.context.i64_type().into(), Type::Float => self.context.f64_type().into(), Type::Boolean => self.context.bool_type().into(), + Type::Array { typ, .. } => { + let i32_type = self.context.i32_type().as_basic_type_enum(); + + let typ = self + .type_as_basic_type(*typ) + .ptr_type(AddressSpace::default()) + .as_basic_type_enum(); + + let vector_type = self.context.struct_type(&[i32_type, i32_type, typ], false); + + let ptr_to_that = vector_type.ptr_type(AddressSpace::default()); + + ptr_to_that.as_basic_type_enum() + } + // Type::Array { typ, len } => self.type_as_basic_type(*typ).array_type(len).into(), _ => todo!(), } } @@ -375,3 +474,89 @@ impl<'ctx> Codegen<'ctx> { file.write_all(buffer.as_slice()).unwrap(); } } + +#[allow(non_snake_case)] +impl<'ctx> Codegen<'ctx> { + fn INTRINSIC_vpush(&mut self, name: &str, typ: Type) { + // Preparing for function + self.references.clear(); + + let bruh = self.type_as_basic_type(Type::Array { + typ: Box::new(typ.clone()), + len: 0, + }); + + let inputs = &[bruh.into(), self.type_as_metadata_type(typ.clone())]; + + // Making the function + let func_type = self.context.void_type().fn_type(inputs, false); + let func = self + .module + .add_function(&format!("vpush{name}"), func_type, None); + + self.current_func = Some(func); + self.current_func_void = true; + + let block = self.context.append_basic_block(func, "entrypoint"); + self.builder.position_at_end(block); + + // Writing the logic + let element_type = self.type_as_basic_type(typ); + let i32_type = self.context.i32_type(); + + let vector_type = self.context.struct_type( + &[ + i32_type.as_basic_type_enum(), + i32_type.as_basic_type_enum(), + element_type + .ptr_type(AddressSpace::default()) + .as_basic_type_enum(), + ], + false, + ); + + let vector_ptr = func.get_nth_param(0).unwrap().into_pointer_value(); + + let size_ptr = self + .builder + .build_struct_gep(vector_type, vector_ptr, 0, "sizegep") + .unwrap(); + let cap_ptr = self + .builder + .build_struct_gep(vector_type, vector_ptr, 1, "capgep") + .unwrap(); + let inner_ptr = self + .builder + .build_struct_gep(vector_type, vector_ptr, 2, "innergep") + .unwrap(); + + let size = self + .builder + .build_load(i32_type, size_ptr, "size") + .into_int_value(); + let _cap = self + .builder + .build_load(i32_type, cap_ptr, "cap") + .into_int_value(); + + // Put the new element into backing array + let slot_ptr = unsafe { + self.builder + .build_gep(element_type, inner_ptr, &[size], "slot") + }; + + let element = func.get_nth_param(1).unwrap(); + self.builder.build_store(slot_ptr, element); + + // TODO: Handle going over capacity + + // Increase size tracker + let new_size = self + .builder + .build_int_add(size, i32_type.const_int(1, false), ""); + self.builder.build_store(size_ptr, new_size); + + // Function return + self.builder.build_return(None); + } +} diff --git a/sloth/src/main.rs b/sloth/src/main.rs index 5ce631a..dec55de 100644 --- a/sloth/src/main.rs +++ b/sloth/src/main.rs @@ -22,7 +22,7 @@ use inkwell::targets::FileType; use itertools::Itertools; use lexer::Lexer; use parser::AstParser; -use symtable::{Symbol, SymbolTable}; +use symtable::{Symbol, SymbolTable, ValueSymbol}; use crate::analysis::analyze; use crate::symtable::Type; @@ -49,6 +49,18 @@ fn main() { global_symtable.insert("Float".into(), Symbol::Type(Type::Float)); global_symtable.insert("Bool".into(), Symbol::Type(Type::Boolean)); + let dummy = Symbol::Value(ValueSymbol { + typ: Type::Function { + inputs: vec![], + output: Box::new(Type::Void), + }, + id: 0, + }); + + global_symtable.insert("vpushi".into(), dummy.clone()); + global_symtable.insert("vpushf".into(), dummy.clone()); + global_symtable.insert("vpushb".into(), dummy); + // Parsing let tokens = Lexer::new(&source).collect_vec(); let mut ast = AstParser::parse(tokens, global_symtable).unwrap(); diff --git a/sloth/src/parser/ast.rs b/sloth/src/parser/ast.rs index 281cd86..1624b0d 100644 --- a/sloth/src/parser/ast.rs +++ b/sloth/src/parser/ast.rs @@ -218,7 +218,7 @@ pub enum StmtKind { DefineVariable { identifier: String, value: Expr, - typ: String, + typ: TypeIdentifier, }, AssignVariable { identifier: String, @@ -235,7 +235,7 @@ pub enum StmtKind { pub struct Function { pub identifier: String, pub inputs: Vec, - pub output: Option, + pub output: Option, pub kind: FunctionKind, } @@ -248,7 +248,30 @@ pub enum FunctionKind { #[derive(PartialEq, Clone, Debug)] pub struct FunctionInput { pub identifier: String, - pub typ: String, + pub typ: TypeIdentifier, +} + +#[derive(PartialEq, Clone, Debug)] +pub struct TypeIdentifier { + pub name: String, + pub is_list: bool, + pub list_len: u32, +} + +impl Display for TypeIdentifier { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.is_list { + write!(f, "[")?; + } + + write!(f, "{}", self.name)?; + + if self.is_list { + write!(f, "]")?; + } + + Ok(()) + } } #[derive(Debug, Clone, PartialEq)] @@ -258,7 +281,7 @@ pub enum Literal { Boolean(bool), Character(char), String(String), - Array(Vec), + Array(Vec), } impl From for Literal { diff --git a/sloth/src/parser/expr.rs b/sloth/src/parser/expr.rs index 7ec283f..3decf5d 100644 --- a/sloth/src/parser/expr.rs +++ b/sloth/src/parser/expr.rs @@ -1,4 +1,4 @@ -use super::ast::{Expr, UnaryOp}; +use super::ast::{Expr, Literal, UnaryOp}; use super::AstParser; use crate::lexer::TokenType; use crate::parser::ast::{BinaryOp, ExprKind}; @@ -67,6 +67,20 @@ impl<'a> AstParser<'a> { TokenType::Literal(literal) => ExprKind::Literal(literal.into()), TokenType::Identifier(identifier) => ExprKind::Identifier(identifier), + TokenType::OpeningBracket => { + let mut contents = Vec::new(); + while !self.eof() && self.peek().tt != TokenType::ClosingBracket { + contents.push(self.expression()?); + if !self.advance_if_eq(&TokenType::Comma) { + break; + } + } + + self.consume(TokenType::ClosingBracket, "Expected ']'")?; + + ExprKind::Literal(Literal::Array(contents)) + } + TokenType::OpeningParen => { let expr = self.expression()?; self.consume(TokenType::ClosingParen, "Must end grouping with ')'")?; diff --git a/sloth/src/parser/mod.rs b/sloth/src/parser/mod.rs index 748a0da..6dd534a 100644 --- a/sloth/src/parser/mod.rs +++ b/sloth/src/parser/mod.rs @@ -3,7 +3,7 @@ pub mod expr; pub mod graph; pub mod stmt; -use self::ast::{Literal, Stmt, StmtKind}; +use self::ast::{Literal, Stmt, StmtKind, TypeIdentifier}; use crate::lexer::{Token, TokenType}; use crate::symtable::SymbolTable; @@ -117,6 +117,31 @@ impl<'a> AstParser<'a> { Ok(identifier) } + pub fn consume_type(&mut self) -> Result { + let is_list = self.peek().tt == TokenType::OpeningBracket; + + if is_list { + self.consume(TokenType::OpeningBracket, "Expected '['")?; + } + + let name = self.consume_identifier()?; + + let mut list_len = 0; + if is_list { + if let Literal::Integer(i) = self.consume_literal()? { + list_len = i as u32; + } + + self.consume(TokenType::ClosingBracket, "Expected ']'")?; + } + + Ok(TypeIdentifier { + name, + is_list, + list_len, + }) + } + pub fn reserve_id(&mut self) -> i32 { let id = self.id; self.id += 1; diff --git a/sloth/src/parser/stmt.rs b/sloth/src/parser/stmt.rs index 973b9c3..d8c091d 100644 --- a/sloth/src/parser/stmt.rs +++ b/sloth/src/parser/stmt.rs @@ -91,7 +91,7 @@ impl<'a> AstParser<'a> { // Get the identifier and type let identifier = self.consume_identifier()?; self.consume(TokenType::Colon, "Expected ':'")?; - let typ = self.consume_identifier()?; + let typ = self.consume_type()?; // Get the default value self.consume(TokenType::Eq, "Expected '='")?; @@ -127,7 +127,7 @@ impl<'a> AstParser<'a> { while matches!(self.peek().tt, TokenType::Identifier(_)) { let input_identifier = self.consume_identifier()?; self.consume(TokenType::Colon, "Expected ':'")?; - let input_type = self.consume_identifier()?; + let input_type = self.consume_type()?; inputs.push(FunctionInput { identifier: input_identifier, @@ -144,8 +144,11 @@ impl<'a> AstParser<'a> { self.consume(TokenType::ClosingParen, "Expected ')'")?; // Get the function output - let output = if matches!(self.peek().tt, TokenType::Identifier(_)) { - Some(self.consume_identifier()?) + let output = if matches!( + self.peek().tt, + TokenType::Identifier(_) | TokenType::OpeningBracket + ) { + Some(self.consume_type()?) } else { None }; @@ -257,6 +260,7 @@ mod tests { use crate::lexer::Lexer; use crate::parser::ast::{ BinaryOp, Expr, ExprKind, Function, FunctionInput, FunctionKind, Literal, Stmt, + TypeIdentifier, }; use crate::symtable::SymbolTable; @@ -304,7 +308,11 @@ mod tests { ExprKind::Literal(Literal::Integer(3)), )), }), - typ: "Int".to_string(), + typ: TypeIdentifier { + name: "Int".to_string(), + is_list: false, + list_len: 0, + }, })); let mut parser = AstParser::new(tokens, SymbolTable::new()); @@ -335,9 +343,17 @@ mod tests { identifier: "foo".to_owned(), inputs: vec![FunctionInput { identifier: "bar".to_owned(), - typ: "Int".to_owned(), + typ: TypeIdentifier { + name: "Int".to_owned(), + is_list: false, + list_len: 0, + }, }], - output: Some("Int".to_owned()), + output: Some(TypeIdentifier { + name: "Int".to_owned(), + is_list: false, + list_len: 0, + }), kind: FunctionKind::Normal { body: Box::new(Stmt::without_table( 10, @@ -355,7 +371,11 @@ mod tests { Literal::Integer(1).into(), )), }), - typ: "Int".to_owned(), + typ: TypeIdentifier { + name: "Int".to_owned(), + is_list: false, + list_len: 0, + }, }), Stmt::without_table(7, StmtKind::AssignVariable { identifier: "baz".to_owned(), diff --git a/sloth/src/symtable.rs b/sloth/src/symtable.rs index ef31584..5a89c7a 100644 --- a/sloth/src/symtable.rs +++ b/sloth/src/symtable.rs @@ -3,6 +3,8 @@ use std::collections::hash_map::Entry::Vacant; use std::collections::HashMap; use std::rc::Rc; +use crate::parser::ast::TypeIdentifier; + #[derive(Debug, Default)] struct Scope { parent: Option>, @@ -52,10 +54,17 @@ impl SymbolTable { None } - pub fn get_type(&self, identifier: &str) -> Option { - let symbol = self.get(identifier)?; + pub fn get_type(&self, identifier: &TypeIdentifier) -> Option { + let symbol = self.get(&identifier.name)?; if let Symbol::Type(ref typ) = *symbol { - return Some(typ.clone()); + let mut typ = typ.clone(); + if identifier.is_list { + typ = Type::Array { + typ: Box::new(typ), + len: identifier.list_len, + }; + } + return Some(typ); } None @@ -125,7 +134,7 @@ impl<'a> Iterator for Iter<'a> { } } -#[derive(Debug)] +#[derive(Clone, Debug)] pub enum Symbol { /// Symbol referencing a compile time type, such as the Int symbol Type(Type), @@ -149,4 +158,8 @@ pub enum Type { inputs: Vec, output: Box, }, + Array { + typ: Box, + len: u32, + }, } diff --git a/test.c b/test.c index 26925e0..605ed13 100644 --- a/test.c +++ b/test.c @@ -1,13 +1,26 @@ #include -long long hehehaha(); +typedef struct { + int size; + int cap; + long* inner; +} IntVec; -long long addz(long long lhs, long long rhs) { - return lhs + rhs + 1; -} +IntVec* test(); int main() { - long long res = hehehaha(); - printf("%d\n", res); - return 0; + IntVec* v = test(); + + int size = (*v).size; + int cap = (*v).cap; + long* inner = (*v).inner; + + printf("%d\n", size); + printf("%d\n", cap); + + for (int i = 0; i < size; ++i) { + long value = inner[i]; + printf("%d ", i); + } + puts("\n"); }