Compare commits
4 Commits
f5e822e916
...
a52e78150e
Author | SHA1 | Date | |
---|---|---|---|
a52e78150e | |||
8dd62e623e | |||
a639641ed2 | |||
097b09b6e3 |
@ -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 {
|
|
||||||
return Err(AnalyzerError::ExpectedIntegerOrFloat {
|
|
||||||
actual: left.as_ref().clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(Type::Integer) | Some(Type::Float) =
|
match (left_type, right_type) {
|
||||||
right.statement.expected_type(self.variables)
|
(Some(Type::Integer), Some(Type::Integer)) => {}
|
||||||
{
|
(Some(Type::Float), Some(Type::Float)) => {}
|
||||||
} else {
|
(Some(Type::String), Some(Type::String)) => {}
|
||||||
return Err(AnalyzerError::ExpectedIntegerOrFloat {
|
(Some(Type::Integer), _) | (Some(Type::Float), _) | (Some(Type::String), _) => {
|
||||||
actual: right.as_ref().clone(),
|
return Err(AnalyzerError::ExpectedIntegerFloatOrString {
|
||||||
});
|
actual: right.as_ref().clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(AnalyzerError::ExpectedIntegerFloatOrString {
|
||||||
|
actual: left.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 {
|
||||||
|
@ -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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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";
|
||||||
|
@ -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,7 +258,19 @@ impl<'src> Parser<'src> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Token::IsEven, left_span) => {
|
(
|
||||||
|
Token::IsEven | Token::IsOdd | Token::Length | Token::ReadLine | Token::WriteLine,
|
||||||
|
left_span,
|
||||||
|
) => {
|
||||||
|
let function = match self.current.0 {
|
||||||
|
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 {
|
||||||
@ -265,80 +282,38 @@ impl<'src> Parser<'src> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (Token::RightParenthesis, _) = self.current {
|
let mut value_arguments: Option<Vec<Node>> = None;
|
||||||
self.next_token()?;
|
|
||||||
} else {
|
loop {
|
||||||
return Err(ParseError::ExpectedClosingParenthesis {
|
if let (Token::RightParenthesis, _) = self.current {
|
||||||
actual: self.current.0.clone(),
|
self.next_token()?;
|
||||||
span: self.current.1,
|
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 {
|
||||||
|
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::IsEven,
|
function,
|
||||||
type_arguments: None,
|
type_arguments: None,
|
||||||
value_arguments: None,
|
value_arguments,
|
||||||
},
|
|
||||||
left_span,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
(Token::IsOdd, 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(
|
|
||||||
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(
|
|
||||||
Statement::BuiltInFunctionCall {
|
|
||||||
function: BuiltInFunction::Length,
|
|
||||||
type_arguments: None,
|
|
||||||
value_arguments: None,
|
|
||||||
},
|
},
|
||||||
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";
|
||||||
|
@ -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, "="),
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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 + "!")
|
|
||||||
|
Loading…
Reference in New Issue
Block a user