Implement subtraction and multiplication

This commit is contained in:
Jeff 2024-08-08 23:28:47 -04:00
parent 57782d3ed6
commit 60bd8f5352
8 changed files with 233 additions and 14 deletions

View File

@ -53,6 +53,7 @@ pub enum Statement {
value_arguments: Option<Vec<Node>>, value_arguments: Option<Vec<Node>>,
}, },
PropertyAccess(Box<Node>, Box<Node>), PropertyAccess(Box<Node>, Box<Node>),
Subtract(Box<Node>, Box<Node>),
List(Vec<Node>), List(Vec<Node>),
Multiply(Box<Node>, Box<Node>), Multiply(Box<Node>, Box<Node>),
@ -75,6 +76,7 @@ impl Statement {
Statement::List(_) => None, Statement::List(_) => None,
Statement::Multiply(left, _) => left.statement.expected_type(variables), Statement::Multiply(left, _) => left.statement.expected_type(variables),
Statement::PropertyAccess(_, _) => None, Statement::PropertyAccess(_, _) => None,
Statement::Subtract(left, _) => left.statement.expected_type(variables),
} }
} }
} }
@ -154,7 +156,6 @@ impl Display for Statement {
write!(f, ")") write!(f, ")")
} }
Statement::PropertyAccess(left, right) => write!(f, "{left}.{right}"),
Statement::List(nodes) => { Statement::List(nodes) => {
write!(f, "[")?; write!(f, "[")?;
for (i, node) in nodes.iter().enumerate() { for (i, node) in nodes.iter().enumerate() {
@ -165,9 +166,11 @@ impl Display for Statement {
} }
write!(f, "]") write!(f, "]")
} }
Statement::PropertyAccess(left, right) => write!(f, "{left}.{right}"),
Statement::Multiply(left, right) => write!(f, "{left} * {right}"), Statement::Multiply(left, right) => write!(f, "{left} * {right}"),
Statement::Constant(value) => write!(f, "{value}"), Statement::Constant(value) => write!(f, "{value}"),
Statement::Identifier(identifier) => write!(f, "{identifier}"), Statement::Identifier(identifier) => write!(f, "{identifier}"),
Statement::Subtract(left, right) => write!(f, "{left} - {right}"),
} }
} }
} }

View File

