aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/hello.sloth15
-rw-r--r--sloth/src/analysis/setup.rs23
-rw-r--r--sloth/src/codegen/mod.rs195
-rw-r--r--sloth/src/main.rs14
-rw-r--r--sloth/src/parser/ast.rs31
-rw-r--r--sloth/src/parser/expr.rs16
-rw-r--r--sloth/src/parser/mod.rs27
-rw-r--r--sloth/src/parser/stmt.rs36
-rw-r--r--sloth/src/symtable.rs21
-rw-r--r--test.c27
10 files changed, 362 insertions, 43 deletions
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<Symbol, AnalysisError> {
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<Symbol, AnalysisError> {
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<FunctionInput>,
- pub output: Option<String>,
+ pub output: Option<TypeIdentifier>,
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<ExprKind>),
+ Array(Vec<Expr>),
}
impl From<lexer::Literal> 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<TypeIdentifier, ParsingError> {
+ 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<Rc<Scope>>,
@@ -52,10 +54,17 @@ impl SymbolTable {
None
}
- pub fn get_type(&self, identifier: &str) -> Option<Type> {
- let symbol = self.get(identifier)?;
+ pub fn get_type(&self, identifier: &TypeIdentifier) -> Option<Type> {
+ 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<Type>,
output: Box<Type>,
},
+ Array {
+ typ: Box<Type>,
+ 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 <stdio.h>
-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");
}