Compare commits

..

5 Commits

Author SHA1 Message Date
bf8baa7cd4 Add parsing for setting indexed values 2025-07-13 18:02:27 +03:00
df6b5ef34b Add array indexing 2025-07-13 17:46:34 +03:00
8f95d445c0 Add array expression parsing 2025-07-13 17:41:57 +03:00
887071eeb6 Add parsing of array type 2025-07-13 17:33:38 +03:00
4e8228f903 Move binop type resolution to common implementation 2025-07-13 17:05:23 +03:00
10 changed files with 177 additions and 79 deletions

11
reid/examples/array.rs Normal file
View File

@ -0,0 +1,11 @@
use reid::compile;
pub static ARRAY: &str = include_str!("./reid/array.reid");
fn main() {
let text = match compile(ARRAY) {
Ok(t) => t,
Err(e) => panic!("{}", e),
};
println!("{}", text);
}

View File

@ -0,0 +1,15 @@
// Arithmetic, function calls and imports!
fn array() -> [u16; 4] {
return [1, 2, 3, 4];
}
fn main() -> u16 {
let heehoo = 10;
let mut list = array();
list[1] = 5;
return list[0];
}

View File

@ -1,12 +0,0 @@
// if-statements, functions
import std::print;
fn fibonacci(value: i32) -> i32 {
if value < 3 {
return 1;
}
return fibonacci(value - 1) + fibonacci(value - 2);
}
print(fibonacci(15));

View File