@ -75,6 +75,9 @@ 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) => {
self.analyze_node(left)?;
self.analyze_node(right)?;
let left_type = left.statement.expected_type(self.variables); let left_type = left.statement.expected_type(self.variables);
let right_type = right.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) => { Statement::Assign(left, right) => {
if let Statement::Identifier(_) = &left.statement { if let Statement::Identifier(_) = &left.statement {
@ -135,6 +135,9 @@ impl<'a> Analyzer<'a> {
} }
} }
Statement::Multiply(left, right) => { Statement::Multiply(left, right) => {
self.analyze_node(left)?;
self.analyze_node(right)?;
if let Some(Type::Integer) | Some(Type::Float) = if let Some(Type::Integer) | Some(Type::Float) =
left.statement.expected_type(self.variables) left.statement.expected_type(self.variables)
{ {
@ -154,9 +157,6 @@ impl<'a> Analyzer<'a> {
position: right.position, position: right.position,
}); });
} }
self.analyze_node(left)?;
self.analyze_node(right)?;
} }
Statement::PropertyAccess(left, right) => { Statement::PropertyAccess(left, right) => {
if let Statement::Identifier(_) | Statement::Constant(_) | Statement::List(_) = if let Statement::Identifier(_) | Statement::Constant(_) | Statement::List(_) =
@ -184,6 +184,30 @@ impl<'a> Analyzer<'a> {
self.analyze_node(right)?; 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(()) Ok(())

View File

@ -103,6 +103,14 @@ impl Lexer {
let (token, span) = if let Some(c) = self.peek_char(source) { let (token, span) = if let Some(c) = self.peek_char(source) {
match c { match c {
'0'..='9' => self.lex_number(source)?, '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)?, 'a'..='z' | 'A'..='Z' => self.lex_alphabetical(source)?,
'"' => self.lex_string('"', source)?, '"' => self.lex_string('"', source)?,
'\'' => self.lex_string('\'', source)?, '\'' => self.lex_string('\'', source)?,
@ -185,6 +193,10 @@ impl Lexer {
let start_pos = self.position; let start_pos = self.position;
let mut is_float = false; let mut is_float = false;
if let Some('-') = self.peek_char(source) {
self.next_char(source);
}
while let Some(c) = self.peek_char(source) { while let Some(c) = self.peek_char(source) {
if c == '.' { if c == '.' {
if let Some('0'..='9') = self.peek_second_char(source) { if let Some('0'..='9') = self.peek_second_char(source) {
@ -324,8 +336,60 @@ impl From<ParseIntError> for LexError {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; 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] #[test]
fn read_line() { fn read_line() {
let input = "read_line()"; let input = "read_line()";

View File

@ -1,10 +1,6 @@
//! The Dust programming language. //! The Dust programming language.
//! //!
//! Dust is a statically typed, interpreted 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 abstract_tree;
pub mod analyzer; pub mod analyzer;
pub mod built_in_function; pub mod built_in_function;

View File

@ -183,6 +183,17 @@ impl<'src> Parser<'src> {
(left_start, right_end), (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 { match self.current.0 {
Token::Dot => 4, Token::Dot => 4,
Token::Equal => 3, Token::Equal => 3,
Token::Plus => 1,
Token::Star => 2, Token::Star => 2,
Token::Plus => 1,
Token::Minus => 1,
_ => 0, _ => 0,
} }
} }
@ -394,6 +406,25 @@ mod tests {
use super::*; 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] #[test]
fn string_concatenation() { fn string_concatenation() {
let input = "\"Hello, \" + \"World!\""; let input = "\"Hello, \" + \"World!\"";

View File

@ -29,6 +29,7 @@ pub enum Token<'src> {
Equal, Equal,
LeftParenthesis, LeftParenthesis,
LeftSquareBrace, LeftSquareBrace,
Minus,
Plus, Plus,
RightParenthesis, RightParenthesis,
RightSquareBrace, RightSquareBrace,
@ -58,6 +59,7 @@ impl<'src> Token<'src> {
Token::RightParenthesis => TokenOwned::RightParenthesis, Token::RightParenthesis => TokenOwned::RightParenthesis,
Token::LeftSquareBrace => TokenOwned::LeftSquareBrace, Token::LeftSquareBrace => TokenOwned::LeftSquareBrace,
Token::RightSquareBrace => TokenOwned::RightSquareBrace, Token::RightSquareBrace => TokenOwned::RightSquareBrace,
Token::Minus => TokenOwned::Minus,
} }
} }
} }
@ -85,6 +87,7 @@ impl<'src> Display for Token<'src> {
Token::RightParenthesis => write!(f, ")"), Token::RightParenthesis => write!(f, ")"),
Token::LeftSquareBrace => write!(f, "["), Token::LeftSquareBrace => write!(f, "["),
Token::RightSquareBrace => write!(f, "]"), Token::RightSquareBrace => write!(f, "]"),
Token::Minus => write!(f, "-"),
} }
} }
} }
@ -117,6 +120,7 @@ pub enum TokenOwned {
Equal, Equal,
LeftParenthesis, LeftParenthesis,
LeftSquareBrace, LeftSquareBrace,
Minus,
Plus, Plus,
RightParenthesis, RightParenthesis,
RightSquareBrace, RightSquareBrace,
@ -146,6 +150,7 @@ impl Display for TokenOwned {
TokenOwned::RightParenthesis => write!(f, ")"), TokenOwned::RightParenthesis => write!(f, ")"),
TokenOwned::LeftSquareBrace => write!(f, "["), TokenOwned::LeftSquareBrace => write!(f, "["),
TokenOwned::RightSquareBrace => write!(f, "]"), TokenOwned::RightSquareBrace => write!(f, "]"),
TokenOwned::Minus => write!(f, "-"),
} }
} }
} }

View File

@ -129,11 +129,31 @@ impl Value {
match (self.inner().as_ref(), other.inner().as_ref()) { match (self.inner().as_ref(), other.inner().as_ref()) {
(ValueInner::Float(left), ValueInner::Float(right)) => Ok(Value::float(left + right)), (ValueInner::Float(left), ValueInner::Float(right)) => Ok(Value::float(left + right)),
(ValueInner::Integer(left), ValueInner::Integer(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())), _ => 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 { impl Display for Value {
@ -653,6 +673,8 @@ impl Display for Function {
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum ValueError { pub enum ValueError {
CannotAdd(Value, Value), CannotAdd(Value, Value),
CannotMultiply(Value, Value),
CannotSubtract(Value, Value),
PropertyNotFound { value: Value, property: Identifier }, PropertyNotFound { value: Value, property: Identifier },
IndexOutOfBounds { value: Value, index: i64 }, IndexOutOfBounds { value: Value, index: i64 },
ExpectedList(Value), ExpectedList(Value),
@ -664,6 +686,12 @@ impl Display for ValueError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
ValueError::CannotAdd(left, right) => write!(f, "Cannot add {} and {}", left, right), 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 } => { ValueError::PropertyNotFound { value, property } => {
write!(f, "{} does not have a property named {}", value, property) write!(f, "{} does not have a property named {}", value, property)
} }

View File

@ -182,7 +182,27 @@ impl Vm {
Ok(Some(Value::list(values))) 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) => { Statement::PropertyAccess(left, right) => {
let left_span = left.position; let left_span = left.position;
let left_value = if let Some(value) = self.run_node(*left, variables)? { let left_value = if let Some(value) = self.run_node(*left, variables)? {
@ -239,6 +259,27 @@ impl Vm {
position: right_span, 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 { mod tests {
use super::*; 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] #[test]
fn boolean() { fn boolean() {
let input = "true"; let input = "true";