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>>,
},
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}"),
}
}
}

View File

@ -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(())

View File

@ -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()";

View File

@ -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;

View File

@ -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!\"";

View File

@ -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, "-"),
}
}
}

View File

@ -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)
}

View File

@ -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";