Implement subtraction and multiplication
This commit is contained in:
parent
57782d3ed6
commit
60bd8f5352
@ -53,6 +53,7 @@ pub enum Statement {
|
||||
value_arguments: Option<Vec<Node>>,
|
||||
},
|
||||
PropertyAccess(Box<Node>, Box<Node>),
|
||||
Subtract(Box<Node>, Box<Node>),
|
||||
List(Vec<Node>),
|
||||
Multiply(Box<Node>, Box<Node>),
|
||||
|
||||
@ -75,6 +76,7 @@ impl Statement {
|
||||
Statement::List(_) => None,
|
||||
Statement::Multiply(left, _) => left.statement.expected_type(variables),
|
||||
Statement::PropertyAccess(_, _) => None,
|
||||
Statement::Subtract(left, _) => left.statement.expected_type(variables),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -154,7 +156,6 @@ impl Display for Statement {
|
||||
|
||||
write!(f, ")")
|
||||
}
|
||||
Statement::PropertyAccess(left, right) => write!(f, "{left}.{right}"),
|
||||
Statement::List(nodes) => {
|
||||
write!(f, "[")?;
|
||||
for (i, node) in nodes.iter().enumerate() {
|
||||
@ -165,9 +166,11 @@ impl Display for Statement {
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
Statement::PropertyAccess(left, right) => write!(f, "{left}.{right}"),
|
||||
Statement::Multiply(left, right) => write!(f, "{left} * {right}"),
|
||||
Statement::Constant(value) => write!(f, "{value}"),
|
||||
Statement::Identifier(identifier) => write!(f, "{identifier}"),
|
||||
Statement::Subtract(left, right) => write!(f, "{left} - {right}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,9 @@ impl<'a> Analyzer<'a> {
|
||||
fn analyze_node(&self, node: &Node) -> Result<(), AnalyzerError> {
|
||||
match &node.statement {
|
||||
Statement::Add(left, right) => {
|
||||
self.analyze_node(left)?;
|
||||
self.analyze_node(right)?;
|
||||
|
||||
let left_type = left.statement.expected_type(self.variables);
|
||||
let right_type = right.statement.expected_type(self.variables);
|
||||
|
||||
@ -95,9 +98,6 @@ impl<'a> Analyzer<'a> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
self.analyze_node(left)?;
|
||||
self.analyze_node(right)?;
|
||||
}
|
||||
Statement::Assign(left, right) => {
|
||||
if let Statement::Identifier(_) = &left.statement {
|
||||
@ -135,6 +135,9 @@ impl<'a> Analyzer<'a> {
|
||||
}
|
||||
}
|
||||
Statement::Multiply(left, right) => {
|
||||
self.analyze_node(left)?;
|
||||
self.analyze_node(right)?;
|
||||
|
||||
if let Some(Type::Integer) | Some(Type::Float) =
|
||||
left.statement.expected_type(self.variables)
|
||||
{
|
||||
@ -154,9 +157,6 @@ impl<'a> Analyzer<'a> {
|
||||
position: right.position,
|
||||
});
|
||||
}
|
||||
|
||||
self.analyze_node(left)?;
|
||||
self.analyze_node(right)?;
|
||||
}
|
||||
Statement::PropertyAccess(left, right) => {
|
||||
if let Statement::Identifier(_) | Statement::Constant(_) | Statement::List(_) =
|
||||
@ -184,6 +184,30 @@ impl<'a> Analyzer<'a> {
|
||||
|
||||
self.analyze_node(right)?;
|
||||
}
|
||||
Statement::Subtract(left, right) => {
|
||||
self.analyze_node(left)?;
|
||||
self.analyze_node(right)?;
|
||||
|
||||
let left_type = left.statement.expected_type(self.variables);
|
||||
let right_type = right.statement.expected_type(self.variables);
|
||||
|
||||
match (left_type, right_type) {
|
||||
(Some(Type::Integer), Some(Type::Integer)) => {}
|
||||
(Some(Type::Float), Some(Type::Float)) => {}
|
||||
(Some(Type::Integer), _) | (Some(Type::Float), _) => {
|
||||
return Err(AnalyzerError::ExpectedIntegerOrFloat {
|
||||
actual: right.as_ref().clone(),
|
||||
position: right.position,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
return Err(AnalyzerError::ExpectedIntegerOrFloat {
|
||||
actual: left.as_ref().clone(),
|
||||
position: left.position,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -103,6 +103,14 @@ impl Lexer {
|
||||
let (token, span) = if let Some(c) = self.peek_char(source) {
|
||||
match c {
|
||||
'0'..='9' => self.lex_number(source)?,
|
||||
'-' => {
|
||||
if let Some('0'..='9') = self.peek_second_char(source) {
|
||||
self.lex_number(source)?
|
||||
} else {
|
||||
self.position += 1;
|
||||
(Token::Minus, (self.position - 1, self.position))
|
||||
}
|
||||
}
|
||||
'a'..='z' | 'A'..='Z' => self.lex_alphabetical(source)?,
|
||||
'"' => self.lex_string('"', source)?,
|
||||
'\'' => self.lex_string('\'', source)?,
|
||||
@ -185,6 +193,10 @@ impl Lexer {
|
||||
let start_pos = self.position;
|
||||
let mut is_float = false;
|
||||
|
||||
if let Some('-') = self.peek_char(source) {
|
||||
self.next_char(source);
|
||||
}
|
||||
|
||||
while let Some(c) = self.peek_char(source) {
|
||||
if c == '.' {
|
||||
if let Some('0'..='9') = self.peek_second_char(source) {
|
||||
@ -324,8 +336,60 @@ impl From<ParseIntError> for LexError {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn max_integer() {
|
||||
let input = "9223372036854775807";
|
||||
|
||||
assert_eq!(
|
||||
lex(input),
|
||||
Ok(vec![
|
||||
(Token::Integer(i64::MAX), (0, 19)),
|
||||
(Token::Eof, (19, 19)),
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn min_integer() {
|
||||
let input = "-9223372036854775808";
|
||||
|
||||
assert_eq!(
|
||||
lex(input),
|
||||
Ok(vec![
|
||||
(Token::Integer(i64::MIN), (0, 20)),
|
||||
(Token::Eof, (20, 20)),
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subtract_negative_integers() {
|
||||
let input = "-42 - -42";
|
||||
|
||||
assert_eq!(
|
||||
lex(input),
|
||||
Ok(vec![
|
||||
(Token::Integer(-42), (0, 3)),
|
||||
(Token::Minus, (4, 5)),
|
||||
(Token::Integer(-42), (6, 9)),
|
||||
(Token::Eof, (9, 9)),
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn negative_integer() {
|
||||
let input = "-42";
|
||||
|
||||
assert_eq!(
|
||||
lex(input),
|
||||
Ok(vec![(Token::Integer(-42), (0, 3)), (Token::Eof, (3, 3))])
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_line() {
|
||||
let input = "read_line()";
|
||||
|
@ -1,10 +1,6 @@
|
||||
//! The Dust programming language.
|
||||
//!
|
||||
//! Dust is a statically typed, interpreted programming language.
|
||||
//!
|
||||
//! The [interpreter] module contains the `Interpreter` struct, which is used to lex, parse and/or
|
||||
//! interpret Dust code. The `interpret` function is a convenience function that creates a new
|
||||
//! `Interpreter` and runs the given source code.
|
||||
pub mod abstract_tree;
|
||||
pub mod analyzer;
|
||||
pub mod built_in_function;
|
||||
|
@ -183,6 +183,17 @@ impl<'src> Parser<'src> {
|
||||
(left_start, right_end),
|
||||
));
|
||||
}
|
||||
(Token::Minus, _) => {
|
||||
self.next_token()?;
|
||||
|
||||
let right_node = self.parse_node(self.current_precedence())?;
|
||||
let right_end = right_node.position.1;
|
||||
|
||||
return Ok(Node::new(
|
||||
Statement::Subtract(Box::new(left_node), Box::new(right_node)),
|
||||
(left_start, right_end),
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -338,8 +349,9 @@ impl<'src> Parser<'src> {
|
||||
match self.current.0 {
|
||||
Token::Dot => 4,
|
||||
Token::Equal => 3,
|
||||
Token::Plus => 1,
|
||||
Token::Star => 2,
|
||||
Token::Plus => 1,
|
||||
Token::Minus => 1,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
@ -394,6 +406,25 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn subtract_negative_integers() {
|
||||
let input = "-1 - -2";
|
||||
|
||||
assert_eq!(
|
||||
parse(input),
|
||||
Ok(AbstractSyntaxTree {
|
||||
nodes: [Node::new(
|
||||
Statement::Subtract(
|
||||
Box::new(Node::new(Statement::Constant(Value::integer(-1)), (0, 2))),
|
||||
Box::new(Node::new(Statement::Constant(Value::integer(-2)), (5, 7)))
|
||||
),
|
||||
(0, 7)
|
||||
)]
|
||||
.into()
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_concatenation() {
|
||||
let input = "\"Hello, \" + \"World!\"";
|
||||
|
@ -29,6 +29,7 @@ pub enum Token<'src> {
|
||||
Equal,
|
||||
LeftParenthesis,
|
||||
LeftSquareBrace,
|
||||
Minus,
|
||||
Plus,
|
||||
RightParenthesis,
|
||||
RightSquareBrace,
|
||||
@ -58,6 +59,7 @@ impl<'src> Token<'src> {
|
||||
Token::RightParenthesis => TokenOwned::RightParenthesis,
|
||||
Token::LeftSquareBrace => TokenOwned::LeftSquareBrace,
|
||||
Token::RightSquareBrace => TokenOwned::RightSquareBrace,
|
||||
Token::Minus => TokenOwned::Minus,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -85,6 +87,7 @@ impl<'src> Display for Token<'src> {
|
||||
Token::RightParenthesis => write!(f, ")"),
|
||||
Token::LeftSquareBrace => write!(f, "["),
|
||||
Token::RightSquareBrace => write!(f, "]"),
|
||||
Token::Minus => write!(f, "-"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -117,6 +120,7 @@ pub enum TokenOwned {
|
||||
Equal,
|
||||
LeftParenthesis,
|
||||
LeftSquareBrace,
|
||||
Minus,
|
||||
Plus,
|
||||
RightParenthesis,
|
||||
RightSquareBrace,
|
||||
@ -146,6 +150,7 @@ impl Display for TokenOwned {
|
||||
TokenOwned::RightParenthesis => write!(f, ")"),
|
||||
TokenOwned::LeftSquareBrace => write!(f, "["),
|
||||
TokenOwned::RightSquareBrace => write!(f, "]"),
|
||||
TokenOwned::Minus => write!(f, "-"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -129,11 +129,31 @@ impl Value {
|
||||
match (self.inner().as_ref(), other.inner().as_ref()) {
|
||||
(ValueInner::Float(left), ValueInner::Float(right)) => Ok(Value::float(left + right)),
|
||||
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
|
||||
Ok(Value::integer(left + right))
|
||||
Ok(Value::integer(left.saturating_add(*right)))
|
||||
}
|
||||
_ => Err(ValueError::CannotAdd(self.clone(), other.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subtract(&self, other: &Value) -> Result<Value, ValueError> {
|
||||
match (self.inner().as_ref(), other.inner().as_ref()) {
|
||||
(ValueInner::Float(left), ValueInner::Float(right)) => Ok(Value::float(left - right)),
|
||||
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
|
||||
Ok(Value::integer(left.saturating_sub(*right)))
|
||||
}
|
||||
_ => Err(ValueError::CannotSubtract(self.clone(), other.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn multiply(&self, other: &Value) -> Result<Value, ValueError> {
|
||||
match (self.inner().as_ref(), other.inner().as_ref()) {
|
||||
(ValueInner::Float(left), ValueInner::Float(right)) => Ok(Value::float(left * right)),
|
||||
(ValueInner::Integer(left), ValueInner::Integer(right)) => {
|
||||
Ok(Value::integer(left * right))
|
||||
}
|
||||
_ => Err(ValueError::CannotMultiply(self.clone(), other.clone())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Value {
|
||||
@ -653,6 +673,8 @@ impl Display for Function {
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum ValueError {
|
||||
CannotAdd(Value, Value),
|
||||
CannotMultiply(Value, Value),
|
||||
CannotSubtract(Value, Value),
|
||||
PropertyNotFound { value: Value, property: Identifier },
|
||||
IndexOutOfBounds { value: Value, index: i64 },
|
||||
ExpectedList(Value),
|
||||
@ -664,6 +686,12 @@ impl Display for ValueError {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
ValueError::CannotAdd(left, right) => write!(f, "Cannot add {} and {}", left, right),
|
||||
ValueError::CannotMultiply(left, right) => {
|
||||
write!(f, "Cannot multiply {} and {}", left, right)
|
||||
}
|
||||
ValueError::CannotSubtract(left, right) => {
|
||||
write!(f, "Cannot subtract {} and {}", left, right)
|
||||
}
|
||||
ValueError::PropertyNotFound { value, property } => {
|
||||
write!(f, "{} does not have a property named {}", value, property)
|
||||
}
|
||||
|
@ -182,7 +182,27 @@ impl Vm {
|
||||
|
||||
Ok(Some(Value::list(values)))
|
||||
}
|
||||
Statement::Multiply(_, _) => todo!(),
|
||||
Statement::Multiply(left, right) => {
|
||||
let left_span = left.position;
|
||||
let left = if let Some(value) = self.run_node(*left, variables)? {
|
||||
value
|
||||
} else {
|
||||
return Err(VmError::ExpectedValue {
|
||||
position: left_span,
|
||||
});
|
||||
};
|
||||
let right_span = right.position;
|
||||
let right = if let Some(value) = self.run_node(*right, variables)? {
|
||||
value
|
||||
} else {
|
||||
return Err(VmError::ExpectedValue {
|
||||
position: right_span,
|
||||
});
|
||||
};
|
||||
let product = left.multiply(&right)?;
|
||||
|
||||
Ok(Some(product))
|
||||
}
|
||||
Statement::PropertyAccess(left, right) => {
|
||||
let left_span = left.position;
|
||||
let left_value = if let Some(value) = self.run_node(*left, variables)? {
|
||||
@ -239,6 +259,27 @@ impl Vm {
|
||||
position: right_span,
|
||||
})
|
||||
}
|
||||
Statement::Subtract(left, right) => {
|
||||
let left_span = left.position;
|
||||
let left = if let Some(value) = self.run_node(*left, variables)? {
|
||||
value
|
||||
} else {
|
||||
return Err(VmError::ExpectedValue {
|
||||
position: left_span,
|
||||
});
|
||||
};
|
||||
let right_span = right.position;
|
||||
let right = if let Some(value) = self.run_node(*right, variables)? {
|
||||
value
|
||||
} else {
|
||||
return Err(VmError::ExpectedValue {
|
||||
position: right_span,
|
||||
});
|
||||
};
|
||||
let difference = left.subtract(&right)?;
|
||||
|
||||
Ok(Some(difference))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -341,6 +382,33 @@ impl Display for VmError {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn integer_saturating_add() {
|
||||
let input = "9223372036854775807 + 1";
|
||||
|
||||
assert_eq!(
|
||||
run(input, &mut HashMap::new()),
|
||||
Ok(Some(Value::integer(i64::MAX)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn integer_saturating_sub() {
|
||||
let input = "-9223372036854775808 - 1";
|
||||
|
||||
assert_eq!(
|
||||
run(input, &mut HashMap::new()),
|
||||
Ok(Some(Value::integer(i64::MIN)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiply() {
|
||||
let input = "2 * 3";
|
||||
|
||||
assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(6))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boolean() {
|
||||
let input = "true";
|
||||
|
Loading…
Reference in New Issue
Block a user