@ -6,10 +6,10 @@ use crate::token_stream::TokenRange;
pub mod parse; pub mod parse;
pub mod process; pub mod process;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone)]
pub struct Type(pub TypeKind, pub TokenRange); pub struct Type(pub TypeKind, pub TokenRange);
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone)]
pub enum TypeKind { pub enum TypeKind {
Bool, Bool,
I8, I8,
@ -22,6 +22,7 @@ pub enum TypeKind {
U32, U32,
U64, U64,
U128, U128,
Array(Box<TypeKind>, u64),
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -37,6 +38,8 @@ pub struct Expression(pub ExpressionKind, pub TokenRange);
pub enum ExpressionKind { pub enum ExpressionKind {
VariableName(String), VariableName(String),
Literal(Literal), Literal(Literal),
Array(Vec<Expression>),
Index(Box<Expression>, u64),
Binop(BinaryOperator, Box<Expression>, Box<Expression>), Binop(BinaryOperator, Box<Expression>, Box<Expression>),
FunctionCall(Box<FunctionCallExpression>), FunctionCall(Box<FunctionCallExpression>),
BlockExpr(Box<Block>), BlockExpr(Box<Block>),
@ -129,11 +132,17 @@ pub struct Block(
pub TokenRange, pub TokenRange,
); );
#[derive(Debug, Clone)]
pub enum VariableReference {
Name(String),
Index(Box<VariableReference>, u64),
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum BlockLevelStatement { pub enum BlockLevelStatement {
Let(LetStatement), Let(LetStatement),
/// Try to set a variable to a specified expression value /// Try to set a variable to a specified expression value
Set(String, Expression, TokenRange), Set(VariableReference, Expression, TokenRange),
Import { Import {
_i: ImportStatement, _i: ImportStatement,
}, },

View File

@ -13,8 +13,20 @@ where
impl Parse for Type { impl Parse for Type {
fn parse(mut stream: TokenStream) -> Result<Self, Error> { fn parse(mut stream: TokenStream) -> Result<Self, Error> {
let kind = if let Some(Token::Identifier(ident)) = stream.next() { let kind = if let Some(Token::BracketOpen) = stream.peek() {
Ok(match &*ident { stream.expect(Token::BracketOpen)?;
let inner = stream.parse::<Type>()?;
stream.expect(Token::Semi)?;
let length = if let Some(Token::DecimalValue(length)) = stream.next() {
length
} else {
return Err(stream.expected_err("array length (number)")?);
};
stream.expect(Token::BracketClose)?;
TypeKind::Array(Box::new(inner.0), length)
} else {
if let Some(Token::Identifier(ident)) = stream.next() {
match &*ident {
"bool" => TypeKind::Bool, "bool" => TypeKind::Bool,
"i8" => TypeKind::I8, "i8" => TypeKind::I8,
"i16" => TypeKind::I16, "i16" => TypeKind::I16,
@ -26,11 +38,12 @@ impl Parse for Type {
"u32" => TypeKind::U32, "u32" => TypeKind::U32,
"u64" => TypeKind::U64, "u64" => TypeKind::U64,
"u128" => TypeKind::U128, "u128" => TypeKind::U128,
_ => panic!("asd"), _ => Err(stream.expected_err("known type identifier")?)?,
}) }
} else { } else {
Err(stream.expected_err("type identifier")?) return Err(stream.expected_err("type identifier")?)?;
}?; }
};
Ok(Type(kind, stream.get_range().unwrap())) Ok(Type(kind, stream.get_range().unwrap()))
} }
@ -50,7 +63,7 @@ impl Parse for PrimaryExpression {
fn parse(mut stream: TokenStream) -> Result<Self, Error> { fn parse(mut stream: TokenStream) -> Result<Self, Error> {
use ExpressionKind as Kind; use ExpressionKind as Kind;
let expr = if let Ok(exp) = stream.parse() { let mut expr = if let Ok(exp) = stream.parse() {
Expression( Expression(
Kind::FunctionCall(Box::new(exp)), Kind::FunctionCall(Box::new(exp)),
stream.get_range().unwrap(), stream.get_range().unwrap(),
@ -68,7 +81,7 @@ impl Parse for PrimaryExpression {
Expression(Kind::VariableName(v.clone()), stream.get_range().unwrap()) Expression(Kind::VariableName(v.clone()), stream.get_range().unwrap())
} }
Token::DecimalValue(v) => Expression( Token::DecimalValue(v) => Expression(
Kind::Literal(Literal::Number(v.parse().unwrap())), Kind::Literal(Literal::Number(*v)),
stream.get_range().unwrap(), stream.get_range().unwrap(),
), ),
Token::True => Expression( Token::True => Expression(
@ -84,11 +97,31 @@ impl Parse for PrimaryExpression {
stream.expect(Token::ParenClose)?; stream.expect(Token::ParenClose)?;
exp exp
} }
_ => Err(stream.expected_err("identifier, constant or parentheses")?)?, Token::BracketOpen => {
let mut expressions = Vec::new();
if let Ok(exp) = stream.parse() {
expressions.push(exp);
while let Some(Token::Comma) = stream.peek() {
stream.next(); // Consume comma
expressions.push(stream.parse()?);
}
}
stream.expect(Token::BracketClose)?;
Expression(Kind::Array(expressions), stream.get_range().unwrap())
}
_ => Err(stream.expected_err("identifier, constant, parentheses or brackets")?)?,
} }
} else { } else {
Err(stream.expected_err("expression")?)? Err(stream.expected_err("expression")?)?
}; };
while let Ok(ValueIndex(idx)) = stream.parse() {
expr = Expression(
ExpressionKind::Index(Box::new(expr), idx),
stream.get_range().unwrap(),
);
}
Ok(PrimaryExpression(expr)) Ok(PrimaryExpression(expr))
} }
} }
@ -321,7 +354,6 @@ impl Parse for Block {
// Special list of expressions that are simply not warned about, // Special list of expressions that are simply not warned about,
// if semicolon is missing. // if semicolon is missing.
if !matches!(e, Expression(ExpressionKind::IfExpr(_), _)) { if !matches!(e, Expression(ExpressionKind::IfExpr(_), _)) {
dbg!(r_type, &e);
println!("Oh no, does this statement lack ;"); println!("Oh no, does this statement lack ;");
} }
@ -348,6 +380,39 @@ impl Parse for Block {
} }
} }
impl Parse for VariableReference {
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
if let Some(Token::Identifier(ident)) = stream.next() {
let mut var_ref = VariableReference::Name(ident);
dbg!(&var_ref);
while let Ok(ValueIndex(idx)) = stream.parse() {
dbg!(idx);
var_ref = VariableReference::Index(Box::new(var_ref), idx);
}
Ok(var_ref)
} else {
Err(stream.expected_err("identifier")?)?
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct ValueIndex(u64);
impl Parse for ValueIndex {
fn parse(mut stream: TokenStream) -> Result<Self, Error> {
stream.expect(Token::BracketOpen)?;
if let Some(Token::DecimalValue(idx)) = stream.next() {
stream.expect(Token::BracketClose)?;
Ok(ValueIndex(idx))
} else {
return Err(stream.expected_err("array index (number)")?);
}
}
}
impl Parse for BlockLevelStatement { impl Parse for BlockLevelStatement {
fn parse(mut stream: TokenStream) -> Result<Self, Error> { fn parse(mut stream: TokenStream) -> Result<Self, Error> {
use BlockLevelStatement as Stmt; use BlockLevelStatement as Stmt;
@ -382,18 +447,15 @@ impl Parse for BlockLevelStatement {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct SetStatement(String, Expression, TokenRange); pub struct SetStatement(VariableReference, Expression, TokenRange);
impl Parse for SetStatement { impl Parse for SetStatement {
fn parse(mut stream: TokenStream) -> Result<Self, Error> { fn parse(mut stream: TokenStream) -> Result<Self, Error> {
if let Some(Token::Identifier(ident)) = stream.next() { let var_ref = stream.parse()?;
stream.expect(Token::Equals)?; stream.expect(Token::Equals)?;
let expr = stream.parse()?; let expr = stream.parse()?;
stream.expect(Token::Semi)?; stream.expect(Token::Semi)?;
Ok(Self(ident, expr, stream.get_range().unwrap())) Ok(SetStatement(var_ref, expr, stream.get_range().unwrap()))
} else {
Err(stream.expected_err("identifier")?)?
}
} }
} }

View File

@ -29,6 +29,7 @@ impl ast::Module {
name: signature.name.clone(), name: signature.name.clone(),
return_type: signature return_type: signature
.return_type .return_type
.clone()
.map(|r| r.0.into()) .map(|r| r.0.into())
.unwrap_or(mir::TypeKind::Void), .unwrap_or(mir::TypeKind::Void),
parameters: signature parameters: signature
@ -44,8 +45,6 @@ impl ast::Module {
} }
} }
// TODO do something with state here
mir::Module { mir::Module {
name: self.name.clone(), name: self.name.clone(),
imports, imports,
@ -65,6 +64,7 @@ impl ast::Block {
mir::VariableReference( mir::VariableReference(
s_let s_let
.1 .1
.clone()
.map(|t| t.0.into()) .map(|t| t.0.into())
.unwrap_or(mir::TypeKind::Vague(mir::VagueType::Unknown)), .unwrap_or(mir::TypeKind::Vague(mir::VagueType::Unknown)),
s_let.0.clone(), s_let.0.clone(),
@ -79,7 +79,7 @@ impl ast::Block {
StmtKind::Set( StmtKind::Set(
VariableReference( VariableReference(
mir::TypeKind::Vague(mir::VagueType::Unknown), mir::TypeKind::Vague(mir::VagueType::Unknown),
name.clone(), todo!(), // was name.clone()
(*range).into(), (*range).into(),
), ),
expression.process(), expression.process(),
@ -149,6 +149,8 @@ impl ast::Expression {
}; };
mir::ExprKind::If(mir::IfExpression(Box::new(cond), then_block, else_block)) mir::ExprKind::If(mir::IfExpression(Box::new(cond), then_block, else_block))
} }
ast::ExpressionKind::Array(expressions) => todo!(),
ast::ExpressionKind::Index(expression, _) => todo!(),
}; };
mir::Expression(kind, self.1.into()) mir::Expression(kind, self.1.into())
@ -183,7 +185,7 @@ impl ast::Literal {
impl From<ast::TypeKind> for mir::TypeKind { impl From<ast::TypeKind> for mir::TypeKind {
fn from(value: ast::TypeKind) -> Self { fn from(value: ast::TypeKind) -> Self {
match value { match &value {
ast::TypeKind::Bool => mir::TypeKind::Bool, ast::TypeKind::Bool => mir::TypeKind::Bool,
ast::TypeKind::I8 => mir::TypeKind::I8, ast::TypeKind::I8 => mir::TypeKind::I8,
ast::TypeKind::I16 => mir::TypeKind::I16, ast::TypeKind::I16 => mir::TypeKind::I16,
@ -195,6 +197,7 @@ impl From<ast::TypeKind> for mir::TypeKind {
ast::TypeKind::U32 => mir::TypeKind::U32, ast::TypeKind::U32 => mir::TypeKind::U32,
ast::TypeKind::U64 => mir::TypeKind::U64, ast::TypeKind::U64 => mir::TypeKind::U64,
ast::TypeKind::U128 => mir::TypeKind::U128, ast::TypeKind::U128 => mir::TypeKind::U128,
ast::TypeKind::Array(type_kind, length) => todo!(),
} }
} }
} }

View File

@ -7,7 +7,7 @@ pub enum Token {
// Values // Values
Identifier(String), Identifier(String),
/// Number with at most one decimal point /// Number with at most one decimal point
DecimalValue(String), DecimalValue(u64),
// Keywords // Keywords
/// `let` /// `let`
@ -62,6 +62,10 @@ pub enum Token {
BraceOpen, BraceOpen,
/// `}` /// `}`
BraceClose, BraceClose,
/// `[`
BracketOpen,
/// `]`
BracketClose,
/// `,` /// `,`
Comma, Comma,
@ -194,7 +198,7 @@ pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error
value += &c.to_string(); value += &c.to_string();
cursor.next(); cursor.next();
} }
Token::DecimalValue(value) Token::DecimalValue(value.parse().expect("Decimal not parseable to u64"))
} }
'-' if cursor.first() == Some('>') => { '-' if cursor.first() == Some('>') => {
cursor.next(); // Eat `>` cursor.next(); // Eat `>`
@ -213,6 +217,8 @@ pub fn tokenize<T: Into<String>>(to_tokenize: T) -> Result<Vec<FullToken>, Error
'!' => Token::Exclamation, '!' => Token::Exclamation,
'(' => Token::ParenOpen, '(' => Token::ParenOpen,
')' => Token::ParenClose, ')' => Token::ParenClose,
'[' => Token::BracketOpen,
']' => Token::BracketClose,
'{' => Token::BraceOpen, '{' => Token::BraceOpen,
'}' => Token::BraceClose, '}' => Token::BraceClose,
',' => Token::Comma, ',' => Token::Comma,

View File

@ -38,6 +38,8 @@ pub enum ErrorKind {
InvalidAmountParameters(String, usize, usize), InvalidAmountParameters(String, usize, usize),
#[error("Unable to infer type {0}")] #[error("Unable to infer type {0}")]
TypeNotInferrable(TypeKind), TypeNotInferrable(TypeKind),
#[error("Expected branch type to be {0}, found {1} instead")]
BranchTypesDiffer(TypeKind, TypeKind),
} }
/// Struct used to implement a type-checking pass that can be performed on the /// Struct used to implement a type-checking pass that can be performed on the
@ -118,7 +120,7 @@ impl Block {
let res = expression.typecheck(&mut state, &hints, Some(var_t_resolved)); let res = expression.typecheck(&mut state, &hints, Some(var_t_resolved));
// If expression resolution itself was erronous, resolve as // If expression resolution itself was erronous, resolve as
// Unknown. // Unknown and note error.
let res = state.or_else(res, Vague(Unknown), expression.1); let res = state.or_else(res, Vague(Unknown), expression.1);
// Make sure the expression and variable type really is the same // Make sure the expression and variable type really is the same
@ -286,8 +288,8 @@ impl Expression {
rhs.typecheck(state, &hints, Some(collapsed)).ok(); rhs.typecheck(state, &hints, Some(collapsed)).ok();
} }
let res = lhs_type.binop_type(&op, &rhs_type)?; let both_t = lhs_type.collapse_into(&rhs_type)?;
Ok(res) Ok(both_t.binop_type(op))
} }
ExprKind::FunctionCall(function_call) => { ExprKind::FunctionCall(function_call) => {
let true_function = state let true_function = state
@ -338,7 +340,6 @@ impl Expression {
} }
} }
ExprKind::If(IfExpression(cond, lhs, rhs)) => { ExprKind::If(IfExpression(cond, lhs, rhs)) => {
// TODO make sure cond_res is Boolean here
let cond_res = cond.typecheck(state, &hints, Some(Bool)); let cond_res = cond.typecheck(state, &hints, Some(Bool));
let cond_t = state.or_else(cond_res, Vague(Unknown), cond.1); let cond_t = state.or_else(cond_res, Vague(Unknown), cond.1);
state.ok(cond_t.collapse_into(&Bool), cond.1); state.ok(cond_t.collapse_into(&Bool), cond.1);
@ -351,11 +352,15 @@ impl Expression {
let res = else_block.typecheck(state, &hints, hint_t); let res = else_block.typecheck(state, &hints, hint_t);
state.or_else(res, Vague(Unknown), else_block.meta) state.or_else(res, Vague(Unknown), else_block.meta)
} else { } else {
// TODO assert that then_ret_t is Void // Else return type is Void if it does not exist
Vague(Unknown) Void
}; };
let collapsed = then_ret_t.collapse_into(&else_ret_t)?; // Make sure then and else -blocks have the same return type
let collapsed = then_ret_t
.collapse_into(&else_ret_t)
.or(Err(ErrorKind::BranchTypesDiffer(then_ret_t, else_ret_t)))?;
if let Some(rhs) = rhs { if let Some(rhs) = rhs {
// If rhs existed, typecheck both sides to perform type // If rhs existed, typecheck both sides to perform type
// coercion. // coercion.
@ -391,6 +396,8 @@ impl Literal {
(L::U64(_), U64) => self, (L::U64(_), U64) => self,
(L::U128(_), U128) => self, (L::U128(_), U128) => self,
(L::Bool(_), Bool) => self, (L::Bool(_), Bool) => self,
// TODO make sure that v is actually able to fit in the
// requested type
(L::Vague(VagueL::Number(v)), I8) => L::I8(v as i8), (L::Vague(VagueL::Number(v)), I8) => L::I8(v as i8),
(L::Vague(VagueL::Number(v)), I16) => L::I16(v as i16), (L::Vague(VagueL::Number(v)), I16) => L::I16(v as i16),
(L::Vague(VagueL::Number(v)), I32) => L::I32(v as i32), (L::Vague(VagueL::Number(v)), I32) => L::I32(v as i32),
@ -430,17 +437,6 @@ impl TypeKind {
} }
} }
fn binop_type(&self, op: &BinaryOperator, other: &TypeKind) -> Result<TypeKind, ErrorKind> {
let res = self.collapse_into(other)?;
Ok(match op {
BinaryOperator::Add => res,
BinaryOperator::Minus => res,
BinaryOperator::Mult => res,
BinaryOperator::And => res,
BinaryOperator::Cmp(_) => Bool,
})
}
fn resolve_hinted(&self, hints: &TypeRefs) -> TypeKind { fn resolve_hinted(&self, hints: &TypeRefs) -> TypeKind {
match self { match self {
Vague(TypeRef(idx)) => hints.retrieve_type(*idx).unwrap(), Vague(TypeRef(idx)) => hints.retrieve_type(*idx).unwrap(),

View File

@ -194,12 +194,6 @@ impl<'outer> ScopeTypeRefs<'outer> {
rhs: &mut TypeRef<'outer>, rhs: &mut TypeRef<'outer>,
) -> Option<TypeRef<'outer>> { ) -> Option<TypeRef<'outer>> {
let ty = lhs.narrow(rhs)?; let ty = lhs.narrow(rhs)?;
Some(match op { self.from_type(&ty.as_type().binop_type(op))
BinaryOperator::Add => ty,
BinaryOperator::Minus => ty,
BinaryOperator::Mult => ty,
BinaryOperator::And => self.from_type(&TypeKind::Bool).unwrap(),
BinaryOperator::Cmp(_) => self.from_type(&TypeKind::Bool).unwrap(),
})
} }
} }

View File

@ -9,7 +9,24 @@ pub enum ReturnTypeOther {
NoBlockReturn(Metadata), NoBlockReturn(Metadata),
} }
impl TypeKind {
/// Return the type that is the result of a binary operator between two
/// values of this type
pub fn binop_type(&self, op: &BinaryOperator) -> TypeKind {
// TODO make some type of mechanism that allows to binop two values of
// differing types..
match op {
BinaryOperator::Add => *self,
BinaryOperator::Minus => *self,
BinaryOperator::Mult => *self,
BinaryOperator::And => TypeKind::Bool,
BinaryOperator::Cmp(_) => TypeKind::Bool,
}
}
}
pub trait ReturnType { pub trait ReturnType {
/// Return the return type of this node
fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther>; fn return_type(&self) -> Result<(ReturnKind, TypeKind), ReturnTypeOther>;
} }
@ -24,9 +41,6 @@ impl ReturnType for Block {
} }
} }
// TODO should actually probably prune all instructions after this one
// as to not cause problems in codegen later (when unable to delete the
// block)
if let Some((ReturnKind::Hard, ret_ty)) = early_return { if let Some((ReturnKind::Hard, ret_ty)) = early_return {
return Ok((ReturnKind::Hard, ret_ty)); return Ok((ReturnKind::Hard, ret_ty));
} }