Add modulo support
This commit is contained in:
parent
2cf580d111
commit
24a2642f17
@ -36,7 +36,7 @@ count_slowly = fn (
|
||||
|
||||
async {
|
||||
count_slowly(200) # Finishes last
|
||||
count_slowly(100) # Finishes seconds
|
||||
count_slowly(100) # Finishes second
|
||||
count_slowly(50) # Finishes first
|
||||
}
|
||||
```
|
||||
|
@ -233,6 +233,7 @@ pub enum BinaryOperator {
|
||||
// Math
|
||||
Add,
|
||||
Divide,
|
||||
Modulo,
|
||||
Multiply,
|
||||
Subtract,
|
||||
|
||||
@ -257,6 +258,7 @@ impl Display for BinaryOperator {
|
||||
BinaryOperator::GreaterOrEqual => write!(f, ">="),
|
||||
BinaryOperator::Less => write!(f, "<"),
|
||||
BinaryOperator::LessOrEqual => write!(f, "<="),
|
||||
BinaryOperator::Modulo => write!(f, "%"),
|
||||
BinaryOperator::Multiply => write!(f, "*"),
|
||||
BinaryOperator::Or => write!(f, "||"),
|
||||
BinaryOperator::Subtract => write!(f, "-"),
|
||||
|
@ -119,7 +119,7 @@ impl Lexer {
|
||||
(Token::Minus, (self.position - 1, self.position))
|
||||
}
|
||||
}
|
||||
'a'..='z' | 'A'..='Z' => self.lex_alphabetical(source)?,
|
||||
'a'..='z' | 'A'..='Z' => self.lex_alphanumeric(source)?,
|
||||
'"' => self.lex_string('"', source)?,
|
||||
'\'' => self.lex_string('\'', source)?,
|
||||
'+' => {
|
||||
@ -204,6 +204,11 @@ impl Lexer {
|
||||
|
||||
(Token::Slash, (self.position - 1, self.position))
|
||||
}
|
||||
'%' => {
|
||||
self.position += 1;
|
||||
|
||||
(Token::Percent, (self.position - 1, self.position))
|
||||
}
|
||||
_ => {
|
||||
self.position += 1;
|
||||
|
||||
@ -320,14 +325,14 @@ impl Lexer {
|
||||
}
|
||||
|
||||
/// Lex an identifier token.
|
||||
fn lex_alphabetical<'src>(
|
||||
fn lex_alphanumeric<'src>(
|
||||
&mut self,
|
||||
source: &'src str,
|
||||
) -> Result<(Token<'src>, Span), LexError> {
|
||||
let start_pos = self.position;
|
||||
|
||||
while let Some(c) = self.peek_char(source) {
|
||||
if c.is_ascii_alphabetic() || c == '_' {
|
||||
if c.is_ascii_alphanumeric() || c == '_' {
|
||||
self.next_char(source);
|
||||
} else {
|
||||
break;
|
||||
@ -430,6 +435,21 @@ impl From<ParseIntError> for LexError {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn modulo() {
|
||||
let input = "42 % 2";
|
||||
|
||||
assert_eq!(
|
||||
lex(input),
|
||||
Ok(vec![
|
||||
(Token::Integer(42), (0, 2)),
|
||||
(Token::Percent, (3, 4)),
|
||||
(Token::Integer(2), (5, 6)),
|
||||
(Token::Eof, (6, 6)),
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn divide() {
|
||||
let input = "42 / 2";
|
||||
|
@ -302,6 +302,23 @@ impl<'src> Parser<'src> {
|
||||
(left_start, right_end),
|
||||
));
|
||||
}
|
||||
(Token::Percent, _) => {
|
||||
let operator = Node::new(BinaryOperator::Modulo, self.current.1);
|
||||
|
||||
self.next_token()?;
|
||||
|
||||
let right_node = self.parse_node(self.current_precedence())?;
|
||||
let right_end = right_node.position.1;
|
||||
|
||||
return Ok(Node::new(
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(left_node),
|
||||
operator,
|
||||
right: Box::new(right_node),
|
||||
},
|
||||
(left_start, right_end),
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -524,6 +541,7 @@ impl<'src> Parser<'src> {
|
||||
match self.current.0 {
|
||||
Token::Greater | Token::GreaterEqual | Token::Less | Token::LessEqual => 5,
|
||||
Token::Dot => 4,
|
||||
Token::Percent => 3,
|
||||
Token::Star => 2,
|
||||
Token::Slash => 2,
|
||||
Token::Plus => 1,
|
||||
@ -595,6 +613,26 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn modulo() {
|
||||
let input = "42 % 2";
|
||||
|
||||
assert_eq!(
|
||||
parse(input),
|
||||
Ok(AbstractSyntaxTree {
|
||||
nodes: [Node::new(
|
||||
Statement::BinaryOperation {
|
||||
left: Box::new(Node::new(Statement::Constant(Value::integer(42)), (0, 2))),
|
||||
operator: Node::new(BinaryOperator::Modulo, (3, 4)),
|
||||
right: Box::new(Node::new(Statement::Constant(Value::integer(2)), (5, 6)))
|
||||
},
|
||||
(0, 6)
|
||||
)]
|
||||
.into()
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn divide() {
|
||||
let input = "42 / 2";
|
||||
|
@ -37,6 +37,7 @@ pub enum Token<'src> {
|
||||
Less,
|
||||
LessEqual,
|
||||
Minus,
|
||||
Percent,
|
||||
Plus,
|
||||
RightCurlyBrace,
|
||||
RightParenthesis,
|
||||
@ -69,6 +70,7 @@ impl<'src> Token<'src> {
|
||||
Token::Less => TokenOwned::Less,
|
||||
Token::LessEqual => TokenOwned::LessOrEqual,
|
||||
Token::Minus => TokenOwned::Minus,
|
||||
Token::Percent => TokenOwned::Percent,
|
||||
Token::Plus => TokenOwned::Plus,
|
||||
Token::ReadLine => TokenOwned::ReadLine,
|
||||
Token::RightCurlyBrace => TokenOwned::RightCurlyBrace,
|
||||
@ -104,6 +106,7 @@ impl<'src> Token<'src> {
|
||||
Token::Less => "<",
|
||||
Token::LessEqual => "<=",
|
||||
Token::Minus => "-",
|
||||
Token::Percent => "%",
|
||||
Token::Plus => "+",
|
||||
Token::ReadLine => "read_line",
|
||||
Token::RightCurlyBrace => "}",
|
||||
@ -148,6 +151,7 @@ impl<'src> PartialEq for Token<'src> {
|
||||
(Token::Less, Token::Less) => true,
|
||||
(Token::LessEqual, Token::LessEqual) => true,
|
||||
(Token::Minus, Token::Minus) => true,
|
||||
(Token::Percent, Token::Percent) => true,
|
||||
(Token::Plus, Token::Plus) => true,
|
||||
(Token::ReadLine, Token::ReadLine) => true,
|
||||
(Token::RightCurlyBrace, Token::RightCurlyBrace) => true,
|
||||
@ -198,6 +202,7 @@ pub enum TokenOwned {
|
||||
Less,
|
||||
LessOrEqual,
|
||||
Minus,
|
||||
Percent,
|
||||
Plus,
|
||||
RightCurlyBrace,
|
||||
RightParenthesis,
|
||||
@ -230,6 +235,7 @@ impl Display for TokenOwned {
|
||||
TokenOwned::Less => Token::Less.fmt(f),
|
||||
TokenOwned::LessOrEqual => Token::LessEqual.fmt(f),
|
||||
TokenOwned::Minus => Token::Minus.fmt(f),
|
||||
TokenOwned::Percent => Token::Percent.fmt(f),
|
||||
TokenOwned::Plus => Token::Plus.fmt(f),
|
||||
TokenOwned::ReadLine => Token::ReadLine.fmt(f),
|
||||
TokenOwned::RightCurlyBrace => Token::RightCurlyBrace.fmt(f),
|
||||
|
@ -178,6 +178,20 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn modulo(&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)) => {
|
||||
if right == &0 {
|
||||
Err(ValueError::DivisionByZero)
|
||||
} else {
|
||||
Ok(Value::integer(left % right))
|
||||
}
|
||||
}
|
||||
_ => Err(ValueError::CannotModulo(self.clone(), other.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn less_than(&self, other: &Value) -> Result<Value, ValueError> {
|
||||
match (self.inner().as_ref(), other.inner().as_ref()) {
|
||||
(ValueInner::Float(left), ValueInner::Float(right)) => Ok(Value::boolean(left < right)),
|
||||
@ -778,6 +792,7 @@ pub enum ValueError {
|
||||
CannotGreaterThanOrEqual(Value, Value),
|
||||
CannotLessThan(Value, Value),
|
||||
CannotLessThanOrEqual(Value, Value),
|
||||
CannotModulo(Value, Value),
|
||||
CannotMultiply(Value, Value),
|
||||
CannotSubtract(Value, Value),
|
||||
CannotOr(Value, Value),
|
||||
@ -800,6 +815,9 @@ impl Display for ValueError {
|
||||
ValueError::CannotDivide(left, right) => {
|
||||
write!(f, "Cannot divide {} by {}", left, right)
|
||||
}
|
||||
ValueError::CannotModulo(left, right) => {
|
||||
write!(f, "Cannot modulo {} by {}", left, right)
|
||||
}
|
||||
ValueError::CannotMultiply(left, right) => {
|
||||
write!(f, "Cannot multiply {} and {}", left, right)
|
||||
}
|
||||
|
@ -102,6 +102,7 @@ impl Vm {
|
||||
}
|
||||
BinaryOperator::Less => left_value.less_than(&right_value),
|
||||
BinaryOperator::LessOrEqual => left_value.less_than_or_equal(&right_value),
|
||||
BinaryOperator::Modulo => left_value.modulo(&right_value),
|
||||
BinaryOperator::Multiply => left_value.multiply(&right_value),
|
||||
BinaryOperator::Or => left_value.or(&right_value),
|
||||
BinaryOperator::Subtract => left_value.subtract(&right_value),
|
||||
@ -432,6 +433,13 @@ impl Display for VmError {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn modulo() {
|
||||
let input = "42 % 2";
|
||||
|
||||
assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(0))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn divide() {
|
||||
let input = "42 / 2";
|
||||
|
@ -1 +0,0 @@
|
||||
nightly
|
@ -1,3 +0,0 @@
|
||||
length = fn (list: [any]) -> int {
|
||||
__LENGTH__(list)
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
fs = {
|
||||
read_file = fn (path: str) -> str {
|
||||
__READ_FILE__(path)
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
io = {
|
||||
read_line = fn () -> str {
|
||||
__READ_LINE__()
|
||||
}
|
||||
|
||||
write_line = fn (output: any) {
|
||||
__WRITE_LINE__(output)
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
json = {
|
||||
parse = fn <T> (input: str) -> T {
|
||||
__JSON_PARSE__::<T>(input)
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
thread = {
|
||||
sleep = fn (milliseconds: int) {
|
||||
__SLEEP__(milliseconds)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user