1
0
Fork 0

Add optional typing to var declarations

This commit is contained in:
Vili Sinervä 2025-02-04 18:05:56 +02:00
parent dc9cfcd211
commit 0d19f447f9
No known key found for this signature in database
GPG key ID: DF8FEAF54EFAC996
6 changed files with 89 additions and 14 deletions

View file

@ -1,6 +1,12 @@
use crate::compiler::token::CodeLocation; use crate::compiler::token::CodeLocation;
use std::fmt; use std::fmt;
#[derive(Debug, PartialEq)]
pub enum TypeExpression {
Int(CodeLocation),
Bool(CodeLocation),
}
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Expression<'source> { pub enum Expression<'source> {
EmptyLiteral(CodeLocation), EmptyLiteral(CodeLocation),
@ -14,7 +20,12 @@ pub enum Expression<'source> {
&'source str, &'source str,
Box<Expression<'source>>, Box<Expression<'source>>,
), ),
VarDeclaration(CodeLocation, &'source str, Box<Expression<'source>>), VarDeclaration(
CodeLocation,
&'source str,
Box<Expression<'source>>,
Option<TypeExpression>,
),
Conditional( Conditional(
CodeLocation, CodeLocation,
Box<Expression<'source>>, Box<Expression<'source>>,
@ -38,7 +49,7 @@ impl<'source> Expression<'source> {
Expression::BoolLiteral(loc, _) => *loc, Expression::BoolLiteral(loc, _) => *loc,
Expression::Identifier(loc, _) => *loc, Expression::Identifier(loc, _) => *loc,
Expression::UnaryOp(loc, _, _) => *loc, Expression::UnaryOp(loc, _, _) => *loc,
Expression::VarDeclaration(loc, _, _) => *loc, Expression::VarDeclaration(loc, _, _, _) => *loc,
Expression::BinaryOp(loc, _, _, _) => *loc, Expression::BinaryOp(loc, _, _, _) => *loc,
Expression::Conditional(loc, _, _, _) => *loc, Expression::Conditional(loc, _, _, _) => *loc,
Expression::While(loc, _, _) => *loc, Expression::While(loc, _, _) => *loc,
@ -70,7 +81,7 @@ impl<'source> Expression<'source> {
Expression::BoolLiteral(_, val) => val.to_string(), Expression::BoolLiteral(_, val) => val.to_string(),
Expression::Identifier(_, name) => name.to_string(), Expression::Identifier(_, name) => name.to_string(),
Expression::UnaryOp(_, op, _) => op.to_string(), Expression::UnaryOp(_, op, _) => op.to_string(),
Expression::VarDeclaration(_, name, _) => name.to_string(), Expression::VarDeclaration(_, name, _, _) => name.to_string(),
Expression::BinaryOp(_, _, op, _) => op.to_string(), Expression::BinaryOp(_, _, op, _) => op.to_string(),
Expression::Conditional(_, condition, _, _) => format!("if {}", condition), Expression::Conditional(_, condition, _, _) => format!("if {}", condition),
Expression::While(_, condition, _) => format!("while {}", condition), Expression::While(_, condition, _) => format!("while {}", condition),

View file

@ -78,7 +78,7 @@ pub fn interpret<'source>(
op_fn(&[interpret(left, symbols), interpret(right, symbols)]) op_fn(&[interpret(left, symbols), interpret(right, symbols)])
} }
}, },
VarDeclaration(_, name, expr) => { VarDeclaration(_, name, expr, _) => {
let val = interpret(expr, symbols); let val = interpret(expr, symbols);
symbols.insert(name, val); symbols.insert(name, val);
Value::None() Value::None()

View file

@ -3,7 +3,10 @@ mod parser_utilities;
mod tests; mod tests;
use crate::compiler::{ use crate::compiler::{
ast::Expression::{self, *}, ast::{
Expression::{self, *},
TypeExpression::{self},
},
parser::parser_utilities::*, parser::parser_utilities::*,
token::{Token, TokenType}, token::{Token, TokenType},
}; };
@ -162,9 +165,22 @@ fn parse_var_declaration<'source>(
) -> Expression<'source> { ) -> Expression<'source> {
consume_string(pos, tokens, "var"); consume_string(pos, tokens, "var");
let name_token = consume_type(pos, tokens, TokenType::Identifier); let name_token = consume_type(pos, tokens, TokenType::Identifier);
let mut type_expr = None;
if peek(pos, tokens).text == ":" {
consume_string(pos, tokens, ":");
let type_token = consume_type(pos, tokens, TokenType::Identifier);
type_expr = match type_token.text {
"Int" => Some(TypeExpression::Int(type_token.loc)),
"Bool" => Some(TypeExpression::Bool(type_token.loc)),
_ => panic! {"Unknown type indicator!"},
}
}
consume_string(pos, tokens, "="); consume_string(pos, tokens, "=");
let value = parse_expression(0, pos, tokens); let value = parse_expression(0, pos, tokens);
VarDeclaration(name_token.loc, name_token.text, Box::new(value)) VarDeclaration(name_token.loc, name_token.text, Box::new(value), type_expr)
} }
fn parse_conditional<'source>(pos: &mut usize, tokens: &[Token<'source>]) -> Expression<'source> { fn parse_conditional<'source>(pos: &mut usize, tokens: &[Token<'source>]) -> Expression<'source> {

View file

@ -104,8 +104,8 @@ macro_rules! empty_ast {
} }
macro_rules! var_ast { macro_rules! var_ast {
($x:expr, $y:expr) => { ($x:expr, $y:expr, $z:expr) => {
VarDeclaration(CodeLocation::new(usize::MAX, usize::MAX), $x, $y) VarDeclaration(CodeLocation::new(usize::MAX, usize::MAX), $x, $y, $z)
}; };
} }
@ -525,19 +525,48 @@ fn test_block_missing_semicolon() {
#[test] #[test]
fn test_var_basic() { fn test_var_basic() {
let result = parse(&tokenize("var x = 1")); let result = parse(&tokenize("var x = 1"));
assert_eq!(result, var_ast!("x", int_ast_b!(1))); assert_eq!(result, var_ast!("x", int_ast_b!(1), None));
let result = parse(&tokenize("{ var x = 1; x = 2; }")); let result = parse(&tokenize("{ var x = 1; x = 2; }"));
assert_eq!( assert_eq!(
result, result,
block_ast!(vec![ block_ast!(vec![
var_ast!("x", int_ast_b!(1)), var_ast!("x", int_ast_b!(1), None),
bin_ast!(id_ast_b!("x"), "=", int_ast_b!(2)), bin_ast!(id_ast_b!("x"), "=", int_ast_b!(2)),
empty_ast!() empty_ast!()
]) ])
); );
} }
#[test]
fn test_var_typed() {
let result = parse(&tokenize("var x: Int = 1"));
assert_eq!(
result,
var_ast!(
"x",
int_ast_b!(1),
Some(TypeExpression::Int(CodeLocation::new(
usize::MAX,
usize::MAX
)))
)
);
let result = parse(&tokenize("var x: Bool = true"));
assert_eq!(
result,
var_ast!(
"x",
bool_ast_b!(true),
Some(TypeExpression::Bool(CodeLocation::new(
usize::MAX,
usize::MAX
)))
)
);
}
#[test] #[test]
#[should_panic] #[should_panic]
fn test_var_chain() { fn test_var_chain() {

View file

@ -11,7 +11,7 @@ pub fn tokenize(code: &str) -> Vec<Token> {
TokenType::Operator, TokenType::Operator,
Regex::new(r"^(==|!=|<=|>=|=|<|>|\+|-|\*|/|\%)").unwrap(), Regex::new(r"^(==|!=|<=|>=|=|<|>|\+|-|\*|/|\%)").unwrap(),
), ),
(TokenType::Punctuation, Regex::new(r"^[\(\){},;]").unwrap()), (TokenType::Punctuation, Regex::new(r"^[\(\){},;:]").unwrap()),
(TokenType::Integer, Regex::new(r"^[0-9]+").unwrap()), (TokenType::Integer, Regex::new(r"^[0-9]+").unwrap()),
( (
TokenType::Identifier, TokenType::Identifier,
@ -177,7 +177,7 @@ mod tests {
#[test] #[test]
fn test_tokenize_punctuation_basic() { fn test_tokenize_punctuation_basic() {
let loc = CodeLocation::new(usize::MAX, usize::MAX); let loc = CodeLocation::new(usize::MAX, usize::MAX);
let result = tokenize("{var = (1 + 2, 3);}"); let result = tokenize("{var = (1 + 2, 3);:}");
use TokenType::*; use TokenType::*;
assert_eq!( assert_eq!(
@ -194,6 +194,7 @@ mod tests {
Token::new("3", Integer, loc), Token::new("3", Integer, loc),
Token::new(")", Punctuation, loc), Token::new(")", Punctuation, loc),
Token::new(";", Punctuation, loc), Token::new(";", Punctuation, loc),
Token::new(":", Punctuation, loc),
Token::new("}", Punctuation, loc), Token::new("}", Punctuation, loc),
) )
); );

View file

@ -1,5 +1,8 @@
use crate::compiler::{ use crate::compiler::{
ast::Expression::{self, *}, ast::{
Expression::{self, *},
TypeExpression,
},
symtab::SymTab, symtab::SymTab,
variable::Type, variable::Type,
}; };
@ -84,8 +87,23 @@ pub fn type_check<'source>(ast: &Expression<'source>, symbols: &mut SymTab<'sour
(**sig_ret_type).clone() (**sig_ret_type).clone()
} }
}, },
VarDeclaration(_, name, expr) => { VarDeclaration(_, name, expr, type_expr) => {
let type_var = type_check(expr, symbols); let type_var = type_check(expr, symbols);
if let Some(type_expr) = type_expr {
let expected_type = match type_expr {
TypeExpression::Int(_) => Type::Int,
TypeExpression::Bool(_) => Type::Bool,
};
if type_var != expected_type {
panic!(
"Expected type {:?} does not match actual type {:?} in var declaration",
expected_type, type_var
)
}
}
symbols.insert(name, type_var); symbols.insert(name, type_var);
Type::Unit Type::Unit
} }