1
0

Compare commits

..

4 Commits

8 changed files with 267 additions and 139 deletions

View File

@ -71,22 +71,23 @@ impl<'a> Analyzer<'a> {
fn analyze_node(&self, node: &Node) -> Result<(), AnalyzerError> { fn analyze_node(&self, node: &Node) -> Result<(), AnalyzerError> {
match &node.statement { match &node.statement {
Statement::Add(left, right) => { Statement::Add(left, right) => {
if let Some(Type::Integer) | Some(Type::Float) = let left_type = left.statement.expected_type(self.variables);
left.statement.expected_type(self.variables) let right_type = right.statement.expected_type(self.variables);
{
} else { match (left_type, right_type) {
return Err(AnalyzerError::ExpectedIntegerOrFloat { (Some(Type::Integer), Some(Type::Integer)) => {}
(Some(Type::Float), Some(Type::Float)) => {}
(Some(Type::String), Some(Type::String)) => {}
(Some(Type::Integer), _) | (Some(Type::Float), _) | (Some(Type::String), _) => {
return Err(AnalyzerError::ExpectedIntegerFloatOrString {
actual: right.as_ref().clone(),
});
}
_ => {
return Err(AnalyzerError::ExpectedIntegerFloatOrString {
actual: left.as_ref().clone(), actual: left.as_ref().clone(),
}); });
} }
if let Some(Type::Integer) | Some(Type::Float) =
right.statement.expected_type(self.variables)
{
} else {
return Err(AnalyzerError::ExpectedIntegerOrFloat {
actual: right.as_ref().clone(),
});
} }
self.analyze_node(left)?; self.analyze_node(left)?;
@ -183,6 +184,7 @@ pub enum AnalyzerError {
ExpectedIdentifier { actual: Node }, ExpectedIdentifier { actual: Node },
ExpectedIdentifierOrValue { actual: Node }, ExpectedIdentifierOrValue { actual: Node },
ExpectedIntegerOrFloat { actual: Node }, ExpectedIntegerOrFloat { actual: Node },
ExpectedIntegerFloatOrString { actual: Node },
UnexpectedIdentifier { identifier: Node }, UnexpectedIdentifier { identifier: Node },
} }
@ -192,6 +194,52 @@ mod tests {
use super::*; use super::*;
#[test]
fn add_expects_same_types() {
let abstract_tree = AbstractSyntaxTree {
nodes: [Node::new(
Statement::Add(
Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))),
Box::new(Node::new(Statement::Constant(Value::float(1.0)), (1, 2))),
),
(0, 2),
)]
.into(),
};
let variables = HashMap::new();
let analyzer = Analyzer::new(&abstract_tree, &variables);
assert_eq!(
analyzer.analyze(),
Err(AnalyzerError::ExpectedIntegerFloatOrString {
actual: Node::new(Statement::Constant(Value::float(1.0)), (1, 2))
})
)
}
#[test]
fn add_expects_integer_float_or_string() {
let abstract_tree = AbstractSyntaxTree {
nodes: [Node::new(
Statement::Add(
Box::new(Node::new(Statement::Constant(Value::boolean(true)), (0, 1))),
Box::new(Node::new(Statement::Constant(Value::integer(1)), (1, 2))),
),
(0, 2),
)]
.into(),
};
let variables = HashMap::new();
let analyzer = Analyzer::new(&abstract_tree, &variables);
assert_eq!(
analyzer.analyze(),
Err(AnalyzerError::ExpectedIntegerFloatOrString {
actual: Node::new(Statement::Constant(Value::boolean(true)), (0, 1))
})
)
}
#[test] #[test]
fn is_even_expects_number() { fn is_even_expects_number() {
let abstract_tree = AbstractSyntaxTree { let abstract_tree = AbstractSyntaxTree {
@ -277,32 +325,6 @@ mod tests {
) )
} }
#[test]
fn add_expect_integer_or_float() {
let abstract_tree = AbstractSyntaxTree {
nodes: [Node::new(
Statement::Add(
Box::new(Node::new(Statement::Constant(Value::integer(1)), (0, 1))),
Box::new(Node::new(
Statement::Constant(Value::boolean(false)),
(1, 2),
)),
),
(0, 2),
)]
.into(),
};
let variables = HashMap::new();
let analyzer = Analyzer::new(&abstract_tree, &variables);
assert_eq!(
analyzer.analyze(),
Err(AnalyzerError::ExpectedIntegerOrFloat {
actual: Node::new(Statement::Constant(Value::boolean(false)), (1, 2))
})
)
}
#[test] #[test]
fn assignment_expect_identifier() { fn assignment_expect_identifier() {
let abstract_tree = AbstractSyntaxTree { let abstract_tree = AbstractSyntaxTree {

View File

@ -1,4 +1,7 @@
use std::fmt::{self, Display, Formatter}; use std::{
fmt::{self, Display, Formatter},
io::{self, stdin},
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -6,9 +9,16 @@ use crate::{Type, Value};
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum BuiltInFunction { pub enum BuiltInFunction {
// Integer and float tools
IsEven, IsEven,
IsOdd, IsOdd,
// List tools
Length, Length,
// I/O
ReadLine,
WriteLine,
} }
impl BuiltInFunction { impl BuiltInFunction {
@ -17,6 +27,8 @@ impl BuiltInFunction {
BuiltInFunction::IsEven => "is_even", BuiltInFunction::IsEven => "is_even",
BuiltInFunction::IsOdd => "is_odd", BuiltInFunction::IsOdd => "is_odd",
BuiltInFunction::Length => "length", BuiltInFunction::Length => "length",
BuiltInFunction::ReadLine => "read_line",
BuiltInFunction::WriteLine => "write_line",
} }
} }
@ -24,13 +36,13 @@ impl BuiltInFunction {
&self, &self,
_type_arguments: Option<Vec<Type>>, _type_arguments: Option<Vec<Type>>,
value_arguments: Option<Vec<Value>>, value_arguments: Option<Vec<Value>>,
) -> Result<Value, BuiltInFunctionError> { ) -> Result<Option<Value>, BuiltInFunctionError> {
match self { match self {
BuiltInFunction::IsEven => { BuiltInFunction::IsEven => {
if let Some(value_arguments) = value_arguments { if let Some(value_arguments) = value_arguments {
if value_arguments.len() == 1 { if value_arguments.len() == 1 {
if let Some(integer) = value_arguments[0].as_integer() { if let Some(integer) = value_arguments[0].as_integer() {
Ok(Value::boolean(integer % 2 == 0)) Ok(Some(Value::boolean(integer % 2 == 0)))
} else { } else {
Err(BuiltInFunctionError::ExpectedInteger) Err(BuiltInFunctionError::ExpectedInteger)
} }
@ -45,7 +57,7 @@ impl BuiltInFunction {
if let Some(value_arguments) = value_arguments { if let Some(value_arguments) = value_arguments {
if value_arguments.len() == 1 { if value_arguments.len() == 1 {
if let Some(integer) = value_arguments[0].as_integer() { if let Some(integer) = value_arguments[0].as_integer() {
Ok(Value::boolean(integer % 2 != 0)) Ok(Some(Value::boolean(integer % 2 != 0)))
} else { } else {
Err(BuiltInFunctionError::ExpectedInteger) Err(BuiltInFunctionError::ExpectedInteger)
} }
@ -60,7 +72,7 @@ impl BuiltInFunction {
if let Some(value_arguments) = value_arguments { if let Some(value_arguments) = value_arguments {
if value_arguments.len() == 1 { if value_arguments.len() == 1 {
if let Some(list) = value_arguments[0].as_list() { if let Some(list) = value_arguments[0].as_list() {
Ok(Value::integer(list.len() as i64)) Ok(Some(Value::integer(list.len() as i64)))
} else { } else {
Err(BuiltInFunctionError::ExpectedInteger) Err(BuiltInFunctionError::ExpectedInteger)
} }
@ -71,6 +83,30 @@ impl BuiltInFunction {
Err(BuiltInFunctionError::WrongNumberOfValueArguments) Err(BuiltInFunctionError::WrongNumberOfValueArguments)
} }
} }
BuiltInFunction::ReadLine => {
if value_arguments.is_none() {
let mut input = String::new();
stdin().read_line(&mut input)?;
Ok(Some(Value::string(input)))
} else {
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
}
}
BuiltInFunction::WriteLine => {
if let Some(value_arguments) = value_arguments {
if value_arguments.len() == 1 {
println!("{}", value_arguments[0]);
Ok(None)
} else {
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
}
} else {
Err(BuiltInFunctionError::WrongNumberOfValueArguments)
}
}
} }
} }
@ -79,6 +115,8 @@ impl BuiltInFunction {
BuiltInFunction::IsEven => Some(Type::Boolean), BuiltInFunction::IsEven => Some(Type::Boolean),
BuiltInFunction::IsOdd => Some(Type::Boolean), BuiltInFunction::IsOdd => Some(Type::Boolean),
BuiltInFunction::Length => Some(Type::Integer), BuiltInFunction::Length => Some(Type::Integer),
BuiltInFunction::ReadLine => Some(Type::String),
BuiltInFunction::WriteLine => None,
} }
} }
} }
@ -92,5 +130,12 @@ impl Display for BuiltInFunction {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum BuiltInFunctionError { pub enum BuiltInFunctionError {
ExpectedInteger, ExpectedInteger,
Io(io::ErrorKind),
WrongNumberOfValueArguments, WrongNumberOfValueArguments,
} }
impl From<io::Error> for BuiltInFunctionError {
fn from(error: io::Error) -> Self {
Self::Io(error.kind())
}
}

View File

@ -108,6 +108,8 @@ impl<'a> Lexer<'a> {
match c { match c {
'0'..='9' => self.lex_number()?, '0'..='9' => self.lex_number()?,
'a'..='z' | 'A'..='Z' => self.lex_alphabetical()?, 'a'..='z' | 'A'..='Z' => self.lex_alphabetical()?,
'"' => self.lex_string('"')?,
'\'' => self.lex_string('\'')?,
'+' => { '+' => {
self.position += 1; self.position += 1;
(Token::Plus, (self.position - 1, self.position)) (Token::Plus, (self.position - 1, self.position))
@ -248,11 +250,34 @@ impl<'a> Lexer<'a> {
"is_even" => Token::IsEven, "is_even" => Token::IsEven,
"is_odd" => Token::IsOdd, "is_odd" => Token::IsOdd,
"length" => Token::Length, "length" => Token::Length,
"read_line" => Token::ReadLine,
"write_line" => Token::WriteLine,
_ => Token::Identifier(Identifier::new(string)), _ => Token::Identifier(Identifier::new(string)),
}; };
Ok((token, (start_pos, self.position))) Ok((token, (start_pos, self.position)))
} }
fn lex_string(&mut self, delimiter: char) -> Result<(Token, Span), LexError> {
let start_pos = self.position;
self.next_char();
while let Some(c) = self.peek_char() {
if c == delimiter {
self.next_char();
break;
} else {
self.next_char();
}
}
let string = &self.source[start_pos + 1..self.position - 1];
Ok((
Token::String(string.to_string()),
(start_pos, self.position),
))
}
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
@ -277,6 +302,34 @@ impl From<ParseIntError> for LexError {
mod tests { mod tests {
use super::*; use super::*;
#[test]
fn string_concatenation() {
let input = "'Hello, ' + 'world!'";
assert_eq!(
lex(input),
Ok(vec![
(Token::String("Hello, ".to_string()), (0, 9)),
(Token::Plus, (10, 11)),
(Token::String("world!".to_string()), (12, 20)),
(Token::Eof, (20, 20)),
])
)
}
#[test]
fn string() {
let input = "'Hello, world!'";
assert_eq!(
lex(input),
Ok(vec![
(Token::String("Hello, world!".to_string()), (0, 15)),
(Token::Eof, (15, 15)),
])
)
}
#[test] #[test]
fn r#true() { fn r#true() {
let input = "true"; let input = "true";

View File

@ -206,6 +206,11 @@ impl<'src> Parser<'src> {
Ok(Node::new(Statement::Identifier(identifier), span)) Ok(Node::new(Statement::Identifier(identifier), span))
} }
(Token::String(string), span) => {
self.next_token()?;
Ok(Node::new(Statement::Constant(Value::string(string)), span))
}
(Token::LeftParenthesis, left_span) => { (Token::LeftParenthesis, left_span) => {
self.next_token()?; self.next_token()?;
@ -253,37 +258,19 @@ impl<'src> Parser<'src> {
} }
} }
} }
(Token::IsEven, left_span) => { (
self.next_token()?; Token::IsEven | Token::IsOdd | Token::Length | Token::ReadLine | Token::WriteLine,
if let (Token::LeftParenthesis, _) = self.current {
self.next_token()?;
} else {
return Err(ParseError::ExpectedOpeningParenthesis {
actual: self.current.0.clone(),
span: self.current.1,
});
}
if let (Token::RightParenthesis, _) = self.current {
self.next_token()?;
} else {
return Err(ParseError::ExpectedClosingParenthesis {
actual: self.current.0.clone(),
span: self.current.1,
});
}
Ok(Node::new(
Statement::BuiltInFunctionCall {
function: BuiltInFunction::IsEven,
type_arguments: None,
value_arguments: None,
},
left_span, left_span,
)) ) => {
} let function = match self.current.0 {
(Token::IsOdd, left_span) => { Token::IsEven => BuiltInFunction::IsEven,
Token::IsOdd => BuiltInFunction::IsOdd,
Token::Length => BuiltInFunction::Length,
Token::ReadLine => BuiltInFunction::ReadLine,
Token::WriteLine => BuiltInFunction::WriteLine,
_ => unreachable!(),
};
self.next_token()?; self.next_token()?;
if let (Token::LeftParenthesis, _) = self.current { if let (Token::LeftParenthesis, _) = self.current {
@ -295,50 +282,38 @@ impl<'src> Parser<'src> {
}); });
} }
let mut value_arguments: Option<Vec<Node>> = None;
loop {
if let (Token::RightParenthesis, _) = self.current { if let (Token::RightParenthesis, _) = self.current {
self.next_token()?; self.next_token()?;
break;
}
if let (Token::Comma, _) = self.current {
self.next_token()?;
continue;
}
if let Ok(node) = self.parse_node(0) {
if let Some(ref mut arguments) = value_arguments {
arguments.push(node);
} else {
value_arguments = Some(vec![node]);
}
} else { } else {
return Err(ParseError::ExpectedClosingParenthesis { return Err(ParseError::ExpectedClosingParenthesis {
actual: self.current.0.clone(), actual: self.current.0.clone(),
span: self.current.1, span: self.current.1,
}); });
} }
Ok(Node::new(
Statement::BuiltInFunctionCall {
function: BuiltInFunction::IsOdd,
type_arguments: None,
value_arguments: None,
},
left_span,
))
}
(Token::Length, left_span) => {
self.next_token()?;
if let (Token::LeftParenthesis, _) = self.current {
self.next_token()?;
} else {
return Err(ParseError::ExpectedOpeningParenthesis {
actual: self.current.0.clone(),
span: self.current.1,
});
}
if let (Token::RightParenthesis, _) = self.current {
self.next_token()?;
} else {
return Err(ParseError::ExpectedClosingParenthesis {
actual: self.current.0.clone(),
span: self.current.1,
});
} }
Ok(Node::new( Ok(Node::new(
Statement::BuiltInFunctionCall { Statement::BuiltInFunctionCall {
function: BuiltInFunction::Length, function,
type_arguments: None, type_arguments: None,
value_arguments: None, value_arguments,
}, },
left_span, left_span,
)) ))
@ -379,6 +354,47 @@ mod tests {
use super::*; use super::*;
#[test]
fn string_concatenation() {
let input = "\"Hello, \" + \"World!\"";
assert_eq!(
parse(input),
Ok(AbstractSyntaxTree {
nodes: [Node::new(
Statement::Add(
Box::new(Node::new(
Statement::Constant(Value::string("Hello, ")),
(0, 9)
)),
Box::new(Node::new(
Statement::Constant(Value::string("World!")),
(12, 20)
))
),
(0, 20)
)]
.into()
})
);
}
#[test]
fn string() {
let input = "\"Hello, World!\"";
assert_eq!(
parse(input),
Ok(AbstractSyntaxTree {
nodes: [Node::new(
Statement::Constant(Value::string("Hello, World!")),
(0, 15)
)]
.into()
})
);
}
#[test] #[test]
fn boolean() { fn boolean() {
let input = "true"; let input = "true";

View File

@ -14,22 +14,25 @@ pub enum Token {
Boolean(bool), Boolean(bool),
Float(f64), Float(f64),
Integer(i64), Integer(i64),
String(String),
// Keywords // Keywords
IsEven, IsEven,
IsOdd, IsOdd,
Length, Length,
ReadLine,
WriteLine,
// Symbols // Symbols
Comma, Comma,
Dot, Dot,
Equal, Equal,
Plus,
Star,
LeftParenthesis, LeftParenthesis,
RightParenthesis,
LeftSquareBrace, LeftSquareBrace,
Plus,
RightParenthesis,
RightSquareBrace, RightSquareBrace,
Star,
} }
impl Display for Token { impl Display for Token {
@ -40,9 +43,12 @@ impl Display for Token {
Token::Boolean(boolean) => write!(f, "{boolean}"), Token::Boolean(boolean) => write!(f, "{boolean}"),
Token::Float(float) => write!(f, "{float}"), Token::Float(float) => write!(f, "{float}"),
Token::Integer(integer) => write!(f, "{integer}"), Token::Integer(integer) => write!(f, "{integer}"),
Token::String(string) => write!(f, "{string}"),
Token::IsEven => write!(f, "is_even"), Token::IsEven => write!(f, "is_even"),
Token::IsOdd => write!(f, "is_odd"), Token::IsOdd => write!(f, "is_odd"),
Token::Length => write!(f, "length"), Token::Length => write!(f, "length"),
Token::ReadLine => write!(f, "read_line"),
Token::WriteLine => write!(f, "write_line"),
Token::Comma => write!(f, ","), Token::Comma => write!(f, ","),
Token::Dot => write!(f, "."), Token::Dot => write!(f, "."),
Token::Equal => write!(f, "="), Token::Equal => write!(f, "="),

View File

@ -527,18 +527,6 @@ impl ValueInner {
} }
} }
pub trait ValueProperties<'a> {}
pub struct IntegerProperties<'a>(&'a Value);
impl<'a> IntegerProperties<'a> {
pub fn is_even(&self) -> bool {
self.0.as_integer().unwrap() % 2 == 0
}
}
impl<'a> ValueProperties<'a> for IntegerProperties<'a> {}
impl Eq for ValueInner {} impl Eq for ValueInner {}
impl PartialOrd for ValueInner { impl PartialOrd for ValueInner {

View File

@ -114,7 +114,7 @@ impl Vm {
}; };
let function_call_return = function.call(None, values)?; let function_call_return = function.call(None, values)?;
Ok(Some(function_call_return)) Ok(function_call_return)
} }
Statement::Constant(value) => Ok(Some(value.clone())), Statement::Constant(value) => Ok(Some(value.clone())),
Statement::FunctionCall { Statement::FunctionCall {
@ -226,7 +226,7 @@ impl Vm {
let function_call_return = function.call(None, Some(value_arguments))?; let function_call_return = function.call(None, Some(value_arguments))?;
return Ok(Some(function_call_return)); return Ok(function_call_return);
} }
Err(VmError::ExpectedIdentifierOrInteger { Err(VmError::ExpectedIdentifierOrInteger {

View File

@ -1,8 +1,6 @@
use std.io write_line("Hello, world!")
write_line("Enter your name...")
io.write_line("Hello, world!") name = read_line()
io.write_line("Enter your name...")
message = io.read_line() write_line("Hello " + name + "!")
io.write_line("Hello " + message + "!")