Add optional typing to var declarations
This commit is contained in:
parent
dc9cfcd211
commit
0d19f447f9
6 changed files with 89 additions and 14 deletions
|
@ -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),
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue